Various text changes. Mod translations should really be named data translations, because that's what they are.

master
Albert Graef 2018-08-27 16:01:19 +02:00
parent 8e01664860
commit 3c51d494ad
2 changed files with 229 additions and 150 deletions

View File

@ -209,7 +209,11 @@ Input messages can be processed in two different ways, "key mode" and "data mode
- Since program changes have no parameter value associated with them, they don't really have an "on" or "off" status. But they are treated in the same key-like fashion anyway, assuming that they are "pressed" and then "released" immediately afterwards.
*Data mode* is available for all messages whose parameter value may continuously change over time, i.e., key and channel pressure, control changes, and pitch bends. In this mode, the actual *amount* of change in the value of the message (increment or decrement, a.k.a. "up" or "down") is processed rather than the on/off state. Data mode is indicated with a special suffix on the message token which indicates the direction of the change which the rule should apply to: increment (`+`), decrement (`-`), or both (`=`).
Also note that since an input message is only on or off in key mode, there's no step size in this mode. If the input message is followed by a step size then it is always processed in data mode.
*Data mode* is available for all messages whose parameter value may continuously change over time, i.e., key and channel pressure, control changes, and pitch bends. In this mode, the actual *amount* of change in the value of the message (increment or decrement, a.k.a. "up" or "down") is processed rather than the on/off state. Data mode is indicated with a special suffix on the message token which indicates (1) a step size and (2) the direction of the change which the rule should apply to: increment (`+`), decrement (`-`), or both (`=`). Both parts are optional, but at least one of them must be present (otherwise the message is processed in key mode). (Data mode messages with just a step size and no increment suffix are treated specially. As these translations are somewhat esoteric and mostly used with MIDI feedback generated by the host application, we'll have a look at them later, see the *Specialized Data Translations* section near the end of the manual.)
In the following, we only consider standard data mode messages having an increment suffix. In this case, the optional step size in brackets indicates the amount of change required to trigger the translation, so its effect is to downscale the amount of change in the input value.
Data mode usually tracks changes in the *absolute* value of a control. However, for `CC` messages there's also an alternative mode for so-called *incremental* controllers, or *encoders* for short, which can found on some DAW controllers. (These usually take the form of jog wheels or rotary encoders which can be turned endlessly in either direction. In contrast, absolute-valued controllers are usually faders or knobs which are confined to a range between minimum and maximum values.)
@ -221,7 +225,7 @@ Translations must be determined uniquely in each translation class. That is, the
The right-hand side of a translation (i.e., everything following the first token) is a sequence of one or more tokens, separated by whitespace, indicating either MIDI messages or X11 keyboard and mouse events to be output.
Let's look at keyboard and mouse output first. It consists of X key codes with optional up/down indicators, or strings of printable characters enclosed in double quotes. The syntax of these items, as well as the special `RELEASE` and `SHIFT` tokens which will be discussed later, are described by the following grammar:
In this section, we first have a look at keyboard and mouse output. It consists of X key codes with optional up/down indicators, or strings of printable characters enclosed in double quotes. The syntax of these items, as well as the special `RELEASE` and `SHIFT` tokens which will be discussed later, are described by the following grammar:
~~~
token ::= "RELEASE" | "SHIFT" | keycode [ "/" flag ] | string
@ -270,18 +274,25 @@ CC60> XK_Right
(The corresponding "bidirectional" translations, which are indicated with the `=` and `~` suffixes, are rarely used with keyboard and mouse translations. Same goes for the special `SHIFT` token. Thus we'll discuss these in later sections, see *MIDI Translations* and *Shift State* below.)
In data mode, input messages can also have a *step size* associated with them, which enables you to scale down changes in the parameter value. The default step size is 1 (no scaling). To change it, the desired step size is written in brackets immediately after the message token to be translated, but before the increment suffix. A step size *k* indicates that the translation is executed whenever the input value has changed by *k* units. For instance, to slow down the cursor movement in the example above by a factor of 4:
In data mode, input messages can also have a *step size* associated with them, which has the effect of downscaling changes in the parameter value. The default step size is 1 (no scaling). To change it, the desired step size is written in brackets immediately after the message token and before the increment suffix. A step size *k* indicates that the translation is executed whenever the input value has changed by *k* units. For instance, to slow down the cursor movement in the example above by a factor of 4:
~~~
CC7[4]- XK_Left
CC7[4]+ XK_Right
~~~
Note that there is no step size with input messages in (standard) key mode. The message is either just "on" or "off", with any nonzero value denoting off. There is a special variation of key mode which has a step size associated with it, but it functions differently. As these translations are somewhat esoteric and mostly used with MIDI feedback generated by the host application, we'll have a look at them later, see the *MIDI Feedback* section near the end of the manual.
The same goes for incremental `CC` messages:
~~~
CC60[4]< XK_Left
CC60[4]> XK_Right
~~~
Note that since there's no persistent absolute controller state in this case, this simply scales down the actual increment value in the message itself.
## MIDI Translations
Most of the notation for MIDI messages on the left-hand side of a translation rule also carry over to the output side. The only real difference is that the increment suffixes `+-=<>` aren't permitted here, as they are only used to determine the input mode (key or data) of the entire translation. (The `~` suffix *is* allowed, however, to indicate output in incremental bit-sign format in data translations, see below.)
Most of the notation for MIDI messages on the left-hand side of a translation rule also carry over to the output side. The only real difference is that the increment suffixes `+-=<>` aren't permitted here, as they are only used to determine the input mode (key or data) of the entire translation. The `~` suffix *is* allowed, however, to indicate output in incremental bit-sign format in data translations, see below. Step sizes are permitted as well on the output side, in *both* key and data translations. Their meaning depends on the kind of translation, however. In key translations, they denote the (nonzero) value to be used for the "on" state in the press sequence; in data translations, they indicate the amount of change for each unit input change (which has the effect of *upscaling* the value change).
The output sequence can involve as many MIDI messages as you want, and these can be combined freely with keyboard and mouse events in any order. However, as already discussed in Section *MIDI Output* above, you need to invoke the midizap program with the `-o` option to make MIDI output work. Otherwise, MIDI messages in the output translations will just be silently ignored.
@ -350,6 +361,14 @@ The step size can also be negative, which allows you to reverse the direction of
CC1= CC1[-1]
~~~
Another possible use is to employ step sizes on *both* the left-hand and right-hand side of a rule, in order to approximate a rational scale factor:
~~~
CC1[3]= CC1[2]
~~~
The above translation will only be triggered when the input value changes by 3 units, and the change in the output value will then be doubled, so that the net effect is to scale the amount of change by 2/3. Note that this will only work if the input and output step sizes are reasonably small, so for most rational scale factors this method can only provide a very rough approximation.
## Shift State
The special `SHIFT` token toggles an internal shift state, which can be used to generate alternative output for certain MIDI messages. Please note that, like the `CH` token, the `SHIFT` token doesn't generate any output by itself; it merely toggles the internal shift bit which can then be queried in other translations to distinguish between shifted and unshifted bindings for the same input message.
@ -379,7 +398,7 @@ To keep things simple, only one shift status is available in the present impleme
# Advanced Features
This section covers some more advanced functionality which isn't used nearly as often as the basic features discussed in previous sections, but will come in handy in some situations. Specifically, we'll discuss *MIDI feedback*, which is needed to properly implement bidirectional communication with some controllers, as well as a special kind of key translations which helps implementing some types of feedback, but also has its uses in "normal" processing.
This section covers some more advanced functionality which is a bit more complicated and is used less frequently than the basic features discussed in previous sections, but will come in handy in some more specialized use cases. Specifically, we'll discuss *MIDI feedback*, which is needed to properly implement bidirectional communication with some controllers, as well as a special kind of data translations which helps implement some types of feedback, but also has its uses in "normal" processing.
## MIDI Feedback
@ -387,11 +406,11 @@ Some MIDI controllers need a more elaborate setup than what we've seen so far, b
You then wire up midizap's `midi_in` and `midi_out` ports to controller and application as before, but in addition you also connect the application back to midizap's `midi_in2` port, and the `midi_out2` port to the controller. This reverse path is what is needed to translate the feedback from the application and send it back to the controller. A full-blown example for this kind of setup can be found in examples/APCmini.midizaprc in the sources, which shows how to emulate a Mackie controller with AKAI's APCmini device, so that it readily works with DAW software such as Ardour.
## Specialized Key Translations
## Specialized Data Translations
Most of the time, MIDI feedback uses just the standard kinds of MIDI messages readily supported by midizap, such as note messages which make buttons light up in different colors, or control change messages which set the positions of motor faders. However, there are some encodings of MIDI messages employed in feedback, such as time and meter displays, which combine different bits of information in a single message, making them difficult or even impossible to translate using the simple kinds of rules we've seen so far.
midizap offers a special variation of key mode to help decoding at least some of these special messages. For reasons which will become obvious in a moment, we also call these *mod key translations*, or just *mod translations* for short. The extended MIDI syntax being used here is described by the following grammar rules (please refer to the beginning of Section *Translation Syntax* for the parts of the syntax not explicitly defined here):
midizap offers a special variation of data mode to help decoding at least some of these special messages. For reasons which will become obvious in a moment, we also call these *mod data translations*, or just *mod translations* for short. The extended MIDI syntax being used here is described by the following grammar rules (please refer to the beginning of Section *Translation Syntax* for the parts of the syntax not explicitly defined here):
~~~
token ::= msg [ steps ] [ "-" number]
@ -399,35 +418,35 @@ steps ::= "[" list "]" | "[" number "]" "[" list "]"
list ::= number { "," number | ":" number }
~~~
To explain the meaning of these translations, we take the mapping of channel pressure to notes as a running example here. But the same works with any kind of message having a parameter value (i.e., anything but `PC`). In its most basic form, the translation looks as follows:
To explain the meaning of these translations, we take the mapping of channel pressure to notes indicating buttons on the AKAI APCmini (cf. examples/APCmini.midizaprc in the sources) as a running example here. But the same works with any kind of message having a parameter value (i.e., anything but `PC`) and any kind of MIDI output.
In its most basic form, the translation simply looks as follows:
~~~
CP[16] C0
~~~
This looks like a key translation with a step size, but is treated differently. Firstly, there are no separate press and release sequences like in other key translations, only a single output sequence. Thus, like in data mode, all down keys will be released immediately (there aren't any in this example, but in general, all kinds of output events are allowed, like in other translations).
In contrast to standard data translations, there's no increment suffix here, so the translation does *not* indicate an incremental value change of some sort. Rather, the output messages are constructed directly from the input value by some arithmetic calculations. To these ends, the step size on the left-hand side is actually being used as a *modulus* in order to decompose the input value into two separate quantities, *quotient* and *remainder*. Only the latter becomes the value of the output message, while the former is used as an *offset* to modify the output message. (Note that `CP` and `PB` messages don't have a modifiable offset, so if you use these on the output side of a mod translation, the offset part of the input value will simply be ignored. The same goes for the `PC` message, which doesn't have a parameter value either, so that the remainder value will be ignored as well.)
Secondly, there's no "on"/"off" status here to determine the output values either. Rather, the output messages are constructed directly from the input value by some arithmetic calculations. To these ends, the step size is actually being used as a *modulus* in order to decompose the input value into two separate quantities, *quotient* and *remainder*. Only the latter becomes the value of the output message, while the former is used as an *offset* to modify the output message.
In order to describe exactly how this works, let's assume an input value *v* and a modulus *k*. Mathematically, *v* is divided by *k*, yielding the offset *q* = [*v*/*k*] (i.e., *v*/*k* rounded down to the nearest integer towards zero), and the remainder *r* = *v* - *kq* of that division. E.g., with *k* = 16 and *v* = 21, we have that 16 + 5 = 21 and thus you'll get *q* = 1 and *r* = 5. (In layman's terms, 21 divided by 16 yields 1 with a remainder of 5.) The calculated offset *q* is then applied to the note itself, and the remainder *r* becomes the velocity of that note. So in the example the output would be the note `C#0` (`C0` offset by 1) with a velocity of 5. On the APCmini, this message would then light up the second button in the bottom row of the 8x8 grid in yellow.
More precisely, the input value *v*, say, is divided by the given step size *k*, yielding the offset *p* = *v/k* (rounded to the nearest integer towards zero), and the remainder *q* of that division. E.g., with *k* = 16 and *v* = 21, you'll get *p* = 1 and *q* = 5 (21 divided by 16 yields 1 with remainder 5, because 16×1 + 5 = 21). The offset *p* is then applied to the note itself, and the remainder *q* becomes the velocity of that note. So in the example the output would be the note `C#0` (`C0` offset by 1) with a velocity of 5.
If all this sounds a bit esoteric, please bear with us. There is in fact an important use case for it, namely decoding meter information in the Mackie control protocol. Each meter value is sent by the host application in the form of a key pressure message whose value consists of a mixer channel index 0..7 in the "high nibble" (bits 4..6) and the corresponding meter value in the "low nibble" (bits 0..3), which is why we used 16 as the modulus in this example.
(Note that `CP` and `PB` messages don't have a modifiable offset, so if you use these on the output side of a mod translation, the offset part of the input value will be ignored. The same goes for the `PC` message, which doesn't have a parameter value either, so that the remainder value will be ignored, too.)
The above might seem a bit esoteric, but this is in fact exactly how you decode meter information in the Mackie control protocol, which consists of a mixer channel index 0..7 in the high nibble (bits 4..6) and the corresponding meter value in the low nibble (bits 0..3) of a key pressure message, which is why we used 16 as the modulus in this example. There are also some variations of the syntax which make this kind of translation more flexible. In particular, on the right-hand side of the rule you can specify a step size if the remainder value needs to be scaled:
There are some variations of the syntax which make this kind of translation more flexible. In particular, on the right-hand side of the rule you can specify a step size if the remainder *r* needs to be scaled:
~~~
CP[16] C0[2]
~~~
Or you can specify a *list* of discrete velocity values instead. E.g., the APCmini uses the velocities 1, 3 and 5 to denote the colors green, red and yellow, respectively, and a zero velocity denotes "off", so you can map the meter value to different colors as follows:
But in many cases the required transformations on *r* will be more complicated. Therefore it is also possible to specify a *list* of discrete velocity values instead. E.g., the APCmini uses the velocities 0, 1, 3 and 5 to denote "off" and the colors green, red and yellow, respectively, so you can map the meter value to different colors as follows:
~~~
CP[16] C0[0,1,1,1,1,5,5,5,3]
~~~
The remainder of the input value will then be used as an index into the list to give the translated value. E.g., in our example 0 will be mapped to 0 (off), 1..4 to 1 (green), 5..7 to 5 (yellow), and 8 to 3 (red). Also, the last value in the list will be used for any index which runs past the end of the list. Thus in the example, if for some reason you'd receive a meter value of 10, say, the output will still be 3, since it's the last value in the list.
The remainder *r* will then be used as an index into the list to give the translated value. E.g., in our example 0 will be mapped to 0 (off), 1..4 to 1 (green), 5..7 to 5 (yellow), and 8 to 3 (red), which actually matches the Mackie protocol specifications. Also, the last value in the list will be used for any index which runs past the end of the list. Thus, if you receive a meter value of 10, say, which isn't in the list, the output will still be 3, since it's the last value in the list.
Note that there are a lot of repeated values in this example. For convenience, it's possible to abbreviate these using the notation *value*`:`*count*, which also helps readability. The following denotes exactly the same list as above:
There are a lot of repeated values in this example. For convenience, it's possible to abbreviate these using the notation *value*`:`*count*, which also helps readability. The following denotes exactly the same list as above:
~~~
CP[16] C0[0,1:4,5:3,3]
@ -439,57 +458,59 @@ You can also scale the *offset* value, by adding a second step size to the left-
CP[16][8] C0[0,1:4,5:3,3]
~~~
Now, a channel pressure value of 24 (denoting a meter value of 8 on the second mixer channel) will output the note `A0` (`C0` offset by 8) with velocity 3, which on the APCmini will light up the first LED in the second row in red. Instead of a single step size, it's also possible to specify a list of discrete offset values, so that you can achieve any regular or irregular output pattern that you like:
Now, a channel pressure value of 24 (denoting a meter value of 8 on the second mixer channel) will output the note `G#0` (`C0` offset by 8) with velocity 3, which on the APCmini will light up the first button in the *second* row in red. Instead of a single step size, it's also possible to specify a list of discrete offset values, so that you can achieve any regular or irregular output pattern that you want:
~~~
CP[16][1,8,17,24] C0[0,1:4,5:3,3]
~~~
You might also output several notes at once, to show a horizontal or vertical strip of LEDs for each mixer channel. For instance, suppose that we'd like to use an LED in the bottom row of the APCmini for the green, and the LEDs in the two rows above it for the yellow and red values, respectively. You can do that as follows:
You might also output several notes at once, in order to display a horizontal or vertical meter strip for each mixer channel. For instance, suppose that we'd like to use a button in the bottom row of the APCmini for the green, and the buttons in the two rows above it for the yellow and red values, respectively. You can do that as follows:
~~~
CP[16] C0[0,1] G#0[0:5,5] E1[0:8,3]
~~~
Note that in this case each of the output notes will be offset by the same amount, so that an input value of 24 will cause the second LED in the bottom row to light up in green, and the two LEDs above it in yellow and red, respectively. For more examples, please have a look at the APCmini.midizaprc file in the sources which has a collection of similar rules to implement the meter display.
Note that each of the output notes will be offset by the same amount, so that the green, yellow and red buttons will always be lined up vertically in this example, as required. For more examples, please have a look at the APCmini.midizaprc file in the sources which uses similar, but more elaborate rules to implement the meter display.
It goes without saying that this is not a universal solution, but it covers at least one important real-world use case, and should work with almost any other kind of "scrambled" MIDI feedback which packs two separate values together. Although we try to keep midizap lean and mean, we might add some more special-case key translations like these in the future, as the need arises.
While mod translations were specifically designed to help with the important case of Mackie meter feedback, this method should work with almost any other kind of "scrambled" MIDI data which packs two separate values together. There might be other special cases of MIDI data calling for different approaches, however. So, although we try to keep midizap lean and mean, we might add some more special-case data translations in the future, as the need arises.
## Other Uses
Mod translations work with all kinds of output, so that you can also output X11 key and mouse events along with the transformed MIDI data if needed. While mod translations are most commonly employed for MIDI feedback, they also have their uses in ordinary (forward) translations. The input message may be anything which has a parameter value, i.e., any MIDI message but `PC`, and you can choose the modulus large enough (8192 for `PB`, 128 for other messages) so that the offset is always zero, if you just want to employ the discrete value lists for your translations. These offer a great deal of flexibility, much more than can be achieved with simple step sizes. In fact, they can be used to realize *any* conceivable mapping between input and output values. For instance, suppose that we'd like to map controller values to the first few Fibonacci numbers:
Mod translations work with all kinds of output, so that you can also output X11 key and mouse events along with the transformed MIDI data if needed, and the input may be any kind of message which has a parameter value. So, while mod translations are most commonly employed for MIDI feedback, they can also be used as a more capable replacement for "ordinary" data translations in various contexts. We discuss some of these use cases below and show how they're implemented.
In particular, note you can always choose the modulus large enough (> 8192 for `PB`, > 127 for other messages) so that the offset becomes zero and thus inconsequential. This is useful if you just want to employ the discrete value lists (which at present are only available in mod translations) for your mappings. These offer a great deal of flexibility, much more than can be achieved with simple step sizes. In fact, they can be used to realize *any* conceivable mapping between input and output values. For instance, here's how to map controller values to the first few Fibonacci numbers:
~~~
CC1[128] CC1[0,1,1,2,3,5,8,13,21,34,55,89]
~~~
The output values don't have to be increasing either; they might be in any order you like:
The output values don't have to be increasing either; they might be in any order you want:
~~~
CC1[128] CC1[0,2,1,4,3,6,5,8,7,10,9,...]
~~~
You can also use a modulus of 1 if you'd like to map, say, controller values to note *numbers* (rather than velocities):
On the other hand, you can also use a modulus of 1 if you're only interested in the *offset* and don't care about the output value. This is useful, e.g., if you want to map controller values to note *numbers* (rather than velocities):
~~~
CC1[1] C0
~~~
This will output the note with the same number as the controller value, `C0` for value 0, `C#0` for value 1, etc. Note that the remainder value, which becomes the velocity of the output note, will always be zero here, so the above translation turns all notes off. If we want a nonzero velocity, we have to specify it in a value list:
This will output the note with the same number as the controller value, `C0` for value 0, `C#0` for value 1, etc. Note that the remainder value, which becomes the velocity of the output note, will always be zero here, so the above translation turns all notes off. To get a nonzero velocity, you have to specify it in a value list:
~~~
CC2[1] C0[127:1]
~~~
Now we can turn notes on with `CC2` and turn them off again with `CC1`. Note the little bit of trickery there on the right-hand side. Just `[127]` would be interpreted as a simple step size, which wouldn't do us much good here since the remainder value to be scaled is always zero. Thus we need to write `[127:1]` instead to make sure that the parser properly recognizes this as a value list. The list we used here will map any input to 127, which is exactly what we want here.
Now we can turn notes on with `CC2` and turn them off again with `CC1`. Note the little bit of trickery there on the right-hand side. Just `[127]` would be interpreted as a simple step size, which wouldn't do us much good here since the remainder value to be scaled is always zero. Thus we need to write `[127:1]` instead to make sure that the parser recognizes this as a value list. The `[127:1]` list will map any input (including zero) to 127, which is exactly what we want here.
For the sake of a more practical example, let's have another look at MIDI feedback in the Mackie protocol. The following rule decodes the lowest digit in the time display (`CC69`) to count off time on the 4 bottommost scene launch buttons on the AKAI APCmini. Note that the digits are actually encoded in ASCII, therefore the copious amount of initial zeros in the value lists below to skip over all the non-digit characters at the beginning of the ASCII table.
For the sake of a more practical example, let's have another look at MIDI feedback in the Mackie protocol. The following rule decodes the lowest digit in the time display (`CC69`) to count off time on the scene launch buttons on the AKAI APCmini. Note that the digits are actually encoded in ASCII, hence the copious amount of initial zeros in the value lists below with which we skip over all the non-digit characters at the beginning of the ASCII table.
~~~
CC69[128] F7[0:49,1,0] E7[0:50,1,0] Eb7[0:51,1,0] D7[0:52,1,0]
~~~
As you can see, mod key translations in combination with discrete value lists are really very powerful and let you implement pretty much any desired 1-1 mapping with ease. There *are* some limitations, though. In particular, mappings involving multiple different translations of the same input aren't possible right now, because translations must be unique. Also, there's no way to combine the values of several input messages into a single output message.
As you can see, mod data translations in combination with discrete value lists are really very powerful and let you implement pretty much any desired 1-1 mapping with ease. There *are* some limitations, though. In particular, mappings involving multiple different translations of the same input aren't possible right now, because translations must be unique. Also, there's no way to combine the values of several input messages into a single output message.
# Bugs

300
midizap.1
View File

@ -582,6 +582,11 @@ But they are treated in the same key\-like fashion anyway, assuming that
they are \[lq]pressed\[rq] and then \[lq]released\[rq] immediately
afterwards.
.PP
Also note that since an input message is only on or off in key mode,
there's no step size in this mode.
If the input message is followed by a step size then it is always
processed in data mode.
.PP
\f[I]Data mode\f[] is available for all messages whose parameter value
may continuously change over time, i.e., key and channel pressure,
control changes, and pitch bends.
@ -590,8 +595,23 @@ message (increment or decrement, a.k.a.
\[lq]up\[rq] or \[lq]down\[rq]) is processed rather than the on/off
state.
Data mode is indicated with a special suffix on the message token which
indicates the direction of the change which the rule should apply to:
increment (\f[C]+\f[]), decrement (\f[C]\-\f[]), or both (\f[C]=\f[]).
indicates (1) a step size and (2) the direction of the change which the
rule should apply to: increment (\f[C]+\f[]), decrement (\f[C]\-\f[]),
or both (\f[C]=\f[]).
Both parts are optional, but at least one of them must be present
(otherwise the message is processed in key mode).
(Data mode messages with just a step size and no increment suffix are
treated specially.
As these translations are somewhat esoteric and mostly used with MIDI
feedback generated by the host application, we'll have a look at them
later, see the \f[I]Specialized Data Translations\f[] section near the
end of the manual.)
.PP
In the following, we only consider standard data mode messages having an
increment suffix.
In this case, the optional step size in brackets indicates the amount of
change required to trigger the translation, so its effect is to
downscale the amount of change in the input value.
.PP
Data mode usually tracks changes in the \f[I]absolute\f[] value of a
control.
@ -628,7 +648,7 @@ first token) is a sequence of one or more tokens, separated by
whitespace, indicating either MIDI messages or X11 keyboard and mouse
events to be output.
.PP
Let's look at keyboard and mouse output first.
In this section, we first have a look at keyboard and mouse output.
It consists of X key codes with optional up/down indicators, or strings
of printable characters enclosed in double quotes.
The syntax of these items, as well as the special \f[C]RELEASE\f[] and
@ -764,12 +784,11 @@ Thus we'll discuss these in later sections, see \f[I]MIDI
Translations\f[] and \f[I]Shift State\f[] below.)
.PP
In data mode, input messages can also have a \f[I]step size\f[]
associated with them, which enables you to scale down changes in the
associated with them, which has the effect of downscaling changes in the
parameter value.
The default step size is 1 (no scaling).
To change it, the desired step size is written in brackets immediately
after the message token to be translated, but before the increment
suffix.
after the message token and before the increment suffix.
A step size \f[I]k\f[] indicates that the translation is executed
whenever the input value has changed by \f[I]k\f[] units.
For instance, to slow down the cursor movement in the example above by a
@ -782,16 +801,18 @@ CC7[4]+\ XK_Right
\f[]
.fi
.PP
Note that there is no step size with input messages in (standard) key
mode.
The message is either just \[lq]on\[rq] or \[lq]off\[rq], with any
nonzero value denoting off.
There is a special variation of key mode which has a step size
associated with it, but it functions differently.
As these translations are somewhat esoteric and mostly used with MIDI
feedback generated by the host application, we'll have a look at them
later, see the \f[I]MIDI Feedback\f[] section near the end of the
manual.
The same goes for incremental \f[C]CC\f[] messages:
.IP
.nf
\f[C]
CC60[4]<\ XK_Left
CC60[4]>\ XK_Right
\f[]
.fi
.PP
Note that since there's no persistent absolute controller state in this
case, this simply scales down the actual increment value in the message
itself.
.SS MIDI Translations
.PP
Most of the notation for MIDI messages on the left\-hand side of a
@ -799,8 +820,15 @@ translation rule also carry over to the output side.
The only real difference is that the increment suffixes \f[C]+\-=<>\f[]
aren't permitted here, as they are only used to determine the input mode
(key or data) of the entire translation.
(The \f[C]~\f[] suffix \f[I]is\f[] allowed, however, to indicate output
in incremental bit\-sign format in data translations, see below.)
The \f[C]~\f[] suffix \f[I]is\f[] allowed, however, to indicate output
in incremental bit\-sign format in data translations, see below.
Step sizes are permitted as well on the output side, in \f[I]both\f[]
key and data translations.
Their meaning depends on the kind of translation, however.
In key translations, they denote the (nonzero) value to be used for the
\[lq]on\[rq] state in the press sequence; in data translations, they
indicate the amount of change for each unit input change (which has the
effect of \f[I]upscaling\f[] the value change).
.PP
The output sequence can involve as many MIDI messages as you want, and
these can be combined freely with keyboard and mouse events in any
@ -931,6 +959,23 @@ values go up from 0 to 127:
CC1=\ CC1[\-1]
\f[]
.fi
.PP
Another possible use is to employ step sizes on \f[I]both\f[] the
left\-hand and right\-hand side of a rule, in order to approximate a
rational scale factor:
.IP
.nf
\f[C]
CC1[3]=\ CC1[2]
\f[]
.fi
.PP
The above translation will only be triggered when the input value
changes by 3 units, and the change in the output value will then be
doubled, so that the net effect is to scale the amount of change by 2/3.
Note that this will only work if the input and output step sizes are
reasonably small, so for most rational scale factors this method can
only provide a very rough approximation.
.SS Shift State
.PP
The special \f[C]SHIFT\f[] token toggles an internal shift state, which
@ -1005,12 +1050,13 @@ faders on a device which only has a single set of faders, by assigning
the shifted faders to the encoders, as shown above.
.SH Advanced Features
.PP
This section covers some more advanced functionality which isn't used
nearly as often as the basic features discussed in previous sections,
but will come in handy in some situations.
This section covers some more advanced functionality which is a bit more
complicated and is used less frequently than the basic features
discussed in previous sections, but will come in handy in some more
specialized use cases.
Specifically, we'll discuss \f[I]MIDI feedback\f[], which is needed to
properly implement bidirectional communication with some controllers, as
well as a special kind of key translations which helps implementing some
well as a special kind of data translations which helps implement some
types of feedback, but also has its uses in \[lq]normal\[rq] processing.
.SS MIDI Feedback
.PP
@ -1040,7 +1086,7 @@ A full\-blown example for this kind of setup can be found in
examples/APCmini.midizaprc in the sources, which shows how to emulate a
Mackie controller with AKAI's APCmini device, so that it readily works
with DAW software such as Ardour.
.SS Specialized Key Translations
.SS Specialized Data Translations
.PP
Most of the time, MIDI feedback uses just the standard kinds of MIDI
messages readily supported by midizap, such as note messages which make
@ -1052,10 +1098,10 @@ information in a single message, making them difficult or even
impossible to translate using the simple kinds of rules we've seen so
far.
.PP
midizap offers a special variation of key mode to help decoding at least
some of these special messages.
midizap offers a special variation of data mode to help decoding at
least some of these special messages.
For reasons which will become obvious in a moment, we also call these
\f[I]mod key translations\f[], or just \f[I]mod translations\f[] for
\f[I]mod data translations\f[], or just \f[I]mod translations\f[] for
short.
The extended MIDI syntax being used here is described by the following
grammar rules (please refer to the beginning of Section \f[I]Translation
@ -1070,10 +1116,13 @@ list\ \ ::=\ number\ {\ ","\ number\ |\ ":"\ number\ }
.fi
.PP
To explain the meaning of these translations, we take the mapping of
channel pressure to notes as a running example here.
channel pressure to notes indicating buttons on the AKAI APCmini
(cf.\ examples/APCmini.midizaprc in the sources) as a running example
here.
But the same works with any kind of message having a parameter value
(i.e., anything but \f[C]PC\f[]).
In its most basic form, the translation looks as follows:
(i.e., anything but \f[C]PC\f[]) and any kind of MIDI output.
.PP
In its most basic form, the translation simply looks as follows:
.IP
.nf
\f[C]
@ -1081,53 +1130,52 @@ CP[16]\ C0
\f[]
.fi
.PP
This looks like a key translation with a step size, but is treated
differently.
Firstly, there are no separate press and release sequences like in other
key translations, only a single output sequence.
Thus, like in data mode, all down keys will be released immediately
(there aren't any in this example, but in general, all kinds of output
events are allowed, like in other translations).
.PP
Secondly, there's no \[lq]on\[rq]/\[lq]off\[rq] status here to determine
the output values either.
In contrast to standard data translations, there's no increment suffix
here, so the translation does \f[I]not\f[] indicate an incremental value
change of some sort.
Rather, the output messages are constructed directly from the input
value by some arithmetic calculations.
To these ends, the step size is actually being used as a
\f[I]modulus\f[] in order to decompose the input value into two separate
quantities, \f[I]quotient\f[] and \f[I]remainder\f[].
To these ends, the step size on the left\-hand side is actually being
used as a \f[I]modulus\f[] in order to decompose the input value into
two separate quantities, \f[I]quotient\f[] and \f[I]remainder\f[].
Only the latter becomes the value of the output message, while the
former is used as an \f[I]offset\f[] to modify the output message.
.PP
More precisely, the input value \f[I]v\f[], say, is divided by the given
step size \f[I]k\f[], yielding the offset \f[I]p\f[] = \f[I]v/k\f[]
(rounded to the nearest integer towards zero), and the remainder
\f[I]q\f[] of that division.
E.g., with \f[I]k\f[] = 16 and \f[I]v\f[] = 21, you'll get \f[I]p\f[] =
1 and \f[I]q\f[] = 5 (21 divided by 16 yields 1 with remainder 5,
because 16×1 + 5 = 21).
The offset \f[I]p\f[] is then applied to the note itself, and the
remainder \f[I]q\f[] becomes the velocity of that note.
So in the example the output would be the note \f[C]C#0\f[] (\f[C]C0\f[]
offset by 1) with a velocity of 5.
.PP
(Note that \f[C]CP\f[] and \f[C]PB\f[] messages don't have a modifiable
offset, so if you use these on the output side of a mod translation, the
offset part of the input value will be ignored.
offset part of the input value will simply be ignored.
The same goes for the \f[C]PC\f[] message, which doesn't have a
parameter value either, so that the remainder value will be ignored,
too.)
parameter value either, so that the remainder value will be ignored as
well.)
.PP
The above might seem a bit esoteric, but this is in fact exactly how you
decode meter information in the Mackie control protocol, which consists
of a mixer channel index 0..7 in the high nibble (bits 4..6) and the
corresponding meter value in the low nibble (bits 0..3) of a key
pressure message, which is why we used 16 as the modulus in this
example.
There are also some variations of the syntax which make this kind of
In order to describe exactly how this works, let's assume an input value
\f[I]v\f[] and a modulus \f[I]k\f[].
Mathematically, \f[I]v\f[] is divided by \f[I]k\f[], yielding the offset
\f[I]q\f[] = [\f[I]v\f[]/\f[I]k\f[]] (i.e., \f[I]v\f[]/\f[I]k\f[]
rounded down to the nearest integer towards zero), and the remainder
\f[I]r\f[] = \f[I]v\f[] \- \f[I]kq\f[] of that division.
E.g., with \f[I]k\f[] = 16 and \f[I]v\f[] = 21, we have that 16 + 5 = 21
and thus you'll get \f[I]q\f[] = 1 and \f[I]r\f[] = 5.
(In layman's terms, 21 divided by 16 yields 1 with a remainder of 5.)
The calculated offset \f[I]q\f[] is then applied to the note itself, and
the remainder \f[I]r\f[] becomes the velocity of that note.
So in the example the output would be the note \f[C]C#0\f[] (\f[C]C0\f[]
offset by 1) with a velocity of 5.
On the APCmini, this message would then light up the second button in
the bottom row of the 8x8 grid in yellow.
.PP
If all this sounds a bit esoteric, please bear with us.
There is in fact an important use case for it, namely decoding meter
information in the Mackie control protocol.
Each meter value is sent by the host application in the form of a key
pressure message whose value consists of a mixer channel index 0..7 in
the \[lq]high nibble\[rq] (bits 4..6) and the corresponding meter value
in the \[lq]low nibble\[rq] (bits 0..3), which is why we used 16 as the
modulus in this example.
.PP
There are some variations of the syntax which make this kind of
translation more flexible.
In particular, on the right\-hand side of the rule you can specify a
step size if the remainder value needs to be scaled:
step size if the remainder \f[I]r\f[] needs to be scaled:
.IP
.nf
\f[C]
@ -1135,11 +1183,13 @@ CP[16]\ C0[2]
\f[]
.fi
.PP
Or you can specify a \f[I]list\f[] of discrete velocity values instead.
E.g., the APCmini uses the velocities 1, 3 and 5 to denote the colors
green, red and yellow, respectively, and a zero velocity denotes
\[lq]off\[rq], so you can map the meter value to different colors as
follows:
But in many cases the required transformations on \f[I]r\f[] will be
more complicated.
Therefore it is also possible to specify a \f[I]list\f[] of discrete
velocity values instead.
E.g., the APCmini uses the velocities 0, 1, 3 and 5 to denote
\[lq]off\[rq] and the colors green, red and yellow, respectively, so you
can map the meter value to different colors as follows:
.IP
.nf
\f[C]
@ -1147,17 +1197,17 @@ CP[16]\ C0[0,1,1,1,1,5,5,5,3]
\f[]
.fi
.PP
The remainder of the input value will then be used as an index into the
list to give the translated value.
The remainder \f[I]r\f[] will then be used as an index into the list to
give the translated value.
E.g., in our example 0 will be mapped to 0 (off), 1..4 to 1 (green),
5..7 to 5 (yellow), and 8 to 3 (red).
5..7 to 5 (yellow), and 8 to 3 (red), which actually matches the Mackie
protocol specifications.
Also, the last value in the list will be used for any index which runs
past the end of the list.
Thus in the example, if for some reason you'd receive a meter value of
10, say, the output will still be 3, since it's the last value in the
list.
Thus, if you receive a meter value of 10, say, which isn't in the list,
the output will still be 3, since it's the last value in the list.
.PP
Note that there are a lot of repeated values in this example.
There are a lot of repeated values in this example.
For convenience, it's possible to abbreviate these using the notation
\f[I]value\f[]\f[C]:\f[]\f[I]count\f[], which also helps readability.
The following denotes exactly the same list as above:
@ -1178,12 +1228,12 @@ CP[16][8]\ C0[0,1:4,5:3,3]
.fi
.PP
Now, a channel pressure value of 24 (denoting a meter value of 8 on the
second mixer channel) will output the note \f[C]A0\f[] (\f[C]C0\f[]
second mixer channel) will output the note \f[C]G#0\f[] (\f[C]C0\f[]
offset by 8) with velocity 3, which on the APCmini will light up the
first LED in the second row in red.
first button in the \f[I]second\f[] row in red.
Instead of a single step size, it's also possible to specify a list of
discrete offset values, so that you can achieve any regular or irregular
output pattern that you like:
output pattern that you want:
.IP
.nf
\f[C]
@ -1191,11 +1241,11 @@ CP[16][1,8,17,24]\ C0[0,1:4,5:3,3]
\f[]
.fi
.PP
You might also output several notes at once, to show a horizontal or
vertical strip of LEDs for each mixer channel.
For instance, suppose that we'd like to use an LED in the bottom row of
the APCmini for the green, and the LEDs in the two rows above it for the
yellow and red values, respectively.
You might also output several notes at once, in order to display a
horizontal or vertical meter strip for each mixer channel.
For instance, suppose that we'd like to use a button in the bottom row
of the APCmini for the green, and the buttons in the two rows above it
for the yellow and red values, respectively.
You can do that as follows:
.IP
.nf
@ -1204,39 +1254,45 @@ CP[16]\ C0[0,1]\ G#0[0:5,5]\ E1[0:8,3]
\f[]
.fi
.PP
Note that in this case each of the output notes will be offset by the
same amount, so that an input value of 24 will cause the second LED in
the bottom row to light up in green, and the two LEDs above it in yellow
and red, respectively.
Note that each of the output notes will be offset by the same amount, so
that the green, yellow and red buttons will always be lined up
vertically in this example, as required.
For more examples, please have a look at the APCmini.midizaprc file in
the sources which has a collection of similar rules to implement the
meter display.
the sources which uses similar, but more elaborate rules to implement
the meter display.
.PP
It goes without saying that this is not a universal solution, but it
covers at least one important real\-world use case, and should work with
almost any other kind of \[lq]scrambled\[rq] MIDI feedback which packs
two separate values together.
Although we try to keep midizap lean and mean, we might add some more
special\-case key translations like these in the future, as the need
arises.
While mod translations were specifically designed to help with the
important case of Mackie meter feedback, this method should work with
almost any other kind of \[lq]scrambled\[rq] MIDI data which packs two
separate values together.
There might be other special cases of MIDI data calling for different
approaches, however.
So, although we try to keep midizap lean and mean, we might add some
more special\-case data translations in the future, as the need arises.
.SS Other Uses
.PP
Mod translations work with all kinds of output, so that you can also
output X11 key and mouse events along with the transformed MIDI data if
needed.
While mod translations are most commonly employed for MIDI feedback,
they also have their uses in ordinary (forward) translations.
The input message may be anything which has a parameter value, i.e., any
MIDI message but \f[C]PC\f[], and you can choose the modulus large
enough (8192 for \f[C]PB\f[], 128 for other messages) so that the offset
is always zero, if you just want to employ the discrete value lists for
your translations.
needed, and the input may be any kind of message which has a parameter
value.
So, while mod translations are most commonly employed for MIDI feedback,
they can also be used as a more capable replacement for
\[lq]ordinary\[rq] data translations in various contexts.
We discuss some of these use cases below and show how they're
implemented.
.PP
In particular, note you can always choose the modulus large enough (>
8192 for \f[C]PB\f[], > 127 for other messages) so that the offset
becomes zero and thus inconsequential.
This is useful if you just want to employ the discrete value lists
(which at present are only available in mod translations) for your
mappings.
These offer a great deal of flexibility, much more than can be achieved
with simple step sizes.
In fact, they can be used to realize \f[I]any\f[] conceivable mapping
between input and output values.
For instance, suppose that we'd like to map controller values to the
first few Fibonacci numbers:
For instance, here's how to map controller values to the first few
Fibonacci numbers:
.IP
.nf
\f[C]
@ -1245,7 +1301,7 @@ CC1[128]\ CC1[0,1,1,2,3,5,8,13,21,34,55,89]
.fi
.PP
The output values don't have to be increasing either; they might be in
any order you like:
any order you want:
.IP
.nf
\f[C]
@ -1253,8 +1309,10 @@ CC1[128]\ CC1[0,2,1,4,3,6,5,8,7,10,9,...]
\f[]
.fi
.PP
You can also use a modulus of 1 if you'd like to map, say, controller
values to note \f[I]numbers\f[] (rather than velocities):
On the other hand, you can also use a modulus of 1 if you're only
interested in the \f[I]offset\f[] and don't care about the output value.
This is useful, e.g., if you want to map controller values to note
\f[I]numbers\f[] (rather than velocities):
.IP
.nf
\f[C]
@ -1267,7 +1325,7 @@ This will output the note with the same number as the controller value,
Note that the remainder value, which becomes the velocity of the output
note, will always be zero here, so the above translation turns all notes
off.
If we want a nonzero velocity, we have to specify it in a value list:
To get a nonzero velocity, you have to specify it in a value list:
.IP
.nf
\f[C]
@ -1282,17 +1340,17 @@ Just \f[C][127]\f[] would be interpreted as a simple step size, which
wouldn't do us much good here since the remainder value to be scaled is
always zero.
Thus we need to write \f[C][127:1]\f[] instead to make sure that the
parser properly recognizes this as a value list.
The list we used here will map any input to 127, which is exactly what
we want here.
parser recognizes this as a value list.
The \f[C][127:1]\f[] list will map any input (including zero) to 127,
which is exactly what we want here.
.PP
For the sake of a more practical example, let's have another look at
MIDI feedback in the Mackie protocol.
The following rule decodes the lowest digit in the time display
(\f[C]CC69\f[]) to count off time on the 4 bottommost scene launch
buttons on the AKAI APCmini.
Note that the digits are actually encoded in ASCII, therefore the
copious amount of initial zeros in the value lists below to skip over
(\f[C]CC69\f[]) to count off time on the scene launch buttons on the
AKAI APCmini.
Note that the digits are actually encoded in ASCII, hence the copious
amount of initial zeros in the value lists below with which we skip over
all the non\-digit characters at the beginning of the ASCII table.
.IP
.nf
@ -1301,7 +1359,7 @@ CC69[128]\ F7[0:49,1,0]\ E7[0:50,1,0]\ Eb7[0:51,1,0]\ D7[0:52,1,0]
\f[]
.fi
.PP
As you can see, mod key translations in combination with discrete value
As you can see, mod data translations in combination with discrete value
lists are really very powerful and let you implement pretty much any
desired 1\-1 mapping with ease.
There \f[I]are\f[] some limitations, though.