Open SuperFromND opened 1 year ago
For future reference (my own included): the current implementation of Open Manifold's time synchronization code is very loosely based on this old comment by (former) Redditor /u/jusksmit. It's one of the earliest (and least elegant) parts of the codebase, and definitely the one in most need of a refactor. It was also written before SDL_Mixer implemented Mix_GetMusicPosition, so much of it is likely obsolete/redundant.
Okay, never mind on the obsolete part. Turns out Mix_GetMusicPosition works on seconds, not milliseconds (what we need).
To elaborate: Currently, what we do is set a value at the start of a beat in milliseconds, and then a second value which is the first value + the length of a beat, and then we just keep checking the time until it's equal or more than that second value, in which we then increment both. It's rough and it does work, but definitely not ideal.
The problem is, SDL_Mixer has no way of getting the current play time, so instead we set the first value using SDL_GetTicks()
and then use the BPM value to calculate the rest from there. Obviously, not ideal.
One idea I had is to use the BPM value to calculate exact beat times instead of approximating them with our current implementation. I don't know if that would help much though.
Another idea is to use Mix_HookMusic or Mix_SetPostMix to track the playhead ourselves, but I'm not entirely sure how to do that either, and it's questionable if this would even be more accurate or precise than SDL's GetTicks
function.
Why not just use a chunk instead of music for this? While it's technically wrong, it'd work better since it loads everything up front.
Unfortunately, chunks don't support Mix_SetMusicPosition();
, which is how Open Manifold handles playback rewinding in the case of missing shapes.
Currently, Open Manifold runs under the assumption that a music file will always be able to playback with 100% consistency, never stuttering or pausing to load more streamed audio data at all. However, this simply isn't true in practice, and if your computer has even a slight hitch while loading in new music data mid-gameplay, the game's internal metronome (which is used to control beat checks and music progression) will NOT pause or take it into account, which results in desynced music; a deal-breaker for a rhythm game.
There is a very janky, but somewhat effective workaround to this problem: the
loop()
function includes a bit of code that essentially "snaps" the music playhead back to where it's expected to be at the start of every shape:However, this is merely a bandaid to cover a much bigger problem, and one that really should be addressed.
Preloading the entire music file before starting the level would be able to fix this issue, at the cost of eating up a bit of RAM and longer level-load times. Unfortunately, SDL2 Mixer doesn't appear to support preloading an entire music file beforehand by its very design: