Skip to content

Commit 099010a

Browse files
authored
Merge pull request #575 from shorepine/clear_one_midi_cc
src/parse.c: Rework 'ic255' handling
2 parents ee2434a + 5a3ac76 commit 099010a

4 files changed

Lines changed: 99 additions & 36 deletions

File tree

amy/test.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,57 @@ def test(self):
10061006
return is_ok, message
10071007

10081008

1009+
class TestClearMidiCCs(AmyTest):
1010+
"""Test that ic255 clears the MIDI CC settings."""
1011+
1012+
def test(self):
1013+
_amy.stop()
1014+
_amy.start(0)
1015+
amy.send(time=0, synth=1, num_voices=4, oscs_per_voice=2)
1016+
amy.send(time=0, synth=1, osc=0, wave=amy.SINE, freq=110, chained_osc=1)
1017+
amy.send(time=0, synth=1, osc=1, wave=amy.SAW_UP, freq=440)
1018+
amy.send_raw('i1ic5,0,0,10,0,hello')
1019+
amy.send_raw('i1ic10,1,1,100,1,i%id%v')
1020+
# Test that you can have other commands after the ic255 too.
1021+
amy.send_raw('i1ic255v0f999')
1022+
amy.render(1) # Let the events execute.
1023+
commands = amy.get_synth_commands(1)
1024+
expected = """v0f999.000c1Z
1025+
v1w3f440.000Z"""
1026+
if commands != expected:
1027+
is_ok = False
1028+
message = 'TestClearMidiCcs : get_synth_commands mismatch: expected:\n++\n%s\n--\n;saw:\n++\n%s\n--;' % (expected, commands)
1029+
else:
1030+
is_ok = True
1031+
message = 'TestClearMidiCcs : ok'
1032+
return is_ok, message
1033+
1034+
class TestClearOneMidiCC(AmyTest):
1035+
"""Test that ic5 with no further args clears that MIDI CC."""
1036+
1037+
def test(self):
1038+
_amy.stop()
1039+
_amy.start(0)
1040+
amy.send(time=0, synth=1, num_voices=4, oscs_per_voice=2)
1041+
amy.send(time=0, synth=1, osc=0, wave=amy.SINE, freq=110, chained_osc=1)
1042+
amy.send(time=0, synth=1, osc=1, wave=amy.SAW_UP, freq=440)
1043+
amy.send_raw('i1ic5,0,0,10,0,hello')
1044+
amy.send_raw('i1ic10,1,1,100,1,i%id%v')
1045+
amy.send_raw('i1ic5v0f999')
1046+
amy.render(1) # Let the events execute.
1047+
commands = amy.get_synth_commands(1)
1048+
expected = """v0f999.000c1Z
1049+
v1w3f440.000Z
1050+
ic10,1,1.000,100.000,1.000,i%id%vZ"""
1051+
if commands != expected:
1052+
is_ok = False
1053+
message = 'TestClearOneMidiCC : get_synth_commands mismatch: expected:\n++\n%s\n--\n;saw:\n++\n%s\n--;' % (expected, commands)
1054+
else:
1055+
is_ok = True
1056+
message = 'TestClearOneMidiCC : ok'
1057+
return is_ok, message
1058+
1059+
10091060
def main(argv):
10101061
if len(argv) > 1 and argv[1] == 'quiet':
10111062
quiet = True

