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) {
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:

View File

@ -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;

View File

@ -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;