2.6.2: sample-based synth output intermittently degrades over a short time

As I play sequences via the Lua SDK using sample-based synths I can sometimes hear the sound quality intermittently get worse then go back to normal, at different times and for different time intervals. This happens both in the simulator and on device. I'm not entirely sure what's happening here, so I thought I'd put together a test script to reproduce in the hope that someone can help figure it out.

To show what I'm hearing, I've placed a recording of the sound produced by the test script in this shared folder (example_output.mp3); the phenomenon can be heard mainly around 2.0, 4.25, 10.0 and 13.5 seconds (especially around 10 seconds).

The shared folder also includes the test script and the sample wave file and MIDI file used to produce the recording. I'll paste the script here as well, but I don't think I'm doing anything out of the ordinary (the sequence is made to loop, but this is mostly for testing convenience, as the problem also happens when not looping):

local pdSound <const> = playdate.sound
local pdInstrument <const> = pdSound.instrument
local pdSynth <const> = pdSound.synth
local pdSample <const> = pdSound.sample

-- Create a sound sequence from a MIDI file and assign instruments to all of its tracks
local seq <const> = pdSound.sequence.new("test.mid")
local sample <const>, err <const> = pdSample.new("sample.wav")

local instrument
local synth
for idx = 1, seq:getTrackCount() do
    instrument = pdInstrument.new()
    synth = pdSynth.new(sample)
    instrument:addVoice(synth, 42)
    seq:getTrackAtIndex(idx):setInstrument(instrument)
end

-- Loop endlessly; this is not necessary to reproduce, but makes it easier to hear the problem
seq:setLoops(0)
seq:play()

function playdate.update()
end

Running the script, it's sufficient to let the sequence loop a few times to start hearing what I called degradation. This makes in-game tracks sound much less than ideal obviously. What could be the cause of this?

Sorry I missed this when you posted it. Feel free to @ me on these!

This was an interesting one! Turns out the root cause of this was an obscure bug where if one note ends and the next one starts within one render cycle (256 samples, or ~5.8 ms) it'll start the second note as soon as the first ends instead of waiting for the correct time. You managed to create the perfect test scenario to find the bug. :slight_smile: In this case instead of playing the sample twice at exactly the same time, doubling the amplitude, it's playing with a short delay--short enough that it sounds like one sound with a comb filter instead of two sounds. If you were playing two different samples it wouldn't have been noticeable.. unless you've got a really great ear for timing.

Thanks for catching this!

1 Like

Thank you for looking into it, it was a tough one to describe so I figured the audio could do the talking better than I could. Sometimes I wish I had more visibility under the hood so I could go further in diagnosing or at least ruling things out.

Sorry I missed this when you posted it. Feel free to @ me on these!

No worries, will do!