src/amy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,7 @@ extern void reset_osc_by_pointer(struct synthinfo *psynth, struct mod_synthinfo
870870
extern void reset_osc(uint16_t i );
871871

872872
extern int midi_store_control_code(int channel, int code, int is_log, float min_val, float max_val, float offset_val, char *message);
873+
extern int midi_clear_control_code(int channel, int code);
873874
extern bool midi_fetch_control_code_command(int channel, int code, char *s, size_t len);
874875
extern void cc_mapping_debug();
875876
extern void midi_mappings_init();

src/midi_mappings.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,17 @@ struct cc_mapping **cc_mapping_find(int channel, int code) {
114114
return NULL;
115115
}
116116

117+
int midi_clear_control_code(int channel, int code) {
118+
if (code == 255) {
119+
// Magic value means clear all MIDI CCs for this channel
120+
midi_clear_channel_mappings(channel);
121+
return 1;
122+
}
123+
struct cc_mapping **p_mapping = cc_mapping_find(channel, code);
124+
if (p_mapping) { cc_mapping_free(p_mapping); return 1; }
125+
return 0; // nothing found.
126+
}
127+
117128
int midi_store_control_code(int channel, int code, int is_log, float min_val, float max_val, float offset_val, char *message) {
118129
// Register a MIDI control code and mapping and a wire code template.
119130
// Strip trailing wire protocol terminator(s) so they don't accumulate on round-trips.

src/parse.c

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,6 @@ float atoff(const char *s) {
6868
int16_t: atoi \
6969
)
7070

71-
#define PARSE_VAL_TO_SEP(type) \
72-
int parse_val_to_sep_##type(char *message, type *val, char sep) { \
73-
int c = 0; \
74-
*val = PARSE_LIST_ATO(*val)(message); \
75-
c = strspn(message, PARSE_LIST_STRSPN2(*val)); \
76-
if (message[c] == sep) { \
77-
return c + 1; \
78-
} \
79-
return -1; \
80-
}
81-
82-
PARSE_VAL_TO_SEP(float)
83-
PARSE_VAL_TO_SEP(int32_t)
84-
8571
#define PARSE_LIST(type) \
8672
int parse_list_##type(char *message, type *vals, int max_num_vals, type skipped_val) { \
8773
uint16_t c = 0, last_c; \
@@ -118,6 +104,17 @@ PARSE_LIST(int32_t)
118104
PARSE_LIST(int16_t)
119105

120106

107+
#define PARSE_VAL(type) \
108+
int parse_val_##type(char *message, type *val) { \
109+
int c = 0; \
110+
*val = PARSE_LIST_ATO(*val)(message); \
111+
c = strspn(message, PARSE_LIST_STRSPN2(*val)); \
112+
return c; \
113+
}
114+
115+
PARSE_VAL(float)
116+
PARSE_VAL(int32_t)
117+
121118
char *copy_with_trim(char *dest, size_t dest_len, const char *src, size_t src_len) {
122119
// Copy a string while trimming leading and trailing spaces.
123120
const char *s = src;
@@ -310,18 +307,17 @@ extern const mp_obj_fun_builtin_var_t tulip_pcm_load_file_obj;
310307
#endif
311308

312309
int parse_midi_cc_payload(char *message, int32_t *p_cc_code, int32_t *p_is_log, float *p_min_val, float *p_max_val, float *p_offset_val) {
313-
int c;
314310
char *m = message;
315-
if ((c = parse_val_to_sep_int32_t(m, p_cc_code, ',')) < 0) return -1;
316-
m += c;
317-
if ((c = parse_val_to_sep_int32_t(m, p_is_log, ',')) < 0) return -1;
318-
m += c;
319-
if ((c = parse_val_to_sep_float(m, p_min_val, ',')) < 0) return -1;
320-
m += c;
321-
if ((c = parse_val_to_sep_float(m, p_max_val, ',')) < 0) return -1;
322-
m += c;
323-
if ((c = parse_val_to_sep_float(m, p_offset_val, ',')) < 0) return -1;
324-
m += c;
311+
m += parse_val_int32_t(m, p_cc_code);
312+
if (m[0] != ',') goto end; else ++m;
313+
m += parse_val_int32_t(m, p_is_log);
314+
if (m[0] != ',') goto end; else ++m;
315+
m += parse_val_float(m, p_min_val);
316+
if (m[0] != ',') goto end; else ++m;
317+
m += parse_val_float(m, p_max_val);
318+
if (m[0] != ',') goto end; else ++m;
319+
m += parse_val_float(m, p_offset_val);
320+
end:
325321
return m - message;
326322
}
327323

@@ -345,20 +341,24 @@ int amy_parse_synth_layer_message(char *message, amy_event *e) {
345341
else if (cmd == 'c') {
346342
// MIDI CC mapping ic<C>,<L>,<N>,<X>,<O>,<CODE>, see https://github.com/shorepine/amy/issues/524
347343
// ic255 clears all MIDI CC mappings for this synth (short form, no extra fields needed).
348-
if (atoi(message) == 255) {
349-
midi_clear_channel_mappings(e->synth);
350-
skip_chars = strlen(message) + 1;
351-
} else {
352-
int32_t cc_code, is_log;
353-
float min_val, max_val, offset_val;
354-
skip_chars = parse_midi_cc_payload(message, &cc_code, &is_log, &min_val, &max_val, &offset_val);
355-
if (skip_chars < 0) {
344+
int32_t cc_code, is_log;
345+
float min_val, max_val, offset_val;
346+
AMY_UNSET(cc_code);
347+
AMY_UNSET(is_log);
348+
skip_chars = parse_midi_cc_payload(message, &cc_code, &is_log, &min_val, &max_val, &offset_val);
349+
if (*(message + skip_chars) != ',') {
350+
if (AMY_IS_UNSET(cc_code) || AMY_IS_SET(is_log)) {
351+
// Either parsing bailed without even a CC code, or it got past the is_log, meaning it wasn't a bare ic<NUM> command.
356352
fprintf(stderr, "synth_layer: midi cc payload didn't parse for %s.\n", message - 1);
357-
return 1; // skip over the 'c'.
353+
return skip_chars; // maybe the rest will parse?
358354
}
359-
midi_store_control_code(e->synth, cc_code, is_log, min_val, max_val, offset_val, message + skip_chars);
360-
skip_chars = strlen(message) + 1;
355+
// Else we got an incomplete message with a valid CC code - clear it
356+
midi_clear_control_code(e->synth, cc_code); // (handles 255 as special case).
357+
return skip_chars;
361358
}
359+
++skip_chars; // step over the "," before the wire string template.
360+
midi_store_control_code(e->synth, cc_code, is_log, min_val, max_val, offset_val, message + skip_chars);
361+
skip_chars = strlen(message) + 1;
362362
}
363363
else fprintf(stderr, "Unrecognized synth-level command '%s'\n", message - 1);
364364
return skip_chars;

0 commit comments

Comments
 (0)