I got preoccupied last night and didn't have time to check out your template but I think you can get away with calculating on the fly. You also do not need to factor in FPS, just compare the times on frame...
So, lets imagine were firing on the whole notes on a 4/4 time signature at 120 BPM with a loop that lasts four bars.
A quarter note is 500ms at 120 BPM, a whole note is 2000ms. This of course can be calculated, as
described here
i can only find bpm to milliseconds and i dont remember how to switch around algebraic equasions that have 2 variables.
this is what someone said in a forum:
y = (240,000 * (d / t)) / x
where t is the divisions of a bar (e.g. 16)
and d is the delay time (e.g. 3)
and x is the bpm of your song
y is the delay time in milliseconds
so what would it look like the other way around please.
First, we need a vector to store the expected firing times of all of the notes. We're over simplifying it for discussion so there will only be 4.
Code: Select all
noteTime[1];
noteTime[2];
noteTime[3];
noteTime[4];
Lets also pretend we've already calculated our expected note length time, its 2000ms in this example so
Next, we need a variable to store the start time for when we press the play button. So we'll call that startTime. Lets just say we hit it at 500,000ms. We can then use this value, plus our expected note length value, to construct the expected time that the note length should fire. Again, the code is over simplified (should be a for loop) but the principal is here. We are also going to store which note should be the next one to fire. IRL we again want to run a for loop and compare the times, but we will just say it is note 1 for now.
Code: Select all
//on expression startButton.x == 1
startTime = time;
//startTime = 500000;
noteTime[1] = startTime + (noteLength * 1); //502,000ms
noteTime[2] = startTime + (noteLength * 2); //504,000ms
noteTime[3] = startTime + (noteLength * 3); //506,000ms
noteTime[4] = startTime + (noteLength * 4); //508,000ms
nextNote = 1;
So we've got all that stored and prepped, now all we need is the onFrame event to check whether its time to fire a note. If the time is at all greater than the note time, then we fire the note, re-calculate its next firing time, as well as advance the nextNote counter so that the onFrame will begin checking for that one. Now we are checking for whether its time to fire the note independent of frame rate. Each individual note could fire up to 16-33ms later than it should, but that is a very nominal diffrence. By comparing the expected versus the actual time on every frame, we effectively negate the issues of fluctuating frame rates.
Code: Select all
//onFrame
if (time > noteTime[nextNote] {
//fire the note here
//now we need to advance the firing time of this note for the next cycle of the loop.
noteTime[nextNote] = noteTime[nextNote] + (noteLength * 4);
//this note has fired for this iteration, so tell the onFrame which to listen for next.
nextNote = nextNote + 1;
}
Again, this is all very static math to show the principals but it will work and I highly doubt it will be too heavy for lemur to handle, and what we end up with is a sequencer where each note may deviate by a few ms, but because each one is independent of the next the overall integrity of the tempo is kept intact.