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};
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
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)
int pbval = 0;
if (incr) {
if (!step) return;
incr *= step;
if (incr > 0) {
if (pbvalue[chan] >= 16383) return;
pbvalue[chan] += 1170;
pbvalue[chan] += incr;
if (pbvalue[chan] > 16383) pbvalue[chan] = 16383;
} else {
if (pbvalue[chan] == 0) return;
pbvalue[chan] -= 1170;
pbvalue[chan] += incr;
if (pbvalue[chan] < 0) pbvalue[chan] = 0;
}
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);
nkeys++;
} else {
send_midi(s->status, s->data, index, incr);
send_midi(s->status, s->data, index, incr, s->step);
}
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};
static uint8_t notedown[16][128];
static uint8_t inccdown[16][128];
static uint8_t inpbdown[16];
@ -380,10 +383,17 @@ handle_event(uint8_t *msg)
send_strokes(tr, status, chan, msg[1], 1, 0);
break;
case 0x90:
if (msg[2])
send_strokes(tr, status, chan, msg[1], 0, 0);
else
send_strokes(tr, status, chan, msg[1], 1, 0);
if (msg[2]) {
if (!notedown[chan][msg[1]]) {
send_strokes(tr, status, chan, msg[1], 0, 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;
case 0xb0:
if (msg[2]) {
@ -398,12 +408,24 @@ handle_event(uint8_t *msg)
}
}
if (check_incr(tr, chan, msg[1])) {
// incremental controller a la MCU XXXTODO: maybe we should handle
// speed of control changes here?
// Incremental controller a la MCU. NB: This assumed a signed bit
// 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) {
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) {
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]) {
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) {
int incr = inpbvalue[chan] - 8192 > bend ? -1 : 1;
while (inpbvalue[chan] - 8192 != bend) {
int d = abs(inpbvalue[chan] - 8192 - bend);
// scaled to ca. 7 steps in either direction, like on output
if (d > 1170) d = 1170;
if (d < 1170) break;
send_strokes(tr, status, chan, 0, 0, incr);
inpbvalue[chan] += incr*d;
int step = tr->step[chan][incr>0];
if (step) {
while (inpbvalue[chan] - 8192 != bend) {
int d = abs(inpbvalue[chan] - 8192 - bend);
if (d > step) d = step;
if (d < step) break;
send_strokes(tr, status, chan, 0, 0, incr);
inpbvalue[chan] += incr*d;
}
}
}
break;
@ -449,10 +473,10 @@ handle_event(uint8_t *msg)
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, "-j enable Jack MIDI output\n");
fprintf(stderr, "-r config file name (default: SHUTTLE_CONFIG_FILE variable or ~/.shuttlerc)\n");
fprintf(stderr, "-t enable MIDI output\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");
}
@ -469,12 +493,12 @@ main(int argc, char **argv)
uint8_t msg[3];
int opt;
while ((opt = getopt(argc, argv, "hjd::r:")) != -1) {
while ((opt = getopt(argc, argv, "htd::r:")) != -1) {
switch (opt) {
case 'h':
help(argv[0]);
exit(0);
case 'j':
case 't':
enable_jack_output = 1;
break;
case 'd':

View File

@ -49,6 +49,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)
// the dirty bit indicates a MIDI event for which a release event still
// needs to be generated in key events
int dirty;
@ -70,6 +71,7 @@ typedef struct _translation {
stroke *ccs[NUM_CHAN][NUM_KEYS][2];
stroke *pb[NUM_CHAN][2];
stroke *pbs[NUM_CHAN][2];
int step[NUM_CHAN][2]; // step size for pitch bends (1 by default)
} translation;
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->keysym = sym;
s->press = press;
s->status = s->data = s->dirty = 0;
s->status = s->data = s->step = s->dirty = 0;
if (*first_stroke) {
last_stroke->next = s;
} else {
@ -566,7 +566,7 @@ append_stroke(KeySym sym, int press)
}
void
append_midi(int status, int data)
append_midi(int status, int data, int step)
{
stroke *s = (stroke *)allocate(sizeof(stroke));
@ -575,6 +575,7 @@ append_midi(int status, int data)
s->press = 0;
s->status = status;
s->data = data;
s->step = step;
// 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
// 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]
note ::= ( "a" | ... | "g" ) [ "#" | "b" ]
msg ::= "ch" | "pb" | "pc" | "cc"
msg ::= "ch" | "pb" [ "[" number "]" ] | "pc" | "cc"
incr ::= "-" | "+" | "<" | ">"
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)
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
byte, and "ch" may *not* have a channel number suffix on it. (In fact, "ch"
is no real MIDI message at all; it just sets the default MIDI channel for
subsequent messages.) The incr flag is only permitted in the first token of
a translation, and only in conjunction with "pb" or "cc", whereas "ch" must
*not* occur as the first token. */
byte, but may be followed by a step size in brackets; and "ch" may *not*
have a channel number suffix on it. (In fact, "ch" is no real MIDI message
at all; it just sets the default MIDI channel for subsequent messages.) The
incr flag is only permitted in the first token of a translation, and only
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)
{
@ -688,12 +690,12 @@ static int note_number(char c, char b, int k)
}
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;
int n, m = -1, k = midi_channel;
int n, m = -1, k = midi_channel, l;
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
// the token by itself
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")) {
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])) {
// suffix with MIDI channel (not permitted with 'ch')
if (strcmp(s, "ch") == 0) return 0;
@ -764,7 +780,7 @@ parse_midi(char *tok, char *s, int *incr, int *status, int *data)
int
start_translation(translation *tr, char *which_key)
{
int status, data, incr;
int status, data, incr, step;
char buf[100];
//printf("start_translation(%s)\n", which_key);
@ -780,7 +796,7 @@ start_translation(translation *tr, char *which_key)
regular_key_down = 0;
modifier_count = 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;
switch (status & 0xf0) {
case 0x90:
@ -819,13 +835,18 @@ start_translation(translation *tr, char *which_key)
is_keystroke = 1;
} else {
// 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]);
tr->step[chan][incr] = step;
}
break;
default:
// this can't happen
fprintf(stderr, "bad message name: [%s]%s\n", current_translation, which_key);
return 1;
return 1;
}
} else {
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;
while (s) {
if (!s->keysym && s->dirty) {
append_midi(s->status, s->data);
append_midi(s->status, s->data, s->step);
s->dirty = 0;
}
s = s->next;
@ -929,15 +950,18 @@ add_string(char *str)
void
add_midi(char *tok)
{
int status, data;
int status, data, step = 0;
char buf[100];
if (parse_midi(tok, buf, NULL, &status, &data)) {
if (parse_midi(tok, buf, NULL, &step, &status, &data)) {
if (status == 0) {
// 'ch' token; this doesn't actually generate any output, it just sets
// the default MIDI channel
midi_channel = data;
} 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 {
// inspect the token that was actually recognized (if any) to give some