# midimap.lv2 - config/map file # # Lines starting with a hash (#) are ignored, as are empty lines. # Note that: whitespace is significant (beware of trailing spaces). # # at first we need to set the file-format version, midimap v1 #### strategy ## ## Forward all events that don't match any filter-rule as-is. ## If not set, only events matching a rule will generate output forward-unmatched ## One to many. ## If set all rules that match an incoming MIDI event will be matched ## This can result in multiple messages being generated by a single event. ## ## If not set all rules will be matched in order given in the configuration ## file, processing stops after the first matching rule. #match-all #### Filter Rules ## ## Here comes the tricky part :) -- see below for aliases and simple examples ## ## The general form is ## ## | ## ### Operation Principle ## ## A MIDI message is a sequence of bytes. ## ## The expression is a byte pattern. If the pattern matches ## an incoming messages, that message is replaced with a given byte-sequence. ## e.g 250 | 251 ## ## This replaces a MIDI "start" command (1 byte: 250) with a "stop" command: 251 ### Filter description ## ## The MIDI filter consists of up to three whitespace separated fields ## which are matched against the bytes of a MIDI message. ## ## For note-events (3 bytes) the fields are ## Type|Channel Key Velocity ## ## For control-changes (CC) ## Type|Channel Control Value ## ## see also http://www.midi.org/techspecs/midimessages.php ## ## Each of the filter fields is a number pair separated by a slash: ## / ## The numbers can be given in decimal or hexadecimal. ## a prefix "0x" indicates the latter. ## ## An incoming byte is masked against the bit-mask and then compared: ## e.g. incoming message: 0x85, filter: 0x80/0xf0 ## 0x85 & 0xf0 = 0x80 that equals the given 0x80 so the rule matches. ## ## The byte-mask may be omitted in which case it is 0xff (match value exactly) ## ### Replacement description ## ## Like the filter, replacement also consists of two fields per MIDI-byte ## / ## The existing MIDI message is binary-AND masked and the result is ## binary-OR combined with the number. ## ## the byte-mask may be omitted in which case it is 0x00 (set value exactly) ## ## e.g incoming message: 0x85, replacement 0x01/0xf0 ## 0x85 & 0xf0 = 0x80, 0x80 | 0x01 = 0x81 ## ## Currently the replacement must have the same length as the incoming ## message. ## #### Examples ## # 0x90 69 127 | 0x91 64 127 # ## The above filter matches a ## note-on on channel 0 (0x90) of key 69 (A4) with velocity 127 ## event and replace it with a ## note-on on channel 1 (0x91) of key 60 (C4) with velocity 127 ## ## the long form of this filter is # # 0x90/0xff 69/0xff 127/0xff | 0x91/0x00 64/0x00 127/0x00 # ### a more complete example: ## Byte-masks are useful to express wild cards: # # 0x80/0xe0 0/0 0/0 | 0x05/0xf0 0/0x7f 0/0x7f # ## 0x80/0xe0 matches all note events ## (note-off = 0x80 .. 0x8f for chan 0..15) ## (note-on = 0x90 .. 0x9f for chan 0..15) ## The filter expression to be evaluated is (status_byte & 0xe0) == 0x80 ## ## Next are two "any" placeholders for key and velocity. ## 0/0 is compiled into the expression (value & 0) == 0 ## Since any number binary-AND 0 is zero, that expression is always true. ## ## So this Filter matches all note-on and all note-off events. ## ## The second part after the pipe (|) is the message that is generated ## every time this filter is matched ## ## 0x05/0xf0 ## take the existing status AND with 0xf0, this strips the channel (1..f) but ## leaves the status (note-on (0x90), note-off 0x80) in place. ## next it is OR'ed with 0x05 (MIDI channel 5) ## ## 0/0x7f for key and velocity just pass the value as-is ## AND 0x7f leaves all 7bits (0..127) as-is, OR 0 adds nothing ## ## The above rule just forces all note-on/off events to channel 5. ## ### another example: # # 0x90/0xf0 0/0 0 | 0x80/0x0f 0/0x7f 0 # ## This translates all note-on events with velocity zero, to note-off events, ## while keeping the MIDI-channel. ## ## The filter is ## 0x90/0xf0 -- matches all note-on (0x90 .. 0x9f) ## 0/0 -- regardless of key ## 0 -- with velocity 0 (could also be written as 0x0/0x7f) ## the replacement is ## 0x80/0x0f -- keep channel only (0x0f) then set note-off (0x80) ## 0/0x7f -- keep key as-is (0x7f) ## 0 -- set velocity to 0 ## #### Placeholders / Shortcuts ## ## shortcuts are case-insensitive, following StatusByte filters are available: ## 'ANY' == "0x00/0x00" // Any message ## "Note" == "0x80/0xe0" // Note on and off, 3 bytes ## "NoteOff" == "0x80/0xf0" // Note off, 3 bytes ## "NoteOn" == "0x90/0xf0" // Note on, 3 bytes ## "KeyPressure" == "0xa0/0xf0" // Polyphonic Key Pressure, 3bytes ## "CC" == "0xb0/0xf0" // Control Change, 3 bytes ## "ChanPressure" == "0xd0/0xf0" // Channel Pressure, 2 bytes ## "Pitch" == "0xe0/0xf0" // Pitch Bend, 3 bytes ## "NOTEC" == "0x8X/0xeX" // Note on and off for the given channel ## ## for the data bytes ## 'ANY' == "0x00/0x00" // Any value ## ## likewise in the replacement section: ## 'SAME' == "0x00/0xff" // keep value as-is ## "CHN" == "0x0X/0xfX" // keep status, set channel (only for status byte) ## ## ## The following rule # # NOTE ANY ANY | 0x05/0xf0 SAME SAME # ## is equivalent to the one we've seen above: # # 0x80/0xe0 0/0 0/0 | 0x05/0xf0 0/0x7f 0/0x7f # ## ## Here's a rule that translates the note "C3" on channel 5 to "A3 on channel 7 # # NOTEC5 60 ANY | CHN7 58 SAME # ## it it is equivalent to # # 0x85/0xef 60/0x7f 0/0 | 0x07/0xf0 58/0 0/0x7f # ### Midi Note Shortcuts ## ## since MIDI-note numbers are not always convenient, there are shortcuts in the form of ## ## [#b] ## ## is case-insensitive a-g ## an optional modifier "#" sharp, or "b" flat ## followed by an octave -1 to 9 ## ## Examples: ## C-1 = note number 0 ## G#0 = note number 20 ## Bb8 = note number 118 ## ## the following two are equivalent # # NOTEC5 48 ANY | CHN7 69 SAME # NOTEC5 C3 ANY | CHN7 A4 SAME # ### That's it :) #### More examples ## translate all C3 to C4, keep channel # NOTE C3 ANY | SAME C4 SAME ## any note becomes an "A" on the first channel # NOTE ANY ANY | CHN0 A3 SAME ## force all note events to channel 0 # NOTE ANY ANY | 0x00/0xf0 SAME SAME ## also force all note events to channel 0 # NOTE ANY ANY | CHN0 SAME SAME ## map note C3 on channel 5 to D2 on channel 1 # NOTEC5 C3 ANY | CHN1 D2 SAME #### and finally a real active rule: NOTE ANY ANY | CHN0 SAME SAME