Allow step sizes also with keypress-like events (note etc.) in the output, improved debugging output.

master
Albert Graef 2018-08-12 23:52:11 +02:00
parent b6c6188b16
commit ffa3ba5e05
3 changed files with 132 additions and 50 deletions

View File

@ -78,7 +78,8 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i
switch (status & 0xf0) { switch (status & 0xf0) {
case 0x90: case 0x90:
if (!index) { if (!index) {
msg[2] = 127; msg[2] = step?step:127;
if (msg[2] > 127) msg[2] = 127;
} else { } else {
msg[2] = 0; msg[2] = 0;
} }
@ -91,7 +92,7 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i
} else { } else {
// increment (dir==1) or decrement (dir==-1) the current value, // increment (dir==1) or decrement (dir==-1) the current value,
// clamping it to the 0..127 data byte range // clamping it to the 0..127 data byte range
if (!step) return; if (!step) step = 1;
dir *= step; dir *= step;
if (dir > 0) { if (dir > 0) {
if (ccvalue[chan][data] >= 127) return; 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]; msg[2] = ccvalue[chan][data];
} }
} else if (!index) { } else if (!index) {
msg[2] = 127; msg[2] = step?step:127;
if (msg[2] > 127) msg[2] = 127;
} else { } else {
msg[2] = 0; 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) // range (0..16383, with 8192 being the center value)
int pbval = 0; int pbval = 0;
if (dir) { if (dir) {
if (!step) return; if (!step) step = 1;
dir *= step; dir *= step;
if (dir > 0) { if (dir > 0) {
if (pbvalue[chan] >= 16383) return; 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]; pbval = pbvalue[chan];
} else if (!index) { } else if (!index) {
pbval = 16383; pbval = 8192+(step?step:8191);
if (pbval > 16383) pbval = 16383;
} else { } else {
// we use 8192 (center) as the "home" (a.k.a. "off") value, so the pitch // 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 // will only bend up, never down below the center value
@ -192,10 +195,12 @@ void reload_callback(void)
static void debug_section(translation *tr) 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; last_translation = tr;
have_window = 1; have_window = 1;
if (tr != NULL) { if (tr) {
printf("translation: %s for %s (class %s)\n", printf("translation: %s for %s (class %s)\n",
tr->name, last_window_name, last_window_class); tr->name, last_window_name, last_window_class);
} else { } else {
@ -266,6 +271,32 @@ static void debug_input(translation *tr, int portno,
debug_key(tr, name, status, chan, data, 0), data2); 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 void
send_strokes(translation *tr, uint8_t portno, int status, int chan, int data, send_strokes(translation *tr, uint8_t portno, int status, int chan, int data,
int index, int dir) 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); s = fetch_stroke(tr, portno, status, chan, data, index, dir);
} }
if (tr && debug_regex) { if (debug_regex) {
debug_section(tr); 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) { 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]); if (debug_midi) debug_input(tr, portno, status, chan, msg[1], msg[2]);
switch (status) { switch (status) {
case 0xc0: case 0xc0:
start_debug();
send_strokes(tr, portno, status, chan, msg[1], 0, 0); send_strokes(tr, portno, status, chan, msg[1], 0, 0);
send_strokes(tr, portno, status, chan, msg[1], 1, 0); send_strokes(tr, portno, status, chan, msg[1], 1, 0);
end_debug();
break; break;
case 0x90: case 0x90:
start_debug();
if (msg[2]) { if (msg[2]) {
if (!notedown[portno][chan][msg[1]]) { if (!notedown[portno][chan][msg[1]]) {
send_strokes(tr, portno, status, chan, msg[1], 0, 0); 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; notedown[portno][chan][msg[1]] = 0;
} }
} }
end_debug();
break; break;
case 0xb0: case 0xb0:
start_debug();
if (msg[2]) { if (msg[2]) {
if (!inccdown[portno][chan][msg[1]]) { if (!inccdown[portno][chan][msg[1]]) {
send_strokes(tr, portno, status, chan, msg[1], 0, 0); 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; 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])) { if (check_incr(tr, portno, chan, msg[1])) {
// Incremental controller a la MCU. NB: This assumes a signed bit // Incremental controller a la MCU. NB: This assumes a signed bit
// representation (values above 0x40 indicate counter-clockwise // representation (values above 0x40 indicate counter-clockwise
@ -565,9 +621,11 @@ handle_event(uint8_t *msg, uint8_t portno)
} }
} }
} }
end_debug();
break; break;
case 0xe0: { case 0xe0: {
int bend = ((msg[2] << 7) | msg[1]) - 8192; int bend = ((msg[2] << 7) | msg[1]) - 8192;
start_debug();
//fprintf(stderr, "pb %d\n", bend); //fprintf(stderr, "pb %d\n", bend);
if (bend) { if (bend) {
if (!inpbdown[portno][chan]) { if (!inpbdown[portno][chan]) {
@ -580,6 +638,7 @@ handle_event(uint8_t *msg, uint8_t portno)
inpbdown[portno][chan] = 0; inpbdown[portno][chan] = 0;
} }
} }
debug_count = 0;
if (check_pbs(tr, portno, chan) && inpbvalue[portno][chan] - 8192 != bend) { if (check_pbs(tr, portno, chan) && inpbvalue[portno][chan] - 8192 != bend) {
int dir = inpbvalue[portno][chan] - 8192 > bend ? -1 : 1; int dir = inpbvalue[portno][chan] - 8192 > bend ? -1 : 1;
int step = get_pb_step(tr, portno, chan, dir); int step = get_pb_step(tr, portno, chan, dir);
@ -593,6 +652,7 @@ handle_event(uint8_t *msg, uint8_t portno)
} }
} }
} }
end_debug();
break; break;
} }
default: default:

