Add support for recursive translations (experimental).

master
Albert Graef 2018-08-26 14:47:52 +02:00
parent 0d5a542146
commit 5241d9a0c9
3 changed files with 110 additions and 49 deletions

136
midizap.c
View File

@ -92,11 +92,15 @@ static int datavals(int val, int step, int *steps, int n_steps)
return val;
}
void
handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive);
void
send_midi(uint8_t portno, int status, int data,
int step, int n_steps, int *steps,
int incr, int index, int dir,
int mod, int mod_step, int mod_n_steps, int *mod_steps, int val)
int mod, int mod_step, int mod_n_steps, int *mod_steps, int val,
int recursive, int depth)
{
if (!jack_num_outputs) return; // MIDI output not enabled
uint8_t msg[3];
@ -271,7 +275,10 @@ send_midi(uint8_t portno, int status, int data,
default:
return;
}
queue_midi(&seq, msg, portno);
if (!recursive)
queue_midi(&seq, msg, portno);
else
handle_event(msg, portno, depth+1, recursive);
}
static int stroke_data_cmp(const void *a, const void *b)
@ -705,9 +712,12 @@ static void end_debug()
debug_state = 0;
}
// maximum recursion depth
#define MAX_DEPTH 10
void
send_strokes(translation *tr, uint8_t portno, int status, int chan,
int data, int data2, int index, int dir)
int data, int data2, int index, int dir, int depth)
{
int nkeys = 0, step = 0, n_steps = 0, *steps = 0, is_incr = 0, mod = 0;
stroke *s = fetch_stroke(tr, portno, status, chan, data, index, dir,
@ -769,9 +779,18 @@ send_strokes(translation *tr, uint8_t portno, int status, int chan,
// toggle shift status
shift = !shift;
} else {
send_midi(portno, s->status, s->data, s->step, s->n_steps, s->steps,
s->incr, index, dir,
mod, step, n_steps, steps, data2);
if (s->recursive && depth >= MAX_DEPTH) {
char name[100];
if (tr && tr->name)
fprintf(stderr, "Error: [%s]$%s: recursion too deep\n",
tr->name, debug_key(tr, name, status, chan, data, dir));
else
fprintf(stderr, "Error: $%s: recursion too deep\n",
debug_key(tr, name, status, chan, data, dir));
} else
send_midi(portno, s->status, s->data, s->step, s->n_steps, s->steps,
s->incr, index, dir,
mod, step, n_steps, steps, data2, s->recursive, depth);
}
s = s->next;
}
@ -1204,8 +1223,20 @@ get_pb_mod(translation *tr, uint8_t portno, int chan)
return 0;
}
static int
check_recursive(int status, int chan, int data, int recursive)
{
// only mod translations can be used in recursive calls
if (recursive) {
char name[100];
fprintf(stderr, "Warning: $%s: undefined\n",
debug_key(0, name, status, chan, data, 0));
}
return recursive;
}
void
handle_event(uint8_t *msg, uint8_t portno)
handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
{
translation *tr = get_focused_window_translation();
@ -1221,22 +1252,27 @@ handle_event(uint8_t *msg, uint8_t portno)
switch (status) {
case 0xc0:
start_debug();
send_strokes(tr, portno, status, chan, msg[1], 0, 0, 0);
send_strokes(tr, portno, status, chan, msg[1], 0, 1, 0);
if (check_recursive(status, chan, msg[1], recursive)) break;
send_strokes(tr, portno, status, chan, msg[1], 0, 0, 0, depth);
send_strokes(tr, portno, status, chan, msg[1], 0, 1, 0, depth);
end_debug();
break;
case 0xb0:
start_debug();
if (get_cc_mod(tr, portno, chan, msg[1]))
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0);
else if (msg[2]) {
if (get_cc_mod(tr, portno, chan, msg[1])) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
end_debug();
break;
}
if (check_recursive(status, chan, msg[1], recursive)) break;
if (msg[2]) {
if (!keydown_tracker || !inccdown[portno][chan][msg[1]]) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0);
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
inccdown[portno][chan][msg[1]] = 1;
}
} else {
if (!keydown_tracker || inccdown[portno][chan][msg[1]]) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0);
send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0, depth);
inccdown[portno][chan][msg[1]] = 0;
}
}
@ -1258,7 +1294,7 @@ handle_event(uint8_t *msg, uint8_t portno)
if (step) {
int d = msg[2]/step;
while (d) {
send_strokes(tr, portno, status, chan, msg[1], 0, 0, 1);
send_strokes(tr, portno, status, chan, msg[1], 0, 0, 1, depth);
d--;
}
}
@ -1267,7 +1303,7 @@ handle_event(uint8_t *msg, uint8_t portno)
if (step) {
int d = (msg[2]-64)/step;
while (d) {
send_strokes(tr, portno, status, chan, msg[1], 0, 0, -1);
send_strokes(tr, portno, status, chan, msg[1], 0, 0, -1, depth);
d--;
}
}
@ -1281,7 +1317,7 @@ handle_event(uint8_t *msg, uint8_t portno)
int d = abs(inccvalue[portno][chan][msg[1]] - msg[2]);
if (d > step) d = step;
if (d < step) break;
send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir);
send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir, depth);
inccvalue[portno][chan][msg[1]] += dir*d;
}
}
@ -1290,16 +1326,20 @@ handle_event(uint8_t *msg, uint8_t portno)
break;
case 0x90:
start_debug();
if (get_note_mod(tr, portno, chan, msg[1]))
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0);
else if (msg[2]) {
if (get_note_mod(tr, portno, chan, msg[1])) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
end_debug();
break;
}
if (check_recursive(status, chan, msg[1], recursive)) break;
if (msg[2]) {
if (!keydown_tracker || !innotedown[portno][chan][msg[1]]) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0);
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
innotedown[portno][chan][msg[1]] = 1;
}
} else {
if (!keydown_tracker || innotedown[portno][chan][msg[1]]) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0);
send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0, depth);
innotedown[portno][chan][msg[1]] = 0;
}
}
@ -1313,7 +1353,7 @@ handle_event(uint8_t *msg, uint8_t portno)
int d = abs(innotevalue[portno][chan][msg[1]] - msg[2]);
if (d > step) d = step;
if (d < step) break;
send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir);
send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir, depth);
innotevalue[portno][chan][msg[1]] += dir*d;
}
}
@ -1322,16 +1362,20 @@ handle_event(uint8_t *msg, uint8_t portno)
break;
case 0xa0:
start_debug();
if (get_kp_mod(tr, portno, chan, msg[1]))
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0);
else if (msg[2]) {
if (get_kp_mod(tr, portno, chan, msg[1])) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
end_debug();
break;
}
if (check_recursive(status, chan, msg[1], recursive)) break;
if (msg[2]) {
if (!keydown_tracker || !inkpdown[portno][chan][msg[1]]) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0);
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
inkpdown[portno][chan][msg[1]] = 1;
}
} else {
if (!keydown_tracker || inkpdown[portno][chan][msg[1]]) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0);
send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0, depth);
inkpdown[portno][chan][msg[1]] = 0;
}
}
@ -1345,7 +1389,7 @@ handle_event(uint8_t *msg, uint8_t portno)
int d = abs(inkpvalue[portno][chan][msg[1]] - msg[2]);
if (d > step) d = step;
if (d < step) break;
send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir);
send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir, depth);
inkpvalue[portno][chan][msg[1]] += dir*d;
}
}
@ -1354,16 +1398,20 @@ handle_event(uint8_t *msg, uint8_t portno)
break;
case 0xd0:
start_debug();
if (get_cp_mod(tr, portno, chan))
send_strokes(tr, portno, status, chan, 0, msg[1], 0, 0);
else if (msg[1]) {
if (get_cp_mod(tr, portno, chan)) {
send_strokes(tr, portno, status, chan, 0, msg[1], 0, 0, depth);
end_debug();
break;
}
if (check_recursive(status, chan, msg[1], recursive)) break;
if (msg[1]) {
if (!keydown_tracker || !incpdown[portno][chan]) {
send_strokes(tr, portno, status, chan, 0, 0, 0, 0);
send_strokes(tr, portno, status, chan, 0, 0, 0, 0, depth);
incpdown[portno][chan] = 1;
}
} else {
if (!keydown_tracker || incpdown[portno][chan]) {
send_strokes(tr, portno, status, chan, 0, 0, 1, 0);
send_strokes(tr, portno, status, chan, 0, 0, 1, 0, depth);
incpdown[portno][chan] = 0;
}
}
@ -1377,7 +1425,7 @@ handle_event(uint8_t *msg, uint8_t portno)
int d = abs(incpvalue[portno][chan] - msg[1]);
if (d > step) d = step;
if (d < step) break;
send_strokes(tr, portno, status, chan, 0, 0, 0, dir);
send_strokes(tr, portno, status, chan, 0, 0, 0, dir, depth);
incpvalue[portno][chan] += dir*d;
}
}
@ -1388,16 +1436,20 @@ handle_event(uint8_t *msg, uint8_t portno)
int bend = ((msg[2] << 7) | msg[1]) - 8192;
start_debug();
//fprintf(stderr, "pb %d\n", bend);
if (get_pb_mod(tr, portno, chan))
send_strokes(tr, portno, status, chan, 0, bend, 0, 0);
else if (bend) {
if (get_pb_mod(tr, portno, chan)) {
send_strokes(tr, portno, status, chan, 0, bend, 0, 0, depth);
end_debug();
break;
}
if (check_recursive(status, chan, msg[1], recursive)) break;
if (bend) {
if (!keydown_tracker || !inpbdown[portno][chan]) {
send_strokes(tr, portno, status, chan, 0, 0, 0, 0);
send_strokes(tr, portno, status, chan, 0, 0, 0, 0, depth);
inpbdown[portno][chan] = 1;
}
} else {
if (!keydown_tracker || inpbdown[portno][chan]) {
send_strokes(tr, portno, status, chan, 0, 0, 1, 0);
send_strokes(tr, portno, status, chan, 0, 0, 1, 0, depth);
inpbdown[portno][chan] = 0;
}
}
@ -1410,7 +1462,7 @@ handle_event(uint8_t *msg, uint8_t portno)
int d = abs(inpbvalue[portno][chan] - 8192 - bend);
if (d > step) d = step;
if (d < step) break;
send_strokes(tr, portno, status, chan, 0, 0, 0, dir);
send_strokes(tr, portno, status, chan, 0, 0, 0, dir, depth);
inpbvalue[portno][chan] += dir*d;
}
}
@ -1626,7 +1678,7 @@ main(int argc, char **argv)
exit(0);
}
while (pop_midi(&seq, msg, &portno)) {
handle_event(msg, portno);
handle_event(msg, portno, 0, 0);
time_t t = time(0);
if (t > t0) {
// Check whether to reload the config file every sec.

View File

@ -59,6 +59,9 @@ typedef struct _stroke {
// 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;
// the recursive bit indicates a MIDI message which is to be translated
// recursively
uint8_t recursive;
// the dirty bit indicates a MIDI event for which a release event still
// needs to be generated in key events
uint8_t dirty;

View File

@ -629,6 +629,7 @@ print_stroke(stroke *s, int mod, int step, int n_steps, int *steps, int val)
} else {
int status = s->status & 0xf0;
int channel = (s->status & 0x0f) + 1;
if (s->recursive) printf("$");
switch (status) {
case 0x90:
if (mod) {
@ -792,7 +793,8 @@ append_shift(void)
}
void
append_midi(int status, int data, int step, int n_steps, int *steps, int incr)
append_midi(int status, int data, int step, int n_steps, int *steps, int incr,
int recursive)
{
stroke *s = (stroke *)allocate(sizeof(stroke));
@ -806,6 +808,7 @@ append_midi(int status, int data, int step, int n_steps, int *steps, int incr)
s->n_steps = n_steps;
s->steps = stepsdup(n_steps, steps);
s->incr = incr;
s->recursive = recursive;
// 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
@ -1494,7 +1497,7 @@ add_release(int all_keys)
stroke *s = *press_first_stroke;
while (s) {
if (!s->keysym && !s->shift && s->dirty) {
append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr);
append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr, s->recursive);
s->dirty = 0;
}
s = s->next;
@ -1516,7 +1519,7 @@ add_release(int all_keys)
} else if (s->shift) {
append_shift();
} else {
append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr);
append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr, s->recursive);
}
s = s->next;
}
@ -1531,7 +1534,7 @@ add_release(int all_keys)
} else if (s->shift) {
append_shift();
} else {
append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr);
append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr, s->recursive);
}
s = s->next;
}
@ -1544,7 +1547,7 @@ add_release(int all_keys)
} else if (s->shift) {
append_shift();
} else {
append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr);
append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr, s->recursive);
}
s = s->next;
}
@ -1584,15 +1587,18 @@ void
add_midi(char *tok)
{
int status, data, step, n_steps, *steps, incr, dir = 0, mod = 0;
int recursive = *tok == '$';
char buf[100];
if (parse_midi(tok, buf, 0, mode, &status, &data, &step, &n_steps, &steps, &incr, &dir, &mod)) {
if (parse_midi(tok+recursive, buf, 0, mode, &status, &data, &step, &n_steps, &steps, &incr, &dir, &mod)) {
if (status == 0) {
// 'ch' token; this doesn't actually generate any output, it just sets
// the default MIDI channel
midi_channel = data;
if (recursive)
fprintf(stderr, "recursion not permitted: %s\n", tok);
} else {
if ((status & 0xf0) != 0xe0 || step != 0)
append_midi(status, data, step, n_steps, steps, incr!=0);
append_midi(status, data, step, n_steps, steps, incr!=0, recursive);
else
fprintf(stderr, "zero step size not permitted: %s\n", tok);
}