In my constant... need... to get everything music instrument related to communicate with each other, I wanted to look into ways to get some of my keyboards/synths with only MIDI over USB to talk to devices with regular good old-fashioned 5-pin MIDI ports from the eighties.
First I had a quick look at off the shelf solutions. The most interesting one being the Kenton MIDI USB Host – providing MIDI host functionality for USB devices as well as regular MIDI in and out in a small box. Unfortunately it is rather expensive (~125 €) and a reliable online source warned me that it was not entirely stable in collaboration with my OP-1, so I started thinking of more... home-grown solutions.
I decided to try to use my old Raspberry Pi and see if that would serve as a USB host with a borrowed MIDI USB adapter. (Thanks Simon.) A cheaper, and, as an added boon, a nerdier solution.
pi@raspberrypi ~ $ aconnect -i
client 0: 'System' [type=kernel]
0 'Timer '
1 'Announce '
client 14: 'Midi Through' [type=kernel]
0 'Midi Through Port-0'
client 16: 'Keystation Mini 32' [type=kernel]
0 'Keystation Mini 32 MIDI 1'
client 20: 'E-MU XMidi1X1' [type=kernel]
0 'E-MU XMidi1X1 MIDI 1'
The important part is the number after each keyword client. They tell you how to refer to the different devices when connecting them. So, if we want to send signals from the only port (0) of the Keystation Mini (client 16) to the only port (0) of the 5-pin out (client 20) we can set up a connection using the command:
pi@raspberrypi ~ $ aconnect 16:0 20:0
Voila, if we inspect the connections again (with an added -l) it will report this:
pi@raspberrypi ~ $ aconnect -i -l
client 0: 'System' [type=kernel]
0 'Timer '
1 'Announce '
client 14: 'Midi Through' [type=kernel]
0 'Midi Through Port-0'
client 16: 'Keystation Mini 32' [type=kernel]
0 'Keystation Mini 32 MIDI 1'
Connecting To: 20:0
client 20: 'E-MU XMidi1X1' [type=kernel]
0 'E-MU XMidi1X1 MIDI 1'
Connected From: 16:0
To test this marvel I hooked up my Volca FM to the receiving end of the MIDI chain, and lo and behold it received glorious MIDI signals and spat out magnificent frequency modulated digital synthesis!
The way this works at this point in the story is that I hook the Pi up to the ethernet at home – with the midi devices already hooked up. Then I SSH to it from my computer and execute the commands listed above. Hardly ideal for a situation anywhere out in the real world, so I looked into ways to automate it. I wanted to automatically create the connections when I plug the devices...
pi@raspberrypi ~ $ cat dev/ruby/midiconnect/connectall.rb
#!/usr/bin/ruby
#
t = `aconnect -i -l`
ports = []
t.lines.each do |l|
/client (\d*)\:/=~l
port = $1
# we skip empty lines and the "Through" port
ports << port unless $1.nil? || $1 == '0' || /Through/=~l
end
ports.each do |p1|
ports.each do |p2|
unless p1 == p2 # probably not a good idea to connect a port to itself
system "aconnect #{p1}:0 #{p2}:0"
end
end
end
(This is a rather brute force way of doing this as it will connect everything to everything but itself. The bonus is that if I connect several MIDI related devices using a USB hub everything will be connected to everything.)
And it worked, so I created a convenience wrapper in a bash script for it, like so:
pi@raspberrypi ~ $ cat bin/connectall.sh
#!/bin/bash
#
/usr/bin/ruby /home/pi/dev/ruby/midiconnect/connectall.rb
One chmod +x later and it is really easy to reestablish connections semi-automatically.
I added this script to the boot up sequence of the Pi, but that meant that I needed to reboot it if I do any changes. Hardly ideal, so I wanted to take it further.
If we run lsusb we get a list of USB devices connected to the system. Like so, for example:
pi@raspberrypi ~ $ lsusb
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 012: ID 0a4d:129d Evolution Electronics, Ltd
Bus 001 Device 009: ID 041e:3f07 Creative Technology, Ltd E-Mu Xmidi 1x1
The devices we are after are the two last entries. (Sometimes the list might be slightly cryptic, so you might want to run lsusb before and after connecting the device to see what has changed.)
We see that the Keystation has a hexadecimal vendor ID of 0a4d and a ditto device ID of 129d, so we can add a file called, for example, 99-keystation-mini.rules in the /etc/udev/rules.d/ folder containing:
ATTRS{idVendor}=="0a4d", ATTRS{idProduct}=="129d", RUN+="/home/pi/bin/connectall.sh"
I added one similar file per USB MIDI device I want the Pi to be able to talk to and we are good to go. Reload the udev rules with a sudo udevadm control --reload-rules and we can plug and play as we want.
There must be more generic solutions to this – using other things than udev, maybe? But, hey, this works for me for now, so further optimisation will have to wait. Other kinds of things to look into would be:
Cables! |
First I had a quick look at off the shelf solutions. The most interesting one being the Kenton MIDI USB Host – providing MIDI host functionality for USB devices as well as regular MIDI in and out in a small box. Unfortunately it is rather expensive (~125 €) and a reliable online source warned me that it was not entirely stable in collaboration with my OP-1, so I started thinking of more... home-grown solutions.
I decided to try to use my old Raspberry Pi and see if that would serve as a USB host with a borrowed MIDI USB adapter. (Thanks Simon.) A cheaper, and, as an added boon, a nerdier solution.
Step 1: Get the USB MIDI device up and running
This was the easy part. The device I have been lent is a very straight forward Creative Technology, Ltd E-Mu Xmidi 1x1. This was picked up by the ALSA drivers on the Pi. (If you don't have ALSA already installed it is just a matter of doing a sudo apt-get install alsa, basically.) The USB-only MIDI device I wanted to use for testing was my good old road-worn class-compliant M-Audio KeyStation 32. Plugged and it in and it was picked up by the system.Step 2: Connect the two worlds
To be able to route MIDI from the one to the other we need to use aconnect. (This should come with your ALSA installation.) A quick aconnect -i will list all the input ports of your connected MIDI devices. (Along with some concepts of through and system.) Here's what my system looks like at this point:pi@raspberrypi ~ $ aconnect -i
client 0: 'System' [type=kernel]
0 'Timer '
1 'Announce '
client 14: 'Midi Through' [type=kernel]
0 'Midi Through Port-0'
client 16: 'Keystation Mini 32' [type=kernel]
0 'Keystation Mini 32 MIDI 1'
client 20: 'E-MU XMidi1X1' [type=kernel]
0 'E-MU XMidi1X1 MIDI 1'
The important part is the number after each keyword client. They tell you how to refer to the different devices when connecting them. So, if we want to send signals from the only port (0) of the Keystation Mini (client 16) to the only port (0) of the 5-pin out (client 20) we can set up a connection using the command:
pi@raspberrypi ~ $ aconnect 16:0 20:0
Voila, if we inspect the connections again (with an added -l) it will report this:
pi@raspberrypi ~ $ aconnect -i -l
client 0: 'System' [type=kernel]
0 'Timer '
1 'Announce '
client 14: 'Midi Through' [type=kernel]
0 'Midi Through Port-0'
client 16: 'Keystation Mini 32' [type=kernel]
0 'Keystation Mini 32 MIDI 1'
Connecting To: 20:0
client 20: 'E-MU XMidi1X1' [type=kernel]
0 'E-MU XMidi1X1 MIDI 1'
Connected From: 16:0
To test this marvel I hooked up my Volca FM to the receiving end of the MIDI chain, and lo and behold it received glorious MIDI signals and spat out magnificent frequency modulated digital synthesis!
The way this works at this point in the story is that I hook the Pi up to the ethernet at home – with the midi devices already hooked up. Then I SSH to it from my computer and execute the commands listed above. Hardly ideal for a situation anywhere out in the real world, so I looked into ways to automate it. I wanted to automatically create the connections when I plug the devices...
Step 3: Automatic connection
The first thing I did was to write a script that connects the various devices as they are connected. I picked Ruby as scripting language of choice. (bash would probably be a more sane choice, but I like my daily dose of ruby...)pi@raspberrypi ~ $ cat dev/ruby/midiconnect/connectall.rb
#!/usr/bin/ruby
#
t = `aconnect -i -l`
ports = []
t.lines.each do |l|
/client (\d*)\:/=~l
port = $1
# we skip empty lines and the "Through" port
ports << port unless $1.nil? || $1 == '0' || /Through/=~l
end
ports.each do |p1|
ports.each do |p2|
unless p1 == p2 # probably not a good idea to connect a port to itself
system "aconnect #{p1}:0 #{p2}:0"
end
end
end
(This is a rather brute force way of doing this as it will connect everything to everything but itself. The bonus is that if I connect several MIDI related devices using a USB hub everything will be connected to everything.)
And it worked, so I created a convenience wrapper in a bash script for it, like so:
pi@raspberrypi ~ $ cat bin/connectall.sh
#!/bin/bash
#
/usr/bin/ruby /home/pi/dev/ruby/midiconnect/connectall.rb
One chmod +x later and it is really easy to reestablish connections semi-automatically.
I added this script to the boot up sequence of the Pi, but that meant that I needed to reboot it if I do any changes. Hardly ideal, so I wanted to take it further.
Step 4: Automatic detection
Next up is to set up automatic detection of my USB devices. To accomplish this I'll rely on udev and sets of rules based on hardware IDs of the devices in question – lsusb to the resque.If we run lsusb we get a list of USB devices connected to the system. Like so, for example:
pi@raspberrypi ~ $ lsusb
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 012: ID 0a4d:129d Evolution Electronics, Ltd
Bus 001 Device 009: ID 041e:3f07 Creative Technology, Ltd E-Mu Xmidi 1x1
The devices we are after are the two last entries. (Sometimes the list might be slightly cryptic, so you might want to run lsusb before and after connecting the device to see what has changed.)
We see that the Keystation has a hexadecimal vendor ID of 0a4d and a ditto device ID of 129d, so we can add a file called, for example, 99-keystation-mini.rules in the /etc/udev/rules.d/ folder containing:
ATTRS{idVendor}=="0a4d", ATTRS{idProduct}=="129d", RUN+="/home/pi/bin/connectall.sh"
I added one similar file per USB MIDI device I want the Pi to be able to talk to and we are good to go. Reload the udev rules with a sudo udevadm control --reload-rules and we can plug and play as we want.
Final notes
Now I have a stand alone little gadget that bridges the world of USB MIDI and the world of standard 5-pin MIDI. The Raspberry Pi is powered by standard 5V over micro USB, so I've hooked it up to the USB port of my power bank I use for guitar pedals, volcas, etc. This means it can be used in the shade in a parc.
Outdoor season with 1bisHill. |
- Fancy routing options? Especially if more devices are hooked up with a USB hub?
- MIDI filtering/translations?
- Security-wise it is probably not a good idea to let the udev call scripts in my home dir, but this is really handy... (I should look into what user level these things run under...)
I have had some weird issues with my OP-1 not sending MIDI signals some times with this setup just after hooking things up. (The solution, bizarrely, seems to be swapping between COM programs, and then going into synth mode, and it works again.) However this is also happening when I use it as a MIDI controller hooked up to my mac so I won't blame the Pi for this yet. Also, once things are connected it works like a charm. (Touch wood.)
If I need more on the fly configurability I might still want to try to use my iPad coupled with a USB hub as a bridge (using apps like midiflow or similar to do the routing), but I'm happy with the added portability (and nerd-cred) from the solving this with the Pi.
References
Luckily others has done some of this before, so I could base my work on their findings. I found the following URLs helpful:- Basically a better write up of step 1 and 2 above can be found here: http://sandsoftwaresound.net/send-midi-from-usb-b-to-5-pin/
- My main source of information for step 4: https://unix.stackexchange.com/questions/65891/how-to-execute-a-shellscript-when-i-plug-in-a-usb-device
- And: https://raspberrypi.stackexchange.com/questions/19600/is-there-a-way-to-automatically-activate-a-script-when-a-usb-device-connects
- And: https://stimresp.wordpress.com/2016/02/08/using-a-raspberry-pi-as-usb-midi-host/
- The great Cuckoo discussing the Kenton MIDI USB Host: https://youtu.be/s6D9jafo5_I?t=8m20s (Bonus: lots of Volca FM sounds!)
Great article, thank you. In my installation i receive this erroe when i launch connectall.rb.
ReplyDelete""
/usr/local/bin/connectall.rb:12:in `block in ': undefined local variable or method `names' for main:Object (NameE rror)
from /usr/local/bin/connectall.rb:6:in `each'
from /usr/local/bin/connectall.rb:6:in `'
culd you help me please?
I have a raspberry pi 3 b+.
DeleteThanks. I don't know why this error would occur. It mentions a local variable 'names' that is not in my code above... Can you paste your entire connectall.rb here, maybe?
DeleteThanks.
Hi I've executed your idea successfully. BUT now there is an extra challenge. I. have a Roland A-800 PRO controller keyboard. This keyboard has three midi ports, one physical midi port and 2 over USB. The script is automatically connecting 'client':0 but not 'client':1 and :2. (which are both USB ports) Is there a way to change the script to connect to all ports?
ReplyDeleteWhen running connect -l I get:
client 0: 'System' [type=kernel]
0 'Timer '
1 'Announce '
client 14: 'Midi Through' [type=kernel]
0 'Midi Through Port-0'
client 20: 'A-PRO' [type=kernel,card=1]
0 'A-PRO MIDI 1 '
Connecting To: 24:0
Connected From: 24:0
1 'A-PRO MIDI 2 '
2 'A-PRO MIDI 3 '
client 24: 'mio' [type=kernel,card=2]
0 'mio MIDI 1 '
Connecting To: 20:0
Connected From: 20:0
I'd like to get to (I've connected 1 and 2 manually, this works) automatically:
client 0: 'System' [type=kernel]
0 'Timer '
1 'Announce '
client 14: 'Midi Through' [type=kernel]
0 'Midi Through Port-0'
client 20: 'A-PRO' [type=kernel,card=1]
0 'A-PRO MIDI '
Connecting To: 24:0
Connected From: 24:0
1 'A-PRO 1 '
Connecting To: 24:0
2 'A-PRO 2 '
Connecting To: 24:0
client 24: 'mio' [type=kernel,card=2]
0 'mio MIDI 1 '
Connecting To: 20:0
Connected From: 20:0, 20:1, 20:2
Can you help me out?
I have the same sort of issue. My LaunchPad X has two subports a '0' port that connects and a '1' port. The 1 port is actually the on that I need connected. I can make a manual connection but would love for the '1' ports to automatically connect, too.
DeleteHere's a version that works with multi-port MIDI devices: https://gist.github.com/chmanie/4f2838f4548d25b9c883f7d6d074f67c
DeleteGreat! Thanks Chris!
Delete