Audio getOffset() values are off on device

I’m playing with a rhythm gameplay and I run into an issue with fileplayer. I prepared a minimal example and tried to test a lot, so hopefully it’s not something wrong with me — I’m definitely not a drummer :sweat_smile:

EDIT: I tested the issue with sampleplayer and the results are very similar. Not sure what to make of that, as in the case of fileplayer, I expected it to be some kind of a buffering issue. I edited the title to reflect that. The sampleplayer issue should be reproducible with the modified example included below.

I’m using a 120 bpm music file with beats nicely aligned with the half/full seconds — I can reliably hit these in Simulator (on macOS) but tapping on beat on device (revA) returns values that seem to be sliding off (by up to ~0.2s). The deviation seems to be changing over time, so below are values from about a minute and half into the music track. However, it’s noticeable right away.

Here’s my minimal example code (example source with the track and pdx):

local player = playdate.sound.fileplayer.new("120bpm")
player:play(0)

function playdate.update()
	if playdate.buttonJustPressed(playdate.kButtonA) then
		print(player:getOffset())
	end
end

The difference is pretty stark:

These are values when I’m tapping a key in Simulator (avg deviation from %0.5 values is 0.0167):

81.02917
81.52723
82.03111
82.52919
83.02144
83.51952
84.0176
84.52144
85.01954
85.51759
86.05042
86.50796
87.0118
87.47511
88.00793
88.51181
89.00986
89.50793
90.01181
90.50986
91.00214
91.50021
91.99829
92.50217
93.00021
93.53304
94.03113
94.53496
95.02146
95.49056

And these are from a device connected to a Sim (avg deviation 0.0871):

81.92569
82.42373
82.92179
83.39671
83.90637
84.416
84.95463
85.46429
85.94496
86.44883
86.94691
87.41602
87.9199
88.41794
88.92181
89.43148
89.90637
90.40442
90.90829
91.41796
91.89283
92.38512
92.88898
93.39864
93.90831
94.3774
94.85231
95.36196

Looking at your code there's no refresh rate defined. So it will be running at 30fps. This could mean that, depending on how/when the SDK polls button presses, there could be up to 0.0333s of delay for each button press.

But that would only explain half of the difference.

buttonJustPressed means pressed and released so is slow when compared to buttonIsPressed which only concerns itself with the press (and not the release).

There's also button debounce. Not sure if that applies to Simulator.

1 Like

Ah, I may have oversimplified the example — i’m running the code at 50 fps. (I’ll test with a fixed example to be sure.)

The buttonJustPressed should be the keyDown equivalent (in browser JS terms), but I’ll try a different way to check the button for sure (I normally use getButtonState but went with this for simplicity when prototyping).

Re debounce: it did not occur to me that this could be a more general issue with input. I’ll try to find a way to check how quickly playdate reads the input (perhaps with a high framerate rec in my phone). But 150+ ms sounds a bit too much for me not to notice before.

This is the example source I posted above running at 50 fps, checking the button via pressed in getButtonState() (it should be equivalent to the justPressed used in the original). The values are still consistently different, with the avg delay from .5s points being 0.046 in Sim and 0.1 on device.

The issue is that some of the values stray pretty long way (~130ms), so that makes detecting the on-beat complicated (false misses are a big bummer in a rhythm game).

My current workaround is to add a 80ms delay on device by default and increase the buffer around it a bit. However, a fix could break the game in the future, so I’d be happy to hear if this issue is unsolvable.

Aside from that, I checked the button press read and it seems to be pretty okay — from the visible button moving to the screen redraw (gfx.clear() with a specified color), it seems to be about ~10–12 frames at 240fps recording (0.04s, see frames 296 - 308) a the button is probably actuated later than on frame 1 — so I think the readout from the button might be up to date right away at the closest update.

numbered

I tried to hit that button as fast as possible, poor Playdate :sweat_smile:

Note: My friend had an idea that it’s the difference in how I hear the audio, less bass in Playdate might actually trigger me to press the button later than the solid bass from the PC headphones. We tested this with the headphones on the Playdate and there’s still a difference.

I did a few more rounds of testing after seeing a change in the data with the headphones on, and it seems that my friend was right — it’s probably the sound being different from the playdate speaker than from the headphones.

The bass hits much less intensively from the speaker on Playdate and so it makes us press the button with a delay. With headphones on, the measured data become inconclusive after a longer session (with the example above running at 50 fps), with some deviations possibly caused by us and the song.

This probably means that I’ll need to just widen the time window for non-headphone use and test each song on headphones and the speaker with a special setting.

If I find out more, I’ll post below, but so far I’m marking this as Solved.

1 Like

Very cool! Thanks for sharing the exploration

1 Like