You're not going to ever get a good consistency working with events or timers in Flash (fun fact: I recently discovered there is some discrepancy in flash over whether a a second takes 1000 or 1024 milliseconds to complete, depending on what method you use. *facepalm*), I haven't actually looked into Flashpunk at all yet, so I'm not completely sure how it's handling all that.
Basically, what you're going to want to do is set up a SampleDataEvent to play all your sounds. I made this tonematrixy thing for playing sampled sounds a while back (you can see what it does
here,
very rudimentary), it's in some serious need of cleaning up/optimization, but it basically works.
- Code: Select all
var mtx:Array = new Array();
var mList:Array = new Array();
var mc:MovieClip;
var n:uint = 0;
for (n=0; n<2; n++) {
mtx.push([]);
mList.push([]);
while (mtx[n].length < 16) {
mc = new Box();
mc.x = mtx[n].length*25;
mc.y = n*25;
addChild(mc);
mtx[n].push(0);//uint(Math.random()*2));
mc.gotoAndStop(mtx[n][mtx[n].length-1]+1);
mList[n].push(mc);
}
}
stage.addEventListener(MouseEvent.CLICK,md);
function md(e:MouseEvent):void {
var mX:int = int(mouseX/25);
var mY:int = int(mouseY/25);
if (mX >= 0 && mX < mtx[0].length && mY >= 0 && mY < mtx.length) {
mtx[mY][mX] = (mtx[mY][mX]+1)%2;
mList[mY][mX].gotoAndStop(mtx[mY][mX]+1);
}
}
var noteIndex:Array = [0,1,0,1,0,1,0,1,2,1,2,1,2,1,2,1,
3,4,5,6,3,4,5,6,3,4,5,6,3,4,5,6];
////////////////////////////////////////////
const bpm:uint = 240;
const span:uint = uint(2646000/bpm);
var a:Array = new Array();
var a1:Vector.<int> = new Vector.<int>(0,false);
var a2:Vector.<ByteArray> = new Vector.<ByteArray>(0,false);
var a3:Vector.<uint> = new Vector.<uint>(0,false);
var _mp3:Sound;
var _sound:Sound;
var _target:ByteArray;
var _position:Number;
const BLOCK_SIZE:int = 3072;//2048-8192 // 3072
_sound = new Sound();
_sound.addEventListener(SampleDataEvent.SAMPLE_DATA,sampleData);
_sound.play();
function newNote(nt:uint=0,off:int=0):void {
switch (nt) {
case 0:
a.push(new Note0());
break;
case 1:
a.push(new Note1());
break;
case 2:
a.push(new Note2());
break;
case 3:
a.push(new BNote0());
break;
case 4:
a.push(new BNote1());
break;
case 5:
a.push(new BNote2());
break;
case 6:
a.push(new BNote3());
break;
default:
a.push(new SingleNote2());
}
a1.push(off);
a2.push(new ByteArray());
a3.push(0);
}
function sampleData(e:SampleDataEvent):void {
var data:ByteArray = e.data;
var l0:Number;
var r0:Number;
//_mp3,_position,_target,n
for (var n2:uint=0; n2<a.length; n2++) {
a2[n2].position = 0;
var read:int = a[n2].extract(a2[n2],BLOCK_SIZE,a1[n2]);
a3[n2] = (read == BLOCK_SIZE) ? BLOCK_SIZE : read;
}
for (var i:int=0; i<BLOCK_SIZE; ++i) {
l0 = r0 = 0;
for (n2=0; n2<a.length; n2++) {
if (i < a3[n2]) {
if (a1[n2]+i >= BLOCK_SIZE) {
a2[n2].position = i << 3;
l0 += a2[n2].readFloat();
r0 += a2[n2].readFloat();
}
} else {
a.splice(n2,1);
a1.splice(n2,1);
a2.splice(n2,1);
a3.splice(n2--,1);
}
}
data.writeFloat(l0);
data.writeFloat(r0);
}
i = uint(e.position/span + 1)%16;
l0 = e.position%span;
if ((e.position+BLOCK_SIZE)%span < l0) {
plyr.x = ((i+15)%16)*25 + 12.5;
if (mtx[0][i] == 1) newNote(noteIndex[0*16+i],l0-span);
if (mtx[1][i] == 1) newNote(noteIndex[1*16+i],l0-span);
}
for (n2=0; n2<a.length; n2++) a1[n2] += BLOCK_SIZE;
}
The important part is this bit at the end though
- Code: Select all
i = uint(e.position/span + 1)%16;
l0 = e.position%span;
if ((e.position+BLOCK_SIZE)%span < l0) {
plyr.x = ((i+15)%16)*25 + 12.5;
if (mtx[0][i] == 1) newNote(noteIndex[0*16+i],l0-span);
if (mtx[1][i] == 1) newNote(noteIndex[1*16+i],l0-span);
}
Basically, it takes the current sound position and checks to see if a beat will start in the next sampleDataEvent pass. If it will, it adds the notes it needs to play into the mix as necessary (and gives them a slight bit of silence at their start according to how long until they need to begin playing to keep on time). This is the place to do any tempo-based sprite changes (or to set a variable which will cause everything to change on the next frame so you can do it outside of the sampleEventData listener if that's important).
After that, it's just a matter of reading your samples efficiently (which this code could do better, I suspect). If I remember correctly, this implementation can play about 21 samples at once before I start to hear crackling, but to be safe I wouldn't go over eight at once. Where you get into trouble are sounds that have a long tail and end up still playing out as other sounds start.
Hopefully that helps. Let me know if it doesn't.