Skip to content

Commit

Permalink
Implement basic 'last note played' note priority algorithm
Browse files Browse the repository at this point in the history
Literally only remembers the last note for now.
  • Loading branch information
rhargreaves committed Jul 16, 2024
1 parent 4ef9241 commit 6fbb961
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 4 deletions.
34 changes: 31 additions & 3 deletions src/midi.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ struct MidiChannel {
u8 program;
u8 pan;
u16 pitchBend;
u8 prevPitch;
u8 pitch;
DeviceSelect deviceSelect;
};

Expand Down Expand Up @@ -121,6 +123,8 @@ static void initMidiChannel(u8 midiChan)
chan->pan = DEFAULT_MIDI_PAN;
chan->volume = MAX_MIDI_VOLUME;
chan->pitchBend = DEFAULT_MIDI_PITCH_BEND;
chan->pitch = 0;
chan->prevPitch = 0;
chan->deviceSelect = Auto;
}

Expand Down Expand Up @@ -404,6 +408,15 @@ void midi_note_on(u8 chan, u8 pitch, u8 velocity)
log_warn("Ch %d: Dropped note %d", chan + 1, pitch);
return;
}

if (!dynamicMode) {
MidiChannel* midiChannel = &midiChannels[chan];
if (midiChannel->pitch != 0) {
midiChannel->prevPitch = midiChannel->pitch;
}
midiChannel->pitch = pitch;
}

devChan->midiChannel = chan;
updateDeviceChannelFromAssociatedMidiChannel(devChan);
devChan->pitch = pitch;
Expand All @@ -413,11 +426,26 @@ void midi_note_on(u8 chan, u8 pitch, u8 velocity)

void midi_note_off(u8 chan, u8 pitch)
{
MidiChannel* midiChannel = &midiChannels[chan];
DeviceChannel* devChan;
while ((devChan = findChannelPlayingNote(chan, pitch)) != NULL) {
devChan->noteOn = false;
devChan->pitch = 0;
devChan->ops->noteOff(devChan->number, pitch);
if (!dynamicMode && midiChannel->prevPitch != 0) {
devChan->pitch = midiChannel->prevPitch;
devChan->noteOn = true;
devChan->ops->noteOn(devChan->number, midiChannel->prevPitch,
127); // TODO: Remember velocity!
midiChannel->pitch = midiChannel->prevPitch;
midiChannel->prevPitch = 0;
} else {
devChan->noteOn = false;
devChan->pitch = 0;
devChan->ops->noteOff(devChan->number, pitch);
midiChannel->pitch = 0;
}
}

if (midiChannel->prevPitch == pitch) {
midiChannel->prevPitch = 0;
}
}

Expand Down
7 changes: 7 additions & 0 deletions tests/asserts.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ void stub_usb_receive_note_on(u8 chan, u8 key, u8 velocity)
stub_usb_receive_byte(velocity);
}

void stub_usb_receive_note_off(u8 chan, u8 key)
{
stub_usb_receive_byte(0x80 + chan);
stub_usb_receive_byte(key);
stub_usb_receive_byte(0);
}

void stub_usb_receive_pitch_bend(u8 chan, u16 bend)
{
u8 lower = bend & 0x007F;
Expand Down
1 change: 1 addition & 0 deletions tests/asserts.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ void expect_usb_sent_byte(u8 value);
void stub_usb_receive_byte(u8 value);
void stub_usb_receive_cc(u8 chan, u8 cc, u8 value);
void stub_usb_receive_note_on(u8 chan, u8 key, u8 velocity);
void stub_usb_receive_note_off(u8 chan, u8 key);
void stub_usb_receive_pitch_bend(u8 chan, u16 bend);
void stub_comm_read_returns_midi_event(u8 status, u8 data, u8 data2);
void expect_ym2612_write_reg_any_data(u8 part, u8 reg);
Expand Down
4 changes: 3 additions & 1 deletion tests/system/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ int main(void)
e2e_test(test_sets_separate_ch3_operator_frequencies),
e2e_test(test_pitch_bends_ch3_special_mode_operators),
e2e_test(test_write_directly_to_ym2612_regs_via_sysex),
e2e_test(test_plays_pcm_sample)
e2e_test(test_plays_pcm_sample),
e2e_test(test_midi_last_note_played_priority_respected_on_fm),
e2e_test(test_midi_last_note_played_cleared_when_released_on_fm)
// clang-format on
};

Expand Down
43 changes: 43 additions & 0 deletions tests/system/test_e2e.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,46 @@ static void test_plays_pcm_sample(void** state)
expect_value(__wrap_SND_startPlay_PCM, loop, 0);
midi_receiver_read();
}

static void test_midi_last_note_played_priority_respected_on_fm(void** state)
{
stub_usb_receive_note_on(TEST_MIDI_CHANNEL_1, 48, 127);
expect_ym2612_write_channel(0, 0xA4, 0x1A);
expect_ym2612_write_channel(0, 0xA0, 0x84);
expect_ym2612_write_reg(0, 0x28, 0xF0);
midi_receiver_read();

stub_usb_receive_note_on(TEST_MIDI_CHANNEL_1, 50, 127);
expect_ym2612_write_channel(0, 0xA4, 0x1A);
expect_ym2612_write_channel(0, 0xA0, 0xD2);
expect_ym2612_write_reg(0, 0x28, 0xF0);
midi_receiver_read();

stub_usb_receive_note_off(TEST_MIDI_CHANNEL_1, 50);
expect_ym2612_write_channel(0, 0xA4, 0x1A);
expect_ym2612_write_channel(0, 0xA0, 0x84);
expect_ym2612_write_reg(0, 0x28, 0xF0);
midi_receiver_read();
}

static void test_midi_last_note_played_cleared_when_released_on_fm(void** state)
{
stub_usb_receive_note_on(TEST_MIDI_CHANNEL_1, 48, 127);
expect_ym2612_write_channel(0, 0xA4, 0x1A);
expect_ym2612_write_channel(0, 0xA0, 0x84);
expect_ym2612_write_reg(0, 0x28, 0xF0);
midi_receiver_read();

stub_usb_receive_note_on(TEST_MIDI_CHANNEL_1, 50, 127);
expect_ym2612_write_channel(0, 0xA4, 0x1A);
expect_ym2612_write_channel(0, 0xA0, 0xD2);
expect_ym2612_write_reg(0, 0x28, 0xF0);
midi_receiver_read();

stub_usb_receive_note_off(TEST_MIDI_CHANNEL_1, 48);
midi_receiver_read();

stub_usb_receive_note_off(TEST_MIDI_CHANNEL_1, 50);
expect_ym2612_write_reg(0, 0x28, 0x0);
midi_receiver_read();
}

0 comments on commit 6fbb961

Please sign in to comment.