More MIDI-related fixes and cosmetic changes.

master
Albert Graef 2018-08-08 19:33:26 +02:00
parent d0f27891ee
commit e4d90cad02
3 changed files with 92 additions and 42 deletions

View File

@ -75,7 +75,7 @@ static int16_t pbvalue[16] =
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192}; 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192};
void void
send_midi(int status, int data, int index, int incr) send_midi(int status, int data, int index, int incr, int step)
{ {
if (!enable_jack_output) return; // MIDI output not enabled if (!enable_jack_output) return; // MIDI output not enabled
uint8_t msg[3]; uint8_t msg[3];
@ -112,13 +112,15 @@ send_midi(int status, int data, int index, int incr)
// 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 (incr) { if (incr) {
if (!step) return;
incr *= step;
if (incr > 0) { if (incr > 0) {
if (pbvalue[chan] >= 16383) return; if (pbvalue[chan] >= 16383) return;
pbvalue[chan] += 1170; pbvalue[chan] += incr;
if (pbvalue[chan] > 16383) pbvalue[chan] = 16383; if (pbvalue[chan] > 16383) pbvalue[chan] = 16383;
} else { } else {
if (pbvalue[chan] == 0) return; if (pbvalue[chan] == 0) return;
pbvalue[chan] -= 1170; pbvalue[chan] += incr;
if (pbvalue[chan] < 0) pbvalue[chan] = 0; if (pbvalue[chan] < 0) pbvalue[chan] = 0;
} }
pbval = pbvalue[chan]; pbval = pbvalue[chan];
@ -218,7 +220,7 @@ send_strokes(translation *tr, int status, int chan, int data, int index, int inc
send_key(s->keysym, s->press); send_key(s->keysym, s->press);
nkeys++; nkeys++;
} else { } else {
send_midi(s->status, s->data, index, incr); send_midi(s->status, s->data, index, incr, s->step);
} }
s = s->next; s = s->next;
} }
@ -336,6 +338,7 @@ static int16_t inpbvalue[16] =
{8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, {8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192}; 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192};
static uint8_t notedown[16][128];
static uint8_t inccdown[16][128]; static uint8_t inccdown[16][128];
static uint8_t inpbdown[16]; static uint8_t inpbdown[16];
@ -380,10 +383,17 @@ handle_event(uint8_t *msg)
send_strokes(tr, status, chan, msg[1], 1, 0); send_strokes(tr, status, chan, msg[1], 1, 0);
break; break;
case 0x90: case 0x90:
if (msg[2]) if (msg[2]) {
send_strokes(tr, status, chan, msg[1], 0, 0); if (!notedown[chan][msg[1]]) {
else send_strokes(tr, status, chan, msg[1], 0, 0);
send_strokes(tr, status, chan, msg[1], 1, 0); notedown[chan][msg[1]] = 1;
}
} else {
if (notedown[chan][msg[1]]) {
send_strokes(tr, status, chan, msg[1], 1, 0);
notedown[chan][msg[1]] = 0;
}
}
break; break;
case 0xb0: case 0xb0:
if (msg[2]) { if (msg[2]) {
@ -398,12 +408,24 @@ handle_event(uint8_t *msg)
} }
} }
if (check_incr(tr, chan, msg[1])) { if (check_incr(tr, chan, msg[1])) {
// incremental controller a la MCU XXXTODO: maybe we should handle // Incremental controller a la MCU. NB: This assumed a signed bit
// speed of control changes here? // representation (values above 0x40 indicate counter-clockwise
// rotation), which seems to be what most DAWs expect nowadays.
// But some DAWs may also have it the other way round, so that you may
// have to swap the actions for increment and decrement. XXXTODO:
// Maybe the encoding should be a configurable parameter?
if (msg[2] < 64) { if (msg[2] < 64) {
send_strokes(tr, status, chan, msg[1], 0, 1); int d = msg[2];
while (d) {
send_strokes(tr, status, chan, msg[1], 0, 1);
d--;
}
} else if (msg[2] > 64) { } else if (msg[2] > 64) {
send_strokes(tr, status, chan, msg[1], 0, -1); int d = msg[2]-64;
while (d) {
send_strokes(tr, status, chan, msg[1], 0, -1);
d--;
}
} }
} else if (inccvalue[chan][msg[1]] != msg[2]) { } else if (inccvalue[chan][msg[1]] != msg[2]) {
int incr = inccvalue[chan][msg[1]] > msg[2] ? -1 : 1; int incr = inccvalue[chan][msg[1]] > msg[2] ? -1 : 1;
@ -429,13 +451,15 @@ handle_event(uint8_t *msg)
} }
if (check_pbs(tr, chan) && inpbvalue[chan] - 8192 != bend) { if (check_pbs(tr, chan) && inpbvalue[chan] - 8192 != bend) {
int incr = inpbvalue[chan] - 8192 > bend ? -1 : 1; int incr = inpbvalue[chan] - 8192 > bend ? -1 : 1;
while (inpbvalue[chan] - 8192 != bend) { int step = tr->step[chan][incr>0];
int d = abs(inpbvalue[chan] - 8192 - bend); if (step) {
// scaled to ca. 7 steps in either direction, like on output while (inpbvalue[chan] - 8192 != bend) {
if (d > 1170) d = 1170; int d = abs(inpbvalue[chan] - 8192 - bend);
if (d < 1170) break; if (d > step) d = step;
send_strokes(tr, status, chan, 0, 0, incr); if (d < step) break;
inpbvalue[chan] += incr*d; send_strokes(tr, status, chan, 0, 0, incr);
inpbvalue[chan] += incr*d;
}
} }
} }
break; break;
@ -449,10 +473,10 @@ handle_event(uint8_t *msg)
void help(char *progname) void help(char *progname)
{ {
fprintf(stderr, "Usage: %s [-h] [-j] [-r rcfile] [-d[rsk]]\n", progname); fprintf(stderr, "Usage: %s [-h] [-t] [-r rcfile] [-d[rskj]]\n", progname);
fprintf(stderr, "-h print this message\n"); fprintf(stderr, "-h print this message\n");
fprintf(stderr, "-j enable Jack MIDI output\n"); fprintf(stderr, "-t enable MIDI output\n");
fprintf(stderr, "-r config file name (default: SHUTTLE_CONFIG_FILE variable or ~/.shuttlerc)\n"); fprintf(stderr, "-r config file name (default: MIDIZAP_CONFIG_FILE variable or ~/.midizaprc)\n");
fprintf(stderr, "-d debug (r = regex, s = strokes, k = keys, j = jack; default: all)\n"); fprintf(stderr, "-d debug (r = regex, s = strokes, k = keys, j = jack; default: all)\n");
} }
@ -469,12 +493,12 @@ main(int argc, char **argv)
uint8_t msg[3]; uint8_t msg[3];
int opt; int opt;
while ((opt = getopt(argc, argv, "hjd::r:")) != -1) { while ((opt = getopt(argc, argv, "htd::r:")) != -1) {
switch (opt) { switch (opt) {
case 'h': case 'h':
help(argv[0]); help(argv[0]);
exit(0); exit(0);
case 'j': case 't':
enable_jack_output = 1; enable_jack_output = 1;
break; break;
case 'd': case 'd':

View File

@ -49,6 +49,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)
// the dirty bit indicates a MIDI event for which a release event still // the dirty bit indicates a MIDI event for which a release event still
// needs to be generated in key events // needs to be generated in key events
int dirty; int dirty;
@ -70,6 +71,7 @@ typedef struct _translation {
stroke *ccs[NUM_CHAN][NUM_KEYS][2]; stroke *ccs[NUM_CHAN][NUM_KEYS][2];
stroke *pb[NUM_CHAN][2]; stroke *pb[NUM_CHAN][2];
stroke *pbs[NUM_CHAN][2]; stroke *pbs[NUM_CHAN][2];
int step[NUM_CHAN][2]; // step size for pitch bends (1 by default)
} translation; } translation;
extern translation *get_translation(char *win_title, char *win_class); extern translation *get_translation(char *win_title, char *win_class);

View File

@ -556,7 +556,7 @@ append_stroke(KeySym sym, int press)
s->next = NULL; s->next = NULL;
s->keysym = sym; s->keysym = sym;
s->press = press; s->press = press;
s->status = s->data = s->dirty = 0; s->status = s->data = s->step = s->dirty = 0;
if (*first_stroke) { if (*first_stroke) {
last_stroke->next = s; last_stroke->next = s;
} else { } else {
@ -566,7 +566,7 @@ append_stroke(KeySym sym, int press)
} }
void void
append_midi(int status, int data) append_midi(int status, int data, int step)
{ {
stroke *s = (stroke *)allocate(sizeof(stroke)); stroke *s = (stroke *)allocate(sizeof(stroke));
@ -575,6 +575,7 @@ append_midi(int status, int data)
s->press = 0; s->press = 0;
s->status = status; s->status = status;
s->data = data; s->data = data;
s->step = step;
// if this is a keystroke event, for all messages but program change (which // if this is a keystroke event, for all messages but program change (which
// has no "on" and "off" states), mark the event as "dirty" so that the // has no "on" and "off" states), mark the event as "dirty" so that the
// corresponding "off" event gets added later to the "release" strokes // corresponding "off" event gets added later to the "release" strokes
@ -660,7 +661,7 @@ re_press_temp_modifiers(void)
tok ::= ( note | msg ) [number] [ "-" number] [incr] tok ::= ( note | msg ) [number] [ "-" number] [incr]
note ::= ( "a" | ... | "g" ) [ "#" | "b" ] note ::= ( "a" | ... | "g" ) [ "#" | "b" ]
msg ::= "ch" | "pb" | "pc" | "cc" msg ::= "ch" | "pb" [ "[" number "]" ] | "pc" | "cc"
incr ::= "-" | "+" | "<" | ">" incr ::= "-" | "+" | "<" | ">"
Numbers are always in decimal. The meaning of the first number depends on Numbers are always in decimal. The meaning of the first number depends on
@ -668,11 +669,12 @@ re_press_temp_modifiers(void)
messages). If present, the suffix with the second number (after the dash) messages). If present, the suffix with the second number (after the dash)
denotes the MIDI channel, otherwise the default MIDI channel is used. Note denotes the MIDI channel, otherwise the default MIDI channel is used. Note
that not all combinations are possible -- "pb" is *not* followed by a data that not all combinations are possible -- "pb" is *not* followed by a data
byte, and "ch" may *not* have a channel number suffix on it. (In fact, "ch" byte, but may be followed by a step size in brackets; and "ch" may *not*
is no real MIDI message at all; it just sets the default MIDI channel for have a channel number suffix on it. (In fact, "ch" is no real MIDI message
subsequent messages.) The incr flag is only permitted in the first token of at all; it just sets the default MIDI channel for subsequent messages.) The
a translation, and only in conjunction with "pb" or "cc", whereas "ch" must incr flag is only permitted in the first token of a translation, and only
*not* occur as the first token. */ in conjunction with "pb" or "cc", whereas "ch" must *not* occur as the
first token. */
static int note_number(char c, char b, int k) static int note_number(char c, char b, int k)
{ {
@ -688,12 +690,12 @@ static int note_number(char c, char b, int k)
} }
int int
parse_midi(char *tok, char *s, int *incr, int *status, int *data) parse_midi(char *tok, char *s, int *incr, int *step, int *status, int *data)
{ {
char *p = tok, *t; char *p = tok, *t;
int n, m = -1, k = midi_channel; int n, m = -1, k = midi_channel, l;
s[0] = 0; s[0] = 0;
while (*p && !isdigit(*p) && !strchr("+-<>", *p)) p++; while (*p && !isdigit(*p) && !strchr("+-<>[", *p)) p++;
if (p == tok || p-tok > 10) return 0; // no valid token if (p == tok || p-tok > 10) return 0; // no valid token
// the token by itself // the token by itself
strncpy(s, tok, p-tok); s[p-tok] = 0; strncpy(s, tok, p-tok); s[p-tok] = 0;
@ -706,6 +708,20 @@ parse_midi(char *tok, char *s, int *incr, int *status, int *data)
} else if (strcmp(s, "pb")) { } else if (strcmp(s, "pb")) {
return 0; return 0;
} }
// step size ('pb' only)
if (*p == '[') {
if (strcmp(s, "pb")) return 0;
if (sscanf(++p, "%d%n", &l, &n) == 1) {
p += n;
if (*p != ']') return 0;
p++;
*step = l;
} else {
return 0;
}
} else if (strcmp(s, "pb") == 0) {
*step = 1;
}
if (p[0] == '-' && isdigit(p[1])) { if (p[0] == '-' && isdigit(p[1])) {
// suffix with MIDI channel (not permitted with 'ch') // suffix with MIDI channel (not permitted with 'ch')
if (strcmp(s, "ch") == 0) return 0; if (strcmp(s, "ch") == 0) return 0;
@ -764,7 +780,7 @@ parse_midi(char *tok, char *s, int *incr, int *status, int *data)
int int
start_translation(translation *tr, char *which_key) start_translation(translation *tr, char *which_key)
{ {
int status, data, incr; int status, data, incr, step;
char buf[100]; char buf[100];
//printf("start_translation(%s)\n", which_key); //printf("start_translation(%s)\n", which_key);
@ -780,7 +796,7 @@ start_translation(translation *tr, char *which_key)
regular_key_down = 0; regular_key_down = 0;
modifier_count = 0; modifier_count = 0;
midi_channel = 0; midi_channel = 0;
if (parse_midi(which_key, buf, &incr, &status, &data)) { if (parse_midi(which_key, buf, &incr, &step, &status, &data)) {
int chan = status & 0x0f; int chan = status & 0x0f;
switch (status & 0xf0) { switch (status & 0xf0) {
case 0x90: case 0x90:
@ -819,13 +835,18 @@ start_translation(translation *tr, char *which_key)
is_keystroke = 1; is_keystroke = 1;
} else { } else {
// pb (step up, down) // pb (step up, down)
if (step <= 0) {
fprintf(stderr, "zero or negative step size not permitted here: [%s]%s\n", current_translation, which_key);
return 1;
}
first_stroke = &(tr->pbs[chan][incr]); first_stroke = &(tr->pbs[chan][incr]);
tr->step[chan][incr] = step;
} }
break; break;
default: default:
// this can't happen // this can't happen
fprintf(stderr, "bad message name: [%s]%s\n", current_translation, which_key); fprintf(stderr, "bad message name: [%s]%s\n", current_translation, which_key);
return 1; return 1;
} }
} else { } else {
fprintf(stderr, "bad message name: [%s]%s\n", current_translation, which_key); fprintf(stderr, "bad message name: [%s]%s\n", current_translation, which_key);
@ -884,7 +905,7 @@ add_release(int all_keys)
stroke *s = *press_first_stroke; stroke *s = *press_first_stroke;
while (s) { while (s) {
if (!s->keysym && s->dirty) { if (!s->keysym && s->dirty) {
append_midi(s->status, s->data); append_midi(s->status, s->data, s->step);
s->dirty = 0; s->dirty = 0;
} }
s = s->next; s = s->next;
@ -929,15 +950,18 @@ add_string(char *str)
void void
add_midi(char *tok) add_midi(char *tok)
{ {
int status, data; int status, data, step = 0;
char buf[100]; char buf[100];
if (parse_midi(tok, buf, NULL, &status, &data)) { if (parse_midi(tok, buf, NULL, &step, &status, &data)) {
if (status == 0) { if (status == 0) {
// 'ch' token; this doesn't actually generate any output, it just sets // 'ch' token; this doesn't actually generate any output, it just sets
// the default MIDI channel // the default MIDI channel
midi_channel = data; midi_channel = data;
} else { } else {
append_midi(status, data); if ((status & 0xf0) != 0xe0 || step != 0)
append_midi(status, data, step);
else
fprintf(stderr, "zero step size not permitted: %s\n", tok);
} }
} else { } else {
// inspect the token that was actually recognized (if any) to give some // inspect the token that was actually recognized (if any) to give some