diff --git a/examples/APCmini.midizaprc b/examples/APCmini.midizaprc index 75c5f66..5c74141 100644 --- a/examples/APCmini.midizaprc +++ b/examples/APCmini.midizaprc @@ -173,6 +173,25 @@ A#5 D#7 B5 E7 C6 F7 +# meter feedback + +# The Mackie protocol packs all 8 channel meters into a single channel +# pressure message which we need to transform into the corresponding +# note messages for the LEDs. Note that we only use the upper 5x8 part +# of the grid here, in order not to clobber the 3 rows at the bottom +# with the rec/solo/mute buttons. This means that the meters, which +# actually have 8 different values, have to be squashed into 5 LEDs per +# channel. + +?CP[16] C2[0,1] G#2[0,0,0,1] E3[0,0,0,0,0,5] C4[0,0,0,0,0,0,0,5] G#4[0,0,0,0,0,0,0,0,3] + +# Here's an alternative decoding, which creates horizontal meters +# covering the full range, with the first channel on top. Note, +# however, that this ranges across the entire 8x8 grid and will thus +# clobber the rec/solo/mute controls! + +#?CP[16][-8] G#4[0,1] A4[0,0,1] A#4[0,0,0,1] B4[0,0,0,0,1] C5[0,0,0,0,0,5] C#5[0,0,0,0,0,0,5] D5[0,0,0,0,0,0,0,5] D#5[0,0,0,0,0,0,0,0,3] + # no feedback for faders (faders aren't motorized) # NOTE: Feedback for rec/solo/mute/select also needs to be recognized in shift diff --git a/midizap.c b/midizap.c index e708d5b..d187288 100644 --- a/midizap.c +++ b/midizap.c @@ -77,8 +77,25 @@ static int dataval(int val, int min, int max) return val; } +static int datavals(int val, int step, int *steps, int n_steps) +{ + if (val < 0) + return -datavals(-val, step, steps, n_steps); + else if (val < n_steps) + return steps[val]; + else if (n_steps) + return steps[n_steps-1]; + else if (step) + return step*val; + else + return val; +} + void -send_midi(uint8_t portno, int status, int data, int step, int incr, int index, int dir) +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) { if (!jack_num_outputs) return; // MIDI output not enabled uint8_t msg[3]; @@ -87,7 +104,14 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i msg[1] = data; switch (status & 0xf0) { case 0x90: - if (!index) { + if (mod) { + int d = msg[1] + datavals(val/mod, mod_step, mod_steps, mod_n_steps); + int v = datavals(val%mod, step, steps, n_steps); + if (d > 127 || d < 0) return; + if (v > 127 || v < 0) return; + msg[1] = d; + msg[2] = v; + } else if (!index) { msg[2] = dataval(step, 0, 127); } else { msg[2] = 0; @@ -118,6 +142,13 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i } msg[2] = ccvalue[chan][data]; } + } else if (mod) { + int d = msg[1] + datavals(val/mod, mod_step, mod_steps, mod_n_steps); + int v = datavals(val%mod, step, steps, n_steps); + if (d > 127 || d < 0) return; + if (v > 127 || v < 0) return; + msg[1] = d; + msg[2] = v; } else if (!index) { msg[2] = dataval(step, 0, 127); } else { @@ -140,6 +171,13 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i if (kpvalue[chan][data] < 0) kpvalue[chan][data] = 0; } msg[2] = kpvalue[chan][data]; + } else if (mod) { + int d = msg[1] + datavals(val/mod, mod_step, mod_steps, mod_n_steps); + int v = datavals(val%mod, step, steps, n_steps); + if (d > 127 || d < 0) return; + if (v > 127 || v < 0) return; + msg[1] = d; + msg[2] = v; } else if (!index) { msg[2] = dataval(step, 0, 127); } else { @@ -162,6 +200,10 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i if (cpvalue[chan] < 0) cpvalue[chan] = 0; } msg[1] = cpvalue[chan]; + } else if (mod) { + int v = datavals(val%mod, step, steps, n_steps); + if (v > 127 || v < 0) return; + msg[1] = v; } else if (!index) { msg[1] = dataval(step, 0, 127); } else { @@ -185,6 +227,10 @@ send_midi(uint8_t portno, int status, int data, int step, int incr, int index, i if (pbvalue[chan] < 0) pbvalue[chan] = 0; } pbval = pbvalue[chan]; + } else if (mod) { + int v = datavals(val%mod, step, steps, n_steps); + if (v > 8191 || v < -8192) return; + pbval = 8192+v; } else if (!index) { pbval = 8192+dataval(step, -8192, 8191); } else { @@ -219,7 +265,8 @@ static int stroke_data_cmp(const void *a, const void *b) static stroke *find_stroke_data(stroke_data *sd, int chan, int data, int index, - int *step, int *incr, + int *step, int *n_steps, int **steps, + int *incr, int *mod, uint16_t n) { if (n < 16) { @@ -230,7 +277,10 @@ static stroke *find_stroke_data(stroke_data *sd, for (i = 0; i < n; i++) { if (sd[i].chan == chan && sd[i].data == data) { if (step) *step = sd[i].step[index]; + if (n_steps) *n_steps = sd[i].n_steps[index]; + if (steps) *steps = sd[i].steps[index]; if (incr) *incr = sd[i].is_incr; + if (mod) *mod = sd[i].mod; return sd[i].s[index]; } else if (sd[i].chan > chan || (sd[i].chan == chan && sd[i].data > data)) @@ -244,7 +294,10 @@ static stroke *find_stroke_data(stroke_data *sd, ret = bsearch(&key, sd, n, sizeof(stroke_data), stroke_data_cmp); if (ret) { if (step) *step = ret->step[index]; + if (n_steps) *n_steps = ret->n_steps[index]; + if (steps) *steps = ret->steps[index]; if (incr) *incr = ret->is_incr; + if (mod) *mod = ret->mod; return ret->s[index]; } else return NULL; @@ -252,105 +305,117 @@ static stroke *find_stroke_data(stroke_data *sd, } static stroke *find_note(translation *tr, int shift, - int chan, int data, int index) + int chan, int data, int index, int *mod, + int *step, int *n_steps, int **steps) { - return find_stroke_data(tr->note[shift], chan, data, index, 0, 0, + return find_stroke_data(tr->note[shift], chan, data, index, + step, n_steps, steps, 0, mod, tr->n_note[shift]); } static stroke *find_pc(translation *tr, int shift, int chan, int data, int index) { - return find_stroke_data(tr->pc[shift], chan, data, index, 0, 0, + return find_stroke_data(tr->pc[shift], chan, data, index, 0, 0, 0, 0, 0, tr->n_pc[shift]); } static stroke *find_cc(translation *tr, int shift, - int chan, int data, int index) + int chan, int data, int index, int *mod, + int *step, int *n_steps, int **steps) { - return find_stroke_data(tr->cc[shift], chan, data, index, 0, 0, + return find_stroke_data(tr->cc[shift], chan, data, index, + step, n_steps, steps, 0, mod, tr->n_cc[shift]); } static stroke *find_ccs(translation *tr, int shift, int chan, int data, int index, int *step, int *incr) { - return find_stroke_data(tr->ccs[shift], chan, data, index, step, incr, + return find_stroke_data(tr->ccs[shift], chan, data, index, step, 0, 0, + incr, 0, tr->n_ccs[shift]); } static stroke *find_kp(translation *tr, int shift, - int chan, int data, int index) + int chan, int data, int index, int *mod, + int *step, int *n_steps, int **steps) { - return find_stroke_data(tr->kp[shift], chan, data, index, 0, 0, + return find_stroke_data(tr->kp[shift], chan, data, index, + step, n_steps, steps, 0, mod, tr->n_kp[shift]); } static stroke *find_kps(translation *tr, int shift, int chan, int data, int index, int *step) { - return find_stroke_data(tr->kps[shift], chan, data, index, step, 0, + return find_stroke_data(tr->kps[shift], chan, data, index, step, 0, 0, 0, 0, tr->n_kps[shift]); } static stroke *find_cp(translation *tr, int shift, - int chan, int index) + int chan, int index, int *mod, + int *step, int *n_steps, int **steps) { - return find_stroke_data(tr->cp[shift], chan, 0, index, 0, 0, + return find_stroke_data(tr->cp[shift], chan, 0, index, + step, n_steps, steps, 0, mod, tr->n_cp[shift]); } static stroke *find_cps(translation *tr, int shift, int chan, int index, int *step) { - return find_stroke_data(tr->cps[shift], chan, 0, index, step, 0, + return find_stroke_data(tr->cps[shift], chan, 0, index, step, 0, 0, 0, 0, tr->n_cps[shift]); } static stroke *find_pb(translation *tr, int shift, - int chan, int index) + int chan, int index, int *mod, + int *step, int *n_steps, int **steps) { - return find_stroke_data(tr->pb[shift], chan, 0, index, 0, 0, + return find_stroke_data(tr->pb[shift], chan, 0, index, + step, n_steps, steps, 0, mod, tr->n_pb[shift]); } static stroke *find_pbs(translation *tr, int shift, int chan, int index, int *step) { - return find_stroke_data(tr->pbs[shift], chan, 0, index, step, 0, + return find_stroke_data(tr->pbs[shift], chan, 0, index, step, 0, 0, 0, 0, tr->n_pbs[shift]); } stroke * fetch_stroke(translation *tr, uint8_t portno, int status, int chan, int data, - int index, int dir, int *step, int *incr) + int index, int dir, int *step, int *n_steps, int **steps, + int *incr, int *mod) { if (tr && tr->portno == portno) { switch (status) { case 0x90: - return find_note(tr, shift, chan, data, index); + return find_note(tr, shift, chan, data, index, mod, step, n_steps, steps); case 0xc0: return find_pc(tr, shift, chan, data, index); case 0xb0: if (dir) return find_ccs(tr, shift, chan, data, dir>0, step, incr); else - return find_cc(tr, shift, chan, data, index); + return find_cc(tr, shift, chan, data, index, mod, step, n_steps, steps); case 0xa0: if (dir) return find_kps(tr, shift, chan, data, dir>0, step); else - return find_kp(tr, shift, chan, data, index); + return find_kp(tr, shift, chan, data, index, mod, step, n_steps, steps); case 0xd0: if (dir) return find_cps(tr, shift, chan, dir>0, step); else - return find_cp(tr, shift, chan, index); + return find_cp(tr, shift, chan, index, mod, step, n_steps, steps); case 0xe0: if (dir) return find_pbs(tr, shift, chan, dir>0, step); else - return find_pb(tr, shift, chan, index); + return find_pb(tr, shift, chan, index, mod, step, n_steps, steps); default: return NULL; } @@ -389,35 +454,95 @@ static void debug_section(translation *tr) } } +static char *note_name(int n) +{ + static char *note_names[] = { "C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B" }; + if (n < 0 && n%12) + return note_names[12+n%12]; + else + return note_names[n%12]; +} + +static int note_octave(int n) +{ + if (n < 0 && n%12) + return n/12-1 + midi_octave; + else + return n/12 + midi_octave; +} + static char *debug_key(translation *tr, char *name, int status, int chan, int data, int dir) { - static char *note_names[] = { "C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B" }; char *prefix = shift?"^":"", *suffix = ""; strcpy(name, "??"); switch (status) { - case 0x90: - sprintf(name, "%s%s%d-%d", prefix, note_names[data % 12], - data / 12 + midi_octave, chan+1); + case 0x90: { + int mod = 0, step, n_steps, *steps; + (void)find_note(tr, shift, chan, data, 0, &mod, &step, &n_steps, &steps); + if (mod) + if (step != 1) + sprintf(name, "%s%s%d[%d][%d]-%d", prefix, note_name(data), + note_octave(data), mod, step, chan+1); + else if (n_steps) { + sprintf(name, "%s%s%d[%d][", prefix, note_name(data), + note_octave(data), mod); + int l = strlen(name); + for (int i = 0; i < n_steps; i++, (l = strlen(name))) + sprintf(name+l, "%s%d", i?",":"", steps[i]); + sprintf(name+l, "]-%d", chan); + } else + sprintf(name, "%s%s%d[%d]-%d", prefix, note_name(data), + note_octave(data), mod, chan+1); + else + sprintf(name, "%s%s%d-%d", prefix, note_name(data), + note_octave(data), chan+1); break; + } case 0xa0: { - int step = 1; - if (tr) (void)find_kps(tr, shift, chan, data, dir>0, &step); + int step = 0, n_steps = 0, *steps = 0, mod = 0; + if (tr) { + if (dir) { + step = 1; + (void)find_kps(tr, shift, chan, data, dir>0, &step); + } else + (void)find_kp(tr, shift, chan, data, 0, &mod, &step, &n_steps, &steps); + } if (!dir) suffix = ""; else suffix = (dir<0)?"-":"+"; if (dir && step != 1) - sprintf(name, "%sKP:%s%d[%d]-%d%s", prefix, note_names[data % 12], - data / 12 + midi_octave, step, chan+1, suffix); + sprintf(name, "%sKP:%s%d[%d]-%d%s", prefix, note_name(data), + note_octave(data), step, chan+1, suffix); + else if (!dir && mod) + if (step != 1) + sprintf(name, "%sKP:%s%d[%d][%d]-%d", prefix, note_name(data), + note_octave(data), mod, step, chan+1); + else if (n_steps) { + sprintf(name, "%sKP:%s%d[%d][", prefix, note_name(data), + note_octave(data), mod); + int l = strlen(name); + for (int i = 0; i < n_steps; i++, (l = strlen(name))) + sprintf(name+l, "%s%d", i?",":"", steps[i]); + sprintf(name+l, "]-%d", chan); + } else + sprintf(name, "%sKP:%s%d[%d]-%d", prefix, note_name(data), + note_octave(data), mod, chan+1); else - sprintf(name, "%sKP:%s%d-%d%s", prefix, note_names[data % 12], - data / 12 + midi_octave, chan+1, suffix); + sprintf(name, "%sKP:%s%d-%d%s", prefix, note_name(data), + note_octave(data), chan+1, suffix); break; } case 0xb0: { - int step = 1, is_incr = 0; - if (tr) (void)find_ccs(tr, shift, chan, data, dir>0, &step, &is_incr); + int step = 0, n_steps = 0, *steps = 0, mod = 0, is_incr = 0; + if (tr) { + if (dir) { + step = 1; + (void)find_ccs(tr, shift, chan, data, dir>0, &step, &is_incr); + } else + (void)find_cc(tr, shift, chan, data, 0, &mod, &step, &n_steps, &steps); + } if (!dir) suffix = ""; else if (is_incr) @@ -426,6 +551,17 @@ static char *debug_key(translation *tr, char *name, suffix = (dir<0)?"-":"+"; if (dir && step != 1) sprintf(name, "%sCC%d[%d]-%d%s", prefix, data, step, chan+1, suffix); + else if (!dir && mod) + if (step != 1) + sprintf(name, "%sCC%d[%d][%d]-%d", prefix, data, mod, step, chan+1); + else if (n_steps) { + sprintf(name, "%sCC%d[%d][", prefix, data, mod); + int l = strlen(name); + for (int i = 0; i < n_steps; i++, (l = strlen(name))) + sprintf(name+l, "%s%d", i?",":"", steps[i]); + sprintf(name+l, "]-%d", chan); + } else + sprintf(name, "%sCC%d[%d]-%d", prefix, data, mod, chan+1); else sprintf(name, "%sCC%d-%d%s", prefix, data, chan+1, suffix); break; @@ -434,14 +570,31 @@ static char *debug_key(translation *tr, char *name, sprintf(name, "%sPC%d-%d", prefix, data, chan+1); break; case 0xd0: { - int step = 1; - if (tr) (void)find_cps(tr, shift, chan, dir>0, &step); + int step = 0, n_steps = 0, *steps = 0, mod = 0; + if (tr) { + if (dir) { + step = 1; + (void)find_cps(tr, shift, chan, dir>0, &step); + } else + (void)find_cp(tr, shift, chan, 0, &mod, &step, &n_steps, &steps); + } if (!dir) suffix = ""; else suffix = (dir<0)?"-":"+"; - if (dir && step != 1) + if (dir && step != 1) sprintf(name, "%sCP[%d]-%d%s", prefix, step, chan+1, suffix); + else if (!dir && mod) + if (step != 1) + sprintf(name, "%sCP[%d][%d]-%d", prefix, mod, step, chan+1); + else if (n_steps) { + sprintf(name, "%sCP[%d][", prefix, mod); + int l = strlen(name); + for (int i = 0; i < n_steps; i++, (l = strlen(name))) + sprintf(name+l, "%s%d", i?",":"", steps[i]); + sprintf(name+l, "]-%d", chan); + } else + sprintf(name, "%sCP[%d]-%d", prefix, mod, chan+1); else sprintf(name, "%sCP-%d%s", prefix, chan+1, suffix); break; @@ -509,26 +662,26 @@ static void end_debug() } void -send_strokes(translation *tr, uint8_t portno, int status, int chan, int data, - int index, int dir) +send_strokes(translation *tr, uint8_t portno, int status, int chan, + int data, int data2, int index, int dir) { - int nkeys = 0, step = 0, is_incr = 0; + 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, - &step, &is_incr); + &step, &n_steps, &steps, &is_incr, &mod); // If there's no press/release translation, check whether we have got at // least the corresponding release/press translation, in order to prevent // spurious error messages if either the press or release translation just // happens to be empty. int chk = s || - (!dir && fetch_stroke(tr, portno, status, chan, data, !index, dir, 0, 0)); + (!dir && fetch_stroke(tr, portno, status, chan, data, !index, dir, 0, 0, 0, 0, 0)); if (!s && jack_num_outputs) { // fall back to default MIDI translation tr = default_midi_translation[portno]; s = fetch_stroke(tr, portno, status, chan, data, index, dir, - &step, &is_incr); + &step, &n_steps, &steps, &is_incr, &mod); chk = chk || s || - (!dir && fetch_stroke(tr, portno, status, chan, data, !index, dir, 0, 0)); + (!dir && fetch_stroke(tr, portno, status, chan, data, !index, dir, 0, 0, 0, 0, 0)); // Ignore all MIDI input on the second port if no translation was found in // the [MIDI2] section (or the section is missing altogether). if (portno && !s) return; @@ -538,9 +691,9 @@ send_strokes(translation *tr, uint8_t portno, int status, int chan, int data, // fall back to the default translation tr = default_translation; s = fetch_stroke(tr, portno, status, chan, data, index, dir, - &step, &is_incr); + &step, &n_steps, &steps, &is_incr, &mod); chk = chk || s || - (!dir && fetch_stroke(tr, portno, status, chan, data, !index, dir, 0, 0)); + (!dir && fetch_stroke(tr, portno, status, chan, data, !index, dir, 0, 0, 0, 0, 0)); } if (debug_regex) { @@ -561,7 +714,8 @@ send_strokes(translation *tr, uint8_t portno, int status, int chan, int data, if (s && debug_keys) { char name[100]; print_stroke_sequence(debug_key(tr, name, status, chan, data, dir), - dir?"":index?"U":"D", s); + (dir||mod)?"":index?"U":"D", s, + mod, step, n_steps, steps, data2); } while (s) { if (s->keysym) { @@ -571,7 +725,9 @@ send_strokes(translation *tr, uint8_t portno, int status, int chan, int data, // toggle shift status shift = !shift; } else { - send_midi(portno, s->status, s->data, s->step, s->incr, index, dir); + 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 = s->next; } @@ -702,6 +858,24 @@ static uint8_t inpbdown[2][16]; static uint8_t inkpdown[2][16][128]; static uint8_t incpdown[2][16]; +int +get_note_mod(translation *tr, uint8_t portno, int chan, int data) +{ + int mod; + if (tr && tr->portno == portno && + find_note(tr, shift, chan, data, 0, &mod, 0, 0, 0)) + return mod; + tr = default_midi_translation[portno]; + if (tr && tr->portno == portno && + find_note(tr, shift, chan, data, 0, &mod, 0, 0, 0)) + return mod; + tr = default_translation; + if (tr && tr->portno == portno && + find_note(tr, shift, chan, data, 0, &mod, 0, 0, 0)) + return mod; + return 0; +} + int check_incr(translation *tr, uint8_t portno, int chan, int data) { @@ -761,6 +935,24 @@ get_cc_step(translation *tr, uint8_t portno, int chan, int data, int dir) return 1; } +int +get_cc_mod(translation *tr, uint8_t portno, int chan, int data) +{ + int mod; + if (tr && tr->portno == portno && + find_cc(tr, shift, chan, data, 0, &mod, 0, 0, 0)) + return mod; + tr = default_midi_translation[portno]; + if (tr && tr->portno == portno && + find_cc(tr, shift, chan, data, 0, &mod, 0, 0, 0)) + return mod; + tr = default_translation; + if (tr && tr->portno == portno && + find_cc(tr, shift, chan, data, 0, &mod, 0, 0, 0)) + return mod; + return 0; +} + int check_kps(translation *tr, uint8_t portno, int chan, int data) { @@ -799,6 +991,24 @@ get_kp_step(translation *tr, uint8_t portno, int chan, int data, int dir) return 1; } +int +get_kp_mod(translation *tr, uint8_t portno, int chan, int data) +{ + int mod; + if (tr && tr->portno == portno && + find_kp(tr, shift, chan, data, 0, &mod, 0, 0, 0)) + return mod; + tr = default_midi_translation[portno]; + if (tr && tr->portno == portno && + find_kp(tr, shift, chan, data, 0, &mod, 0, 0, 0)) + return mod; + tr = default_translation; + if (tr && tr->portno == portno && + find_kp(tr, shift, chan, data, 0, &mod, 0, 0, 0)) + return mod; + return 0; +} + int check_cps(translation *tr, uint8_t portno, int chan) { @@ -837,6 +1047,24 @@ get_cp_step(translation *tr, uint8_t portno, int chan, int dir) return 1; } +int +get_cp_mod(translation *tr, uint8_t portno, int chan) +{ + int mod; + if (tr && tr->portno == portno && + find_cp(tr, shift, chan, 0, &mod, 0, 0, 0)) + return mod; + tr = default_midi_translation[portno]; + if (tr && tr->portno == portno && + find_cp(tr, shift, chan, 0, &mod, 0, 0, 0)) + return mod; + tr = default_translation; + if (tr && tr->portno == portno && + find_cp(tr, shift, chan, 0, &mod, 0, 0, 0)) + return mod; + return 0; +} + int check_pbs(translation *tr, uint8_t portno, int chan) { @@ -875,6 +1103,24 @@ get_pb_step(translation *tr, uint8_t portno, int chan, int dir) return 1; } +int +get_pb_mod(translation *tr, uint8_t portno, int chan) +{ + int mod; + if (tr && tr->portno == portno && + find_pb(tr, shift, chan, 0, &mod, 0, 0, 0)) + return mod; + tr = default_midi_translation[portno]; + if (tr && tr->portno == portno && + find_pb(tr, shift, chan, 0, &mod, 0, 0, 0)) + return mod; + tr = default_translation; + if (tr && tr->portno == portno && + find_pb(tr, shift, chan, 0, &mod, 0, 0, 0)) + return mod; + return 0; +} + void handle_event(uint8_t *msg, uint8_t portno) { @@ -892,20 +1138,22 @@ handle_event(uint8_t *msg, uint8_t portno) 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); + send_strokes(tr, portno, status, chan, msg[1], 0, 0, 0); + send_strokes(tr, portno, status, chan, msg[1], 0, 1, 0); end_debug(); break; case 0x90: start_debug(); - if (msg[2]) { + 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 (!keydown_tracker || !notedown[portno][chan][msg[1]]) { - send_strokes(tr, portno, status, chan, msg[1], 0, 0); + send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0); notedown[portno][chan][msg[1]] = 1; } } else { if (!keydown_tracker || notedown[portno][chan][msg[1]]) { - send_strokes(tr, portno, status, chan, msg[1], 1, 0); + send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0); notedown[portno][chan][msg[1]] = 0; } } @@ -913,14 +1161,16 @@ handle_event(uint8_t *msg, uint8_t portno) break; case 0xb0: start_debug(); - if (msg[2]) { + 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 (!keydown_tracker || !inccdown[portno][chan][msg[1]]) { - send_strokes(tr, portno, status, chan, msg[1], 0, 0); + send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0); inccdown[portno][chan][msg[1]] = 1; } } else { if (!keydown_tracker || inccdown[portno][chan][msg[1]]) { - send_strokes(tr, portno, status, chan, msg[1], 1, 0); + send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0); inccdown[portno][chan][msg[1]] = 0; } } @@ -942,7 +1192,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, 1); + send_strokes(tr, portno, status, chan, msg[1], 0, 0, 1); d--; } } @@ -951,7 +1201,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, -1); + send_strokes(tr, portno, status, chan, msg[1], 0, 0, -1); d--; } } @@ -965,7 +1215,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, dir); + send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir); inccvalue[portno][chan][msg[1]] += dir*d; } } @@ -974,14 +1224,16 @@ handle_event(uint8_t *msg, uint8_t portno) break; case 0xa0: start_debug(); - if (msg[2]) { + 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 (!keydown_tracker || !inkpdown[portno][chan][msg[1]]) { - send_strokes(tr, portno, status, chan, msg[1], 0, 0); + send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0); inkpdown[portno][chan][msg[1]] = 1; } } else { if (!keydown_tracker || inkpdown[portno][chan][msg[1]]) { - send_strokes(tr, portno, status, chan, msg[1], 1, 0); + send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0); inkpdown[portno][chan][msg[1]] = 0; } } @@ -995,7 +1247,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, dir); + send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir); inkpvalue[portno][chan][msg[1]] += dir*d; } } @@ -1004,14 +1256,16 @@ handle_event(uint8_t *msg, uint8_t portno) break; case 0xd0: start_debug(); - if (msg[1]) { + if (get_cp_mod(tr, portno, chan)) + send_strokes(tr, portno, status, chan, 0, msg[1], 0, 0); + else if (msg[1]) { if (!keydown_tracker || !incpdown[portno][chan]) { - send_strokes(tr, portno, status, chan, 0, 0, 0); + send_strokes(tr, portno, status, chan, 0, 0, 0, 0); incpdown[portno][chan] = 1; } } else { if (!keydown_tracker || incpdown[portno][chan]) { - send_strokes(tr, portno, status, chan, 0, 1, 0); + send_strokes(tr, portno, status, chan, 0, 0, 1, 0); incpdown[portno][chan] = 0; } } @@ -1025,7 +1279,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, dir); + send_strokes(tr, portno, status, chan, 0, 0, 0, dir); incpvalue[portno][chan] += dir*d; } } @@ -1036,14 +1290,16 @@ 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 (bend) { + if (get_pb_mod(tr, portno, chan)) + send_strokes(tr, portno, status, chan, 0, bend, 0, 0); + else if (bend) { if (!keydown_tracker || !inpbdown[portno][chan]) { - send_strokes(tr, portno, status, chan, 0, 0, 0); + send_strokes(tr, portno, status, chan, 0, 0, 0, 0); inpbdown[portno][chan] = 1; } } else { if (!keydown_tracker || inpbdown[portno][chan]) { - send_strokes(tr, portno, status, chan, 0, 1, 0); + send_strokes(tr, portno, status, chan, 0, 0, 1, 0); inpbdown[portno][chan] = 0; } } @@ -1056,7 +1312,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, dir); + send_strokes(tr, portno, status, chan, 0, 0, 0, dir); inpbvalue[portno][chan] += dir*d; } } @@ -1146,11 +1402,13 @@ static char *absolute_path(char *name) #define CONF_FREQ 1 #define MAX_COUNT (1000000/CONF_FREQ/POLL_INTERVAL) +#include + int main(int argc, char **argv) { uint8_t msg[3]; - int opt, count = 0; + int opt; // Start recording the command line to be passed to Jack session management. add_command(argv[0]); @@ -1260,6 +1518,7 @@ main(int argc, char **argv) int do_flush = debug_regex || debug_strokes || debug_keys || debug_midi || debug_jack; signal(SIGINT, quitter); + time_t t0 = time(0); while (!quit) { uint8_t portno; if (jack_quit) { @@ -1270,16 +1529,19 @@ main(int argc, char **argv) } while (pop_midi(&seq, msg, &portno)) { handle_event(msg, portno); - count = 0; + time_t t = time(0); + if (t > t0) { + // Check whether to reload the config file every sec. + if (read_config_file()) last_focused_window = 0; + t0 = t; + } } usleep(POLL_INTERVAL); - if (++count >= MAX_COUNT) { - // Check whether to reload the config file if we haven't seen any MIDI - // input in a while. Note that if the file *is* reloaded, then we also - // need to reset last_focused_window here, so that the translations of - // the focused window are recomputed the next time we handle an event. + time_t t = time(0); + if (t > t0) { + // Check again when polling. if (read_config_file()) last_focused_window = 0; - count = 0; + t0 = t; } // Make sure that debugging output gets flushed every once in a while (may // be buffered when midizap is running inside a QjackCtl session). diff --git a/midizap.h b/midizap.h index 272aba5..105eefe 100644 --- a/midizap.h +++ b/midizap.h @@ -54,6 +54,8 @@ typedef struct _stroke { // keysym == shift == 0 => MIDI event int status, data; // status and, if applicable, first data byte int step; // step size (1, 127 or 8191 by default, depending on status) + // discrete steps (for special "modulus" translations only) + int n_steps, *steps; // 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; @@ -67,10 +69,12 @@ typedef struct _stroke_data { uint8_t chan, data; // stroke data, indexed by press/release or up/down index stroke *s[2]; - // step size (CP, KP, CC and PB only) - int step[2]; + // step size + int step[2], n_steps[2], *steps[2]; // incr flag (CC only) uint8_t is_incr; + // modulus + uint8_t mod; } stroke_data; typedef struct _translation { @@ -100,7 +104,9 @@ typedef struct _translation { extern void reload_callback(void); extern int read_config_file(void); extern translation *get_translation(char *win_title, char *win_class); -extern void print_stroke_sequence(char *name, char *up_or_down, stroke *s); +extern void print_stroke_sequence(char *name, char *up_or_down, stroke *s, + int mod, int step, int n_steps, int *steps, + int val); extern translation *default_translation, *default_midi_translation[2]; extern int debug_regex, debug_strokes, debug_keys, debug_midi; extern int default_debug_regex, default_debug_strokes, default_debug_keys, diff --git a/readconfig.c b/readconfig.c index 83e4311..0678553 100644 --- a/readconfig.c +++ b/readconfig.c @@ -201,6 +201,7 @@ free_strokes(stroke *s) stroke *next; while (s != NULL) { next = s->next; + if (s->steps) free(s->steps); free(s); s = next; } @@ -234,13 +235,27 @@ static void free_stroke_data(stroke_data *sd, uint16_t n) for (i = 0; i < n; i++) { free_strokes(sd[i].s[0]); free_strokes(sd[i].s[1]); + if (sd[i].steps[0]) free(sd[i].steps[0]); + if (sd[i].steps[1]) free(sd[i].steps[1]); } free(sd); } + +static int *stepsdup(int n_steps, int *steps) +{ + if (n_steps) { + int *ret = malloc(n_steps*sizeof(int)); + memcpy(ret, steps, n_steps*sizeof(int)); + return ret; + } else + return 0; +} + static stroke **find_stroke_data(stroke_data **sd, int chan, int data, int index, - int step, int incr, + int step, int n_steps, int *steps, + int incr, int mod, uint16_t *n, uint16_t *a) { uint16_t i; @@ -248,7 +263,10 @@ static stroke **find_stroke_data(stroke_data **sd, if ((*sd)[i].chan == chan && (*sd)[i].data == data) { // existing entry (*sd)[i].step[index] = step; + (*sd)[i].n_steps[index] = n_steps; + (*sd)[i].steps[index] = stepsdup(n_steps, steps); (*sd)[i].is_incr = incr; + (*sd)[i].mod = mod; return &(*sd)[i].s[index]; } } @@ -262,77 +280,91 @@ static stroke **find_stroke_data(stroke_data **sd, (*sd)[*n].chan = chan; (*sd)[*n].data = data; (*sd)[*n].step[index] = step; + (*sd)[*n].n_steps[index] = n_steps; + (*sd)[*n].steps[index] = stepsdup(n_steps, steps); (*sd)[*n].is_incr = incr; + (*sd)[*n].mod = mod; return &(*sd)[(*n)++].s[index]; } static stroke **find_note(translation *tr, int shift, - int chan, int data, int index) + int chan, int data, int index, int mod, + int step, int n_steps, int *steps) { - return find_stroke_data(&tr->note[shift], chan, data, index, 0, 0, + return find_stroke_data(&tr->note[shift], chan, data, index, + step, n_steps, steps, 0, mod, &tr->n_note[shift], &tr->a_note[shift]); } static stroke **find_pc(translation *tr, int shift, - int chan, int data, int index) + int chan, int data, int index) { - return find_stroke_data(&tr->pc[shift], chan, data, index, 0, 0, + return find_stroke_data(&tr->pc[shift], chan, data, index, 0, 0, 0, 0, 0, &tr->n_pc[shift], &tr->a_pc[shift]); } static stroke **find_cc(translation *tr, int shift, - int chan, int data, int index) + int chan, int data, int index, int mod, + int step, int n_steps, int *steps) { - return find_stroke_data(&tr->cc[shift], chan, data, index, 0, 0, + return find_stroke_data(&tr->cc[shift], chan, data, index, + step, n_steps, steps, 0, mod, &tr->n_cc[shift], &tr->a_cc[shift]); } static stroke **find_ccs(translation *tr, int shift, int chan, int data, int index, int step, int incr) { - return find_stroke_data(&tr->ccs[shift], chan, data, index, step, incr, + return find_stroke_data(&tr->ccs[shift], chan, data, index, step, 0, 0, + incr, 0, &tr->n_ccs[shift], &tr->a_ccs[shift]); } static stroke **find_kp(translation *tr, int shift, - int chan, int data, int index) + int chan, int data, int index, int mod, + int step, int n_steps, int *steps) { - return find_stroke_data(&tr->kp[shift], chan, data, index, 0, 0, + return find_stroke_data(&tr->kp[shift], chan, data, index, + step, n_steps, steps, 0, mod, &tr->n_kp[shift], &tr->a_kp[shift]); } static stroke **find_kps(translation *tr, int shift, int chan, int data, int index, int step) { - return find_stroke_data(&tr->kps[shift], chan, data, index, step, 0, + return find_stroke_data(&tr->kps[shift], chan, data, index, step, 0, 0, 0, 0, &tr->n_kps[shift], &tr->a_kps[shift]); } static stroke **find_cp(translation *tr, int shift, - int chan, int index) + int chan, int index, int mod, + int step, int n_steps, int *steps) { - return find_stroke_data(&tr->cp[shift], chan, 0, index, 0, 0, + return find_stroke_data(&tr->cp[shift], chan, 0, index, + step, n_steps, steps, 0, mod, &tr->n_cp[shift], &tr->a_cp[shift]); } static stroke **find_cps(translation *tr, int shift, int chan, int index, int step) { - return find_stroke_data(&tr->cps[shift], chan, 0, index, step, 0, + return find_stroke_data(&tr->cps[shift], chan, 0, index, step, 0, 0, 0, 0, &tr->n_cps[shift], &tr->a_cps[shift]); } static stroke **find_pb(translation *tr, int shift, - int chan, int index) + int chan, int index, int mod, + int step, int n_steps, int *steps) { - return find_stroke_data(&tr->pb[shift], chan, 0, index, 0, 0, + return find_stroke_data(&tr->pb[shift], chan, 0, index, + step, n_steps, steps, 0, mod, &tr->n_pb[shift], &tr->a_pb[shift]); } static stroke **find_pbs(translation *tr, int shift, int chan, int index, int step) { - return find_stroke_data(&tr->pbs[shift], chan, 0, index, step, 0, + return find_stroke_data(&tr->pbs[shift], chan, 0, index, step, 0, 0, 0, 0, &tr->n_pbs[shift], &tr->a_pbs[shift]); } @@ -495,10 +527,39 @@ KeySym_to_string(KeySym ks) return NULL; } -static char *note_names[] = { "C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B" }; +static char *note_name(int n) +{ + static char *note_names[] = { "C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B" }; + if (n < 0 && n%12) + return note_names[12+n%12]; + else + return note_names[n%12]; +} + +static int note_octave(int n) +{ + if (n < 0 && n%12) + return n/12-1 + midi_octave; + else + return n/12 + midi_octave; +} + +static int datavals(int val, int step, int *steps, int n_steps) +{ + if (val < 0) + return -datavals(-val, step, steps, n_steps); + else if (val < n_steps) + return steps[val]; + else if (n_steps) + return steps[n_steps-1]; + else if (step) + return step*val; + else + return val; +} void -print_stroke(stroke *s) +print_stroke(stroke *s, int mod, int step, int n_steps, int *steps, int val) { char *str; @@ -517,23 +578,54 @@ print_stroke(stroke *s) int channel = (s->status & 0x0f) + 1; switch (status) { case 0x90: - if (s->step) - printf("%s%d[%d]-%d ", note_names[s->data % 12], - s->data / 12 + midi_octave, s->step, channel); + if (mod) { + int d = s->data + datavals(val/mod, step, steps, n_steps); + int v = datavals(val%mod, s->step, s->steps, s->n_steps); + printf("%s%d[%d]-%d ", note_name(d), + note_octave(d), v, channel); + } else if (s->steps) { + printf("%s%d[", note_name(s->data), + note_octave(s->data)); + for (int i = 0; i < s->n_steps; i++) + printf("%s%d", i?",":"", s->steps[i]); + printf("]-%d ", channel); + } else if (s->step) + printf("%s%d[%d]-%d ", note_name(s->data), + note_octave(s->data), s->step, channel); else - printf("%s%d-%d ", note_names[s->data % 12], - s->data / 12 + midi_octave, channel); + printf("%s%d-%d ", note_name(s->data), + note_octave(s->data), channel); break; case 0xa0: - if (s->step) - printf("KP:%s%d[%d]-%d ", note_names[s->data % 12], - s->data / 12 + midi_octave, s->step, channel); + if (mod) { + int d = s->data + datavals(val/mod, step, steps, n_steps); + int v = datavals(val%mod, s->step, s->steps, s->n_steps); + printf("KP:%s%d[%d]-%d ", note_name(d), + note_octave(d), v, channel); + } else if (s->steps) { + printf("KP:%s%d[", note_name(s->data), + note_octave(s->data)); + for (int i = 0; i < s->n_steps; i++) + printf("%s%d", i?",":"", s->steps[i]); + printf("]-%d ", channel); + } else if (s->step) + printf("KP:%s%d[%d]-%d ", note_name(s->data), + note_octave(s->data), s->step, channel); else - printf("KP:%s%d-%d ", note_names[s->data % 12], - s->data / 12 + midi_octave, channel); + printf("KP:%s%d-%d ", note_name(s->data), + note_octave(s->data), channel); break; case 0xb0: - if (s->step) + if (mod) { + int d = s->data + datavals(val/mod, step, steps, n_steps); + int v = datavals(val%mod, s->step, s->steps, s->n_steps); + printf("CC%d[%d]-%d ", d, v, channel); + } else if (s->steps) { + printf("CC%d[", s->data); + for (int i = 0; i < s->n_steps; i++) + printf("%s%d", i?",":"", s->steps[i]); + printf("]-%d ", channel); + } else 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?"~":""); @@ -542,13 +634,29 @@ print_stroke(stroke *s) printf("PC%d-%d ", s->data, channel); break; case 0xd0: - if (s->step) + if (mod) { + int v = datavals(val%mod, s->step, s->steps, s->n_steps); + printf("CP[%d]-%d ", v, channel); + } else if (s->steps) { + printf("CP["); + for (int i = 0; i < s->n_steps; i++) + printf("%s%d", i?",":"", s->steps[i]); + printf("]-%d ", channel); + } else if (s->step) printf("CP[%d]-%d ", s->step, channel); else printf("CP-%d ", channel); break; case 0xe0: - if (s->step) + if (mod) { + int v = datavals(val%mod, s->step, s->steps, s->n_steps); + printf("PB[%d]-%d ", v, channel); + } else if (s->steps) { + printf("PB["); + for (int i = 0; i < s->n_steps; i++) + printf("%s%d", i?",":"", s->steps[i]); + printf("]-%d ", channel); + } else if (s->step) printf("PB[%d]-%d ", s->step, channel); else printf("PB-%d ", channel); @@ -561,14 +669,15 @@ print_stroke(stroke *s) } void -print_stroke_sequence(char *name, char *up_or_down, stroke *s) +print_stroke_sequence(char *name, char *up_or_down, stroke *s, + int mod, int step, int n_steps, int *steps, int val) { if (up_or_down && *up_or_down) printf("%s[%s]: ", name, up_or_down); else printf("%s: ", name); while (s) { - print_stroke(s); + print_stroke(s, mod, step, n_steps, steps, val); s = s->next; } printf("\n"); @@ -630,7 +739,7 @@ append_shift(void) } void -append_midi(int status, int data, int step, int incr) +append_midi(int status, int data, int step, int n_steps, int *steps, int incr) { stroke *s = (stroke *)allocate(sizeof(stroke)); @@ -641,6 +750,8 @@ append_midi(int status, int data, int step, int incr) s->status = status; s->data = data; s->step = step; + s->n_steps = n_steps; + s->steps = stepsdup(n_steps, steps); s->incr = incr; // 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 @@ -781,12 +892,47 @@ static int note_number(char c, char b, int k) } } +static char *parse_steps(char *tok, char *p, + int *step, int *n_steps, int **steps) +{ + int l, n; + if (sscanf(++p, "%d%n", &l, &n) == 1) { + p += n; + if (*p == ',') { + int n_st = 1; + static int st[128]; + st[0] = l; + while (*p == ',') { + if (sscanf(++p, "%d%n", &l, &n) == 1) { + p += n; + } else + return 0; + if (n_st < 128) + st[n_st++] = l; + else if (n_st == 128) + fprintf(stderr, "warning: too many steps: %s\n", tok); + } + *n_steps = n_st; + *steps = st; + *step = 0; + } else { + *n_steps = 0; + *steps = 0; + *step = l; + } + return p; + } else { + return 0; + } +} + int parse_midi(char *tok, char *s, int lhs, int mode, - int *status, int *data, int *step, int *incr, int *dir) + int *status, int *data, int *step, int *n_steps, int **steps, + int *incr, int *dir, int *mod) { char *p = tok, *t; - int n, m = -1, k = midi_channel, l; + int n, m = -1, k = midi_channel; s[0] = 0; while (*p && !isdigit(*p) && !strchr("+-=<>~[:", *p)) p++; if (p == tok || p-tok > 10) return 0; // no valid token @@ -826,14 +972,30 @@ parse_midi(char *tok, char *s, int lhs, int mode, return 0; } } - // step size + // step size / modulus + *mod = 0; + int step2 = 0, n_steps2 = 0, *steps2 = 0; if (*p == '[') { - if (sscanf(++p, "%d%n", &l, &n) == 1) { - if (!l || (lhs && l<0)) return 0; // must be nonzero / positive on lhs - p += n; + if ((p = parse_steps(tok, p, step, n_steps, steps))) { + if (*n_steps) { + // only permitted on the rhs in mod translations + if (lhs || mode < 2) return 0; + } else if (!*step || (lhs && *step<0)) + // must be nonzero / positive on lhs + return 0; if (*p != ']') return 0; p++; - *step = l; + if (*p == '[') { + // possible step size on lhs for mod translations (we just record it + // here, will be resolved later) + if ((p = parse_steps(tok, p, &step2, &n_steps2, &steps2))) { + if (!n_steps2 && !step2) return 0; // must be nonzero + } else { + return 0; + } + if (*p != ']') return 0; + p++; + } } else { return 0; } @@ -841,6 +1003,8 @@ parse_midi(char *tok, char *s, int lhs, int mode, // sentinel value; for the lhs, this will be filled in below; for // the rhs this indicates the default value *step = 0; + *n_steps = 0; + *steps = 0; } // suffix with MIDI channel (not permitted with 'ch') if (p[0] == '-' && isdigit(p[1])) { @@ -872,7 +1036,7 @@ parse_midi(char *tok, char *s, int lhs, int mode, // an endless, sign-bit encoder if (*p != '~') return 0; - else if (!mode) + else if (mode) fprintf(stderr, "warning: incremental flag ignored in key mode: %s\n", tok); *incr = 2; *dir = 0; } @@ -886,6 +1050,8 @@ parse_midi(char *tok, char *s, int lhs, int mode, if (strcmp(s, "ch") == 0) { if (lhs) return 0; // not permitted on lhs if (*step) return 0; // step size not permitted + if (*n_steps) return 0; // steps not permitted + if (steps2 || n_steps2) return 0; // 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 @@ -895,23 +1061,28 @@ parse_midi(char *tok, char *s, int lhs, int mode, } else if (strcmp(s, "pb") == 0) { // pitch bend, no data byte *status = 0xe0 | k; *data = 0; - // negative step size is always permitted on rhs here, even in key mode - // step size only permitted on lhs if incremental - if (lhs && *step && !*incr) return 0; + // step size on lhs indicates modulus if non-incremental + if (lhs && *step && !*incr) { + *mod = *step; *step = step2; + *n_steps = n_steps2; *steps = steps2; + } if (lhs && !*step) *step = 1; // default return 1; } else if (strcmp(s, "cp") == 0) { // channel pressure, no data byte *status = 0xd0 | k; *data = 0; - // negative step size not permitted on rhs in key mode - if (!lhs && *step < 0 && !mode) return 0; - // step size only permitted on lhs if incremental - if (lhs && *step && !*incr) return 0; + // step size on lhs indicates modulus if non-incremental + if (lhs && *step && !*incr) { + *mod = *step; *step = step2; + *n_steps = n_steps2; *steps = steps2; + } 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 (*n_steps) return 0; // steps not permitted + if (steps2 || n_steps2) return 0; // not permitted if (m < 0 || m > 127) return 0; *status = 0xc0 | k; *data = m; return 1; @@ -919,27 +1090,31 @@ parse_midi(char *tok, char *s, int lhs, int mode, // control change if (m < 0 || m > 127) return 0; *status = 0xb0 | k; *data = m; - // negative step size not permitted on rhs in key mode - if (!lhs && *step < 0 && !mode) return 0; - // step size only permitted on lhs if incremental - if (lhs && *step && !*incr) return 0; + // step size on lhs indicates modulus if non-incremental + if (lhs && *step && !*incr) { + *mod = *step; *step = step2; + *n_steps = n_steps2; *steps = steps2; + } if (lhs && !*step) *step = 1; // default return 1; } else if (strcmp(s, "kp") == 0) { // key pressure if (m < 0 || m > 127) return 0; *status = 0xa0 | k; *data = m; - // negative step size not permitted on rhs in key mode - if (!lhs && *step < 0 && !mode) return 0; - // step size only permitted on lhs if incremental - if (lhs && *step && !*incr) return 0; + // step size on lhs indicates modulus if non-incremental + if (lhs && *step && !*incr) { + *mod = *step; *step = step2; + *n_steps = n_steps2; *steps = steps2; + } if (lhs && !*step) *step = 1; // default return 1; } else { - // negative step size not permitted on rhs - if (!lhs && *step < 0) return 0; - // step size not permitted on lhs - if (lhs && *step) return 0; + // step size on lhs indicates modulus + if (lhs && *step) { + *mod = *step; *step = step2; + *n_steps = n_steps2; *steps = steps2; + } + if (lhs && !*step) *step = 1; // default // 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) @@ -953,7 +1128,7 @@ parse_midi(char *tok, char *s, int lhs, int mode, int start_translation(translation *tr, char *which_key) { - int k, status, data, step, incr, dir; + int k, status, data, step, n_steps, *steps, incr, dir, mod; char buf[100]; //printf("start_translation(%s)\n", which_key); @@ -970,19 +1145,29 @@ start_translation(translation *tr, char *which_key) modifier_count = 0; midi_channel = 0; k = *which_key == '^' || (is_anyshift = *which_key == '?'); - if (parse_midi(which_key+k, buf, 1, 0, &status, &data, &step, &incr, &dir)) { + if (parse_midi(which_key+k, buf, 1, 0, &status, &data, &step, &n_steps, &steps, &incr, &dir, &mod)) { int chan = status & 0x0f; - mode = !!incr; + mode = incr?0:mod?2:1; switch (status & 0xf0) { case 0x90: - // note on/off - first_stroke = find_note(tr, k, chan, data, 0); - release_first_stroke = find_note(tr, k, chan, data, 1); - if (is_anyshift) { - alt_press_stroke = find_note(tr, 0, chan, data, 0); - alt_release_stroke = find_note(tr, 0, chan, data, 1); + if (mod) { + // note mod + first_stroke = find_note(tr, k, chan, data, 0, mod, + step, n_steps, steps); + if (is_anyshift) { + alt_press_stroke = find_note(tr, 0, chan, data, 0, mod, + step, n_steps, steps); + } + } else { + // note on/off + first_stroke = find_note(tr, k, chan, data, 0, 0, 0, 0, 0); + release_first_stroke = find_note(tr, k, chan, data, 1, 0, 0, 0, 0); + if (is_anyshift) { + alt_press_stroke = find_note(tr, 0, chan, data, 0, 0, 0, 0, 0); + alt_release_stroke = find_note(tr, 0, chan, data, 1, 0, 0, 0, 0); + } + is_keystroke = 1; } - is_keystroke = 1; break; case 0xc0: // pc: To make our live easier and for consistency with the other @@ -999,16 +1184,7 @@ start_translation(translation *tr, char *which_key) is_keystroke = 1; break; case 0xb0: - if (!incr) { - // cc on/off - first_stroke = find_cc(tr, k, chan, data, 0); - release_first_stroke = find_cc(tr, k, chan, data, 1); - if (is_anyshift) { - alt_press_stroke = find_cc(tr, 0, chan, data, 0); - alt_release_stroke = find_cc(tr, 0, chan, data, 1); - } - is_keystroke = 1; - } else { + if (incr) { // cc (step up, down) if (step <= 0) { fprintf(stderr, "zero or negative step size not permitted here: [%s]%s\n", current_translation, which_key); @@ -1032,19 +1208,27 @@ start_translation(translation *tr, char *which_key) alt_release_stroke = find_ccs(tr, 0, chan, data, 1, step, incr>1); } } + } else if (mod) { + // cc mod + first_stroke = find_cc(tr, k, chan, data, 0, mod, + step, n_steps, steps); + if (is_anyshift) { + alt_press_stroke = find_cc(tr, 0, chan, data, 0, mod, + step, n_steps, steps); + } + } else { + // cc on/off + first_stroke = find_cc(tr, k, chan, data, 0, 0, 0, 0, 0); + release_first_stroke = find_cc(tr, k, chan, data, 1, 0, 0, 0, 0); + if (is_anyshift) { + alt_press_stroke = find_cc(tr, 0, chan, data, 0, 0, 0, 0, 0); + alt_release_stroke = find_cc(tr, 0, chan, data, 1, 0, 0, 0, 0); + } + is_keystroke = 1; } break; case 0xa0: - if (!incr) { - // kp on/off - first_stroke = find_kp(tr, k, chan, data, 0); - release_first_stroke = find_kp(tr, k, chan, data, 1); - if (is_anyshift) { - alt_press_stroke = find_kp(tr, 0, chan, data, 0); - alt_release_stroke = find_kp(tr, 0, chan, data, 1); - } - is_keystroke = 1; - } else { + if (incr) { // kp (step up, down) if (step <= 0) { fprintf(stderr, "zero or negative step size not permitted here: [%s]%s\n", current_translation, which_key); @@ -1061,19 +1245,27 @@ start_translation(translation *tr, char *which_key) alt_release_stroke = find_kps(tr, 0, chan, data, 1, step); } } + } else if (mod) { + // kp mod + first_stroke = find_kp(tr, k, chan, data, 0, mod, + step, n_steps, steps); + if (is_anyshift) { + alt_press_stroke = find_kp(tr, 0, chan, data, 0, mod, + step, n_steps, steps); + } + } else { + // kp on/off + first_stroke = find_kp(tr, k, chan, data, 0, 0, 0, 0, 0); + release_first_stroke = find_kp(tr, k, chan, data, 1, 0, 0, 0, 0); + if (is_anyshift) { + alt_press_stroke = find_kp(tr, 0, chan, data, 0, 0, 0, 0, 0); + alt_release_stroke = find_kp(tr, 0, chan, data, 1, 0, 0, 0, 0); + } + is_keystroke = 1; } break; case 0xd0: - if (!incr) { - // cp on/off - first_stroke = find_cp(tr, k, chan, 0); - release_first_stroke = find_cp(tr, k, chan, 1); - if (is_anyshift) { - alt_press_stroke = find_cp(tr, 0, chan, 0); - alt_release_stroke = find_cp(tr, 0, chan, 1); - } - is_keystroke = 1; - } else { + if (incr) { // cp (step up, down) if (step <= 0) { fprintf(stderr, "zero or negative step size not permitted here: [%s]%s\n", current_translation, which_key); @@ -1090,19 +1282,27 @@ start_translation(translation *tr, char *which_key) alt_release_stroke = find_cps(tr, 0, chan, 1, step); } } + } else if (mod) { + // cp mod + first_stroke = find_cp(tr, k, chan, 0, mod, + step, n_steps, steps); + if (is_anyshift) { + alt_press_stroke = find_cp(tr, 0, chan, 0, mod, + step, n_steps, steps); + } + } else { + // cp on/off + first_stroke = find_cp(tr, k, chan, 0, 0, 0, 0, 0); + release_first_stroke = find_cp(tr, k, chan, 1, 0, 0, 0, 0); + if (is_anyshift) { + alt_press_stroke = find_cp(tr, 0, chan, 0, 0, 0, 0, 0); + alt_release_stroke = find_cp(tr, 0, chan, 1, 0, 0, 0, 0); + } + is_keystroke = 1; } break; case 0xe0: - if (!incr) { - // pb on/off - first_stroke = find_pb(tr, k, chan, 0); - release_first_stroke = find_pb(tr, k, chan, 1); - if (is_anyshift) { - alt_press_stroke = find_pb(tr, 0, chan, 0); - alt_release_stroke = find_pb(tr, 0, chan, 1); - } - is_keystroke = 1; - } else { + if (incr) { // pb (step up, down) if (step <= 0) { fprintf(stderr, "zero or negative step size not permitted here: [%s]%s\n", current_translation, which_key); @@ -1119,6 +1319,23 @@ start_translation(translation *tr, char *which_key) alt_release_stroke = find_pbs(tr, 0, chan, 1, step); } } + } else if (mod) { + // pb mod + first_stroke = find_pb(tr, k, chan, 0, mod, + step, n_steps, steps); + if (is_anyshift) { + alt_press_stroke = find_pb(tr, 0, chan, 0, mod, + step, n_steps, steps); + } + } else { + // pb on/off + first_stroke = find_pb(tr, k, chan, 0, 0, 0, 0, 0); + release_first_stroke = find_pb(tr, k, chan, 1, 0, 0, 0, 0); + if (is_anyshift) { + alt_press_stroke = find_pb(tr, 0, chan, 0, 0, 0, 0, 0); + alt_release_stroke = find_pb(tr, 0, chan, 1, 0, 0, 0, 0); + } + is_keystroke = 1; } break; default: @@ -1187,7 +1404,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->incr); + append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr); s->dirty = 0; } s = s->next; @@ -1209,7 +1426,7 @@ add_release(int all_keys) } else if (s->shift) { append_shift(); } else { - append_midi(s->status, s->data, s->step, s->incr); + append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr); } s = s->next; } @@ -1224,7 +1441,7 @@ add_release(int all_keys) } else if (s->shift) { append_shift(); } else { - append_midi(s->status, s->data, s->step, s->incr); + append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr); } s = s->next; } @@ -1237,7 +1454,7 @@ add_release(int all_keys) } else if (s->shift) { append_shift(); } else { - append_midi(s->status, s->data, s->step, s->incr); + append_midi(s->status, s->data, s->step, s->n_steps, s->steps, s->incr); } s = s->next; } @@ -1276,16 +1493,16 @@ add_string(char *str) void add_midi(char *tok) { - int status, data, step, incr, dir = 0; + int status, data, step, n_steps, *steps, incr, dir = 0, mod = 0; char buf[100]; - if (parse_midi(tok, buf, 0, mode, &status, &data, &step, &incr, &dir)) { + if (parse_midi(tok, 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; } else { if ((status & 0xf0) != 0xe0 || step != 0) - append_midi(status, data, step, incr!=0); + append_midi(status, data, step, n_steps, steps, incr!=0); else fprintf(stderr, "zero step size not permitted: %s\n", tok); } @@ -1309,10 +1526,10 @@ finish_translation(void) add_release(1); if (debug_strokes) { if (is_keystroke) { - print_stroke_sequence(key_name, "D", *press_first_stroke); - print_stroke_sequence(key_name, "U", *release_first_stroke); + print_stroke_sequence(key_name, "D", *press_first_stroke, 0, 0, 0, 0, 0); + print_stroke_sequence(key_name, "U", *release_first_stroke, 0, 0, 0, 0, 0); } else { - print_stroke_sequence(key_name, "", *first_stroke); + print_stroke_sequence(key_name, "", *first_stroke, 0, 0, 0, 0, 0); } printf("\n"); }