Skip to content

Commit

Permalink
Persist pitch bend between notes for FM channels
Browse files Browse the repository at this point in the history
Matches behaviour of PSG. FM special mode will be fixed in another commit
  • Loading branch information
rhargreaves committed Jun 27, 2024
1 parent 9380739 commit 1b623d8
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 8 deletions.
20 changes: 15 additions & 5 deletions src/midi_fm.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct MidiFmChannel {
u8 velocity;
u8 pan;
bool percussive;
u16 pitchBend;
};

static MidiFmChannel fmChannels[MAX_FM_CHANS];
Expand All @@ -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;
Expand All @@ -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]);
}
Expand All @@ -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);
}

Expand All @@ -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);
}

Expand Down
1 change: 1 addition & 0 deletions tests/unit/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
4 changes: 1 addition & 3 deletions tests/unit/test_midi_dynamic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down
19 changes: 19 additions & 0 deletions tests/unit/test_midi_fm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 1b623d8

Please sign in to comment.