Cosmetic changes to meter feedback, also update comments and manual accordingly.

master
Albert Graef 2018-09-01 13:21:18 +02:00
parent 2bb1113fac
commit 3ae0854f88
3 changed files with 183 additions and 188 deletions

View File

@ -421,9 +421,21 @@ An in-depth discussion of controller feedback is beyond the scope of this manual
## Mod 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 feedback messages 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. One important application, which we'll use as a running example below, is the decoding of meter (RMS level) data in the Mackie protocol. There, 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).
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 feedback messages 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 data mode to help decoding such messages. We call them *mod translations*, because they involve calculations with integer moduli which enable you to not only calculate output from input values, but also modify the output messages themselves at the same time.
midizap offers a special variation of data mode to help decoding such messages. This involves calculations with integer moduli which enable you to modify output messages in certain ways, so for want of a better name we also call these bindings *mod translations*. The extended MIDI syntax being used here is described by the following grammar rules (please also refer to the beginning of Section *Translation Syntax* for the rest of the MIDI syntax):
One important task, which we'll use as a running example below, is the decoding of meter (RMS level) data in the Mackie protocol. There, each meter value is represented as 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). Specifically, we will show how to map these values to notes indicating buttons on the AKAI APCmini; please check examples/APCmini.midizaprc in the sources for details about this device. However, mod translations aren't limited to this specific example; similar rules will apply to many other kinds of "scrambled" MIDI data.
In its simplest form, the translation looks as follows:
~~~
CP[16] C0
~~~
In contrast to standard data translations, there's no increment flag here, so the translation does *not* indicate an incremental change of the input value. Instead, the step size on the left-hand side is actually treated 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 be ignored. `PC` messages, on the other hand, lack the parameter value, so in this case the remainder value will be disregarded instead.)
In order to describe more precisely how this works, let's assume an input value *v* and a modulus *k*. We divide *v* by *k*, yielding the quotient (offset) *q* = *v* div *k* and the remainder (value) *r* = *v* mod *k*. E.g., with *k* = 16 and *v* = 21, you'll get *q* = 1 and *r* = 5 (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 above the output would be the note `C#0` (`C0` offset by 1) with a velocity of 5. On the APCmini, this message will light up the second button in the bottom row of the 8x8 grid in yellow.
This simple kind of transformation is surprisingly versatile, and there are some variations of the syntax which make it even more flexible. The extended MIDI syntax being used here is described by the following grammar rules (please also refer to the beginning of Section *Translation Syntax* for the rest of the MIDI syntax):
~~~
token ::= msg [ steps ] [ "-" number] [ flag ]
@ -432,61 +444,51 @@ list ::= number { "," number | ":" number | "-" number }
flag ::= "'"
~~~
There are two new elements in the syntax, the "transposition" flag `'`, and lists of numbers enclosed in curly braces. For convenience, the latter also support the following abbreviations: repetition *a*`:`*b* (denoting *b* consecutive *a*'s) and enumeration *a*`-`*b* (denoting *a*`,`*a*±1`,`...`,`*b*, which ramps either up or down depending on whether *a*<*b* or *a*>*b*, respectively). The meaning of these constructs will be discussed below.
There are two new elements in the syntax, the "transposition" flag `'`, and lists of numbers enclosed in curly braces.
For the sake of a concrete example, in the following we consider the mapping of channel pressure messages encoding MCU meter values to notes indicating buttons on the AKAI APCmini; please check examples/APCmini.midizaprc in the sources for details. But these translations work the same with any kind of message having a parameter value (i.e., anything but `PC`) and any kind of MIDI output, so similar rules will help with other kinds of "scrambled" MIDI data.
- *Transposition*, denoted with the `'` (apostrophe) suffix on an output message, reverses the roles of *q* and *r*, so that the remainder becomes the offset and the quotient the value of the output message. We won't actually use this in the present example, but it's very convenient in some situations, and we'll get back to it in the following sections.
In its most basic form, the translation looks as follows:
- *Value lists*, denoted as lists of numbers separated by commas and enclosed in curly braces, provide a way to describe *discrete mappings* of input to output values. To these ends, the remainder value *r* is used as an index into the list to give the corresponding output value, and the last value in the list will be used for any index which runs past the end of the list. There are also some convenient shortcuts which let you construct these lists more easily: repetition *a*`:`*b* (denoting *b* consecutive *a*'s) and enumeration *a*`-`*b* (denoting *a*`,`*a*±1`,`...`,`*b*, which ramps either up or down depending on whether *a*<=*b* or *a*>*b*, respectively).
~~~
CP[16] C0
~~~
In contrast to standard data translations, there's no increment flag here, so the translation does *not* indicate an incremental change of the input value. Instead, the step size on the left-hand side is actually treated 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 be ignored. The `PC` message, in contrast, lacks the parameter value, so in this case the remainder value will be disregarded instead.)
In order to describe more precisely how this works, let's assume an input value *v* and a modulus *k*. We divide *v* by *k*, yielding the quotient (offset) *q* = *v* div *k* and the remainder (value) *r* = *v* mod *k*. E.g., with *k* = 16 and *v* = 21, you'll get *q* = 1 and *r* = 5 (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 will light up the second button in the bottom row of the 8x8 grid in yellow.
This simple kind of transformation is surprisingly versatile, and there are some variations of the syntax which make it even more flexible. One such variation is *transposition*, denoted with the `'` (apostrophe) flag at the end of the output message. It reverses the roles of *q* and *r*, so that the remainder becomes the offset and the quotient the value of the output message. For instance, with `CP[16] C0'` and the same input value of 21, you'd get the note `F0` (`C0` offset by 5) with a velocity of 1 instead. We won't utilize this in the present example, but it's very convenient in some situations, and we'll see some more examples of its use in the following section.
As usual in data translations, you can also specify a step size to upscale the output value *r*:
Let's return to our example. As usual in data translations, you can also specify a step size to upscale the output value *r*:
~~~
CP[16] C0[2]
~~~
But in many cases the required transformations on *r* will be more complicated. To accommodate these, it is also possible to specify a list of discrete values in curly braces 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:
But in many cases the required transformations on *r* will be more complicated, so we specify them as value lists 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 values to different colors, e.g., as follows:
~~~
CP[16] C0{0,1,1,1,1,5,5,5,3}
CP[16] C0{0,1,1,1,1,1,1,1,1,5,5,5,3}
~~~
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. E.g., if you receive a meter value of 10, which isn't in the list, the output will still be 3, since it's the last value in the list.
There are a lot of repeated values in this example, which makes the notation a bit untidy and error-prone. As a remedy, it's possible to abbreviate repeated values as *value*`:`*count*, which also helps readability. The following denotes exactly the same list as above:
Using the shorthand for repetitions, this can be written more succinctly (which also helps readability):
~~~
CP[16] C0{0,1:4,5:3,3}
CP[16] C0{0,1:8,5:3,3}
~~~
Furthermore, you can also scale the *offset* value, by adding a second step size to the left-hand side:
Thus 0 will be mapped to 0 (off), 1..8 to 1 (green), 9..11 to 5 (yellow), and 12 or more to 3 (red).
The quotient here is the mixer channel index in the high-nibble of the `CP` message, which will be used as an offset for the `C0` note on the right, so the above rule shows the meters as a single row of colored buttons at the bottom of the 8x8 grid on the APCmini (first value on `C0`, second value on `C#0`, etc.). To get a different layout, you can also scale the *offset* value, by adding a second step size to the left-hand side:
~~~
CP[16][8] C0{0,1:4,5:3,3}
CP[16][8] C0{0,1:8,5:3,3}
~~~
With this rule, the buttons for each mixer channel are now spread out across different *rows* rather than columns. 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:
With this rule, the buttons are now in the first *column* of the grid (first value on `C0`, second value on `G#0`, etc.). 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. E.g., the following rule places every other meter value in the second row:
~~~
CP[16]{1,8,17,24} C0{0,1:4,5:3,3}
CP[16]{0,9,2,11,4,13,6,15} C0{0,1:8,5:3,3}
~~~
You might also output several notes at once, in order to display a horizontal or vertical meter *strip* instead of just a single colored button for each mixer channel. For instance:
~~~
CP[16] C0{0,1} G#0{0:5,5} E1{0:8,3}
CP[16] C0{0,1} G#0{0:8,5} E1{0:11,3}
~~~
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. (The APCmini.midizaprc example uses a similar, albeit more elaborate translation to handle MCU meter data.)
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. (The APCmini.midizaprc example uses a similar, albeit more elaborate translation to handle meter data.)
Another example from the Mackie protocol is time feedback. The following rule (also from the APCmini.midizaprc example) decodes the least significant digit of the beat number in the time display (`CC69`) to count off time on some of the scene launch buttons of the 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.
@ -506,7 +508,7 @@ One common trick that we already encountered in the preceding section is to choo
CC1[128] C5
~~~
This translates the `CC1` (modulation wheel) controller to a `C5` (middle C) note message in such a way that the controller value becomes the velocity of the note. Note that this is different from both the key translation `CC1 C5` (which only preserves the "on"/"off" status but looses the actual parameter value) and the incremental data translation `CC1= C5` (which involves some internal state for keeping track of current values, and executes the translation in a step-wise fashion for each unit change). In contrast, a mod translation maps messages one-to-one and does not involve any ramping to arrive at the new value. Therefore, while key and incremental data translations are often used in conjunction with key and mouse output, for pure MIDI bindings like the one above a mod translation is often preferable.
This translates the `CC1` (modulation wheel) controller to a `C5` (middle C) note message in such a way that the controller value becomes the velocity of the note. Note that this is different from both the key translation `CC1 C5` (which only preserves the "on"/"off" status but looses the actual parameter value) and the incremental data translation `CC1= C5` (which executes the translation in a step-wise fashion for each unit change). In contrast, a mod translation always maps messages one-to-one in a single step. While key and incremental data translations are typically used in conjunction with key and mouse output, for pure MIDI bindings like the one above a mod translation is often preferable.
You also need to use a mod translation if your binding involves discrete value lists, because these are not available in other kinds of translations. Value lists can represent *any* discrete mapping from input to output values, and thus offer much more flexibility than simple step sizes. For instance, here's how to map controller values to the first few Fibonacci numbers:
@ -520,7 +522,7 @@ Value lists offer some conveniences to facilitate their use, *repetitions* (whic
CC1[128] CC1{127-0}
~~~
The values in a list may be in any order, and you can throw in any combination of singleton values, enumerations and repetitions. E.g., the list `{0:2-5,7:5-0}` starts with two zeros, then ramps up to 5 followed by five 7s, before finally fading back to 0. For us mortals, that's much easier to read and also much less error-prone to write than `{0,0,1,2,3,4,5,7,7,7,7,7,6,5,4,3,2,1,0}`.
The values in a list may be in any order, and you can throw in any combination of singleton values, enumerations and repetitions. E.g., the list `{0:2-5,7:5-0}` starts with two zeros, then ramps up to 5 followed by five 7s, before finally fading back to 0. That's much easier to read and also much less error-prone to write than `{0,0,1,2,3,4,5,7,7,7,7,7,6,5,4,3,2,1,0}`.
The mod translations above are all trivial translations with a zero offset. Conversely, you can also use a modulus of 1 to nullify the remainder instead, if you're only interested in the offset. This can be used, e.g., if you want to map controller values to note *numbers* (rather than velocities):
@ -528,15 +530,13 @@ The mod translations above are all trivial translations with a zero offset. Conv
CC1[1] C0
~~~
This outputs the note with the same number as the controller value, `C0` for value 0, `C#0` for value 1, etc. Note that the remainder, 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, as shown below. Note that a simple scale factor won't do us much good here since the value to be scaled is always zero. A value list is the only way to turn that zero into a nonzero value.
This outputs the note with the same number as the controller value, `C0` for value 0, `C#0` for value 1, etc. Note that the remainder, 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 specify it in a value list:
~~~
CC2[1] C0{127}
~~~
With both of these rules at hand, we can now turn notes on with `CC2` and turn them off again with `CC1`.
We also mention in passing here that a modulus 1 translation is actually the "dual" of a trivial mod translation, employing the `'` flag on the output message. E.g., these two translations work exactly the same:
We also mention in passing here that a modulus 1 translation is actually the "dual" of a trivial mod translation, employing the `'` flag on the output message to transpose quotient and remainder. E.g., these two translations work exactly the same:
~~~
CC1[1] C0
@ -563,7 +563,7 @@ CC1[16]{0} CC1 CC2'
Using similar rules, you can extract any part of an input value, down to every single bit if needed (see the example at the end of the next section).
As you can see, mod translations in combination with discrete value lists are fairly powerful and let you implement pretty much any desired mapping with ease. There are some limitations, though. In particular, the reversal of the `CC1[1] C0` translation, i.e., *extracting* the note number from a note input, is rather tedious (it involves writing down rules for each and every single note). Also, there's no direct way to combine different kinds of translations of the same input, or to consolidate the values of multiple input messages into a single output message. (The macro facility in the following section lets you address the former, but not the latter problem.)
As you can see, mod translations in combination with discrete value lists are fairly powerful and let you implement pretty much any desired mapping with ease. There are some limitations, though. In particular, the reversal of the `CC1[1] C0` translation, i.e., *extracting* the note number from a note input, is rather tedious (it involves writing down rules for each and every single note). Also, there's no direct way to combine different kinds of translations of the same input, or to consolidate the values of multiple input messages into a single output message. (The macro facility in the following section lets you address the former, but doesn't help with the latter problem.)
## Macro Translations
@ -601,7 +601,7 @@ CC0= C5
CC0[16]{0} CC1
~~~
But we can't write them this way, because we're not allowed to bind `CC0` in two different translations at once (the parser will complain and just ignore the second rule). The single rule `CC0[16]{0} C0 CC1` won't work either since it only passes the low nibble to the `C0` message. However, we can massage those rules a bit to obtain the following:
But we can't write them this way, because we're not allowed to bind `CC0` in two different translations at once (if you try this, the parser will complain and just ignore the second rule). The single rule `CC0[16]{0} C0 CC1` won't work either since it only passes the low nibble to the `C0` message. However, we can massage those rules a bit to obtain the following:
~~~
CC0= C5 $CC1
@ -619,7 +619,7 @@ CC1[128] $CC0 # don't do this!
midizap *will* catch infinite recursion after a few iterations, so for educational purposes you can (and should) try the example above and see what happens. As you'll notice, the program prints an error message indicating the translation and message which caused the problem, so that you can correct your mistake.
So macro translations are too limited to make for a Turing-complete programming language, but there's still a lot that can be done with them. Here's another instructive example which spits out the individual bits of a controller value, using the approach that we discussed earlier in the context of nibble extraction. Input comes from `CC7` in the example, and bit #*i* of the controller value becomes `CC`*i* in the output, where *i* runs from 0 to 6. Note that each of these rules uses successively smaller powers of 2 as its modulus and passes on the remainder to the next rule, while the topmost bit is extracted with transposition.
So macro translations are too limited to make for a Turing-complete programming language, but there's still a lot that can be done with them. Here's another instructive example which spits out the individual bits of a controller value, using the approach that we discussed earlier in the context of nibble extraction. Input comes from `CC7` in the example, and bit #*i* of the controller value becomes `CC`*i* in the output, where *i* runs from 0 to 6. Note that each of these rules uses a successively smaller power of 2 as modulus and passes on the remainder to the next rule, while transposition is used to extract the topmost bit in the quotient.
~~~
CC7[64]{0} $CC6 CC6'
@ -638,9 +638,9 @@ There probably are some. Please submit bug reports and pull requests at the midi
The names of some of the debugging options are rather idiosyncratic. midizap inherited them from Eric Messick's ShuttlePRO program, and we decided to keep them for compatibility reasons.
midizap has only been tested on Linux so far, and its keyboard and mouse support is tailored to X11, i.e., it's pretty much tied to Unix/X11 systems right now. So there's no native Mac or Windows support, and there won't be until someone in the know about Mac and Windows equivalents for the X11 XTest extension comes along and ports midizap to these systems.
midizap has only been tested on Linux so far, and its keyboard and mouse support is tailored to X11, i.e., it's pretty much tied to Unix/X11 systems right now. So there's no native Mac or Windows support, and there won't be until someone in the know about Mac and Windows equivalents for the X11 XTest extension comes along and ports it over.
midizap tries to keep things simple, which implies that it has its limitations. In particular, midizap lacks support for system messages and some more interesting ways of mapping, filtering and recombining MIDI data right now. There are other, more powerful utilities which do these things, but they are also more complicated and usually require at least some programming skills. Fortunately, midizap usually does the job reasonably well for simple mapping tasks (and even some rather complicated ones, such as the APCmini MCU emulation included in the distribution). But if things start getting fiddly then you should consider using a more comprehensive tool like [Pd][] instead.
midizap tries to keep things simple, which implies that it has its limitations. In particular, midizap lacks support for system messages and some more interesting ways of mapping, filtering and recombining MIDI data right now. There are other, more powerful utilities which do these things, but they are also more complicated and usually require at least some programming skills. Fortunately, midizap usually does the job reasonably well for simple mapping tasks (and even some rather complicated ones, such as the APCmini Mackie emulation included in the distribution). But if things start getting fiddly then you should consider using a more comprehensive tool like [Pd][] instead.
# See Also

View File

@ -12,27 +12,28 @@ JACK_NAME "midizap-APCmini"
JACK_PORTS 2
# This turns the APCmini into a Mackie-compatible controller, so that it can
# be used with Linux DAW programs like Ardour. The emulation is complicated by
# the APCmini having no encoders, no motorized faders, and not nearly as many
# dedicated buttons as a Mackie device. But it offers enough controls to be
# usable as a basic DAW controller. I tested it with Ardour.
# be used with Linux DAW programs like Ardour. The emulation is complicated
# by the APCmini having no encoders, no motorized faders, and not nearly as
# many dedicated buttons as a Mackie device. But it offers enough controls to
# be usable as a basic DAW controller. I tested it with Ardour.
# SETUP: In Ardour, enable the Mackie control surface, then connect Ardour's
# "mackie control" ports to midizap's midi_out and midi_in2 ports, and the
# Mackie control ports to midizap's midi_out and midi_in2 ports, and the
# APCmini to midizap's midi_in and midi_out2 ports.
# PROTOCOL DOCUMENTATION: The Mackie protocol is fairly ubiquitous, but since
# the manufacturers can't be bothered to properly *document* their stuff these
# manufacturers can't be bothered to properly *document* their stuff these
# days, we have to rely on volunteers who do their work using some reverse
# engineering. Here are the links that I found most useful:
# engineering. Here are the links that I found most useful:
# http://www.budgetfeatures.com/XctlDOC/Xctl Protocol for X-Touch V1.0.pdf
# (This chart really is a piece of art. It's actually about the Behringer
# X-Touch, but since that device is MCU-compatible, there's a wealth of useful
# information in there; just ignore the bits which pertain to Xctl.)
# (This chart really is a piece of art. It's actually about the Behringer
# X-Touch, but since that device is Mackie-compatible, there's a wealth of
# useful information in there; just ignore the bits which pertain to Xctl.)
# There's another fairly comprehensive one here:
# There's another fairly comprehensive one here (lacks the feedback messages,
# though):
# http://www.jjlee.com/qlab/Mackie Control MIDI Map.pdf
@ -53,8 +54,8 @@ C7 B7 # Rec
C#7 G7 # Rew
D7 G#7 # FFwd
# the next three buttons below are used for the MCU shift keys
# NOTE: The MCU actually has four shift keys, so one has to go. You may want
# the next three buttons below are used for the MC shift keys
# NOTE: The MC actually has four shift keys, so one has to go. You may want
# to rearrange these as needed.
D#7 A#5 # Shift
E7 B5 # Control
@ -74,7 +75,7 @@ F7 C6 # Option
^F7 C#5
# bottom 3x8 grid: mute/solo/rec
# NOTE: Incidentally, these happen to be identical to corresponding MCU input.
# NOTE: Incidentally, these happen to be identical to corresponding MC input.
# rec (bottom row of the grid)
C0 C0
@ -129,7 +130,7 @@ B5 G2
^A#5 F3 # Send
^B5 A3 # Instr (Device)
# faders (MCU uses pitch bends here, use 129 as step size to get full range)
# faders (MC uses pitch bends here, use 129 as step size to get full range)
CC48= PB[129]-1
CC49= PB[129]-2
CC50= PB[129]-3
@ -156,46 +157,51 @@ CC55= PB[129]-8
[MIDI2]
# transport (will light up in green)
A7 A#6
A7 A#6 $CC0 # reset all meters, see "meter feedback" below
A#7 B6
B7 C7[2] # Rec, blinks when engaged
#C#7 C7 # Cycle
G7 C#7
G#7 D7
# Feedback for the MCU shift keys. We've disabled this by default since
# it doesn't add much value and clobbers the time display below.
# Feedback for the MC shift keys. We've disabled this by default since it
# doesn't add much value and clobbers some of the blinkenlights below.
#A#5 D#7
#B5 E7
#C6 F7
# Blinkenlights galore! The Mackie protocol provides us with both time
# and meter data for which we provide some translations here which make
# various LEDs light up in different colors.
# Blinkenlights galore! The Mackie protocol provides us with both time and
# meter data for which we provide some translations here which make various
# LEDs light up in different colors.
# 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.
# Meter feedback: Each meter value is sent as a channel pressure message (on
# the first MIDI channel) with the mixer channel index 0..7 in the hi- and the
# meter value in the lo-nibble of the velocity value. On a real MCP device
# like the X-Touch, the meters have 7 segments (4 green, 3 orange, and 1 red),
# but the actual range of the meter values seems to be more like 0..13 (at
# least that's what Ardour outputs, YMMV). Note that we only use the upper
# 5x8 part of the grid here, in order not to clobber the three rows at the
# bottom with the rec/solo/mute buttons, so we have to squash the meter values
# into those 5 LEDs per strip in some way. The following setup with 3 green,
# 1 yellow and 1 red LED seems to work reasonably well, but you might want to
# adjust the colors and the mapping of the meter values to your liking.
?CP[16] C2{0,1} G#2{0:3,1} E3{0:5,5} C4{0:7,5} G#4{0:8,3}
?CP[16] C2{0,1} G#2{0:3,1} E3{0:6,1} C4{0:9,5} G#4{0:12,3}
# Here's an alternative decoding, which creates horizontal meters
# covering the full range, with the first channel on top. This is just
# for illustration purposes -- as it ranges across the entire 8x8 grid,
# it will clobber the rec/solo/mute controls.
# NOTE: We only report the values as we receive them here, there's no
# automatic decay of the meters like with real Mackie hardware. Thus we
# explicitly reset all meters when transport stops below. (Ardour at least
# does *not* do that automatically.) NB: We use CC0 as a macro here,
# hopefully this won't occur as real feedback?
#?CP[16][-8] G#4{0,1} A4{0,0,1} A#4{0:3,1} B4{0:4,1} C5{0:5,5} C#5{0:6,5} D5{0:7,5} D#5{0:8,3}
?CC0[1] $CP{0} $CP{16} $CP{32} $CP{48} $CP{64} $CP{80} $CP{96} $CP{112}
# This decodes the least significant digit in the time display (CC69) to
# count off time on the 4 bottommost scene launch buttons. Note that
# the digits are encoded in ASCII here, therefore the copious amount of
# zeros in the value lists below to skip over all the non-digit
# characters at the beginning of the ASCII table.
# This decodes the least significant digit in the time display (CC69) to count
# off time on the 4 bottommost scene launch buttons. Note that the digits are
# encoded in ASCII here, therefore the copious amount of zeros in the value
# lists below to 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}

209
midizap.1
View File

@ -1149,52 +1149,27 @@ However, there are some encodings of feedback messages 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.
One important application, which we'll use as a running example below,
is the decoding of meter (RMS level) data in the Mackie protocol.
There, 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).
.PP
midizap offers a special variation of data mode to help decoding such
messages.
This involves calculations with integer moduli which enable you to
modify output messages in certain ways, so for want of a better name we
also call these bindings \f[I]mod translations\f[].
The extended MIDI syntax being used here is described by the following
grammar rules (please also refer to the beginning of Section
\f[I]Translation Syntax\f[] for the rest of the MIDI syntax):
.IP
.nf
\f[C]
token\ ::=\ msg\ [\ steps\ ]\ [\ "\-"\ number]\ [\ flag\ ]
steps\ ::=\ "{"\ list\ "}"\ |\ "["\ number\ "]"\ "{"\ list\ "}"
list\ \ ::=\ number\ {\ ","\ number\ |\ ":"\ number\ |\ "\-"\ number\ }
flag\ \ ::=\ "\[aq]"
\f[]
.fi
We call them \f[I]mod translations\f[], because they involve
calculations with integer moduli which enable you to not only calculate
output from input values, but also modify the output messages themselves
at the same time.
.PP
There are two new elements in the syntax, the \[lq]transposition\[rq]
flag \f[C]\[aq]\f[], and lists of numbers enclosed in curly braces.
For convenience, the latter also support the following abbreviations:
repetition \f[I]a\f[]\f[C]:\f[]\f[I]b\f[] (denoting \f[I]b\f[]
consecutive \f[I]a\f[]'s) and enumeration
\f[I]a\f[]\f[C]\-\f[]\f[I]b\f[] (denoting
\f[I]a\f[]\f[C],\f[]\f[I]a\f[]±1\f[C],\f[]\&...\f[C],\f[]\f[I]b\f[],
which ramps either up or down depending on whether \f[I]a\f[]<\f[I]b\f[]
or \f[I]a\f[]>\f[I]b\f[], respectively).
The meaning of these constructs will be discussed below.
One important task, which we'll use as a running example below, is the
decoding of meter (RMS level) data in the Mackie protocol.
There, each meter value is represented as 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).
Specifically, we will show how to map these values to notes indicating
buttons on the AKAI APCmini; please check examples/APCmini.midizaprc in
the sources for details about this device.
However, mod translations aren't limited to this specific example;
similar rules will apply to many other kinds of \[lq]scrambled\[rq] MIDI
data.
.PP
For the sake of a concrete example, in the following we consider the
mapping of channel pressure messages encoding MCU meter values to notes
indicating buttons on the AKAI APCmini; please check
examples/APCmini.midizaprc in the sources for details.
But these translations work the same with any kind of message having a
parameter value (i.e., anything but \f[C]PC\f[]) and any kind of MIDI
output, so similar rules will help with other kinds of
\[lq]scrambled\[rq] MIDI data.
.PP
In its most basic form, the translation looks as follows:
In its simplest form, the translation looks as follows:
.IP
.nf
\f[C]
@ -1213,7 +1188,7 @@ former is used as an \f[I]offset\f[] to modify the output message.
(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.
The \f[C]PC\f[] message, in contrast, lacks the parameter value, so in
\f[C]PC\f[] messages, on the other hand, lack the parameter value, so in
this case the remainder value will be disregarded instead.)
.PP
In order to describe more precisely how this works, let's assume an
@ -1225,25 +1200,52 @@ E.g., with \f[I]k\f[] = 16 and \f[I]v\f[] = 21, you'll get \f[I]q\f[] =
1 and \f[I]r\f[] = 5 (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.
So in the example above 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 will light up the second button in the
bottom row of the 8x8 grid in yellow.
.PP
This simple kind of transformation is surprisingly versatile, and there
are some variations of the syntax which make it even more flexible.
One such variation is \f[I]transposition\f[], denoted with the
\f[C]\[aq]\f[] (apostrophe) flag at the end of the output message.
It reverses the roles of \f[I]q\f[] and \f[I]r\f[], so that the
remainder becomes the offset and the quotient the value of the output
message.
For instance, with \f[C]CP[16]\ C0\[aq]\f[] and the same input value of
21, you'd get the note \f[C]F0\f[] (\f[C]C0\f[] offset by 5) with a
velocity of 1 instead.
We won't utilize this in the present example, but it's very convenient
in some situations, and we'll see some more examples of its use in the
following section.
The extended MIDI syntax being used here is described by the following
grammar rules (please also refer to the beginning of Section
\f[I]Translation Syntax\f[] for the rest of the MIDI syntax):
.IP
.nf
\f[C]
token\ ::=\ msg\ [\ steps\ ]\ [\ "\-"\ number]\ [\ flag\ ]
steps\ ::=\ "{"\ list\ "}"\ |\ "["\ number\ "]"\ "{"\ list\ "}"
list\ \ ::=\ number\ {\ ","\ number\ |\ ":"\ number\ |\ "\-"\ number\ }
flag\ \ ::=\ "\[aq]"
\f[]
.fi
.PP
There are two new elements in the syntax, the \[lq]transposition\[rq]
flag \f[C]\[aq]\f[], and lists of numbers enclosed in curly braces.
.IP \[bu] 2
\f[I]Transposition\f[], denoted with the \f[C]\[aq]\f[] (apostrophe)
suffix on an output message, reverses the roles of \f[I]q\f[] and
\f[I]r\f[], so that the remainder becomes the offset and the quotient
the value of the output message.
We won't actually use this in the present example, but it's very
convenient in some situations, and we'll get back to it in the following
sections.
.IP \[bu] 2
\f[I]Value lists\f[], denoted as lists of numbers separated by commas
and enclosed in curly braces, provide a way to describe \f[I]discrete
mappings\f[] of input to output values.
To these ends, the remainder value \f[I]r\f[] is used as an index into
the list to give the corresponding output value, and the last value in
the list will be used for any index which runs past the end of the list.
There are also some convenient shortcuts which let you construct these
lists more easily: repetition \f[I]a\f[]\f[C]:\f[]\f[I]b\f[] (denoting
\f[I]b\f[] consecutive \f[I]a\f[]'s) and enumeration
\f[I]a\f[]\f[C]\-\f[]\f[I]b\f[] (denoting
\f[I]a\f[]\f[C],\f[]\f[I]a\f[]±1\f[C],\f[]\&...\f[C],\f[]\f[I]b\f[],
which ramps either up or down depending on whether
\f[I]a\f[]<=\f[I]b\f[] or \f[I]a\f[]>\f[I]b\f[], respectively).
.PP
Let's return to our example.
As usual in data translations, you can also specify a step size to
upscale the output value \f[I]r\f[]:
.IP
@ -1254,59 +1256,54 @@ CP[16]\ C0[2]
.fi
.PP
But in many cases the required transformations on \f[I]r\f[] will be
more complicated.
To accommodate these, it is also possible to specify a list of discrete
values in curly braces instead.
more complicated, so we specify them as value lists 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:
can map the meter values to different colors, e.g., as follows:
.IP
.nf
\f[C]
CP[16]\ C0{0,1,1,1,1,5,5,5,3}
CP[16]\ C0{0,1,1,1,1,1,1,1,1,5,5,5,3}
\f[]
.fi
.PP
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), 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.
E.g., if you receive a meter value of 10, which isn't in the list, the
output will still be 3, since it's the last value in the list.
.PP
There are a lot of repeated values in this example, which makes the
notation a bit untidy and error\-prone.
As a remedy, it's possible to abbreviate repeated values as
\f[I]value\f[]\f[C]:\f[]\f[I]count\f[], which also helps readability.
The following denotes exactly the same list as above:
Using the shorthand for repetitions, this can be written more succinctly
(which also helps readability):
.IP
.nf
\f[C]
CP[16]\ C0{0,1:4,5:3,3}
CP[16]\ C0{0,1:8,5:3,3}
\f[]
.fi
.PP
Furthermore, you can also scale the \f[I]offset\f[] value, by adding a
second step size to the left\-hand side:
Thus 0 will be mapped to 0 (off), 1..8 to 1 (green), 9..11 to 5
(yellow), and 12 or more to 3 (red).
.PP
The quotient here is the mixer channel index in the high\-nibble of the
\f[C]CP\f[] message, which will be used as an offset for the \f[C]C0\f[]
note on the right, so the above rule shows the meters as a single row of
colored buttons at the bottom of the 8x8 grid on the APCmini (first
value on \f[C]C0\f[], second value on \f[C]C#0\f[], etc.).
To get a different layout, you can also scale the \f[I]offset\f[] value,
by adding a second step size to the left\-hand side:
.IP
.nf
\f[C]
CP[16][8]\ C0{0,1:4,5:3,3}
CP[16][8]\ C0{0,1:8,5:3,3}
\f[]
.fi
.PP
With this rule, the buttons for each mixer channel are now spread out
across different \f[I]rows\f[] rather than columns.
With this rule, the buttons are now in the first \f[I]column\f[] of the
grid (first value on \f[C]C0\f[], second value on \f[C]G#0\f[], etc.).
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:
output pattern that you want.
E.g., the following rule places every other meter value in the second
row:
.IP
.nf
\f[C]
CP[16]{1,8,17,24}\ C0{0,1:4,5:3,3}
CP[16]{0,9,2,11,4,13,6,15}\ C0{0,1:8,5:3,3}
\f[]
.fi
.PP
@ -1317,7 +1314,7 @@ For instance:
.IP
.nf
\f[C]
CP[16]\ C0{0,1}\ G#0{0:5,5}\ E1{0:8,3}
CP[16]\ C0{0,1}\ G#0{0:8,5}\ E1{0:11,3}
\f[]
.fi
.PP
@ -1325,7 +1322,7 @@ 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.
(The APCmini.midizaprc example uses a similar, albeit more elaborate
translation to handle MCU meter data.)
translation to handle meter data.)
.PP
Another example from the Mackie protocol is time feedback.
The following rule (also from the APCmini.midizaprc example) decodes the
@ -1376,12 +1373,11 @@ value becomes the velocity of the note.
Note that this is different from both the key translation
\f[C]CC1\ C5\f[] (which only preserves the \[lq]on\[rq]/\[lq]off\[rq]
status but looses the actual parameter value) and the incremental data
translation \f[C]CC1=\ C5\f[] (which involves some internal state for
keeping track of current values, and executes the translation in a
translation \f[C]CC1=\ C5\f[] (which executes the translation in a
step\-wise fashion for each unit change).
In contrast, a mod translation maps messages one\-to\-one and does not
involve any ramping to arrive at the new value.
Therefore, while key and incremental data translations are often used in
In contrast, a mod translation always maps messages one\-to\-one in a
single step.
While key and incremental data translations are typically used in
conjunction with key and mouse output, for pure MIDI bindings like the
one above a mod translation is often preferable.
.PP
@ -1417,8 +1413,7 @@ The values in a list may be in any order, and you can throw in any
combination of singleton values, enumerations and repetitions.
E.g., the list \f[C]{0:2\-5,7:5\-0}\f[] starts with two zeros, then
ramps up to 5 followed by five 7s, before finally fading back to 0.
For us mortals, that's much easier to read and also much less
error\-prone to write than
That's much easier to read and also much less error\-prone to write than
\f[C]{0,0,1,2,3,4,5,7,7,7,7,7,6,5,4,3,2,1,0}\f[].
.PP
The mod translations above are all trivial translations with a zero
@ -1438,11 +1433,7 @@ This outputs the note with the same number as the controller value,
\f[C]C0\f[] for value 0, \f[C]C#0\f[] for value 1, etc.
Note that the remainder, 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, as
shown below.
Note that a simple scale factor won't do us much good here since the
value to be scaled is always zero.
A value list is the only way to turn that zero into a nonzero value.
To get a nonzero velocity, you specify it in a value list:
.IP
.nf
\f[C]
@ -1450,12 +1441,10 @@ CC2[1]\ C0{127}
\f[]
.fi
.PP
With both of these rules at hand, we can now turn notes on with
\f[C]CC2\f[] and turn them off again with \f[C]CC1\f[].
.PP
We also mention in passing here that a modulus 1 translation is actually
the \[lq]dual\[rq] of a trivial mod translation, employing the
\f[C]\[aq]\f[] flag on the output message.
\f[C]\[aq]\f[] flag on the output message to transpose quotient and
remainder.
E.g., these two translations work exactly the same:
.IP
.nf
@ -1511,7 +1500,7 @@ Also, there's no direct way to combine different kinds of translations
of the same input, or to consolidate the values of multiple input
messages into a single output message.
(The macro facility in the following section lets you address the
former, but not the latter problem.)
former, but doesn't help with the latter problem.)
.SS Macro Translations
.PP
There are some situations in which it is hard to construct a translation
@ -1592,8 +1581,8 @@ CC0[16]{0}\ CC1
.fi
.PP
But we can't write them this way, because we're not allowed to bind
\f[C]CC0\f[] in two different translations at once (the parser will
complain and just ignore the second rule).
\f[C]CC0\f[] in two different translations at once (if you try this, the
parser will complain and just ignore the second rule).
The single rule \f[C]CC0[16]{0}\ C0\ CC1\f[] won't work either since it
only passes the low nibble to the \f[C]C0\f[] message.
However, we can massage those rules a bit to obtain the following:
@ -1644,9 +1633,9 @@ the context of nibble extraction.
Input comes from \f[C]CC7\f[] in the example, and bit #\f[I]i\f[] of the
controller value becomes \f[C]CC\f[]\f[I]i\f[] in the output, where
\f[I]i\f[] runs from 0 to 6.
Note that each of these rules uses successively smaller powers of 2 as
its modulus and passes on the remainder to the next rule, while the
topmost bit is extracted with transposition.
Note that each of these rules uses a successively smaller power of 2 as
modulus and passes on the remainder to the next rule, while
transposition is used to extract the topmost bit in the quotient.
.IP
.nf
\f[C]
@ -1684,7 +1673,7 @@ support is tailored to X11, i.e., it's pretty much tied to Unix/X11
systems right now.
So there's no native Mac or Windows support, and there won't be until
someone in the know about Mac and Windows equivalents for the X11 XTest
extension comes along and ports midizap to these systems.
extension comes along and ports it over.
.PP
midizap tries to keep things simple, which implies that it has its
limitations.
@ -1696,7 +1685,7 @@ are also more complicated and usually require at least some programming
skills.
Fortunately, midizap usually does the job reasonably well for simple
mapping tasks (and even some rather complicated ones, such as the
APCmini MCU emulation included in the distribution).
APCmini Mackie emulation included in the distribution).
But if things start getting fiddly then you should consider using a more
comprehensive tool like Pd (http://puredata.info/) instead.
.SH See Also