Skip to content

Commit df5aa70

Browse files
committed
feat: CC 87 stores preset based on channel's current parameters
Also to RAM & SRAM
1 parent eb77409 commit df5aa70

File tree

12 files changed

+141
-13
lines changed

12 files changed

+141
-13
lines changed

src/midi.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,17 @@ static void rpn_data_entry(MidiChannel* chan, u8 value, bool is_msb)
10431043
}
10441044
}
10451045

1046+
static void store_program_from_channel(u8 ch, u8 program)
1047+
{
1048+
FOREACH_DEV_CHAN_WITH_MIDI(ch, devChan) {
1049+
if (devChan->ops == &FM_VTable) {
1050+
midi_fm_store_preset_from_channel(devChan->num, program);
1051+
log_info("Prg %d: FM preset stored", program + 1);
1052+
break; // first chan we find only
1053+
}
1054+
}
1055+
}
1056+
10461057
void midi_cc(u8 ch, u8 controller, u8 value)
10471058
{
10481059
MidiChannel* chan = &midiChannels[ch];
@@ -1104,6 +1115,9 @@ void midi_cc(u8 ch, u8 controller, u8 value)
11041115
case CC_NRPN_LSB:
11051116
case CC_NRPN_MSB:
11061117
break;
1118+
case CC_STORE_PROGRAM:
1119+
store_program_from_channel(ch, value);
1120+
break;
11071121
default:
11081122
fm_parameter_cc(ch, controller, value);
11091123
break;

src/midi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
#define CC_GENMDM_POLYPHONIC_MODE 84
9494
#define CC_FINE_TUNE 85
9595
#define CC_DEVICE_SELECT 86
96+
#define CC_STORE_PROGRAM 87
9697
#define CC_SHOW_PARAMETERS_ON_UI 83
9798
#define CC_GENMDM_SSG_EG_OP1 90
9899
#define CC_GENMDM_SSG_EG_OP2 91

src/midi_fm.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,19 @@ void midi_fm_store_preset(u8 program, const FmPreset* preset)
186186
{
187187
memcpy(&userPresets[program], preset, sizeof(FmPreset));
188188
activeUserPresets[program] = &userPresets[program];
189-
190189
midi_fm_sram_save_preset(program, preset);
191190
}
192191

193192
void midi_fm_clear_preset(u8 program)
194193
{
195194
memset(&userPresets[program], 0, sizeof(FmPreset));
196195
activeUserPresets[program] = NULL;
197-
198196
midi_fm_sram_clear_preset(program);
199197
}
198+
199+
void midi_fm_store_preset_from_channel(u8 chan, u8 program)
200+
{
201+
FmPreset fmPreset;
202+
synth_extract_preset(chan, &fmPreset);
203+
midi_fm_store_preset(program, &fmPreset);
204+
}

src/midi_fm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ u8 midi_fm_pitch_to_octave(u8 pitch);
2424
void midi_fm_pitch(u8 chan, u8 pitch, s8 cents);
2525
u16 midi_fm_pitch_cents_to_freq_num(u8 pitch, s8 cents);
2626
void midi_fm_store_preset(u8 program, const FmPreset* preset);
27-
void midi_fm_clear_preset(u8 program);
27+
void midi_fm_clear_preset(u8 program);
28+
void midi_fm_store_preset_from_channel(u8 chan, u8 program);

src/synth.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,3 +501,15 @@ static void release_z80_bus(void)
501501
YM2612_write(0, YM_DAC_DATA); // Latch reg address for PCM driver
502502
Z80_releaseBus();
503503
}
504+
505+
void synth_extract_preset(u8 ch, FmPreset* preset)
506+
{
507+
const FmChannel* chan = synth_channel_parameters(ch);
508+
preset->algorithm = chan->algorithm;
509+
preset->feedback = chan->feedback;
510+
preset->ams = chan->ams;
511+
preset->fms = chan->fms;
512+
for (u8 op = 0; op < MAX_FM_OPERATORS; op++) {
513+
preset->operators[op] = chan->operators[op];
514+
}
515+
}

src/synth.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ void synth_fms(u8 channel, u8 fms);
8181
u8 synth_busy(void);
8282
void synth_preset(u8 channel, const FmPreset* preset);
8383
const FmChannel* synth_channel_parameters(u8 channel);
84+
void synth_extract_preset(u8 channel, FmPreset* preset);
8485
const Global* synth_global_parameters();
8586
void synth_set_parameter_update_callback(ParameterUpdatedCallback* cb);
8687
void synth_set_special_mode(bool enable);

tests/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ MOCKS=midi_process \
8282
synth_special_mode_volume \
8383
synth_direct_write_ym2612 \
8484
synth_enable_dac \
85+
synth_extract_preset \
8586
midi_note_off \
8687
midi_note_on \
8788
midi_pitch_bend \

tests/mocks/mock_synth.c

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,14 @@ void __wrap_synth_volume(u8 channel, u8 volume)
188188
check_expected(volume);
189189
}
190190

