From a81a0f87dade1fd734301a1ec3a007338b64e7be Mon Sep 17 00:00:00 2001 From: Albert Graef Date: Sat, 15 Sep 2018 23:52:56 +0200 Subject: [PATCH] Add synthetic macro events, change detection, and some refactoring bugfixes. --- midizap.c | 84 +++++++++++++++++++++++++++++++++++----------------- midizap.h | 7 ++++- readconfig.c | 83 ++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 125 insertions(+), 49 deletions(-) diff --git a/midizap.c b/midizap.c index 7164236..1f48072 100644 --- a/midizap.c +++ b/midizap.c @@ -99,15 +99,14 @@ void handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive); void -send_midi(uint8_t portno, int status, int data, - int step, int n_steps, int *steps, - int incr, int index, int dir, +send_midi(uint8_t portno, stroke *s, int index, int dir, int mod, int mod_step, int mod_n_steps, int *mod_steps, - int val, int swap, - int recursive, int depth, - uint8_t ret_msg[3]) + int val, int depth, uint8_t ret_msg[3]) { - if (!jack_num_outputs) return; // MIDI output not enabled + int status = s->status, data = s->data, swap = s->swap, + recursive = s->recursive; + int step = s->step, n_steps = s->n_steps, *steps = s->steps; + if (!recursive && !jack_num_outputs) return; // MIDI output not enabled uint8_t msg[3]; int chan = status & 0x0f; msg[0] = status; @@ -135,6 +134,10 @@ send_midi(uint8_t portno, int status, int data, int v = datavals(r, step, steps, n_steps); if (d > 127 || d < 0) return; if (v > 127 || v < 0) return; + if (s->change) { + if (s->change > 1 && s->d == d && s->v == v) return; // unchanged value + s->d = d; s->v = v; s->change = 2; // >1 => initialized + } msg[1] = d; msg[2] = v; } else if (!index) { @@ -145,7 +148,7 @@ send_midi(uint8_t portno, int status, int data, break; case 0xb0: if (dir) { - if (incr) { + if (s->incr) { // incremental controller, simply spit out a relative sign bit value if (!step) step = 1; dir *= step; @@ -169,11 +172,16 @@ send_midi(uint8_t portno, int status, int data, msg[2] = ccvalue[portno][chan][data]; } } else if (mod) { + int m = (data>=128)*128; int q = swap?val%mod:val/mod, r = swap?val/mod:val%mod; int d = msg[1] + datavals(q, mod_step, mod_steps, mod_n_steps); int v = datavals(r, step, steps, n_steps); - if (d > 127 || d < 0) return; + if (d-m > 127 || d-m < 0) return; if (v > 127 || v < 0) return; + if (s->change) { + if (s->change > 1 && s->d == d && s->v == v) return; // unchanged value + s->d = d; s->v = v; s->change = 2; // >1 => initialized + } msg[1] = d; msg[2] = v; } else if (!index) { @@ -204,6 +212,10 @@ send_midi(uint8_t portno, int status, int data, int v = datavals(r, step, steps, n_steps); if (d > 127 || d < 0) return; if (v > 127 || v < 0) return; + if (s->change) { + if (s->change > 1 && s->d == d && s->v == v) return; // unchanged value + s->d = d; s->v = v; s->change = 2; // >1 => initialized + } msg[1] = d; msg[2] = v; } else if (!index) { @@ -231,6 +243,10 @@ send_midi(uint8_t portno, int status, int data, } else if (mod) { int v = datavals(swap?val/mod:val%mod, step, steps, n_steps); if (v > 127 || v < 0) return; + if (s->change) { + if (s->change > 1 && s->v == v) return; // unchanged value + s->v = v; s->change = 2; // >1 => initialized + } msg[1] = v; } else if (!index) { msg[1] = dataval(step, 0, 127); @@ -258,6 +274,10 @@ send_midi(uint8_t portno, int status, int data, } else if (mod) { int v = datavals(swap?val/mod:val%mod, step, steps, n_steps); if (v > 16383 || v < 0) return; + if (s->change) { + if (s->change > 1 && s->v == v) return; // unchanged value + s->v = v; s->change = 2; // >1 => initialized + } pbval = v; } else if (!index) { pbval = 8192+dataval(step, -8192, 8191); @@ -276,6 +296,10 @@ send_midi(uint8_t portno, int status, int data, if (mod) { int d = msg[1] + datavals(swap?val%mod:val/mod, mod_step, mod_steps, mod_n_steps); if (d > 127 || d < 0) return; + if (s->change) { + if (s->change > 1 && s->d == d) return; // unchanged value + s->d = d; s->change = 2; // >1 => initialized + } msg[1] = d; } // just send the message @@ -284,10 +308,17 @@ send_midi(uint8_t portno, int status, int data, return; } if (ret_msg) memcpy(ret_msg, msg, 3); - if (!recursive) - queue_midi(&seq, msg, portno); - else + if (recursive) { + // As these values may be mutated, we need to save and restore them, in + // case a macro calls itself recursively. + uint8_t change = s->change; + int d = s->d, v = s->v; + s->change = change>0; handle_event(msg, portno, depth+1, recursive); + s->change = change; + s->d = d; s->v = v; + } else + queue_midi(&seq, msg, portno); } static int stroke_data_cmp(const void *a, const void *b) @@ -615,21 +646,24 @@ static char *debug_key(translation *tr, char *name, suffix = (dir<0)?"-":"+"; else suffix = ""; + // check for pseudo CC messages denoting a macro + char *tok = data>=128?"M":"CC"; + data %= 128; if (dir && step != 1) - sprintf(name, "%sCC%d[%d]-%d%s", prefix, data, step, chan+1, suffix); + sprintf(name, "%s%s%d[%d]-%d%s", prefix, tok, data, step, chan+1, suffix); else if (!dir && mod) if (step != 1) - sprintf(name, "%sCC%d[%d][%d]-%d%s", prefix, data, mod, step, chan+1, suffix); + sprintf(name, "%s%s%d[%d][%d]-%d%s", prefix, tok, data, mod, step, chan+1, suffix); else if (n_steps) { - sprintf(name, "%sCC%d[%d]{", prefix, data, mod); + sprintf(name, "%s%s%d[%d]{", prefix, tok, data, mod); int l = strlen(name); for (int i = 0; i < n_steps; i++, (l = strlen(name))) sprintf(name+l, "%s%d", i?",":"", steps[i]); sprintf(name+l, "}-%d%s", chan+1, suffix); } else - sprintf(name, "%sCC%d[%d]-%d%s", prefix, data, mod, chan+1, suffix); + sprintf(name, "%s%s%d[%d]-%d%s", prefix, tok, data, mod, chan+1, suffix); else - sprintf(name, "%sCC%d-%d%s", prefix, data, chan+1, suffix); + sprintf(name, "%s%s%d-%d%s", prefix, tok, data, chan+1, suffix); break; } case 0xc0: @@ -841,22 +875,18 @@ send_strokes(translation *tr, uint8_t portno, int status, int chan, if (!s->recursive && jack_num_outputs > 1) { if (s->feedback == 1) // direct feedback, simply flip the port number - send_midi(!portno, - s->status, s->data, s->step, s->n_steps, s->steps, - s->incr, index, dir, mod, - step, n_steps, steps, data2, s->swap, 0, depth, 0); + send_midi(!portno, s, index, dir, mod, + step, n_steps, steps, data2, depth, 0); else if (portno == 0 && !mod && !dir) // shift feedback, this only works with key translations right // now, and portno *must* be zero - send_midi(1, s->status, s->data, s->step, s->n_steps, s->steps, - s->incr, !shift, 0, 0, - step, n_steps, steps, data2, s->swap, 0, depth, + send_midi(1, s, !shift, 0, 0, + step, n_steps, steps, data2, depth, shift?shift_fb[shift-1]:0); } } else { - send_midi(portno, s->status, s->data, s->step, s->n_steps, s->steps, - s->incr, index, dir, mod, - step, n_steps, steps, data2, s->swap, s->recursive, depth, 0); + send_midi(portno, s, index, dir, mod, + step, n_steps, steps, data2, depth, 0); } } s = s->next; diff --git a/midizap.h b/midizap.h index c2a9d4e..e39895e 100644 --- a/midizap.h +++ b/midizap.h @@ -62,6 +62,11 @@ typedef struct _stroke { // the swap bit indicates that 1st and 2nd data byte are to be swapped in // mod translations uint8_t swap; + // the change bit indicates that the message should only be output if its + // value has changed since the last time + uint8_t change; + // cached values for the change bit + int d, v; // the recursive bit indicates a MIDI message which is to be translated // recursively uint8_t recursive; @@ -83,7 +88,7 @@ typedef struct _stroke_data { // incr flag (CC only) uint8_t is_incr; // modulus - uint8_t mod; + uint16_t mod; // anyshift flag (default rule) uint8_t anyshift; } stroke_data; diff --git a/readconfig.c b/readconfig.c index 0df12e3..e1e3c21 100644 --- a/readconfig.c +++ b/readconfig.c @@ -678,7 +678,10 @@ print_stroke(stroke *s, int mod, int step, int n_steps, int *steps, int val) } else { int status = s->status & 0xf0; int channel = (s->status & 0x0f) + 1; - char *suffix = s->swap?"'":s->incr?"~":""; + char suffix[3] = ""; + if (s->incr) strcpy(suffix, "~"); + if (s->swap) strcat(suffix, "'"); + if (s->change) strcat(suffix, "?"); if (s->recursive) printf("$"); if (s->feedback) printf(s->feedback==2?"^":"!"); switch (status) { @@ -722,22 +725,27 @@ print_stroke(stroke *s, int mod, int step, int n_steps, int *steps, int val) printf("KP:%s%d-%d%s ", note_name(s->data), note_octave(s->data), channel, suffix); break; - case 0xb0: + case 0xb0: { + // check for pseudo CC messages denoting a macro + int data = s->data; + char *tok = data>=128?"M":"CC"; + data %= 128; if (mod) { int q = s->swap?val%mod:val/mod, r = s->swap?val/mod:val%mod; - int d = s->data + datavals(q, step, steps, n_steps); + int d = data + datavals(q, step, steps, n_steps); int v = datavals(r, s->step, s->steps, s->n_steps); - printf("CC%d[%d]-%d%s ", d, v, channel, suffix); + printf("%s%d[%d]-%d%s ", tok, d, v, channel, suffix); } else if (s->steps) { - printf("CC%d{", s->data); + printf("%s%d{", tok, data); for (int i = 0; i < s->n_steps; i++) printf("%s%d", i?",":"", s->steps[i]); printf("}-%d%s ", channel, suffix); } else if (s->step) - printf("CC%d[%d]-%d%s ", s->data, s->step, channel, suffix); + printf("%s%d[%d]-%d%s ", tok, data, s->step, channel, suffix); else - printf("CC%d-%d%s ", s->data, channel, suffix); + printf("%s%d-%d%s ", tok, data, channel, suffix); break; + } case 0xc0: if (mod) { int v = datavals(s->swap?val%mod:val/mod, s->step, s->steps, s->n_steps); @@ -861,7 +869,7 @@ append_nop(void) void append_midi(int status, int data, int step, int n_steps, int *steps, - int swap, int incr, int recursive, int feedback) + int swap, int change, int incr, int recursive, int feedback) { stroke *s = (stroke *)allocate(sizeof(stroke)); @@ -869,6 +877,7 @@ append_midi(int status, int data, int step, int n_steps, int *steps, s->status = status; s->data = data; s->swap = swap; + s->change = change; s->step = step; s->n_steps = n_steps; s->steps = stepsdup(n_steps, steps); @@ -1020,7 +1029,7 @@ static int note_number(char c, char b, int k) } } -#define MAXSTEPS 8193 +#define MAXSTEPS 16384 static char *parse_steps(char *tok, char *p, int *step, int *n_steps, int **steps) @@ -1100,9 +1109,9 @@ static char *parse_steps(char *tok, char *p, } int -parse_midi(char *tok, char *s, int lhs, int mode, +parse_midi(char *tok, char *s, int lhs, int mode, int recursive, int *status, int *data, int *step, int *n_steps, int **steps, - int *incr, int *dir, int *mod, int *swap) + int *incr, int *dir, int *mod, int *swap, int *change) { char *p = tok, *t; int n, m = -1, k = midi_channel; @@ -1192,12 +1201,27 @@ parse_midi(char *tok, char *s, int lhs, int mode, return 0; } } - *incr = *dir = *swap = 0; + *incr = *dir = *swap = *change = 0; if (*p == '\'') { // swap flag (only on rhs in mod translations) if (lhs || mode < 2) return 0; *swap = 1; p++; + if (*p == '?') { + // change flag + *change = 1; + p++; + } + } else if (*p == '?') { + // change flag (only on rhs in mod translations) + if (lhs || mode < 2) return 0; + *change = 1; + p++; + if (*p == '\'') { + // swap flag + *swap = 1; + p++; + } } else if (*p && strchr("+-=<>~", *p)) { // incremental flag (messages with data only, not "ch" or "pc") if (strcmp(s, "ch") == 0) return 0; @@ -1229,7 +1253,7 @@ parse_midi(char *tok, char *s, int lhs, int mode, if (strcmp(s, "ch") == 0) { if (lhs) return 0; // not permitted on lhs if (*step || *n_steps) return 0; // not permitted - if (*swap || steps2 || n_steps2) return 0; // not permitted + if (*swap || *change || steps2 || n_steps2) return 0; // not permitted // we return a bogus status of 0 here, along with the MIDI channel // in the data byte; also check that the MIDI channel is in the // proper range @@ -1280,6 +1304,22 @@ parse_midi(char *tok, char *s, int lhs, int mode, if (lhs && *incr && *step < 0) return 0; // not permitted if (lhs && !*step) *step = 1; // default return 1; + } else if (strcmp(s, "m") == 0) { + // macro, encoded as a pseudo cc message which cannot actually occur on + // input; this is only permitted in macro calls or on the lhs of a mod + // translation + if (m < 0 || m > 127) return 0; + *status = 0xb0 | k; *data = m+128; + // step size on lhs indicates modulus if non-incremental + if (lhs && *step && !*incr) { + *mod = *step; *step = step2; + *n_steps = n_steps2; *steps = steps2; + if (*mod < 0) *mod = 128; + } else if (lhs || !recursive) + return 0; // not permitted + if (lhs && *incr && *step < 0) return 0; // not permitted + if (lhs && !*step) *step = 1; // default + return 1; } else if (strcmp(s, "kp") == 0) { // key pressure if (m < 0 || m > 127) return 0; @@ -1345,7 +1385,7 @@ static void dup_stroke_data(stroke_data **sd, uint16_t *n, uint16_t *a, } else { append_midi(s->status, s->data, s->step, s->n_steps, s->steps, - s->swap, s->incr, s->recursive, s->feedback); + s->swap, s->change, s->incr, s->recursive, s->feedback); } s = s->next; } @@ -1358,7 +1398,8 @@ static void dup_stroke_data(stroke_data **sd, uint16_t *n, uint16_t *a, int start_translation(translation *tr, char *which_key) { - int status, data, step, n_steps, *steps, incr, dir, mod, swap, anyshift; + int status, data, step, n_steps, *steps, incr, dir, mod, swap, change, + anyshift; char buf[100]; //printf("start_translation(%s)\n", which_key); @@ -1386,7 +1427,7 @@ start_translation(translation *tr, char *which_key) } else { anyshift = 1; } - if (parse_midi(which_key+offs, buf, 1, 0, &status, &data, &step, &n_steps, &steps, &incr, &dir, &mod, &swap)) { + if (parse_midi(which_key+offs, buf, 1, 0, 0, &status, &data, &step, &n_steps, &steps, &incr, &dir, &mod, &swap, &change)) { int chan = status & 0x0f; mode = incr?0:mod?2:1; switch (status & 0xf0) { @@ -1615,7 +1656,7 @@ add_release(int all_keys) if (!s->keysym && !s->shift && s->dirty) { append_midi(s->status, s->data, s->step, s->n_steps, s->steps, - s->swap, s->incr, s->recursive, s->feedback); + s->swap, s->change, s->incr, s->recursive, s->feedback); s->dirty = 0; } s = s->next; @@ -1640,7 +1681,7 @@ add_release(int all_keys) } else { append_midi(s->status, s->data, s->step, s->n_steps, s->steps, - s->swap, s->incr, s->recursive, s->feedback); + s->swap, s->change, s->incr, s->recursive, s->feedback); } s = s->next; } @@ -1678,14 +1719,14 @@ add_string(char *str) void add_midi(char *tok) { - int status, data, step, n_steps, *steps, incr, dir = 0, mod = 0, swap = 0; + int status, data, step, n_steps, *steps, incr, dir = 0, mod = 0, swap = 0, change = 0; int recursive = *tok == '$', fb = *tok == '!', fb2 = *tok == '^'; char buf[100]; if (fb2 && mode != 1) { fprintf(stderr, "shift feedback only allowed in key translations: %s\n", tok); return; } - if (parse_midi(tok+recursive+fb+fb2, buf, 0, mode, &status, &data, &step, &n_steps, &steps, &incr, &dir, &mod, &swap)) { + if (parse_midi(tok+recursive+fb+fb2, buf, 0, mode, recursive, &status, &data, &step, &n_steps, &steps, &incr, &dir, &mod, &swap, &change)) { if (status == 0) { // 'ch' token; this doesn't actually generate any output, it just sets // the default MIDI channel @@ -1694,7 +1735,7 @@ add_midi(char *tok) fprintf(stderr, "invalid macro call: %s\n", tok); } else { append_midi(status, data, step, n_steps, steps, - swap, incr!=0, recursive, fb2?2:fb); + swap, change, incr!=0, recursive, fb2?2:fb); } } else { // inspect the token that was actually recognized (if any) to give some