`sequence:goToStep` has incorrect behaviour when moving to the next or previous step

Platform: Playdate simulator running on Windows 10; SDK release 2.0.3

I am just starting out with Playdate development and I'm trying to add crank support to the DrumMachine example. I want it so that when the crank is turned the playhead scrubs forwards or backwards through the sequence. I have added this code to the example (along with import "CoreLibs/crank"):

function playdate.cranked()
	local crank_tick = playdate.getCrankTicks(sequence:getLength())

	if crank_tick ~= 0 then
		local new_step = sequence:getCurrentStep() + crank_tick

		-- wrap when hitting the ends
		if new_step > sequence:getLength() then
			new_step = 1
		elseif new_step < 1 then
			new_step = sequence:getLength()

		sequence:goToStep(new_step, true)

The code is meant to move the sequence's step based on the getCrankTicks function. However, it exhibits strange behaviour: When turning the crank forwards, the playhead stays in the same spot (unless it is turned fast enough to jump 2 at once). When turning it backwards, the playhead does move backwards, but in steps of 2, rather than 1. At this point, it sounds like there is a -1 offset from the specified location, and so adding 1 should fix it (line 5 of the function):

local new_step = sequence:getCurrentStep() + crank_tick + 1

However, that just causes the problem in the other direction; now it jumps forwards 2 steps and won't move backwards. If I multiply crank_tick by 2, then it always jumps the expected 2 steps going both directions. Going to further multiplication levels leads to the following observation:

  • When the sequence is stepped by an odd number in either direction, there seems to be a -1 offset.
  • When it is stepped by an even number in either direction, it works as expected (stepping by that amount).

(Side note: Is the source code for the API publicly available anywhere? I think looking at the implementation could save a lot of guesswork on issues like these.)

The source for all CoreLibs functions are available in the CoreLibs folder in your SDK folder (in the docs, CoreLibs functions will all have a note saying You must import CoreLibs/...).

The source for playdate.getCrankTicks() is in CoreLibs/crank.lua

It a rounding bug. :frowning: If you set the BPM to 105 or 135 it'll work as expected, because that translates to 7 or 9 steps per second, and 7 and 9 divide evenly into the 44,100 Hz sample rate when we're computing a time offset for the step. When it doesn't divide evenly the time value gets rounded down and then getCurrentStep() reports that the sequence is in the previous step. I'll file this and see if I can come up with the right solution. In the mean time here's a workaround which avoids the setCurrentStep() call by tracking the current step in a lua variable: main.lua.zip (2.6 KB)