Another audio bug I'm afraid. This one is a bit of a showstopper for me, because it causes notes get stuck on, resulting in messed up music.
I've discovered that having two PDSynths playing overlapping notes prevents noteOff()
from functioning, at least when both playNote
/ playMIDINote
and noteOff
have been scheduled for some point in the future, rather than passing 0 to their when
parameter. Here's some code to put in the update callback that appears to reproduce the bug 100% of the time:
static PDSynth *synth1 = NULL, *synth2 = NULL;
static uint32_t lastTime = 0;
static int goodFrameCount = 0;
static uint32_t startTime = 0;
uint32_t currentTime = pd->sound->getCurrentTime();
if (!synth1 || !synth2) {
synth1 = pd->sound->synth->newSynth();
pd->sound->synth->setWaveform(synth1, kWaveformTriangle);
pd->sound->channel->addSource(pd->sound->getDefaultChannel(), (SoundSource *)synth1);
synth2 = pd->sound->synth->newSynth();
pd->sound->synth->setWaveform(synth2, kWaveformTriangle);
pd->sound->channel->addSource(pd->sound->getDefaultChannel(), (SoundSource *)synth2);
}
if (startTime == 0) {
// Need to make sure we're getting consistently spaced frames after startup
// before proceeding:
goodFrameCount = (currentTime - lastTime > 500) ? goodFrameCount+1 : 0;
if (goodFrameCount == 5) {
pd->system->logToConsole("Starting...");
startTime = currentTime;
}
}
if (startTime != 0) {
#define STEP(x) (startTime + 5000*x)
if (currentTime >= STEP(0) && lastTime < STEP(0)) {
pd->system->logToConsole("playing synth 1");
pd->sound->synth->playMIDINote(synth1, NOTE_C4, 0.5, -1.0, STEP(2));
}
if (currentTime >= STEP(2) && lastTime < STEP(2)) {
pd->system->logToConsole("playing synth2 / stopping synth1");
pd->sound->synth->noteOff(synth1, STEP(5));
pd->sound->synth->playMIDINote(synth2, NOTE_C4 + 2, 0.5, -1.0, STEP(4));
}
if (currentTime >= STEP(4) && lastTime < STEP(4)) {
pd->system->logToConsole("playing synth1 / stopping synth2");
pd->sound->synth->noteOff(synth2, STEP(7));
pd->sound->synth->playMIDINote(synth1, NOTE_C4 + 4, 0.5, -1.0, STEP(6));
}
if (currentTime >= STEP(6) && lastTime < STEP(6)) {
pd->system->logToConsole("stopping synth1");
pd->sound->synth->noteOff(synth1, STEP(9));
}
if (currentTime >= STEP(10) && lastTime < STEP(10)) {
if (pd->sound->synth->isPlaying(synth1)) {
pd->system->logToConsole("Oh no! synth1 is still playing!");
}
}
}
lastTime = currentTime;
(Note: the bit with startTime is there because I've noticed the Playdate Simulator doesn't always start processing frames at consistent intervals for the first few seconds it's running. That's just there to make sure everything is running smoothly before proceeding with playing any audio, since the code is timing dependent.)
If I set the when
parameter of playMIDINote
to 0, then the bug doesn't occur. So this seems to have something to do with notes being scheduled to play in the future.
So far I still haven't found a workaround, but I'm going to keep bashing my head against it, since my whole music engine is reliant on being able to schedule playing and releasing notes.
edit: This very well may be my bug actually -- still investigating