Add some convenience syntax for bidirectional cc and pb translations.
parent
19e92acdef
commit
af48ab56b2
145
readconfig.c
145
readconfig.c
|
@ -8,7 +8,8 @@
|
|||
|
||||
Lines starting with # are comments.
|
||||
|
||||
Sequence of sections defining translation classes, each section is:
|
||||
The file is a sequence of sections defining translation classes. Each
|
||||
section takes the following form:
|
||||
|
||||
[name] regex
|
||||
CC<0..127> output # control change
|
||||
|
@ -69,7 +70,7 @@
|
|||
|
||||
Any keycode can be followed by an optional /D, /U, or /H, indicating
|
||||
that the key is just going down (without being released), going up,
|
||||
or going down and being held until the shuttlepro key is released.
|
||||
or going down and being held until the "off" event is received.
|
||||
|
||||
So, in general, modifier key codes will be followed by /D, and
|
||||
precede the keycodes they are intended to modify. If a sequence
|
||||
|
@ -96,7 +97,7 @@
|
|||
letter "b" if it is decreased. Also, the number of times one of these
|
||||
keys is output corresponds to the actual change in the controller
|
||||
value. (Thus, if in the example CC7 increases by 32, say, 32 "a"s
|
||||
will be output).
|
||||
will be output.)
|
||||
|
||||
CC7+ "a"
|
||||
CC7+ "b"
|
||||
|
@ -114,6 +115,25 @@
|
|||
CC60< XK_Left
|
||||
CC60> XK_Right
|
||||
|
||||
If the "up" and "down" sequences for controller and pitch bend changes
|
||||
are the same, the notation "=" can be used to indicate that the same
|
||||
sequence should be output in either case. This most commonly arises in
|
||||
pure MIDI translations. For instance, to map the modulation wheel
|
||||
(CC1) to the volume controller (CC7):
|
||||
|
||||
CC1= CC7
|
||||
|
||||
Which is exactly the same as the two translations:
|
||||
|
||||
CC1+ CC7
|
||||
CC1- CC7
|
||||
|
||||
The same goes for "<"/">" and "~" in incremental mode. E.g., CC1~ CC7
|
||||
is exactly the same as:
|
||||
|
||||
CC1< CC7
|
||||
CC1> CC7
|
||||
|
||||
Furthermore, PB (pitch bends) can have a step size associated with
|
||||
them. The default step size is 1. To indicate a different step size,
|
||||
the notation PB[<step size>] is used. E.g., PB[1170] will give you
|
||||
|
@ -130,21 +150,23 @@
|
|||
translated MIDI messages are sent. (Otherwise, MIDI messages in the
|
||||
output translations will be ignored.)
|
||||
|
||||
Note that on output, the "+" and "-" suffixes aren't supported,
|
||||
because the *input* message determines whether it is a key press or
|
||||
value change type of event, and which direction it goes in the latter
|
||||
case. Also, "~" is used in lieu of "<" or ">" to indicate an
|
||||
incremental CC message in sign bit encoding. Finally, there's a
|
||||
special token of the form CH<1..16>. This doesn't actually generate
|
||||
any MIDI message. Rather, it sets the default MIDI channel for
|
||||
subsequent MIDI messages in the same output sequence, which is
|
||||
convenient if multiple messages are output to the same MIDI channel.
|
||||
|
||||
Bindings can involve as many MIDI messages as you want, and these can
|
||||
be combined freely with keypress events in any order. There's no
|
||||
limitation on the type or number of MIDI messages that you can put
|
||||
into a binding.
|
||||
|
||||
Note that on output, the +-=<> suffixes aren't supported, because the
|
||||
*input* message determines whether it is a key press or value change
|
||||
type of event, and which direction it goes in the latter case. Only
|
||||
the "~" suffix can be used to indicate an incremental CC message in
|
||||
sign bit encoding.
|
||||
|
||||
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,
|
||||
it sets the default MIDI channel for subsequent MIDI messages in the
|
||||
same output sequence, which is convenient if multiple messages are
|
||||
output to the same MIDI channel.
|
||||
|
||||
*/
|
||||
|
||||
#include "midizap.h"
|
||||
|
@ -497,7 +519,7 @@ stroke **first_stroke;
|
|||
stroke *last_stroke;
|
||||
stroke **press_first_stroke;
|
||||
stroke **release_first_stroke;
|
||||
int is_keystroke;
|
||||
int is_keystroke, is_bidirectional;
|
||||
int is_midi;
|
||||
char *current_translation;
|
||||
char *key_name;
|
||||
|
@ -623,22 +645,42 @@ re_press_temp_modifiers(void)
|
|||
|
||||
/* Parser for the MIDI message syntax. The syntax we actually parse here is:
|
||||
|
||||
tok ::= ( note | msg ) [number] [ "-" number] [incr]
|
||||
tok ::= ( note | msg ) [ number ] [ "-" number] [ incr ]
|
||||
note ::= ( "a" | ... | "g" ) [ "#" | "b" ]
|
||||
msg ::= "ch" | "pb" [ "[" number "]" ] | "pc" | "cc"
|
||||
incr ::= "-" | "+" | "<" | ">" | "~"
|
||||
incr ::= "-" | "+" | "=" | "<" | ">" | "~"
|
||||
|
||||
Numbers are always in decimal. The meaning of the first number depends on
|
||||
the context (octave number for notes, the actual data byte for other
|
||||
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, but may be followed by a step size in brackets; and "ch" must *not*
|
||||
occur as the first token and must *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 in the output sequence.) The incr flag
|
||||
is only permitted in conjunction with "pb" or "cc", and it takes on a
|
||||
different form in the first token of a translation. */
|
||||
denotes the MIDI channel, otherwise the default MIDI channel is used.
|
||||
|
||||
Note that not all combinations are possible -- "pb" has no data byte, but
|
||||
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.)
|
||||
|
||||
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.
|
||||
|
||||
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. */
|
||||
|
||||
static int note_number(char c, char b, int k)
|
||||
{
|
||||
|
@ -699,17 +741,21 @@ parse_midi(char *tok, char *s, int lhs,
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
if (*p && strchr("+-<>~", *p)) {
|
||||
if (*p && strchr("+-=<>~", *p)) {
|
||||
// incremental flag ("pb" and "cc" only)
|
||||
if (strcmp(s, "pb") && strcmp(s, "cc")) return 0;
|
||||
if ((*p == '<' || *p == '>' || *p == '~') && strcmp(s, "cc")) return 0;
|
||||
// Only the "~" form is permitted in output messages, and the other forms
|
||||
// are only permitted on the lhs of a translation.
|
||||
// these are only permitted with "cc"
|
||||
if (strchr("<>~", *p) && strcmp(s, "cc")) return 0;
|
||||
if (lhs) {
|
||||
if (*p == '~') return 0;
|
||||
*incr = (*p == '-' || *p == '+')? 1 : 2;
|
||||
*dir = (*p == '-' || *p == '<') ? -1 : 1;
|
||||
// *incr = 2 indicates an endless, sign-bit controller
|
||||
*incr = strchr("+-=", *p) ? 1 : 2;
|
||||
// *dir is -1 or +1 for down and up changes, but can also be zero for
|
||||
// *bidirectional translations ("=" and "~")
|
||||
*dir = (*p == '-' || *p == '<') ? -1 :
|
||||
(*p == '+' || *p == '>') ? 1 : 0;
|
||||
} else {
|
||||
// only the "~" form is permitted in output messages, where it indicates
|
||||
// an endless, sign-bit controller
|
||||
if (*p != '~') return 0;
|
||||
*incr = 2; *dir = 0;
|
||||
}
|
||||
|
@ -765,7 +811,7 @@ start_translation(translation *tr, char *which_key)
|
|||
}
|
||||
current_translation = tr->name;
|
||||
key_name = which_key;
|
||||
is_keystroke = is_midi = 0;
|
||||
is_keystroke = is_bidirectional = is_midi = 0;
|
||||
first_release_stroke = 0;
|
||||
regular_key_down = 0;
|
||||
modifier_count = 0;
|
||||
|
@ -783,8 +829,8 @@ start_translation(translation *tr, char *which_key)
|
|||
// pc: To make our live easier and for consistency with the other
|
||||
// messages, we treat this exactly like a note/cc on/off, even though
|
||||
// this message has no off state. Thus, when we receive a pc, it's
|
||||
// supposed to be treated as a "down" sequence immediately followed by
|
||||
// the corresponding "up" sequence.
|
||||
// supposed to be treated as a "press" sequence immediately followed by
|
||||
// the corresponding "release" sequence.
|
||||
first_stroke = &(tr->pc[chan][data][0]);
|
||||
release_first_stroke = &(tr->pc[chan][data][1]);
|
||||
is_keystroke = 1;
|
||||
|
@ -799,6 +845,17 @@ start_translation(translation *tr, char *which_key)
|
|||
// cc (step up, down)
|
||||
tr->is_incr[chan][data] = incr>1;
|
||||
first_stroke = &(tr->ccs[chan][data][dir>0]);
|
||||
if (!dir) {
|
||||
// This is a bidirectional translation (=, ~). We first fill in the
|
||||
// "down" part (pointed to by first_stroke). When finishing off the
|
||||
// translation, we then create an exact duplicate of the sequence
|
||||
// for the "up" part. Note that we (ab)use the release_first_stroke
|
||||
// variable, which normally records the release part of a key
|
||||
// translation, here to remember the "up" part of the translation,
|
||||
// so that we can fill in that part later.
|
||||
is_bidirectional = 1;
|
||||
release_first_stroke = &(tr->ccs[chan][data][1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xe0:
|
||||
|
@ -815,6 +872,10 @@ start_translation(translation *tr, char *which_key)
|
|||
}
|
||||
first_stroke = &(tr->pbs[chan][dir>0]);
|
||||
tr->step[chan][dir>0] = step;
|
||||
if (!dir) {
|
||||
is_bidirectional = 1;
|
||||
release_first_stroke = &(tr->pbs[chan][1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -826,7 +887,8 @@ start_translation(translation *tr, char *which_key)
|
|||
fprintf(stderr, "bad message name: [%s]%s\n", current_translation, which_key);
|
||||
return 1;
|
||||
}
|
||||
if (*first_stroke != NULL) {
|
||||
if (*first_stroke != NULL ||
|
||||
(is_bidirectional && *release_first_stroke != NULL)) {
|
||||
fprintf(stderr, "can't redefine message: [%s]%s\n", current_translation, which_key);
|
||||
return 1;
|
||||
}
|
||||
|
@ -891,6 +953,19 @@ add_release(int all_keys)
|
|||
}
|
||||
regular_key_down = 0;
|
||||
first_release_stroke = 1;
|
||||
if (all_keys && is_bidirectional) {
|
||||
// create a duplicate for bidirectional translations (=, ~)
|
||||
stroke *s = *press_first_stroke;
|
||||
first_stroke = release_first_stroke;
|
||||
while (s) {
|
||||
if (s->keysym) {
|
||||
append_stroke(s->keysym, s->press);
|
||||
} else {
|
||||
append_midi(s->status, s->data, s->step, s->incr);
|
||||
}
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in New Issue