I'm messing around with the sound synthesis stuff using the C API and it seems that SDK 2.1.0 change to make playdate->sound->sequence->setTempo()
take a float stepsPerSecond
doesn't work as intended.
Here's the simple example tune I was experimenting with:
SoundSequence* seq;
void test_music() {
const struct playdate_sound* snd = pd->sound;
PDSynth* square = snd->synth->newSynth();
snd->synth->setWaveform (square, kWaveformSquare);
snd->synth->setAttackTime (square, 0);
snd->synth->setDecayTime (square, .2f);
snd->synth->setSustainLevel(square, .3f);
snd->synth->setReleaseTime (square, .5f);
PDSynthInstrument* inst = snd->instrument->newInstrument();
snd->instrument->addVoice(inst, square, 0, 127, 0);
snd->channel->addSource(snd->getDefaultChannel(), (SoundSource*)inst);
seq = snd->sequence->newSequence();
SequenceTrack* track = snd->sequence->addTrack(seq);
snd->track->setInstrument(track, inst);
snd->track->addNoteEvent(track, 0, 1, NOTE_C4, 1.f); // row
snd->track->addNoteEvent(track, 4, 1, NOTE_C4, 1.f); // row
snd->track->addNoteEvent(track, 8, 1, NOTE_C4, 1.f); // row
snd->track->addNoteEvent(track, 11, 1, NOTE_C4 + 2, 1.f); // your
snd->track->addNoteEvent(track, 12, 1, NOTE_C4 + 4, 1.f); // boat
snd->sequence->setLoops(seq, 0, 15, 0);
snd->sequence->play(seq, NULL, NULL);
}
Everything here works as expected. But, if you try to change the tempo, it breaks horribly, seemingly playing all the notes at once, and sometimes it loops, sometimes it doesn't:
snd->sequence->setTempo(seq, 8.f); // half of the default 16
While trying to figure out what I was doing wrong, I thought I'd call getTempo()
, and see what the tempo is really being set to. That's when I noticed getTempo()
returns an int
, while setTempo()
takes a float
!
pd->system->logToConsole("%i", snd->sequence->getTempo(seq)); // 16 (default)
snd->sequence->setTempo(seq, 8.f); // try to make it play half as fast
pd->system->logToConsole("%i", snd->sequence->getTempo(seq)); // 1090519040 (!!)
Aha! There's the problem—the tempo is way too fast!
After some experimentation, I discovered that replacing the above code with the following does what I actually wanted it to do, i.e. set the tempo to 8 steps per second:
int tempo_i = 8;
float tempo_f;
memcpy(&tempo_f, &tempo_i, sizeof(tempo_i));
snd->sequence->setTempo(seq, tempo_f);
So it seems that the C API setTempo()
function takes in a float, but actually stores it internally as an int
? Or something?
Here's the "fixed" version of the code:
SoundSequence* seq;
void test_music() {
const struct playdate_sound* snd = pd->sound;
PDSynth* square = snd->synth->newSynth();
snd->synth->setWaveform (square, kWaveformSquare);
snd->synth->setAttackTime (square, 0);
snd->synth->setDecayTime (square, .2f);
snd->synth->setSustainLevel(square, .3f);
snd->synth->setReleaseTime (square, .5f);
PDSynthInstrument* inst = snd->instrument->newInstrument();
snd->instrument->addVoice(inst, square, 0, 127, 0);
snd->channel->addSource(snd->getDefaultChannel(), (SoundSource*)inst);
seq = snd->sequence->newSequence();
SequenceTrack* track = snd->sequence->addTrack(seq);
snd->track->setInstrument(track, inst);
snd->track->addNoteEvent(track, 0, 1, NOTE_C4, 1.f); // row
snd->track->addNoteEvent(track, 4, 1, NOTE_C4, 1.f); // row
snd->track->addNoteEvent(track, 8, 1, NOTE_C4, 1.f); // row
snd->track->addNoteEvent(track, 11, 1, NOTE_C4 + 2, 1.f); // your
snd->track->addNoteEvent(track, 12, 1, NOTE_C4 + 4, 1.f); // boat
// begin fix
int tempo_i = 8;
float tempo_f;
memcpy(&tempo_f, &tempo_i, sizeof(tempo_i));
snd->sequence->setTempo(seq, tempo_f);
// end fix
snd->sequence->setLoops(seq, 0, 15, 0);
snd->sequence->play(seq, NULL, NULL);
}