Allow step sizes also with keypress-like events (note etc.) in the output, improved debugging output.
parent
b6c6188b16
commit
ffa3ba5e05
78
midizap.c
78
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:
|
||||
|
|
|
@ -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;
|
||||
|
|
102
readconfig.c
102
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;
|
||||
|
|
Loading…
Reference in New Issue