From 1698aded9a5be8e60ce4dd7d1bbdce85d2a5b3bb Mon Sep 17 00:00:00 2001 From: Albert Graef Date: Sat, 15 Sep 2018 23:53:25 +0200 Subject: [PATCH] Update the documentation. --- README.md | 83 ++++++++++++++++++---- midizap.1 | 200 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 227 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index de076ee..7f5dfed 100644 --- a/README.md +++ b/README.md @@ -539,18 +539,20 @@ This transformation is surprisingly versatile, and there are some extensions of token ::= msg [ steps ] [ "-" number] [ flag ] steps ::= [ "[" [ number ] "]" ] [ "{" list "}" ] list ::= number { "," number | ":" number | "-" number } -flag ::= "'" +flag ::= "'" | "?" | "'?" | "?'" ~~~ -There are three new elements in the syntax, an empty modulus bracket `[]`, the "transposition" flag `'`, and lists of numbers enclosed in curly braces. They have the following meaning: +There are a couple of new elements in the syntax: an empty modulus bracket `[]`, the transposition flag `'`, the change flag `?`, and lists of numbers enclosed in curly braces. They have the following meaning: - The *empty modulus* bracket, denoted `[]` on the left-hand side of a mod translation, indicates a default modulus large enough (16384 for `PB`, 128 for other messages) so that the offset *q* always becomes zero and the translation passes on the entire input value as is. -- *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. +- The *transposition* flag, 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. + +- The *change* flag, denoted with the `?` suffix on an output message, only outputs the message if its value (quotient or remainder) has changed. (Note that transposition and change flag can also be combined in any order.) - *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. The input value 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). -These are often used in concert. We will introduce value lists in a moment, and cover the use of default modulus and transposition in subsequent subsections. +These are often used in concert. We will introduce value lists in a moment, and cover the other options in due course. **NOTE:** In the context of mod translations, pitch bend values are interpreted as *unsigned* quantities in the range 0..16383 (with 8192 denoting the center value), which corresponds to the way they are actually encoded in MIDI. This makes the modular arithmetic work consistently across all types of MIDI messages, and also facilitates conversions between the different types of absolute parameter values. Normally you shouldn't have to worry about this, but the change in representation needs to be taken into account when transforming pitch bend values with value lists. @@ -686,7 +688,31 @@ Note that this works because the output mapping `{0}` (which forces the offset t CC1[16]{0} CC1 CC2' ~~~ -Using similar rules, you can extract almost any part of an input value, down to every single bit if needed (see the end of the next section for an example). +Using similar rules, you can extract almost any part of an input value, down to every single bit if needed (see the *Macro Translations* section below for another example). + +## Detecting Changes + +Let's have another look at the high-nibble extraction rule from above: + +~~~ +CC1[16]{0} CC2' +~~~ + +Note that if the input value changes gradually then many output values will be identical. E.g., if the input values are 0, 10, 19, 32, 64 then the first four high nibbles are all zero, so the output will be 0, 0, 0, 0, 1, with the zero value repeated four times. If this is not desired, you can add the `?` flag to indicate that the message should be output only if the value has changed: + +~~~ +CC1[16]{0} CC2'? +~~~ + +Now, repeated values are suppressed, so with the same inputs the output will be just 0, 1. Note that we used the `?` flag in combination with transposition and the offset forced to zero here, but of course it will work for any kind of change (offset or value, transposed or not). Also note that change detection always considers the "post-transform" values as they would be output, i.e., changes are detected *after* transposition and all mappings of input and output values have been performed. + +Change detection is often useful when input values are projected, as in the above example, but also in many other situations in which you simply want to prevent repeated values. For instance, suppose that we'd like to turn the modulation wheel (`CC1`) into a kind of on/off switch. Using a basic mod translation with a value list and change detection, this can be done quite easily: + +~~~ +CC1[] CC1{0,127}? +~~~ + +This emits a single 127 value whenever the input value becomes nonzero, and a single 0 value when it drops to zero again. Note that without the `?` flag, the 127 value might be repeated any number of times, as long as you keep turning the modulation wheel, which probably wouldn't be wanted here. ## Macro Translations @@ -704,8 +730,6 @@ CC0= $CC1 Before we proceed, let's introduce a few terms which will make it easier to talk about these things. We refer to a mod translation being called in this manner as a *macro translation*, and we also call the left-hand side of the translation a *macro*, and the invocation of a macro using the dollar symbol a *macro call*. -In principle, any message which can occur on the left-hand side of a data translation (i.e., everything but `PC`) can also be defined as a macro. However, you want to make sure that the message isn't already in use as a "real" input, so that you are free to define it as needed. Fortunately, MIDI has plenty of messages on offer (there are 2048 distinct `CC` messages alone, 128 on each of the 16 MIDI channels), so it shouldn't be too hard to find one that can be used for internal purposes. - To continue our example, let's define the `CC1` macro so that it outputs just a single note message: ~~~ @@ -730,7 +754,7 @@ CC0[] C5 CC0[16]{0} CC1 ~~~ -But we can't just put these two rules into a configuration file, 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). And the single rule `CC0[16]{0} C5 CC1` won't work either, because it only passes the low nibble to the `C5` message. However, using a macro call we can massage those rules into an eligible form: +But we can't just put these two rules into the same section, 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). And the single rule `CC0[16]{0} C5 CC1` won't work either, because it only passes the low nibble to the `C5` message. However, using a macro call we can massage those rules into an eligible form: ~~~ CC0[] C5 $CC1 @@ -739,16 +763,16 @@ CC1[16]{0} CC1 This does exactly what we set out to do, and the transformation of the original rules we applied here is actually quite straightforward. In the same vein, we can combine as many different mod translations as we like, even if they involve different moduli and offset transformations. -If you know C, you will have realized by now that macro translations work pretty much like parameter-less macros in the C programming language. The same caveats apply here, too. Specifically, the configuration language provides no way to break out of a recursive macro, so you do *not* want to have a macro invoke itself (either directly or indirectly), because that will always lead to an infinite recursion. E.g.: +If you know C, you will have realized by now that macro translations work pretty much like parameter-less macros in the C programming language. The same caveats apply here, too. Specifically, you usually do *not* want to have a macro invoke itself (either directly or indirectly), because that will almost certainly lead to infinite recursion. E.g.: ~~~ CC0[128] $CC1 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. +midizap *will* catch such mishaps after a few iterations, but it's better to avoid them in the first place. -So macro translations are too limited to make for a Turing-complete programming language, but there's still a lot of fun to be had 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 and output the topmost bit in the quotient. (You may want to run this example with debugging enabled to see what exactly is going on there.) +We mention in passing that while recursive macro calls in conjunction with value lists and change detection quite likely make the configuration language Turing-complete, these facilities are just way too limited to make for a practical programming language. But there's still a lot of fun to be had with macros despite their limitations. 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 and output the topmost bit in the quotient. ~~~ CC7[64]{0} $CC6 CC6' @@ -759,6 +783,41 @@ CC3[4]{0} $CC2 CC2' CC2[2]{0} CC0 CC1' ~~~ +You may want to run this example with debugging enabled to see what exactly is going on there. + +Another potential gotcha is the "naming" of macros. In principle, any message which can occur on the left-hand side of a mod translation (i.e., everything but `PC`) can also be used as a macro. However, if the message is to be used *only* as a macro, you better make sure that it doesn't also occur as a "real" input, so that you are free to define it as needed. While MIDI has plenty of messages on offer, you can never be sure which ones might show up in MIDI input. For instance, in the example above the macro translations for `CC2` to `CC6` might also be triggered by real MIDI input instead of macro calls. While this may be useful at times, e.g., for testing purposes, it is most likely going to confuse unsuspecting end users. + +As a remedy, midizap also provides a special kind of *macro events*, denoted `M0` to `M127`. These "synthetic" messages work exactly like `CC` messages, but they are guaranteed to never occur as real inputs, and they can *only* be used in macro calls and on the left-hand side of mod translations. We can rewrite the previous example using macro events as follows: + +~~~ +CC7[64]{0} $M6 CC6' +M6[32]{0} $M5 CC5' +M5[16]{0} $M4 CC4' +M4[8]{0} $M3 CC3' +M3[4]{0} $M2 CC2' +M2[2]{0} CC0 CC1' +~~~ + +Let's conclude with another, slightly more practical example for the use of macros, which turns the pitch wheel of a MIDI keyboard into a simple kind of "shuttle control". To illustrate how this works, let's emit an `XK_Left` key event when the pitch wheel is pushed left, and an `XK_Right` event when pushed right. This can be done as follows: + +~~~ +PB[] $M0{0:8192,1,2}? +M0[] $M1{1,-1} $M2{-1:2,1,-1} +M1[] XK_Left +M2[] XK_Right +~~~ + +Note that the `M0` macro will be invoked with a value of 0, 1 and 2 if the pitch wheel is down, centered, and up, respectively. The value lists in the definition of `M0` are then used to filter these values and call the appropriate macro (`M1` for value 0, `M2` for value 2) which handles the key output. + +It's easy to adjust the `M1` and `M2` macros for other purposes. E.g., we might output the "Rewind" and "Fast Forward" functions of a Mackie controller: + +~~~ +PB[] $M0{0:8192,1,2}? +M0[] $M1{1,-1} $M2{-1:2,1,-1} +M1[] G7[127] # Rew +M2[] G#7[127] # FFwd +~~~ + # Bugs There probably are some. Please submit bug reports and pull requests at the midizap [git repository][agraef/midizap]. Contributions are also welcome. In particular, we're looking for interesting configurations to be included in the distribution. @@ -767,8 +826,6 @@ The names of some of the debugging options are rather idiosyncratic. midizap inh midizap tries to keep things simple, which implies that it has its limitations. In particular, midizap lacks support for translating 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 programming skills. Fortunately, midizap often 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. -In trying to keep with the simplicity of Eric Messick's original as much as possible, most of the configuration file language is fairly straightforward. But there are some bolted on parts of the syntax (in particular, mod translations and macros) which look rather cryptic. Only use them if you must. :) - 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. Native Mac or Windows support certainly seems possible, but it's not going to happen until someone who's in the know about suitable Mac and Windows replacements for the X11 XTest extension, comes along and ports it over. # See Also diff --git a/midizap.1 b/midizap.1 index a90bc2b..b8287df 100644 --- a/midizap.1 +++ b/midizap.1 @@ -1482,13 +1482,13 @@ for the MIDI syntax): token\ ::=\ msg\ [\ steps\ ]\ [\ "\-"\ number]\ [\ flag\ ] steps\ ::=\ [\ "["\ [\ number\ ]\ "]"\ ]\ [\ "{"\ list\ "}"\ ] list\ \ ::=\ number\ {\ ","\ number\ |\ ":"\ number\ |\ "\-"\ number\ } -flag\ \ ::=\ "\[aq]" +flag\ \ ::=\ "\[aq]"\ |\ "?"\ |\ "\[aq]?"\ |\ "?\[aq]" \f[] .fi .PP -There are three new elements in the syntax, an empty modulus bracket -\f[C][]\f[], the \[lq]transposition\[rq] flag \f[C]\[aq]\f[], and lists -of numbers enclosed in curly braces. +There are a couple of new elements in the syntax: an empty modulus +bracket \f[C][]\f[], the transposition flag \f[C]\[aq]\f[], the change +flag \f[C]?\f[], and lists of numbers enclosed in curly braces. They have the following meaning: .IP \[bu] 2 The \f[I]empty modulus\f[] bracket, denoted \f[C][]\f[] on the @@ -1497,10 +1497,16 @@ enough (16384 for \f[C]PB\f[], 128 for other messages) so that the offset \f[I]q\f[] always becomes zero and the translation passes on the entire input value as is. .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. +The \f[I]transposition\f[] flag, 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. +.IP \[bu] 2 +The \f[I]change\f[] flag, denoted with the \f[C]?\f[] suffix on an +output message, only outputs the message if its value (quotient or +remainder) has changed. +(Note that transposition and change flag can also be combined in any +order.) .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 @@ -1517,8 +1523,8 @@ 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 These are often used in concert. -We will introduce value lists in a moment, and cover the use of default -modulus and transposition in subsequent subsections. +We will introduce value lists in a moment, and cover the other options +in due course. .PP \f[B]NOTE:\f[] In the context of mod translations, pitch bend values are interpreted as \f[I]unsigned\f[] quantities in the range 0..16383 (with @@ -1821,8 +1827,61 @@ CC1[16]{0}\ CC1\ CC2\[aq] .fi .PP Using similar rules, you can extract almost any part of an input value, -down to every single bit if needed (see the end of the next section for -an example). +down to every single bit if needed (see the \f[I]Macro Translations\f[] +section below for another example). +.SS Detecting Changes +.PP +Let's have another look at the high\-nibble extraction rule from above: +.IP +.nf +\f[C] +CC1[16]{0}\ CC2\[aq] +\f[] +.fi +.PP +Note that if the input value changes gradually then many output values +will be identical. +E.g., if the input values are 0, 10, 19, 32, 64 then the first four high +nibbles are all zero, so the output will be 0, 0, 0, 0, 1, with the zero +value repeated four times. +If this is not desired, you can add the \f[C]?\f[] flag to indicate that +the message should be output only if the value has changed: +.IP +.nf +\f[C] +CC1[16]{0}\ CC2\[aq]? +\f[] +.fi +.PP +Now, repeated values are suppressed, so with the same inputs the output +will be just 0, 1. +Note that we used the \f[C]?\f[] flag in combination with transposition +and the offset forced to zero here, but of course it will work for any +kind of change (offset or value, transposed or not). +Also note that change detection always considers the +\[lq]post\-transform\[rq] values as they would be output, i.e., changes +are detected \f[I]after\f[] transposition and all mappings of input and +output values have been performed. +.PP +Change detection is often useful when input values are projected, as in +the above example, but also in many other situations in which you simply +want to prevent repeated values. +For instance, suppose that we'd like to turn the modulation wheel +(\f[C]CC1\f[]) into a kind of on/off switch. +Using a basic mod translation with a value list and change detection, +this can be done quite easily: +.IP +.nf +\f[C] +CC1[]\ CC1{0,127}? +\f[] +.fi +.PP +This emits a single 127 value whenever the input value becomes nonzero, +and a single 0 value when it drops to zero again. +Note that without the \f[C]?\f[] flag, the 127 value might be repeated +any number of times, as long as you keep turning the modulation wheel, +which probably wouldn't be wanted here. .SS Macro Translations .PP There are some situations in which it is hard or even impossible to @@ -1860,16 +1919,6 @@ We refer to a mod translation being called in this manner as a translation a \f[I]macro\f[], and the invocation of a macro using the dollar symbol a \f[I]macro call\f[]. .PP -In principle, any message which can occur on the left\-hand side of a -data translation (i.e., everything but \f[C]PC\f[]) can also be defined -as a macro. -However, you want to make sure that the message isn't already in use as -a \[lq]real\[rq] input, so that you are free to define it as needed. -Fortunately, MIDI has plenty of messages on offer (there are 2048 -distinct \f[C]CC\f[] messages alone, 128 on each of the 16 MIDI -channels), so it shouldn't be too hard to find one that can be used for -internal purposes. -.PP To continue our example, let's define the \f[C]CC1\f[] macro so that it outputs just a single note message: .IP @@ -1916,7 +1965,7 @@ CC0[16]{0}\ CC1 \f[] .fi .PP -But we can't just put these two rules into a configuration file, because +But we can't just put these two rules into the same section, because we're not allowed to bind \f[C]CC0\f[] in two different translations at once (if you try this, the parser will complain and just ignore the second rule). @@ -1942,10 +1991,9 @@ If you know C, you will have realized by now that macro translations work pretty much like parameter\-less macros in the C programming language. The same caveats apply here, too. -Specifically, the configuration language provides no way to break out of -a recursive macro, so you do \f[I]not\f[] want to have a macro invoke -itself (either directly or indirectly), because that will always lead to -an infinite recursion. +Specifically, you usually do \f[I]not\f[] want to have a macro invoke +itself (either directly or indirectly), because that will almost +certainly lead to infinite recursion. E.g.: .IP .nf @@ -1955,13 +2003,15 @@ CC1[128]\ $CC0\ #\ don\[aq]t\ do\ this! \f[] .fi .PP -midizap \f[I]will\f[] catch infinite recursion after a few iterations, -so for educational purposes you can (and should) try the example above -and see what happens. +midizap \f[I]will\f[] catch such mishaps after a few iterations, but +it's better to avoid them in the first place. .PP -So macro translations are too limited to make for a Turing\-complete -programming language, but there's still a lot of fun to be had with -them. +We mention in passing that while recursive macro calls in conjunction +with value lists and change detection quite likely make the +configuration language Turing\-complete, these facilities are just way +too limited to make for a practical programming language. +But there's still a lot of fun to be had with macros despite their +limitations. 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. @@ -1972,8 +2022,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 and output the topmost bit in the quotient. -(You may want to run this example with debugging enabled to see what -exactly is going on there.) .IP .nf \f[C] @@ -1985,6 +2033,80 @@ CC3[4]{0}\ \ $CC2\ CC2\[aq] CC2[2]{0}\ \ CC0\ \ CC1\[aq] \f[] .fi +.PP +You may want to run this example with debugging enabled to see what +exactly is going on there. +.PP +Another potential gotcha is the \[lq]naming\[rq] of macros. +In principle, any message which can occur on the left\-hand side of a +mod translation (i.e., everything but \f[C]PC\f[]) can also be used as a +macro. +However, if the message is to be used \f[I]only\f[] as a macro, you +better make sure that it doesn't also occur as a \[lq]real\[rq] input, +so that you are free to define it as needed. +While MIDI has plenty of messages on offer, you can never be sure which +ones might show up in MIDI input. +For instance, in the example above the macro translations for +\f[C]CC2\f[] to \f[C]CC6\f[] might also be triggered by real MIDI input +instead of macro calls. +While this may be useful at times, e.g., for testing purposes, it is +most likely going to confuse unsuspecting end users. +.PP +As a remedy, midizap also provides a special kind of \f[I]macro +events\f[], denoted \f[C]M0\f[] to \f[C]M127\f[]. +These \[lq]synthetic\[rq] messages work exactly like \f[C]CC\f[] +messages, but they are guaranteed to never occur as real inputs, and +they can \f[I]only\f[] be used in macro calls and on the left\-hand side +of mod translations. +We can rewrite the previous example using macro events as follows: +.IP +.nf +\f[C] +CC7[64]{0}\ $M6\ CC6\[aq] +M6[32]{0}\ \ $M5\ CC5\[aq] +M5[16]{0}\ \ $M4\ CC4\[aq] +M4[8]{0}\ \ \ $M3\ CC3\[aq] +M3[4]{0}\ \ \ $M2\ CC2\[aq] +M2[2]{0}\ \ \ CC0\ \ CC1\[aq] +\f[] +.fi +.PP +Let's conclude with another, slightly more practical example for the use +of macros, which turns the pitch wheel of a MIDI keyboard into a simple +kind of \[lq]shuttle control\[rq]. +To illustrate how this works, let's emit an \f[C]XK_Left\f[] key event +when the pitch wheel is pushed left, and an \f[C]XK_Right\f[] event when +pushed right. +This can be done as follows: +.IP +.nf +\f[C] +PB[]\ $M0{0:8192,1,2}? +M0[]\ $M1{1,\-1}\ $M2{\-1:2,1,\-1} +M1[]\ XK_Left +M2[]\ XK_Right +\f[] +.fi +.PP +Note that the \f[C]M0\f[] macro will be invoked with a value of 0, 1 and +2 if the pitch wheel is down, centered, and up, respectively. +The value lists in the definition of \f[C]M0\f[] are then used to filter +these values and call the appropriate macro (\f[C]M1\f[] for value 0, +\f[C]M2\f[] for value 2) which handles the key output. +.PP +It's easy to adjust the \f[C]M1\f[] and \f[C]M2\f[] macros for other +purposes. +E.g., we might output the \[lq]Rewind\[rq] and \[lq]Fast Forward\[rq] +functions of a Mackie controller: +.IP +.nf +\f[C] +PB[]\ $M0{0:8192,1,2}? +M0[]\ $M1{1,\-1}\ $M2{\-1:2,1,\-1} +M1[]\ G7[127]\ \ #\ Rew +M2[]\ G#7[127]\ #\ FFwd +\f[] +.fi .SH Bugs .PP There probably are some. @@ -2011,14 +2133,6 @@ 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. .PP -In trying to keep with the simplicity of Eric Messick's original as much -as possible, most of the configuration file language is fairly -straightforward. -But there are some bolted on parts of the syntax (in particular, mod -translations and macros) which look rather cryptic. -Only use them if you must. -:) -.PP 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.