MIDIRouter by example – part2

This article provides some detailed example and associated configuration file for MIDIRouter (https://github.com/NothNoth/MIDIRouter).

Using Pitch Wheel as an Aftertouch wheel

So your keyboard has a Pitch Wheel but not an aftertouch one? Worse, keyboard still uses channel 7 (and you’ve lost the manual) and you want Aftertouch events to be sent on channel 1.

It’s pretty easy so setup a simple configuration to convert Pitch Wheel messages into Aftertouch.

{
    "SourceDevice": "Korg",
    "DestinationDevice": "MOOG",
    "DefaultPassthrough": false,
    "Verbose": true,
    "Rules": [
        {
            "Name": "Pitch Wheel to Aftertouch",
            "Filter": {
                "Name": "All Pitch Wheel",
                "MsgType": "Pitch Wheel",
                "Channel": "7",
                "Settings": {
                    "Pitch": "*"
                }
            },
            "Transform": {
                "Mode": "None"
            },
            "Generator": {
                "Name": "Aftertouch out",
                "MsgType":"Aftertouch",
                "Channel": "1",
                "Settings": {
                    "Pressure": "$"
                }
            }
        }
    ]
}

The header section of the figuration file is again qui simple, just set the input MIDI interface and the output one.

We can achieve our use case with just one simple rule.

Filter:

On the filter section, we set MsgType to “Pitch Wheel” in order to look for these messages on our input interface.

We use ‘7’ for midi channel because your Pitch Wheel events are sent on this channel (and you’ve lost the manual, remember).

We set the Pitch value to ‘*’ because we want to process all values.

Transform:

Again we won’t use the Transform section here, we want to map the complete pitch range (0 to 127) to aftertouch events (0 to 127), so we set it to “None”.

Generator:

We want to send Aftertouch events, so we set MsgType to “Aftertouch”.

We want to re-emit the message on the channel 1 instead of the original 7 channel: we set the Channel to ‘1’.

Now comes the (almost)-tricky part:

  • Pitch Wheel have Pitch values
  • Aftertouch have Pressure values

We want to copy the pitch value read from the Pitch Wheel event to the pressure value of the Aftertouch event. We canot use “*” as a Pressure value because Pitch Wheel and Aftertouch are distinct events.

On a Generator configuration, values can be set to:

  • A given hardcoded value (what we did for the MIDI Channel)
  • ‘*’: The original value of same type (what we did on our previous example with Note on events)
  • ‘$’: The extracted value from the captured message

What value is extracted from the Pitch Wheel message? The Pitch value!

So by using “Pressure”: “$” we just copy the extracted Pitch value to the Pressure value.

By using a similar technique, you could also use the Pitch Wheel to generate NoteOn events, use Notes to generate Program Change events and so on!

MIDIRouter by example

This article provides some detailed example and associated configuration file for MIDIRouter (https://github.com/NothNoth/MIDIRouter).

Simple forward

Let’s start with a very simple use case: “I want to forward all messages received on my input MIDI interface to my output MIDI interface”.

This may typically happen when you have a MIDI Usb keyboard connected to your audio interface and you want to send everything to you freshly acquired analog synthesizer connected to your MIDI output.

Our configuration file will first specify your MIDI input and output devices.

Simply run MIDIRouter with no arguments to list available devices:

Usage: ./midirouter <config file>
MIDI inputs:
   Périphérique MIDI USB
   Korg
   MOOG
   Faderfox EC4
   Port 1
   Port 2
   Port 3
   Port 4
   Port 5
   Port 6
   Port 7
   Port 8
MIDI outputs:
   Périphérique MIDI USB
   Korg
   MOOG
   Faderfox EC4
   Port 1
   Port 2
   Port 3
   Port 4
   Port 5
   Port 6
   Port 7
   Port 8

Let’s say I want to route everything from “Korg” to “MOOG”.

{
    "SourceDevice": "Korg",
    "DestinationDevice": "MOOG",
    "DefaultPassthrough": true,
    "Verbose": true
}

The “DefaultPassthrough” simply tells the MIDIRouter to forward all messages not matching any other rule. Since we don’t have any other rule set, this default behaviour will match all captured messages on the input interface.

The “Verbose” option is useful when setting everything up: it will show a message everytime a MIDI event is received (you can switch it to ‘false’ once everything works fine).

Note On/off forward only

On our previous example we’ve used the DefaultPassthrough feature to allow a complete forward of all messages from the input MIDI device to the output MIDI device.

Now we may not want to send everything to our vintage Analog Synth since it only supports NoteOn and NoteOff MIDI mesages. In order to do that, we will use what is the main feature of MIDIRouter: “rules”.

A “Rule” is built using three main sections:

  • A filter which describes what kind of MIDI message to look for on the input MIDI interface
  • A transformation which optionally defines how you want to change the extracted value
  • A generator which describes what kind of MIDI message to replay on the output MIDI interface

Our complete forward scenario is quite simple:

  • Filter: we want to match any Note On or Note Off message whatever the MIDI channel, value or velocity
  • Transform: we don’t want to transform anything
  • Generator: we want to replay using the same type of MIDI message (Note on, note Off), same MIDI channel, same note, same Velocity

We will here need two rules, one for the Note On events and one for the Note Off events.

Let’s start with Note On forward.

{
    "SourceDevice": "Korg",
    "DestinationDevice": "MOOG",
    "DefaultPassthrough": false,
    "Verbose": true,
    "Rules": [
        {
            "Name": "Raw forward of any Note On",
            "Filter": {
                "Name": "Note On in (I can write anything here)",
                "MsgType": "Note On",
                "Channel": "*",
                "Settings": {
                    "Note": "*",
                    "Velocity": "*"
                }
            },
            "Transform": {
                "Mode": "None"
            },
            "Generator": {
                "Name": "Note On out",
                "MsgType":"Note On",
                "Channel": "*",
                "Settings": {
                    "Note": "*",
                    "Velocity": "*"
                }
            }
        }
    ]
}

In the first part of this configuration file, we have the same section defining the input and output. Note that we’ve disabled the Defaultpassthrough option.

Then we have the “Rules” array which – for now – only contains one rule for our “Note On” events. The “Name” fields can be used to put any comments to be used for display only.

The “Filter” is built using:

  • MsgType: the type of midi message to look for (among “Note On”, “Note Off”, “Aftertouch”, “Control Change”, “Program Change”, “Channel Pressure”, “Pitch Wheel”). Here we set it to “Note On”
  • Channel: the MIDI channel to look for (1 to 16). Here we want to match any channel and use ‘*’.
  • Now comes the “Note On” specific settings which are “Note” (the note value) and “Velocity”. Since we want to match any note or velocity, we use ‘*’ again.

Then comes the “Transform” entry which would technically allow us to transform a note to another one. Here we just want to replay the MIDI message as it, so we use “None”.

The “Generator” section describes what kind of MIDI message we want to generate when a MIDI message matching our filter is captured. These settings are quite similar to the filter settings.

  • MsgType: the type of midi message to generate (among “Note On”, “Note Off”, “Aftertouch”, “Control Change”, “Program Change”, “Channel Pressure”, “Pitch Wheel”). So here we set it to “Note On”
  • Channel: the MIDI channel to set (1 to 16). Here we want reuse the same MIDI channel found on the captured Note on, we set it to “*”.
  • Now comes the “Note On” specific settings which are “Note” (the note value) and “Velocity”. Since we want to replay the same note and velocity, we use ‘*’ again.

And that’s it: the rule will match all Note on events and replay them to the output interface using the exact same settings (MIDI channel, note, velocity).

Because we want to send also Note Off messages, we add another rule with very similar settings:

{
    "SourceDevice": "Faderfox EC4",
    "DestinationDevice": "Périphérique MIDI USB",
    "DefaultPassthrough": false,
    "Verbose": false,
    "Rules": [
        {
            "Name": "Match any NoteOn",
            "Filter": {
                "Name": "Note On in",
                "MsgType": "Note On",
                "Channel": "*",
                "Settings": {
                    "Note": "*",
                    "Velocity": "*"
                }
            },
            "Transform": {
                "Mode": "None"
            },
            "Generator": {
                "Name": "Note On out",
                "MsgType":"Note On",
                "Channel": "*",
                "Settings": {
                    "Note": "*",
                    "Velocity": "*"
                }
            }
        },
        {
            "Name": "Match any NoteOff",
            "Filter": {
                "Name": "Note Off in",
                "MsgType": "Note Off",
                "Channel": "*",
                "Settings": {
                    "Note": "*",
                    "Velocity": "*"
                }
            },
            "Transform": {
                "Mode": "None"
            },
            "Generator": {
                "Name": "Note Off out",
                "MsgType":"Note Off",
                "Channel": "*",
                "Settings": {
                    "Note": "*",
                    "Velocity": "*"
                }
            }
        }
    ]
}

On the next article, we will see how we can send Pitch Wheel events captured on a given MIDI channel to another MIDI channel and transform them to Aftertouch events:

MIDIRouter, the Origins

I’ve developped a tool for MacOS called “MIDIRouter” which does what the name implies: it’s able to route (and transform) MIDI messages over MIDI interfaces.

Before going deeper into its features, let’s try to understand what I’m trying to achieve.

I have a few multi-effects on my studio (an Ensoniq DP/4, a Digitech Studio Quad v2) and actually I don’t use them that much. They’re behind me on a rack which is not really convenient to update settings and listen to the results, especially for reverbs.

So I ended up buying a Faderfox EC4 (http://faderfox.de/ec4.html) wich is a programmable MIDI remote.

Using the EC4, I’m able to associate any VPot to a given MIDI mesage type: send a program change, a control change (pitch bend, aftertouch, Note On, etc.) and customize displayed text to my needs.

This looked quite promizing, but the EC4 has some limitations:

  • When using a ControlChange, displayed value does not necessarily match with the ouput value. Control Changes values can be 7 bits (0 to 127) or 14 bits (0 to 16383) while I can setup displayed value to (0, 127), (0,1000), etc.
  • The Ensoniq DP/4 requires proprietary Sysex messages to change its parameters, this is not supported by the EC4
  • I plan to use the EC4 for other purposes (control DAW plugins is among them), so I want it to be connected to my Mac and forward some messages to my effects chain

So I needed a software that would be able to:

  • Watch an input MIDI device for a certain type of messages (with wildcard support)
  • Extract a value (7bits or 14bits) from those messages (a program value from a Program Change, a control value from a Control Change, etc.
  • Remap the value to the expected output value range (“my EC4 shows a value in the range [0-100], sends a [0-127] value and I want to then to my effect a [0-64] value”)
  • Re-emit a MIDI message to the output interface using eventually another MIDI message type (“transform the EC4 Control Change messages into DP/4 Sysex messages) including the extracted value

This actually does not exist, so I developped MIDIRouter: https://github.com/NothNoth/MIDIRouter

MIDIRouter is free to use for personal use and “quite cheap” for professional use.

In the next article, we’ll be setting up a simple routing: