I have a solution to your problem!
This allows you to play in sync with any tempo-- even fractional tempos. I’ve used the technique on much less powerful hardware and it’s worked perfectly with no drift of tempo (tempo never goes out of phase to use a more technical terminology).
What you were doing “wrong” was to compare the current system time to the timestamp of the last beat. This is because the last beat will always be a little “off” for several big reasons I can go into in another post.
Instead, you should be always calculating forward, with high precision floating point math, from the system time at which the play button was originally pressed and triggering the code for the next beat when the current system time passes the value of the system time when play button was pressed PLUS tempo_in_milliseconds TIMES beat_number.
So you’ll need some variables (lua will automatically type float point variables where they are needed, so don’t worry about this):
-
one to store when the play button was pressed, we’ll call this “time_started”.
-
the other variable we’ll use to calculate the system time we have to wait for to play the next beat at, lets call it “next_beat”
-
to keep track of which beat number we’re on we can introduce a third variable, “counter” that counts the beats and initialize it to “1”
4, and finally we’ll need a variable that stores the tempo in milliseconds between beats, “tempoMS”, which we’ll initialize to 600000/tempo
where tempo is whatever bpm value you wish, e.g. 97
Now after calulating tempoMS by the formula 600000/tempo what we’d have as a formula for the system time value of the any given beat is:
next_beat=time_started+(counter*tempoMS)
when that time has passed, we simply increment the counter and wait til the next beat.
Using the time_started value as our perfect anchor point (not the stamp of the last beat played which will be an imperfect anchor point), and using that to calculate forward in the way I just described your tempo will stay in phase and remain in perfect phase til the battery runs dead or the floating point math precision weakens by a tiny amount over time.
Now, the caveat with this method is that you need to check that some hiccup in the OS or program hasn’t introduced a period of time which puts us behind by more than a beat… see the comment “–integrity check” in the code sample.
Now, to illustrate what I’ve been saying, try this code out that outputs to the console… first set the config.lua to output at 60fps:
application =
{
content =
{
fps = 60,
},
}
Now save this audio file I have on Dropbox.com, tick_44.wav, http://db.tt/5qkdbPQO to your project directory.
try this code in main.lua:
[code]local tempo=95.5 – a pretty wierd tempo, to illustrate my point
local tempoMS=60000/tempo/4 – for 16ths at 95.5bpm we’ll divide by 4… for normal quarter-note beats, remove the “/4” from the end of the line
local click = audio.loadSound(“tick_44.wav”)
local timestarted=system.getTimer()
local next_beat=timestarted+tempoMS
print (“tempoMS (beat length): “…tempoMS…”\n”)
local counter=1;
local function checkBeat()
local thetime = system.getTimer()
if thetime>next_beat then
audio.play(click)
print (“only off target ticks by “…math.floor(thetime-next_beat)… “ms”) --this is the amount of milliseconds we are off the perfect system time target that will never go out of phase
print (“timestarted: “…timestarted…” thetime: “…thetime…” next_beat:”…next_beat…”\n”)
–Begin playback integrity check
if thetime>next_beat+tempoMS then – i.e. we are behind two beats because of some kind of overload
print(“OH NO! Tempo has gone out of phase due to an interruption in the runtime or perhaps the audio engine, etc”)
–playback could be halted here, too
end
–End playback integrity check
counter=counter+1
next_beat=next_beat+tempoMS
–the above line could also be expressed as next_beat=timestarted+(counter*tempoMS) to find the timing for a specific beat number, the number of which is represented here by “counter”
end
end
Runtime:addEventListener(“enterFrame”,checkBeat)
–timer.performWithDelay(1,checkBeat,0)
–this should work better in theory, but doesn’t. comment out the Runtime:… line above if you try this method over the enterFrame
[/code]
Outputs this to simulator:
[code]Windows simulator build date: Dec 9 2011 @ 14:01:29
Copyright © 2009-2011 A n s c a , I n c .
Version: 2.0.0
Build: 2011.704
tempoMS (beat length): 157.06806282723
only off target ticks by 14ms
timestarted: 78 thetime: 250 next_beat:235.06806282723
only off target ticks by 13ms
timestarted: 78 thetime: 406 next_beat:392.13612565445
only off target ticks by 12ms
timestarted: 78 thetime: 562 next_beat:549.20418848168
only off target ticks by 11ms
timestarted: 78 thetime: 718 next_beat:706.2722513089
only off target ticks by 11ms
timestarted: 78 thetime: 875 next_beat:863.34031413613
only off target ticks by 10ms
timestarted: 78 thetime: 1031 next_beat:1020.4083769634
only off target ticks by 9ms
timestarted: 78 thetime: 1187 next_beat:1177.4764397906
only off target ticks by 8ms
timestarted: 78 thetime: 1343 next_beat:1334.5445026178
only off target ticks by 8ms
timestarted: 78 thetime: 1500 next_beat:1491.612565445
only off target ticks by 7ms
timestarted: 78 thetime: 1656 next_beat:1648.6806282723
only off target ticks by 6ms
timestarted: 78 thetime: 1812 next_beat:1805.7486910995
only off target ticks by 5ms
timestarted: 78 thetime: 1968 next_beat:1962.8167539267
only off target ticks by 5ms
timestarted: 78 thetime: 2125 next_beat:2119.8848167539
only off target ticks by 4ms
timestarted: 78 thetime: 2281 next_beat:2276.9528795812
only off target ticks by 2ms
timestarted: 78 thetime: 2437 next_beat:2434.0209424084
only off target ticks by 1ms
timestarted: 78 thetime: 2593 next_beat:2591.0890052356
only off target ticks by 1ms
timestarted: 78 thetime: 2750 next_beat:2748.1570680628
only off target ticks by 0ms
timestarted: 78 thetime: 2906 next_beat:2905.2251308901
only off target ticks by 15ms
timestarted: 78 thetime: 3078 next_beat:3062.2931937173
only off target ticks by 14ms
timestarted: 78 thetime: 3234 next_beat:3219.3612565445
only off target ticks by 13ms
timestarted: 78 thetime: 3390 next_beat:3376.4293193717
only off target ticks by 13ms
timestarted: 78 thetime: 3547 next_beat:3533.497382199
only off target ticks by 12ms
timestarted: 78 thetime: 3703 next_beat:3690.5654450262
only off target ticks by 11ms
timestarted: 78 thetime: 3859 next_beat:3847.6335078534
[/code]
and so on… there really isn’t a problem with any tempo from 120.0 bpm to wierd tempos like 95.5 bpm… or even 134.563 bpm.
And the reference beat time value, next_beat at which we trigger the next beat will never go out of phase with the pulse of the ideal tempo any more than the processor hardware and floating precision math of corona will allow.
Each discrete beat could be off from this perfect target ticks value by 16.7 milliseconds (1000 ticks per second/60fps) or less. This is still an issue. Carlos: Can Corona please be given the ability to trigger audio and perhaps some simple display events at a much higher granularity? Especially when not much else is going on?
In spite of a minor delay of each beat from approx 0 to 16.7 milliseconds, with this method at least the tempo pulse itself from which each beat is triggered will be rock solid. Always!
Hope this helps!
I look forward to your reply/replies! [import]uid: 106887 topic_id: 13881 reply_id: 84062[/import]