package com.kevingoldsmith.synth { import __AS3__.vec.Vector; import flash.display.Shader; import flash.display.ShaderJob; import flash.utils.ByteArray; public class FourVoiceSynth { [Embed(source="../pbj/mixer2channel.pbj", mimeType="application/octet-stream")] private var Mixer2:Class; [Embed(source="../pbj/mixer3channel.pbj", mimeType="application/octet-stream")] private var Mixer3:Class; [Embed(source="../pbj/mixer4channel.pbj", mimeType="application/octet-stream")] private var Mixer4:Class; private var m_voices:Vector. = new Vector.(); private var m_volumeSquare:Vector. = new Vector.(2,true); private var m_detuneSquare:Vector. = new Vector.(2,true); private var m_volumeSine:Vector. = new Vector.(2,true); private var m_detuneSine:Vector. = new Vector.(2,true); public function FourVoiceSynth() { m_volumeSine[0] = 0.5; m_volumeSine[1] = 0.5; m_volumeSquare[0] = 0.5; m_volumeSquare[1] = 0.5; m_detuneSine[0] = 0.0; m_detuneSine[1] = 0.0; m_detuneSquare[0] = 0.0; m_detuneSquare[1] = 0.0; } public function get activeVoices():uint { return m_voices.length; } public function SetVolumes( volumeSineL:Number, volumeSineR:Number, volumeSquareL:Number, volumeSquareR:Number ):void { m_volumeSine[0] = volumeSineL; m_volumeSine[1] = volumeSineR; m_volumeSquare[0] = volumeSquareL; m_volumeSquare[1] = volumeSquareR; } public function get squareVolume():Vector. { return m_volumeSquare; } public function get sineVolume():Vector. { return m_volumeSine; } public function SetDetunes( detuneSineL:Number, detuneSineR:Number, detuneSquareL:Number, detuneSquareR:Number ):void { m_detuneSine[0] = detuneSineL; m_detuneSine[1] = detuneSineR; m_detuneSquare[0] = detuneSquareL; m_detuneSquare[1] = detuneSquareR; } public function get detuneSine():Vector. { return m_detuneSine; } public function get detuneSquare():Vector. { return m_detuneSquare; } public function AddVoice( newVoice:SynthVoice ):void { if ( m_voices.push( newVoice ) > 4 ) { m_voices.shift(); } } public function RemoveVoice( pitch:uint ):void { for ( var i:uint = 0; i < m_voices.length; i++ ) { if ( m_voices[i].pitch == pitch ) { m_voices.splice(i, 1); return; } } } public function ClearAllVoices():void { m_voices.splice(0, m_voices.length); } public function GenerateBytes( bufferSize:uint ):ByteArray { var returnBytes:ByteArray = new ByteArray(); if ( m_voices.length == 0 ) { returnBytes.length = bufferSize*8; return returnBytes; } for ( var i:uint = 0; i < m_voices.length; i++ ) { m_voices[i].SetVolumes( m_volumeSine[0], m_volumeSine[1], m_volumeSquare[0], m_volumeSquare[1] ); m_voices[i].SetDetunes( m_detuneSine[0], m_detuneSine[1], m_detuneSquare[0], m_detuneSquare[1] ); } if ( m_voices.length == 1 ) { return m_voices[0].GenerateBytes(bufferSize); } var mixerShader:Shader; switch( m_voices.length ) { case 2: { mixerShader = new Shader( new Mixer2() as ByteArray ); mixerShader.data["volume"].value = [ 0.5, 0.5, 0.5, 0.5 ]; break; } case 3: { mixerShader = new Shader( new Mixer3() as ByteArray ); mixerShader.data["volume"].value = [ 0.33, 0.33, 0.33 ]; break; } case 4: { mixerShader = new Shader( new Mixer4() as ByteArray ); mixerShader.data["volume"].value = [ 0.25, 0.25, 0.25, 0.25 ]; break; } } for ( i=0; i < m_voices.length; i++ ) { mixerShader.data["src"+(i+1)].width = bufferSize / 1024; mixerShader.data["src"+(i+1)].height = 512; mixerShader.data["src"+(i+1)].input = m_voices[i].GenerateBytes(bufferSize); } var mixerJob:ShaderJob = new ShaderJob( mixerShader, returnBytes, bufferSize / 1024, 512 ); mixerJob.start(true); return returnBytes; } } }