From ffa3ba5e0527c842c43daea6ff1613ed3b177cb1 Mon Sep 17 00:00:00 2001 From: Albert Graef Date: Sun, 12 Aug 2018 23:52:11 +0200 Subject: [PATCH] Allow step sizes also with keypress-like events (note etc.) in the output, improved debugging output. --- midizap.c | 78 ++++++++++++++++++++++++++++++++++----- midizap.h | 2 +- readconfig.c | 102 +++++++++++++++++++++++++++++++-------------------- 3 files changed, 132 insertions(+), 50 deletions(-) diff --git a/midizap.c b/midizap.c index d1ee30f..9ca7b07 100644 --- a/midizap.c +++ b/midizap.c @@ -78,7 +78,8 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i switch (status & 0xf0) { case 0x90: if (!index) { - msg[2] = 127; + msg[2] = step?step:127; + if (msg[2] > 127) msg[2] = 127; } else { msg[2] = 0; } @@ -91,7 +92,7 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i } else { // increment (dir==1) or decrement (dir==-1) the current value, // clamping it to the 0..127 data byte range - if (!step) return; + if (!step) step = 1; dir *= step; if (dir > 0) { if (ccvalue[chan][data] >= 127) return; @@ -105,7 +106,8 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i msg[2] = ccvalue[chan][data]; } } else if (!index) { - msg[2] = 127; + msg[2] = step?step:127; + if (msg[2] > 127) msg[2] = 127; } else { msg[2] = 0; } @@ -115,7 +117,7 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i // range (0..16383, with 8192 being the center value) int pbval = 0; if (dir) { - if (!step) return; + if (!step) step = 1; dir *= step; if (dir > 0) { if (pbvalue[chan] >= 16383) return; @@ -128,7 +130,8 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i } pbval = pbvalue[chan]; } else if (!index) { - pbval = 16383; + pbval = 8192+(step?step:8191); + if (pbval > 16383) pbval = 16383; } else { // we use 8192 (center) as the "home" (a.k.a. "off") value, so the pitch // will only bend up, never down below the center value @@ -192,10 +195,12 @@ void reload_callback(void) static void debug_section(translation *tr) { - if (tr && debug_regex && (!have_window || tr != last_translation)) { + // we do some caching of the last printed translation here, so that we don't + // print the same message twice + if (debug_regex && (!have_window || tr != last_translation)) { last_translation = tr; have_window = 1; - if (tr != NULL) { + if (tr) { printf("translation: %s for %s (class %s)\n", tr->name, last_window_name, last_window_class); } else { @@ -266,6 +271,32 @@ static void debug_input(translation *tr, int portno, debug_key(tr, name, status, chan, data, 0), data2); } +// Some machinery to handle the debugging of section matches. This is +// necessary since some inputs may generate a lot of calls to send_strokes() +// without ever actually matching any output sequence at all. In such cases we +// want to prevent a cascade of useless debugging messages by handling the +// message printing in a lazy manner. + +static int debug_state = 0, debug_count = 0; +static translation *debug_tr = NULL; + +static void start_debug() +{ + // start a debugging section + debug_state = debug_regex; + debug_tr = NULL; + debug_count = 0; +} + +static void end_debug() +{ + // end a debugging section; if we still haven't matched an output sequence, + // but processed any input at all, we print the last matched translation + // section now anyway + if (debug_state && debug_count) debug_section(debug_tr); + debug_state = 0; +} + void send_strokes(translation *tr, uint8_t portno, int status, int chan, int data, int index, int dir) @@ -288,8 +319,19 @@ send_strokes(translation *tr, uint8_t portno, int status, int chan, int data, s = fetch_stroke(tr, portno, status, chan, data, index, dir); } - if (tr && debug_regex) { - debug_section(tr); + if (debug_regex) { + if (s) { + // found a sequence, print the matching section now + debug_section(tr); + debug_state = 0; + } else { + // no matches yet; to prevent a cascade of spurious messages, we defer + // printing the matched section for now and just record it instead; it + // may then be printed later + debug_tr = tr; + // record that we actually tried to process some input + debug_count = 1; + } } if (s && debug_keys) { @@ -504,10 +546,13 @@ handle_event(uint8_t *msg, uint8_t portno) if (debug_midi) debug_input(tr, portno, status, chan, msg[1], msg[2]); switch (status) { case 0xc0: + start_debug(); send_strokes(tr, portno, status, chan, msg[1], 0, 0); send_strokes(tr, portno, status, chan, msg[1], 1, 0); + end_debug(); break; case 0x90: + start_debug(); if (msg[2]) { if (!notedown[portno][chan][msg[1]]) { send_strokes(tr, portno, status, chan, msg[1], 0, 0); @@ -519,8 +564,10 @@ handle_event(uint8_t *msg, uint8_t portno) notedown[portno][chan][msg[1]] = 0; } } + end_debug(); break; case 0xb0: + start_debug(); if (msg[2]) { if (!inccdown[portno][chan][msg[1]]) { send_strokes(tr, portno, status, chan, msg[1], 0, 0); @@ -532,6 +579,15 @@ handle_event(uint8_t *msg, uint8_t portno) inccdown[portno][chan][msg[1]] = 0; } } + // This is a bit of a kludge, since controllers can be used in two + // different ways. It may happen that we haven't got any translations for + // the pressed/released state, and that because of a step size >1 the + // incremental controllers also fail to generate a single stroke. In such + // a case, we want to pretend that we haven't actually seen any input at + // all, to prevent spurios section matches when regex debugging is in + // effect. So we reset the debug counter here, which keeps track of the + // number of generated strokes. + debug_count = 0; if (check_incr(tr, portno, chan, msg[1])) { // Incremental controller a la MCU. NB: This assumes a signed bit // representation (values above 0x40 indicate counter-clockwise @@ -565,9 +621,11 @@ handle_event(uint8_t *msg, uint8_t portno) } } } + end_debug(); break; case 0xe0: { int bend = ((msg[2] << 7) | msg[1]) - 8192; + start_debug(); //fprintf(stderr, "pb %d\n", bend); if (bend) { if (!inpbdown[portno][chan]) { @@ -580,6 +638,7 @@ handle_event(uint8_t *msg, uint8_t portno) inpbdown[portno][chan] = 0; } } + debug_count = 0; if (check_pbs(tr, portno, chan) && inpbvalue[portno][chan] - 8192 != bend) { int dir = inpbvalue[portno][chan] - 8192 > bend ? -1 : 1; int step = get_pb_step(tr, portno, chan, dir); @@ -593,6 +652,7 @@ handle_event(uint8_t *msg, uint8_t portno) } } } + end_debug(); break; } default: diff --git a/midizap.h b/midizap.h index 22505da..bf3a0d2 100644 --- a/midizap.h +++ b/midizap.h @@ -50,7 +50,7 @@ typedef struct _stroke { int press; // zero -> release, non-zero -> press // keysym == 0 => MIDI event int status, data; // status and, if applicable, first data byte - int step; // step size for pitch bends (1 by default) + int step; // step size (1, 127 or 8191 by default, depending on status) // the incremental bit indicates an incremental control change (typically // used with endless rotary encoders) to be represented as a sign bit value uint8_t incr; diff --git a/readconfig.c b/readconfig.c index 46e88f1..e933d17 100644 --- a/readconfig.c +++ b/readconfig.c @@ -175,7 +175,9 @@ the "~" suffix can be used to indicate an incremental CC message in sign bit encoding. Specifying step sizes with incremental CC and PB messages works as well, but scales the values *up* rather than down on - the output side. + the output side. In fact, on the output side step sizes also work + with keypress-style messages (except PC), where they set the value for + the "on" state. Finally, on the output side there's a special token of the form CH<1..16>, which doesn't actually generate any MIDI message. Rather, @@ -515,11 +517,15 @@ print_stroke(stroke *s) int channel = (s->status & 0x0f) + 1; switch (status) { case 0x90: - printf("%s%d-%d ", note_names[s->data % 12], - s->data / 12 + midi_octave, channel); + if (s->step) + printf("%s%d[%d]-%d ", note_names[s->data % 12], + s->data / 12 + midi_octave, s->step, channel); + else + printf("%s%d-%d ", note_names[s->data % 12], + s->data / 12 + midi_octave, channel); break; case 0xb0: - if (s->step != 1) + if (s->step) printf("CC%d[%d]-%d%s ", s->data, s->step, channel, s->incr?"~":""); else printf("CC%d-%d%s ", s->data, channel, s->incr?"~":""); @@ -528,7 +534,7 @@ print_stroke(stroke *s) printf("PC%d-%d ", s->data, channel); break; case 0xe0: - if (s->step != 1) + if (s->step) printf("PB[%d]-%d ", s->step, channel); else printf("PB-%d ", channel); @@ -679,7 +685,9 @@ re_press_temp_modifiers(void) } } -/* Parser for the MIDI message syntax. The syntax we actually parse here is: +/* Parser for the MIDI message syntax. The same parser is used for both + the left-hand side (lhs) and the right-hand side (rhs) of a translation. + The syntax we actually parse here is: tok ::= ( note | msg ) [ number ] [ "[" number "]" ] [ "-" number] [ incr ] note ::= ( "a" | ... | "g" ) [ "#" | "b" ] @@ -693,32 +701,33 @@ re_press_temp_modifiers(void) suffix with the third number (after the dash) denotes the MIDI channel; otherwise the default MIDI channel is used. - Note that not all combinations are possible -- "pb" has no data byte; only - "cc" and "pb" may be followed by a step size in brackets; and "ch" must - *not* occur as the first token and is followed by just a channel number. - (In fact, "ch" is no real MIDI message at all; it just sets the default - MIDI channel for subsequent messages in the output sequence.) + Note that not all combinations are possible -- "pb" has no data byte; + on the lhs, a step size in brackets is only permitted with "cc" and + "pb"; and "ch" must *not* occur on the lhs at all, and is followed by + just a channel number. (In fact, "ch" is no real MIDI message at + all; it just sets the default MIDI channel for subsequent messages in + the output sequence.) - The incr flag indicates an "incremental" controller or pitch bend value - which responds to up ("+") and down ("-") changes; it is only permitted in - conjunction with "cc" and "pb", and (with one exception, see below) only on - the left-hand side of a translation. In addition, "<" and ">" can be used - in lieu of "-" and "-" to indicate a relative controller in "sign bit" - representation, where controller values > 64 denote down, and values < 64 - up changes. This notation is only permitted with "cc". It is used for - endless rotary encoders, jog wheels and the like, as can be found, e.g., on - Mackie-like units. + The incr flag indicates an "incremental" controller or pitch bend + value which responds to up ("+") and down ("-") changes; it is only + permitted in conjunction with "cc" and "pb", and (with one exception, + see below) only on the lhs of a translation. In addition, "<" and ">" + can be used in lieu of "-" and "-" to indicate a relative controller + in "sign bit" representation, where controller values > 64 denote + down, and values < 64 up changes. This notation is only permitted + with "cc". It is used for endless rotary encoders, jog wheels and the + like, as can be found, e.g., on Mackie-like units. - Finally, the flags "=" and "~" are used in lieu of "+"/"-" or "<"/">", - respectively, to denote a "bidirectional" translation which applies to both - positive and negative changes of the controller or pitch bend value. Since - bidirectional translations cannot have distinct keystroke sequences for up - and down changes associated with them, this makes most sense with pure MIDI - translations. + Finally, the flags "=" and "~" are used in lieu of "+"/"-" or + "<"/">", respectively, to denote a "bidirectional" translation which + applies to both positive and negative changes of the controller or + pitch bend value. Since bidirectional translations cannot have + distinct keystroke sequences for up and down changes associated with + them, this makes most sense with pure MIDI translations. - The only incr flag which is also permitted on the right-hand side of a - translation, and only with "cc", is the "~" flag, which is used to denote a - relative (sign bit) controller change on output. */ + The only incr flag which is also permitted on the rhs of a + translation, and only with "cc", is the "~" flag, which is used to + denote a relative (sign bit) controller change on output. */ static int note_number(char c, char b, int k) { @@ -755,10 +764,10 @@ parse_midi(char *tok, char *s, int lhs, return 0; } } - // step size ('cc' and 'pb' only) + // step size if (*p == '[') { - if (strcmp(s, "cc") && strcmp(s, "pb")) return 0; if (sscanf(++p, "%d%n", &l, &n) == 1) { + if (l <= 0) return 0; // must be positive p += n; if (*p != ']') return 0; p++; @@ -767,10 +776,12 @@ parse_midi(char *tok, char *s, int lhs, return 0; } } else { - *step = 1; + // sentinel value; for the lhs, this will be filled in below; for + // the rhs this indicates the default value + *step = 0; } + // suffix with MIDI channel (not permitted with 'ch') if (p[0] == '-' && isdigit(p[1])) { - // suffix with MIDI channel (not permitted with 'ch') if (strcmp(s, "ch") == 0) return 0; if (sscanf(++p, "%d%n", &k, &n) == 1) { // check that it is a valid channel number @@ -781,8 +792,8 @@ parse_midi(char *tok, char *s, int lhs, return 0; } } + // incremental flag ("pb" and "cc" only) if (*p && strchr("+-=<>~", *p)) { - // incremental flag ("pb" and "cc" only) if (strcmp(s, "pb") && strcmp(s, "cc")) return 0; // these are only permitted with "cc" if (strchr("<>~", *p) && strcmp(s, "cc")) return 0; @@ -805,19 +816,26 @@ parse_midi(char *tok, char *s, int lhs, } // check for trailing garbage if (*p) return 0; + // check for the different messages types we support if (strcmp(s, "ch") == 0) { - if (lhs) return 0; - // 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 + if (lhs) return 0; // not permitted on lhs + if (*step) return 0; // step size 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 if (m < 1 || m > 16) return 0; *status = 0; *data = m-1; return 1; } else if (strcmp(s, "pb") == 0) { // pitch bend, no data byte *status = 0xe0 | k; *data = 0; + // step size only permitted on lhs if incremental + if (lhs && *step && !*incr) return 0; + if (lhs && !*step) *step = 1; // default return 1; } else if (strcmp(s, "pc") == 0) { // program change + if (*step) return 0; // step size not permitted if (m < 0 || m > 127) return 0; *status = 0xc0 | k; *data = m; return 1; @@ -825,11 +843,15 @@ parse_midi(char *tok, char *s, int lhs, // control change if (m < 0 || m > 127) return 0; *status = 0xb0 | k; *data = m; + // step size only permitted on lhs if incremental + if (lhs && *step && !*incr) return 0; + if (lhs && !*step) *step = 1; // default return 1; } else { - // we must be looking at a MIDI note here, with m denoting the octave - // number; first character is the note name (must be a..g); optionally, - // the second character may denote an accidental (# or b) + if (lhs && *step) return 0; // step size not permitted on lhs + // we must be looking at a MIDI note here, with m denoting the + // octave number; first character is the note name (must be a..g); + // optionally, the second character may denote an accidental (# or b) n = note_number(s[0], s[1], m - midi_octave); if (n < 0 || n > 127) return 0; *status = 0x90 | k; *data = n;