191+
void __wrap_synth_extract_preset(u8 channel, FmPreset* preset)
192+
{
193+
if (disableChecks)
194+
return;
195+
check_expected(channel);
196+
memcpy(preset, mock_type(FmPreset*), sizeof(FmPreset));
197+
}
198+
191199
const FmChannel* __wrap_synth_channel_parameters(u8 channel)
192200
{
193201
return NULL;
@@ -273,17 +281,54 @@ int fmpreset_equality_check(
273281
{
274282
FmPreset* expected = (FmPreset*)value;
275283
FmPreset* actual = (FmPreset*)check_value_data;
284+
bool mismatch = false;
285+
286+
if (actual->algorithm != expected->algorithm) {
287+
print_error("algorithm mismatch: %d != %d\n", actual->algorithm, expected->algorithm);
288+
mismatch = true;
289+
}
290+
291+
if (actual->ams != expected->ams) {
292+
print_error("ams mismatch: %d != %d\n", actual->ams, expected->ams);
293+
mismatch = true;
294+
}
295+
296+
if (actual->fms != expected->fms) {
297+
print_error("fms mismatch: %d != %d\n", actual->fms, expected->fms);
298+
mismatch = true;
299+
}
276300

277-
if ((actual->algorithm == expected->algorithm) && (actual->ams == expected->ams)
278-
&& (actual->feedback == expected->feedback) && (actual->fms == expected->fms)
279-
&& operator_equality_check(&actual->operators[0], &expected->operators[0])
280-
&& operator_equality_check(&actual->operators[1], &expected->operators[1])
281-
&& operator_equality_check(&actual->operators[2], &expected->operators[2])
282-
&& operator_equality_check(&actual->operators[3], &expected->operators[3])) {
283-
return 1;
301+
if (actual->feedback != expected->feedback) {
302+
print_error("feedback mismatch: %d != %d\n", actual->feedback, expected->feedback);
303+
mismatch = true;
284304
}
285305

286-
return 0;
306+
if (actual->fms != expected->fms) {
307+
print_error("fms mismatch: %d != %d\n", actual->fms, expected->fms);
308+
mismatch = true;
309+
}
310+
311+
if (!operator_equality_check(&actual->operators[0], &expected->operators[0])) {
312+
print_error("operator 0 mismatch\n");
313+
mismatch = true;
314+
}
315+
316+
if (!operator_equality_check(&actual->operators[1], &expected->operators[1])) {
317+
print_error("operator 1 mismatch\n");
318+
mismatch = true;
319+
}
320+
321+
if (!operator_equality_check(&actual->operators[2], &expected->operators[2])) {
322+
print_error("operator 2 mismatch\n");
323+
mismatch = true;
324+
}
325+
326+
if (!operator_equality_check(&actual->operators[3], &expected->operators[3])) {
327+
print_error("operator 3 mismatch\n");
328+
mismatch = true;
329+
}
330+
331+
return mismatch ? 0 : 1;
287332
}
288333

289334
void _expect_synth_algorithm(u8 channel, u8 algorithm, const char* const file, const int line)
@@ -448,3 +493,10 @@ void _expect_synth_preset(
448493
expect_value_with_pos(__wrap_synth_preset, channel, channel, file, line);
449494
expect_check_with_pos(__wrap_synth_preset, preset, fmpreset_equality_check, preset, file, line);
450495
}
496+
497+
void _expect_synth_extract_preset(
498+
u8 channel, FmPreset* preset, const char* const file, const int line)
499+
{
500+
expect_value_with_pos(__wrap_synth_extract_preset, channel, channel, file, line);
501+
will_return(__wrap_synth_extract_preset, preset);
502+
}

tests/mocks/mock_synth.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ void __wrap_synth_special_mode_pitch(u8 op, u8 octave, u16 freqNumber);
3838
void __wrap_synth_special_mode_volume(u8 op, u8 volume);
3939
void __wrap_synth_direct_write_ym2612(u8 part, u8 reg, u8 data);
4040
void __wrap_synth_enable_dac(bool enable);
41+
void __wrap_synth_extract_preset(u8 channel, FmPreset* preset);
42+
4143
extern void __real_synth_init(const FmPreset* defaultPreset);
4244
extern void __real_synth_note_on(u8 channel);
4345
extern void __real_synth_note_off(u8 channel);
@@ -70,7 +72,7 @@ extern void __real_synth_special_mode_pitch(u8 op, u8 octave, u16 freqNumber);
7072
extern void __real_synth_special_mode_volume(u8 op, u8 volume);
7173
extern void __real_synth_enable_dac(bool enable);
7274
extern void __real_synth_direct_write_ym2612(u8 part, u8 reg, u8 data);
73-
75+
extern void __real_synth_extract_preset(u8 channel, FmPreset* preset);
7476
int fmpreset_equality_check(
7577
const LargestIntegralType value, const LargestIntegralType check_value_data);
7678
void _expect_synth_algorithm(u8 channel, u8 algorithm, const char* const file, const int line);
@@ -115,6 +117,8 @@ void _expect_synth_direct_write_ym2612(
115117
u8 part, u8 reg, u8 data, const char* const file, const int line);
116118
void _expect_synth_preset(
117119
u8 channel, const FmPreset* preset, const char* const file, const int line);
120+
void _expect_synth_extract_preset(
121+
u8 channel, FmPreset* preset, const char* const file, const int line);
118122

119123
#define expect_synth_preset(channel, preset) \
120124
_expect_synth_preset(channel, preset, __FILE__, __LINE__)
@@ -168,3 +172,5 @@ void _expect_synth_preset(
168172
_expect_synth_set_special_mode(enable, __FILE__, __LINE__)
169173
#define expect_synth_direct_write_ym2612(part, reg, data) \
170174
_expect_synth_direct_write_ym2612(part, reg, data, __FILE__, __LINE__)
175+
#define expect_synth_extract_preset(channel, preset) \
176+
_expect_synth_extract_preset(channel, preset, __FILE__, __LINE__)

tests/unit/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ int main(void)
177177
midi_test(test_midi_sysex_logs_warning_if_clear_all_programs_type_is_incorrect),
178178
midi_test(test_midi_loads_presets_from_sram),
179179
midi_test(test_midi_sysex_does_not_display_loaded_msg_if_no_presets_are_loaded),
180+
midi_test(test_midi_cc_stores_program),
180181

181182
midi_test(
182183
test_midi_fm_note_on_percussion_channel_sets_percussion_preset),

tests/unit/test_midi_sysex.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,4 +471,37 @@ void test_midi_sysex_does_not_display_loaded_msg_if_no_presets_are_loaded(UNUSED
471471
mock_log_enable_checks();
472472
mock_synth_disable_checks();
473473
__real_midi_reset();
474+
}
475+
476+
void test_midi_cc_stores_program(UNUSED void** state)
477+
{
478+
const u8 program = 0x01;
479+
480+
expect_synth_algorithm(FM_CH1, 0x06);
481+
__real_midi_cc(MIDI_CHANNEL_1, CC_GENMDM_FM_ALGORITHM, 111);
482+
483+
FmPreset fmPreset;
484+
memcpy(&fmPreset, &TEST_M_BANK_0_INST_0_GRANDPIANO, sizeof(FmPreset));
485+
fmPreset.algorithm = 0x06;
486+
487+
mock_log_enable_checks();
488+
expect_synth_extract_preset(FM_CH1, &fmPreset);
489+
expect_log_info("Prg %d: FM preset stored");
490+
__real_midi_cc(MIDI_CHANNEL_1, CC_STORE_PROGRAM, program);
491+
492+
expect_synth_algorithm(FM_CH1, 0x00);
493+
__real_midi_cc(MIDI_CHANNEL_1, CC_GENMDM_FM_ALGORITHM, 0);
494+
495+
expect_synth_preset(FM_CH1, &fmPreset);
496+
__real_midi_program(MIDI_CHANNEL_1, program);
497+
498+
// check that the preset was stored to SRAM
499+
const u8 EXPECTED_SRAM_DATA[SRAM_PRESET_LENGTH]
500+
= { /* magic number */ 0x9E, 0x1D, /* version */ 0x01,
501+
/* preset */ 0xC0, 0x00, 0x11, 0xA4, 0xE7, 0x20, 0xA7, 0x00, 0x4D, 0x85, 0x26, 0x4B,
502+
0xA4, 0x00, 0x2F, 0xFE, 0xE9, 0x78, 0x84, 0x00, 0x17, 0xB8, 0x8A, 0x23, 0x02, 0x00,
503+
/* reserved */ 0x00, 0x00, 0x00, 0x00, 0x00, /* checksum */ 0x87, 0x77 };
504+
505+
u16 offset = SRAM_PRESET_START + (program * SRAM_PRESET_LENGTH);
506+
assert_memory_equal(mock_sram_data(offset), EXPECTED_SRAM_DATA, SRAM_PRESET_LENGTH);
474507
}

tests/unit/test_midi_sysex.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ void test_midi_sysex_logs_warning_if_program_clear_type_is_incorrect(UNUSED void
2626
void test_midi_sysex_clears_all_programs(UNUSED void** state);
2727
void test_midi_sysex_logs_warning_if_clear_all_programs_type_is_incorrect(UNUSED void** state);
2828
void test_midi_loads_presets_from_sram(UNUSED void** state);
29-
void test_midi_sysex_does_not_display_loaded_msg_if_no_presets_are_loaded(UNUSED void** state);
29+
void test_midi_sysex_does_not_display_loaded_msg_if_no_presets_are_loaded(UNUSED void** state);
30+
void test_midi_cc_stores_program(UNUSED void** state);

0 commit comments

Comments
 (0)