Need help keeping track of a beat with inconsistent tickspeed

(I'm using windows SDK)
I've been working on a rhythm game for the past couple of days, and I only noticed today that the tick speed is variable and is faster than 30 ticks per second almost always (with my game specifically). I haven't done any tests on the actual hardware yet, but I've realized that this project is kind of dead in the water if I can't keep time in a way that isn't effected by tick speed fluctuations. I honestly am unsure of what to do, I've tried making it so my delta variable is dependent on the getCurrentTimeMilliseconds() function, but that hasn't seemed to fix it at all. I probably should have considered this problem sooner, as I've already built the game engine without consideration of this problem.

I'm not sure what else I should say honestly, I guess I'll explain my method of updating the delta variable based on the getCurrentTimeMilliseconds() function.
I basically have it check the amount of time, in milliseconds, that's elapsed since the last tick. Then I update an idealDelta variable with 30*(elapsedTime/1000), and set the delta variable to idealDelta floored.
I'm checking the beat by playing a song I know is 130 bpm and having a circle on screen pulse every beat. The way I'm doing this is by checking delta % math.floor(1800/songBpm) == 0, and if true setting it to pulse that frame.

I'm not sure if I'm picturing the problem properly, so please let me know if I'm not following correctly:

idealDelta counts the number of frames which have passed since the start of the song, and delta is just idealDelta after truncation. The delta % math.floor(1800/songBpm) should be 0 to indicate a beat on this frame, meaning delta is evenly divisible by math.floor(1800/songBpm). The 1800/songBpm figure looks like it comes from 60 (s/min) * 30 (frame/s) * X (min/beat); that leaves us with a certain number of frames per beat, which makes sense because we want that modulo expression to be 0 each time the number of frames in 1 beat passes.

But you also performed a floor on 1800/songBpm, because we really would never get an even division otherwise. But for 130bpm, that changes the frames per beat from ~13.85 to 13, which is a reduction in excess of 6%. Could this be the source of the issue?

I have a recommendation: Instead of tracking the beat with that modulo expression derived from delta, just use idealDelta as-is like a rolling counter. Wait for it to exceed the number of frames per beat-- about 13.85 for 130bpm-- then pulse the circle and just subtract 13.85 from the rolling counter at that moment, letting it continue to accumulate. I have a hunch doing this could get to to be much more precise.

Have you tried using playdate->sound->getCurrentTime() (C) / playdate.sound.getCurrentTime() (Lua)? I haven't yet myself but it seems like this might be what you're looking for.

Ok, after a lot of work and help from the PlayDate discord, I think I got it working. What I did was base everything off of the audio timer, specifically the offset of the music track that's playing. Instead of trying to make it so it tries to keep a consistent update speed and having things just run off of delta time, I just calculate where things should be based on the current offset of the music file being played.

2 Likes