View File

@ -50,7 +50,7 @@ typedef struct _stroke {
int press; // zero -> release, non-zero -> press int press; // zero -> release, non-zero -> press
// keysym == 0 => MIDI event // keysym == 0 => MIDI event
int status, data; // status and, if applicable, first data byte 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 // the incremental bit indicates an incremental control change (typically
// used with endless rotary encoders) to be represented as a sign bit value // used with endless rotary encoders) to be represented as a sign bit value
uint8_t incr; uint8_t incr;

View File

@ -175,7 +175,9 @@
the "~" suffix can be used to indicate an incremental CC message in the "~" suffix can be used to indicate an incremental CC message in
sign bit encoding. Specifying step sizes with incremental CC and PB sign bit encoding. Specifying step sizes with incremental CC and PB
messages works as well, but scales the values *up* rather than down on 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 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, 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; int channel = (s->status & 0x0f) + 1;
switch (status) { switch (status) {
case 0x90: case 0x90:
printf("%s%d-%d ", note_names[s->data % 12], if (s->step)
s->data / 12 + midi_octave, channel); 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; break;
case 0xb0: case 0xb0:
if (s->step != 1) if (s->step)
printf("CC%d[%d]-%d%s ", s->data, s->step, channel, s->incr?"~":""); printf("CC%d[%d]-%d%s ", s->data, s->step, channel, s->incr?"~":"");
else else
printf("CC%d-%d%s ", s->data, channel, s->incr?"~":""); 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); printf("PC%d-%d ", s->data, channel);
break; break;
case 0xe0: case 0xe0:
if (s->step != 1) if (s->step)
printf("PB[%d]-%d ", s->step, channel); printf("PB[%d]-%d ", s->step, channel);
else else
printf("PB-%d ", channel); 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 ] tok ::= ( note | msg ) [ number ] [ "[" number "]" ] [ "-" number] [ incr ]
note ::= ( "a" | ... | "g" ) [ "#" | "b" ] note ::= ( "a" | ... | "g" ) [ "#" | "b" ]
@ -693,32 +701,33 @@ re_press_temp_modifiers(void)
suffix with the third number (after the dash) denotes the MIDI suffix with the third number (after the dash) denotes the MIDI
channel; otherwise the default MIDI channel is used. channel; otherwise the default MIDI channel is used.
Note that not all combinations are possible -- "pb" has no data byte; only Note that not all combinations are possible -- "pb" has no data byte;
"cc" and "pb" may be followed by a step size in brackets; and "ch" must on the lhs, a step size in brackets is only permitted with "cc" and
*not* occur as the first token and is followed by just a channel number. "pb"; and "ch" must *not* occur on the lhs at all, and is followed by
(In fact, "ch" is no real MIDI message at all; it just sets the default just a channel number. (In fact, "ch" is no real MIDI message at
MIDI channel for subsequent messages in the output sequence.) 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 The incr flag indicates an "incremental" controller or pitch bend
which responds to up ("+") and down ("-") changes; it is only permitted in value which responds to up ("+") and down ("-") changes; it is only
conjunction with "cc" and "pb", and (with one exception, see below) only on permitted in conjunction with "cc" and "pb", and (with one exception,
the left-hand side of a translation. In addition, "<" and ">" can be used see below) only on the lhs of a translation. In addition, "<" and ">"
in lieu of "-" and "-" to indicate a relative controller in "sign bit" can be used in lieu of "-" and "-" to indicate a relative controller
representation, where controller values > 64 denote down, and values < 64 in "sign bit" representation, where controller values > 64 denote
up changes. This notation is only permitted with "cc". It is used for down, and values < 64 up changes. This notation is only permitted
endless rotary encoders, jog wheels and the like, as can be found, e.g., on with "cc". It is used for endless rotary encoders, jog wheels and the
Mackie-like units. like, as can be found, e.g., on Mackie-like units.
Finally, the flags "=" and "~" are used in lieu of "+"/"-" or "<"/">", Finally, the flags "=" and "~" are used in lieu of "+"/"-" or
respectively, to denote a "bidirectional" translation which applies to both "<"/">", respectively, to denote a "bidirectional" translation which
positive and negative changes of the controller or pitch bend value. Since applies to both positive and negative changes of the controller or
bidirectional translations cannot have distinct keystroke sequences for up pitch bend value. Since bidirectional translations cannot have
and down changes associated with them, this makes most sense with pure MIDI distinct keystroke sequences for up and down changes associated with
translations. them, this makes most sense with pure MIDI translations.
The only incr flag which is also permitted on the right-hand side of a 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 translation, and only with "cc", is the "~" flag, which is used to
relative (sign bit) controller change on output. */ denote a relative (sign bit) controller change on output. */
static int note_number(char c, char b, int k) static int note_number(char c, char b, int k)
{ {
@ -755,10 +764,10 @@ parse_midi(char *tok, char *s, int lhs,
return 0; return 0;
} }
} }
// step size ('cc' and 'pb' only) // step size
if (*p == '[') { if (*p == '[') {
if (strcmp(s, "cc") && strcmp(s, "pb")) return 0;
if (sscanf(++p, "%d%n", &l, &n) == 1) { if (sscanf(++p, "%d%n", &l, &n) == 1) {
if (l <= 0) return 0; // must be positive
p += n; p += n;
if (*p != ']') return 0; if (*p != ']') return 0;
p++; p++;
@ -767,10 +776,12 @@ parse_midi(char *tok, char *s, int lhs,
return 0; return 0;
} }
} else { } 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])) { if (p[0] == '-' && isdigit(p[1])) {
// suffix with MIDI channel (not permitted with 'ch')
if (strcmp(s, "ch") == 0) return 0; if (strcmp(s, "ch") == 0) return 0;
if (sscanf(++p, "%d%n", &k, &n) == 1) { if (sscanf(++p, "%d%n", &k, &n) == 1) {
// check that it is a valid channel number // check that it is a valid channel number
@ -781,8 +792,8 @@ parse_midi(char *tok, char *s, int lhs,
return 0; return 0;
} }
} }
// incremental flag ("pb" and "cc" only)
if (*p && strchr("+-=<>~", *p)) { if (*p && strchr("+-=<>~", *p)) {
// incremental flag ("pb" and "cc" only)
if (strcmp(s, "pb") && strcmp(s, "cc")) return 0; if (strcmp(s, "pb") && strcmp(s, "cc")) return 0;
// these are only permitted with "cc" // these are only permitted with "cc"
if (strchr("<>~", *p) && strcmp(s, "cc")) return 0; if (strchr("<>~", *p) && strcmp(s, "cc")) return 0;
@ -805,19 +816,26 @@ parse_midi(char *tok, char *s, int lhs,
} }
// check for trailing garbage // check for trailing garbage
if (*p) return 0; if (*p) return 0;
// check for the different messages types we support
if (strcmp(s, "ch") == 0) { if (strcmp(s, "ch") == 0) {
if (lhs) return 0; if (lhs) return 0; // not permitted on lhs
// we return a bogus status of 0 here, along with the MIDI channel in the if (*step) return 0; // step size not permitted
// data byte; also check that the MIDI channel is in the proper range // 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; if (m < 1 || m > 16) return 0;
*status = 0; *data = m-1; *status = 0; *data = m-1;
return 1; return 1;
} else if (strcmp(s, "pb") == 0) { } else if (strcmp(s, "pb") == 0) {
// pitch bend, no data byte // pitch bend, no data byte
*status = 0xe0 | k; *data = 0; *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; return 1;
} else if (strcmp(s, "pc") == 0) { } else if (strcmp(s, "pc") == 0) {
// program change // program change
if (*step) return 0; // step size not permitted
if (m < 0 || m > 127) return 0; if (m < 0 || m > 127) return 0;
*status = 0xc0 | k; *data = m; *status = 0xc0 | k; *data = m;
return 1; return 1;
@ -825,11 +843,15 @@ parse_midi(char *tok, char *s, int lhs,
// control change // control change
if (m < 0 || m > 127) return 0; if (m < 0 || m > 127) return 0;
*status = 0xb0 | k; *data = m; *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; return 1;
} else { } else {
// we must be looking at a MIDI note here, with m denoting the octave if (lhs && *step) return 0; // step size not permitted on lhs
// number; first character is the note name (must be a..g); optionally, // we must be looking at a MIDI note here, with m denoting the
// the second character may denote an accidental (# or b) // 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); n = note_number(s[0], s[1], m - midi_octave);
if (n < 0 || n > 127) return 0; if (n < 0 || n > 127) return 0;
*status = 0x90 | k; *data = n; *status = 0x90 | k; *data = n;