package com.increpare.bfxr.synthesis.Mixer
{
import com.increpare.bfxr.synthesis.IPlayerInterface;
import com.increpare.bfxr.synthesis.ISerializable;
import com.increpare.bfxr.synthesis.Synthesizer.SfxrSynth;
import com.increpare.bfxr.synthesis.WaveWriter;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.utils.ByteArray;
import flash.utils.Endian;
public class MixerPlayer extends EventDispatcher implements ISerializable, IPlayerInterface
{
public var id:int = -1;
public var volume:Number = 1;
public var tracks:Vector.<MixerTrackPlayer>;
public function Load(data:String):void
{
this.Deserialize(data);
}
public function Play(volume:Number=1):void
{
if (this._mutations.length==0)
{
this.play(null,volume);
}
else
{
this.playRandomMutation(volume);
}
}
public function Cache():void
{
PrepareMixForPlay();
}
private var _mutations:Vector.<ByteArray> = new Vector.<ByteArray>();
public function CacheMutations(amount:Number = 0.05,count:int=16):void
{
_mutations = new Vector.<ByteArray>();
var original:String = this.Serialize();
for (var i:int=0;i<count;i++)
{
for (var j:int=0;j<tracks.length;j++)
{
tracks[j].synth.cacheMutations(count,amount);
}
PrepareMixForPlay(true)
_mutations.push(_waveData);
}
_lastplayeddata="";
}
public function MixerPlayer()
{
tracks = new Vector.<MixerTrackPlayer>();
for (var i:int=0;i<MixerSynth.TRACK_COUNT;i++)
{
var mtp:MixerTrackPlayer = new MixerTrackPlayer();
tracks.push(mtp);
}
if (_zeros==null)
{
_zeros = new ByteArray();
for (i=0;i<400000;i++)
{
_zeros.writeFloat(0.0);
}
}
}
public static function Reverse(ba:ByteArray):ByteArray
{
var result:ByteArray = new ByteArray();
var l:uint=ba.length;
ba.position=ba.length-4;
for (var pos:int=ba.length-4;pos>=0;pos-=4)
{
ba.position=pos;
result.writeFloat(ba.readFloat());
}
return result;
}
public function Serialize():String
{
var result:String="";
result += id.toString() + ">";
result += volume.toString() + ">";
for (var i:int=0;i<tracks.length;i++)
{
if (i>0)
result+=">";
result += tracks[i].Serialize();
}
return result;
}
public function Deserialize(settings:String):void
{
var ar:Array = settings.split(">");
id = int(ar[0]);
volume = Number(ar[1]);
for (var i:int=2;i<ar.length;i++)
{
var s:String = ar[i];
tracks[i-2].Deserialize(s);
}
}
private var _updateCallback:Function=null;
private var _lastplayeddata:String="";
private var _caching:Boolean=false;
private var _channel:SoundChannel;
private var _sound:Sound;
private var _waveData:ByteArray;
private var _waveDataLength:int=-1;
private var _waveDataBytes:int=-1;
private var _waveDataPos:uint=0;
private var _preparedsounds:Vector.<ByteArray>;
private var _preparedvolumes:Vector.<Number>; private static var _zeros:ByteArray;
private function PrepareMixForPlay(mutation:Boolean=false):void
{
var description:String = this.Serialize();
if (_lastplayeddata!=description || mutation)
{
_lastplayeddata = description;
_preparedsounds = new Vector.<ByteArray>();
_preparedvolumes = new Vector.<Number>();
for (var i:int=0;i<tracks.length;i++)
{
if (tracks[i].IsSet()==false)
{
continue;
}
var b:ByteArray = new ByteArray();
var silentbytes:int = int(tracks[i].data.onset*44100/2)*4*2;
while(silentbytes>0)
{
var bytestocopy:int=Math.min(silentbytes,_zeros.length);
b.writeBytes(_zeros,0,bytestocopy);
silentbytes-=bytestocopy;
}
var cached:ByteArray = mutation==false
? tracks[i].synth.cachedWave
: tracks[i].synth.getCachedMutationWave(0);
if (tracks[i].data.reverse)
{
cached=Reverse(cached);
}
b.writeBytes(cached);
b.position=0;
_preparedsounds.push(b);
_preparedvolumes.push(tracks[i].data.volume);
}
Mix();
}
}
public function playRandomMutation(vol:Number=1):void
{
_waveData = _mutations[int(Math.random()*_mutations.length)];
if (_preparedsounds.length==0)
{
return;
}
if (_channel)
{
_channel.stop();
}
_waveData.position = 0;
_waveDataLength = _waveData.length;
_waveDataBytes = 24576;
_waveDataPos = 0;
_caching=true;
if (!_sound) (_sound = new Sound()).addEventListener(SampleDataEvent.SAMPLE_DATA, onSoundData);
_channel = _sound.play(0,0,new SoundTransform(vol,0));
}
public function play(updateCallback:Function=null, vol:Number=1):void
{
_updateCallback=updateCallback;
PrepareMixForPlay();
if (_preparedsounds.length==0)
{
return;
}
if (_channel)
{
_channel.stop();
}
_waveData.position = 0;
_waveDataLength = _waveData.length;
_waveDataBytes = 24576;
_waveDataPos = 0;
_caching=true;
if (!_sound) (_sound = new Sound()).addEventListener(SampleDataEvent.SAMPLE_DATA, onSoundData);
_channel = _sound.play(0,0,new SoundTransform(vol,0));
}
public function stop():void
{
if (_channel)
{
_channel.stop();
_channel = null;
}
}
/**
* Returns a ByteArray of the wave in the form of a .wav file, ready to be saved out
* @param sampleRate Sample rate to generate the .wav at
* @param bitDepth Bit depth to generate the .wav at
* @return Wave in a .wav file
*/
public function getWavFile():ByteArray
{
stop();
PrepareMixForPlay();
var ww:WaveWriter = new WaveWriter(true,32);
ww.addSamples(_waveData);
ww.finalize();
return ww.outBuffer;
}
/** param is whether to work in bytes, shorts, or floats (1,2,4)*/
private function Mix(unitsize:int=4):void
{
var trackcount:int=_preparedsounds.length;
_waveData = new ByteArray();
var added:Boolean=true;
var i:int;
switch(unitsize)
{
case 1:
while (added)
{
added=false;
var val:int=0;
for (i=0;i<trackcount;i++)
{
if (_preparedsounds[i].position<_preparedsounds[i].length-unitsize)
{
val += _preparedsounds[i].readByte()*_preparedvolumes[i];
added=true;
}
}
val*=volume;
if (val >= (1<<7))
val=1<<7;
if (val<= -(1<<7))
val=-(1<<7);
_waveData.writeByte(val);
}
break;
case 2:
while (added)
{
added=false;
val=0;
for (i=0;i<trackcount;i++)
{
if (_preparedsounds[i].position<_preparedsounds[i].length-unitsize)
{
val += _preparedsounds[i].readShort()*_preparedvolumes[i];
added=true;
}
}
val*=volume;
if (val >= (1<<15))
val=1<<15;
if (val<= -(1<<15))
val=-(1<<15);
_waveData.writeShort(val);
}
break;
case 4:
while (added)
{
added=false;
var valf:Number=0;
for (i=0;i<trackcount;i++)
{
if (_preparedsounds[i].position<_preparedsounds[i].length-unitsize)
{
valf += _preparedsounds[i].readFloat()*_preparedvolumes[i];
added=true;
}
}
valf*=volume;
_waveData.writeFloat(valf);
}
break;
}
_waveData.position=0;
}
private function onSoundData(e:SampleDataEvent) : void
{
if (_caching)
{
if (_updateCallback!=null)
{
_updateCallback(_waveDataPos/(4*44100));
}
if(_waveDataPos + _waveDataBytes > _waveDataLength)
{
_waveDataBytes = _waveDataLength - _waveDataPos;
dispatchEvent(new Event(SfxrSynth.PLAY_COMPLETE));
}
if(_waveDataBytes > 0) e.data.writeBytes(_waveData, _waveDataPos, _waveDataBytes);
if (e.data.position<24576)
{
_caching=false;
while (e.data.position<24576)
{
e.data.writeFloat(0.0);
}
}
_waveDataPos += _waveDataBytes;
}
}
}
}