Skip to content

Commit

Permalink
Implement portamento time CC
Browse files Browse the repository at this point in the history
  • Loading branch information
rhargreaves committed Jul 26, 2024
1 parent 939d420 commit 81ee5f6
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 90 deletions.
22 changes: 16 additions & 6 deletions src/midi.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typedef struct MidiChannel {
NotePriorityStack notePriority;
DeviceSelect deviceSelect;
bool portamento;
u16 portamentoTime;
} MidiChannel;

typedef enum MappingMode { MappingMode_Static, MappingMode_Dynamic, MappingMode_Auto } MappingMode;
Expand Down Expand Up @@ -876,9 +877,19 @@ static void setPortamentoMode(u8 chan, bool enable)
midiChannel->portamento = enable;
}

static void setPortamentoTime(u8 chan, u8 value)
{
MidiChannel* midiChannel = &midiChannels[chan];
u16 interval = (127 - value) * 10;
midiChannel->portamentoTime = interval;
}

void midi_cc(u8 chan, u8 controller, u8 value)
{
switch (controller) {
case CC_PORTAMENTO_TIME_MSB:
setPortamentoTime(chan, value);
break;
case CC_VOLUME:
channelVolume(chan, value);
break;
Expand Down Expand Up @@ -922,21 +933,20 @@ void midi_reset(void)
reset();
}

static void processChannelGlide(DeviceChannel* chan)
static void processChannelGlide(DeviceChannel* chan, u16 portamentoTime)
{
if (chan->glideTargetPitch == 0
|| (chan->glideTargetPitch == chan->pitch && chan->cents == 0)) {
return;
}

const u8 increment = 10;
s8 effectiveIncrement;
s16 effectiveIncrement;

if (chan->glideTargetPitch > chan->pitch) {
effectiveIncrement = increment;
effectiveIncrement = portamentoTime;
} else if (chan->glideTargetPitch < chan->pitch
|| (chan->glideTargetPitch == chan->pitch && chan->cents > 0)) {
effectiveIncrement = 0 - increment;
effectiveIncrement = 0 - portamentoTime;
}

PitchCents pc = { .pitch = chan->pitch, .cents = chan->cents };
Expand All @@ -956,7 +966,7 @@ static void processPortamento(void)
}
MidiChannel* midiChannel = &midiChannels[chan->midiChannel];
if (midiChannel->portamento && chan->noteOn) {
processChannelGlide(chan);
processChannelGlide(chan, midiChannel->portamentoTime);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/midi.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#define GENERAL_MIDI_PITCH_BEND_SEMITONE_RANGE 2
#define UNASSIGNED_MIDI_CHANNEL 0x7F

#define MAX_MIDI_CHANS 9
#define MIDI_CHANNELS 16
#define PSG_NOISE_CHAN 3
#define GENERAL_MIDI_PERCUSSION_CHANNEL 9
Expand All @@ -30,6 +29,7 @@
#define DEV_CHAN_MAX_SPECIAL_MODE 12
#define DEV_CHAN_DAC 13

#define CC_PORTAMENTO_TIME_MSB 5
#define CC_DATA_ENTRY_MSB 6
#define CC_VOLUME 7
#define CC_PAN 10
Expand All @@ -48,6 +48,7 @@
#define CC_GENMDM_DETUNE_OP2 25
#define CC_GENMDM_DETUNE_OP3 26
#define CC_GENMDM_DETUNE_OP4 27
#define CC_PORTAMENTO_TIME_LSB 37
#define CC_DATA_ENTRY_LSB 38
#define CC_GENMDM_RATE_SCALING_OP1 39
#define CC_GENMDM_RATE_SCALING_OP2 40
Expand Down
3 changes: 3 additions & 0 deletions tests/system/test_e2e.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cmocka.h>

static const u8 TEST_CC_PAN = 10;
static const u8 TEST_CC_PORTAMENTO_TIME = 5;
static const u8 TEST_CC_VOLUME = 7;
static const u8 TEST_CC_ALGORITHM = 14;
static const u8 TEST_CC_PORTAMENTO_ON = 65;
Expand Down Expand Up @@ -448,6 +449,8 @@ static void test_midi_changing_program_retains_volume(void** state)

static void test_midi_portamento_glides_note(void** state)
{
stub_usb_receive_cc(TEST_MIDI_CHANNEL_1, TEST_CC_PORTAMENTO_TIME, 126);
midi_receiver_read();
stub_usb_receive_cc(TEST_MIDI_CHANNEL_1, TEST_CC_PORTAMENTO_ON, 0x7F);
midi_receiver_read();

Expand Down
6 changes: 4 additions & 2 deletions tests/unit/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

#define midi_test(test) cmocka_unit_test_setup(test, test_midi_setup)
#define midi_pcm_test(test) cmocka_unit_test_setup(test, test_midi_setup)
#define midi_portamento_test(test) cmocka_unit_test_setup(test, test_midi_setup)
#define midi_portamento_test(test) cmocka_unit_test_setup(test, test_midi_portamento_setup)
#define dynamic_midi_test(test) cmocka_unit_test_setup(test, test_dynamic_midi_setup)
#define synth_test(test) cmocka_unit_test_setup(test, test_synth_setup)
#define comm_test(test) cmocka_unit_test_setup(test, test_comm_setup)
Expand Down Expand Up @@ -396,7 +396,9 @@ int main(void)
midi_portamento_test(test_midi_portamento_zeros_any_residual_cents),
midi_portamento_test(test_midi_portamento_glides_note_up_for_psg),
midi_portamento_test(test_midi_portamento_glides_note_up_with_pitch_bend),
midi_portamento_test(test_midi_portamento_glides_note_down_with_pitch_bend)
midi_portamento_test(test_midi_portamento_glides_note_down_with_pitch_bend),
midi_portamento_test(test_midi_portamento_sets_portamento_time_to_minimum),
midi_portamento_test(test_midi_portamento_sets_portamento_time_to_maximum)
// TODO: test for special mode glide
// clang-format on
};
Expand Down
80 changes: 0 additions & 80 deletions tests/unit/test_midi_fm.c
Original file line number Diff line number Diff line change
Expand Up @@ -735,83 +735,3 @@ static void test_midi_drops_note_when_note_priority_stack_full(UNUSED void** sta

__real_midi_note_on(0, 100, MAX_MIDI_VOLUME);
}

static void test_midi_effectivePitchCents(UNUSED void** state)
{
PitchCents pc = pitchcents_bend(50, 0, 0x2000);

assert_int_equal(pc.pitch, 50);
assert_int_equal(pc.cents, 0);
}

static void test_midi_effectivePitchCents_2(UNUSED void** state)
{
PitchCents pc = pitchcents_bend(50, 0, 0);

assert_int_equal(pc.pitch, 48);
assert_int_equal(pc.cents, 0);
}

static void test_midi_effectivePitchCents_3(UNUSED void** state)
{
PitchCents pc = pitchcents_bend(50, 0, 0x4000);

assert_int_equal(pc.pitch, 52);
assert_int_equal(pc.cents, 0);
}

static void test_midi_effectivePitchCents_4(UNUSED void** state)
{
PitchCents pc = pitchcents_bend(50, 0, 0x3000);

assert_int_equal(pc.pitch, 51);
assert_int_equal(pc.cents, 0);
}

static void test_midi_effectivePitchCents_5(UNUSED void** state)
{
PitchCents pc = pitchcents_bend(50, 0, 0x1800);

assert_int_equal(pc.pitch, 49);
assert_int_equal(pc.cents, 50);
}

static void test_midi_effectivePitchCents_6(UNUSED void** state)
{
PitchCents pc = pitchcents_bend(50, 0, 0x2800);

assert_int_equal(pc.pitch, 50);
assert_int_equal(pc.cents, 50);
}

static void test_midi_effectivePitchCents_cents_with_partial_bend_down(UNUSED void** state)
{
PitchCents pc = pitchcents_bend(50, 25, 0x1800);

assert_int_equal(pc.pitch, 49);
assert_int_equal(pc.cents, 75);
}

static void test_midi_effectivePitchCents_high_cents_with_partial_bend_down(UNUSED void** state)
{
PitchCents pc = pitchcents_bend(50, 80, 0x1800);

assert_int_equal(pc.pitch, 50);
assert_int_equal(pc.cents, 30);
}

static void test_midi_effectivePitchCents_cents_with_full_bend_up(UNUSED void** state)
{
PitchCents pc = pitchcents_bend(50, 25, 0x4000);

assert_int_equal(pc.pitch, 52);
assert_int_equal(pc.cents, 25);
}

static void test_midi_effectivePitchCents_high_cents_with_full_bend_up(UNUSED void** state)
{
PitchCents pc = pitchcents_bend(50, 80, 0x4000);

assert_int_equal(pc.pitch, 52);
assert_int_equal(pc.cents, 80);
}
50 changes: 50 additions & 0 deletions tests/unit/test_midi_portamento.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
#include "test_midi.h"

static int test_midi_portamento_setup(UNUSED void** state)
{
test_midi_setup(state);

for (u8 chan = 0; chan < MIDI_CHANNELS; chan++) {
__real_midi_cc(chan, CC_PORTAMENTO_TIME_MSB, 126);
}
return 0;
}

static void test_midi_portamento_glides_note_up(UNUSED void** state)
{
for (u8 chan = 0; chan < MAX_FM_CHANS; chan++) {
Expand Down Expand Up @@ -372,3 +382,43 @@ static void test_midi_portamento_glides_note_down_with_pitch_bend(UNUSED void**
midi_tick();
}
}

static void test_midi_portamento_sets_portamento_time_to_minimum(UNUSED void** state)
{
const u8 chan = 0;

__real_midi_cc(chan, CC_PORTAMENTO_ENABLE, 127);
__real_midi_cc(chan, CC_PORTAMENTO_TIME_MSB, 0);

expect_synth_pitch(chan, 2, 0x439);
expect_synth_volume_any();
expect_synth_noteOn(chan);
__real_midi_note_on(chan, MIDI_PITCH_A2, MAX_MIDI_VOLUME);
__real_midi_note_on(chan, MIDI_PITCH_C4, MAX_MIDI_VOLUME);

expect_synth_pitch(chan, 3, 0x466);
midi_tick();

expect_synth_pitch(chan, 4, 0x495);
midi_tick();
}

static void test_midi_portamento_sets_portamento_time_to_maximum(UNUSED void** state)
{
const u8 chan = 0;

__real_midi_cc(chan, CC_PORTAMENTO_ENABLE, 127);
__real_midi_cc(chan, CC_PORTAMENTO_TIME_MSB, 127);

expect_synth_pitch(chan, 2, 0x439);
expect_synth_volume_any();
expect_synth_noteOn(chan);
__real_midi_note_on(chan, MIDI_PITCH_A2, MAX_MIDI_VOLUME);
__real_midi_note_on(chan, MIDI_PITCH_C4, MAX_MIDI_VOLUME);

expect_synth_pitch(chan, 2, 0x439);
midi_tick();

expect_synth_pitch(chan, 2, 0x439);
midi_tick();
}
2 changes: 1 addition & 1 deletion tests/unit/test_midi_receiver.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static void test_midi_receiver_read_passes_note_on_to_midi_processor(UNUSED void
const u8 expectedData = 60;
const u8 expectedData2 = 127;

for (int chan = 0; chan < MAX_MIDI_CHANS; chan++) {
for (int chan = 0; chan < MIDI_CHANNELS; chan++) {
u8 expectedStatus = 0x90 + chan;

stub_comm_read_returns_midi_event(expectedStatus, expectedData, expectedData2);
Expand Down

0 comments on commit 81ee5f6

Please sign in to comment.