May 12, 2009

network MIDI link using ChucK and OSC

Filed under: Uncategorized — aturley @ 8:58 pm

Every once in a while I run across a blog post or a comment somewhere about how somebody wants to send a MIDI message from one computer to another. Maybe they have a keyboard that they have hooked up to one computer and they want to use it to control somebody else’s Live session. Or maybe they have some windows software that spits out MIDI, and they want to use it with some Mac software that understands MIDI.

So, let’s say you want a cross-platform solution (since if you’re only using Macs then this ships with the operating system), and you want it for free, and maybe you want it to be fairly easy to change the way the system works in case you want to try to do something weird later on down the road. To that end, I’ve written a MIDI-OSC-MIDI bridge, which you can download here. It is actually two programs, a sender and a receiver, and they use Open Sound Control to communicate. To use it, you have to install a programming language/environment called ChucK. You will probably also want to have some MIDI loopback devices set up. In OS X, you can use the Audio MIDI Setup utility to add virtual MIDI ports by selecting “IAC Driver” and adding ports. In Windows you will have to use a program like MIDI Yoke.

Go ahead and install Chuck and set up some MIDI loopback devices if you haven’t already. I’ll wait.

Now that you have ChucK installed, create two files. Let’s call the first one miditoosc.ck. It will look like this:

if (me.args() != 3) {
  <<<"usage: chuck miditoosc.ck:HOST:PORT:MIDIDEVICE">>>;
  me.exit();
 }

me.arg(0) => string host;
me.arg(1) => Std.atoi => int port;
me.arg(2) => Std.atoi => int dev;

MidiIn min;
if (!min.open(dev)) {
  <<<"Could not open MIDI device " + dev>>>;
  me.exit();
 }
MidiMsg msg;

OscSend send;
send.setHost(host, port);

while(min => now) {
  while(min.recv(msg)) {
    send.startMsg("/midi/raw", "iii");
    msg.data1 => send.addInt;
    msg.data2 => send.addInt;
    msg.data3 => send.addInt;
  }
 }

Let’s call the second one osctomidi.ck. It will look like this:

if (me.args() != 2) {
  <<<"usage: chuck osctomidi.ck:PORT:MIDIDEVICE">>>;
  me.exit();
 }

me.arg(0) => Std.atoi => int port;
me.arg(1) => Std.atoi => int dev;

OscRecv recv;
port => recv.port;
recv.event("/midi/raw, iii") @=> OscEvent oe;
recv.listen();

MidiOut mout;
if (!mout.open(dev)) {
  <<<"Could not open MIDI device " + dev>>>;
  me.exit();
 }
MidiMsg msg;

while (oe => now) {
  while(oe.nextMsg() != 0) {
    oe.getInt() => msg.data1;
    oe.getInt() => msg.data2;
    oe.getInt() => msg.data3;
    mout.send(msg);
  }
 }

The first program waits for a MIDI message. When it receives a MIDI message, it will take the data from the message, put it into an OSC message, and send the message. The second program reverses this process, waiting for an OSC message, putting the data into a MIDI message, and sending that message out to a MIDI port.

To use these programs, place the files on two computers that both have ChucK installed. Then, run the following command on both computers:

chuck --probe

This will display a list of the computer’s audio and MIDI interfaces. On my computer I see something like this:

[chuck]: ——( chuck — 2 MIDI inputs )——
[chuck]: [0] : “IAC Driver Bus 1″
[chuck]: [1] : “IAC Driver IAC Bus 2″
[chuck]:
[chuck]: ——( chuck — 2 MIDI outputs )—–
[chuck]: [0] : “IAC Driver Bus 1″
[chuck]: [1] : “IAC Driver IAC Bus 2″
[chuck]:

This tells me that there are two MIDI inputs (numbered 0 and 1) and two MIDI outputs (numbered 0 and 1). If I wanted to run osctomidi.ck and have it listen for OSC messages on port 6969 and send MIDI messages to the output called “IAC Driver Bus1″, I would run the following command from the directory that contained the file osctomidi.ck:

chuck osctomidi.ck:0:6969

If you didn’t already know, arguments are passed to Chuck processes by separating them with colons. The first argument is the device number (0) and the second argument is the port (6969). To run miditoosc.ck, I would use a similar command:

chuck miditoosc.ck:127.0.0.1:1:6969

In this case the first argument is the host to which I want to send the OSC messages (I’m just sending to the local host here), the next argument is the device number, and the final argument is the port. If both of these programs are running and the host named in the second command is the host where the first command is run, then you should have a link between the two computers (or, in this case, one computer) that will connect the MIDI input from the second host to the MIDI output on the first host.

Just a note: If you are doing this all on one computer you may see a message that says something like “cannot bind to tcp port 8888″. You can safely ignore that message.

I’ve defined an OSC message here that looks like this:

/midi/raw [data1] [data2] [data3]

where data1, data2, and data3 are integers. These integers are just the data that would normally make up a MIDI triplet. I’ve used the message address “/midi/raw” because it might make sense at some later point to have different OSC messages for different types of MIDI messages. For example, I might use something like this for MIDI CC messages:

/midi/cc [control] [value] [channel]

But for now I want to remain message-type agnostic.

Since these programs use OSC they can provide a bridge between MIDI and environments that may support OSC more easily than MIDI. For example, there are a number of OSC packages for Python that are easy to use, but MIDI libraries seem to be fairly platform-specific. Or there is the case of Java on the Mac, which does not support the standard Java MIDI libraries.

So there you have it. ChucK, OSC, and MIDI, working together.

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress

On this site: