From 1b623d8f5a2d169214a11841eb3d7df376d32c9e Mon Sep 17 00:00:00 2001 From: Robert Hargreaves Date: Thu, 27 Jun 2024 22:01:50 +0100 Subject: [PATCH] Persist pitch bend between notes for FM channels Matches behaviour of PSG. FM special mode will be fixed in another commit --- src/midi_fm.c | 20 +++++++++++++++----- tests/unit/main.c | 1 + tests/unit/test_midi_dynamic.c | 4 +--- tests/unit/test_midi_fm.c | 19 +++++++++++++++++++ 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/midi_fm.c b/src/midi_fm.c index 1979c04..34fa8f2 100644 --- a/src/midi_fm.c +++ b/src/midi_fm.c @@ -18,6 +18,7 @@ struct MidiFmChannel { u8 velocity; u8 pan; bool percussive; + u16 pitchBend; }; static MidiFmChannel fmChannels[MAX_FM_CHANS]; @@ -26,6 +27,7 @@ static u8 pitchIsOutOfRange(u8 pitch); static u8 effectiveVolume(MidiFmChannel* channelState); static void updatePan(u8 chan); void midi_fm_reset(void); +u16 effectiveFreq(MidiFmChannel* fmChan); static const FmChannel** presets; static const PercussionPreset** percussionPresets; @@ -46,6 +48,7 @@ void midi_fm_reset(void) fmChan->velocity = MAX_MIDI_VOLUME; fmChan->pan = 0; fmChan->percussive = false; + fmChan->pitchBend = DEFAULT_MIDI_PITCH_BEND; } synth_init(presets[0]); } @@ -64,8 +67,9 @@ void midi_fm_note_on(u8 chan, u8 pitch, u8 velocity) fmChan->velocity = velocity; synth_volume(chan, effectiveVolume(fmChan)); fmChan->pitch = pitch; - synth_pitch( - chan, midi_fm_pitchToOctave(pitch), midi_fm_pitchToFreqNumber(pitch)); + + u16 freq = effectiveFreq(fmChan); + synth_pitch(chan, midi_fm_pitchToOctave(fmChan->pitch), freq); synth_noteOn(chan); } @@ -82,12 +86,18 @@ void midi_fm_channel_volume(u8 chan, u8 volume) synth_volume(chan, effectiveVolume(fmChan)); } +u16 effectiveFreq(MidiFmChannel* fmChan) +{ + u16 freq = midi_fm_pitchToFreqNumber(fmChan->pitch); + s16 bendRelative = fmChan->pitchBend - MIDI_PITCH_BEND_CENTRE; + return freq + (bendRelative / 75); +} + void midi_fm_pitch_bend(u8 chan, u16 bend) { MidiFmChannel* fmChan = &fmChannels[chan]; - u16 freq = midi_fm_pitchToFreqNumber(fmChan->pitch); - s16 bendRelative = bend - MIDI_PITCH_BEND_CENTRE; - freq = freq + (bendRelative / 75); + fmChan->pitchBend = bend; + u16 freq = effectiveFreq(fmChan); synth_pitch(chan, midi_fm_pitchToOctave(fmChan->pitch), freq); } diff --git a/tests/unit/main.c b/tests/unit/main.c index af349f3..fa5c2db 100644 --- a/tests/unit/main.c +++ b/tests/unit/main.c @@ -98,6 +98,7 @@ int main(void) midi_test(test_midi_channel_volume_sets_psg_attenuation), midi_test(test_midi_channel_volume_sets_psg_attenuation_2), midi_test(test_midi_sets_synth_pitch_bend), + midi_test(test_midi_persists_pitch_bend_between_notes), midi_test(test_midi_sets_psg_pitch_bend_down), midi_test(test_midi_sets_psg_pitch_bend_up), midi_test(test_midi_psg_pitch_bend_persists_after_tick), diff --git a/tests/unit/test_midi_dynamic.c b/tests/unit/test_midi_dynamic.c index 8c4a1f6..225a4e7 100644 --- a/tests/unit/test_midi_dynamic.c +++ b/tests/unit/test_midi_dynamic.c @@ -368,7 +368,6 @@ static void test_midi_dynamic_maintains_pan_on_remapping(UNUSED void** state) static void test_midi_dynamic_maintains_pitch_bend_on_remapping( UNUSED void** state) { - /* Buggy behaviour characterisation */ const u16 midi_bend = 0x3000; // Note 1 @@ -383,8 +382,7 @@ static void test_midi_dynamic_maintains_pitch_bend_on_remapping( // Note 2 expect_synth_pitch(1, 7, 0x295); - expect_synth_pitch(1, 7, - 0x25f); // defaults back as pitch bend not taken into consideration on + expect_synth_pitch(1, 7, 0x295); // triggered when pitch bend "copied" // note on expect_synth_volume(1, MAX_MIDI_VOLUME); expect_value(__wrap_synth_noteOn, channel, 1); diff --git a/tests/unit/test_midi_fm.c b/tests/unit/test_midi_fm.c index face197..e65aebe 100644 --- a/tests/unit/test_midi_fm.c +++ b/tests/unit/test_midi_fm.c @@ -452,6 +452,25 @@ static void test_midi_sets_synth_pitch_bend(UNUSED void** state) } } +static void test_midi_persists_pitch_bend_between_notes(UNUSED void** state) +{ + for (int chan = 0; chan <= MAX_FM_CHAN; chan++) { + print_message("chan %d\n", chan); + expect_synth_pitch(chan, 4, SYNTH_NTSC_C); + expect_synth_volume_any(); + expect_value(__wrap_synth_noteOn, channel, chan); + __real_midi_note_on(chan, 60, MAX_MIDI_VOLUME); + + expect_synth_pitch(chan, 4, 0x225); + __real_midi_pitch_bend(chan, 1000); + + expect_synth_pitch(chan, 4, 0x225); + expect_synth_volume_any(); + expect_value(__wrap_synth_noteOn, channel, chan); + __real_midi_note_on(chan, 60, MAX_MIDI_VOLUME); + } +} + static void remap_midi_channel(u8 midiChannel, u8 deviceChannel) { u8 sequence[] = { SYSEX_MANU_EXTENDED, SYSEX_MANU_REGION, SYSEX_MANU_ID,