Hello world!

Addi.tv web home of additv, which is the handle of the Toronto based New Media artist, programmer, visualist and technologist Rob King. You can find out more about me here, check out some of my work here, or contact me directly at rob at this domain name.

Latest

Moco: MIDI – OSC Converter

Moco: Molybdenum-GexacarbonylI love OpenSoundControl.

I really do, but sometimes I’m startled that certain things haven’t been done, or aren’t standardized in OSC.  For one, there seems to be no real standard, cross-platform MIDI to OSC and back again converter.  There are a couple platform specific options (e.g. Occam) or some bits for Max, PD, or Supercollider, but nothing really cross platform and stand-alone.

Now I’ve read all about the issues with MIDI clock timing, synchronization, sysex, and innumerable other nuances of MIDI that are problematic to reproduce in OSC. But most of the time I don’t really care about all that.  I just want something that will either throw some notes out on the network, or let me play some notes on a synth by throwing out some packets.

It seems like I’ve written bits of code for translating midi to OSC innumerable times, when really all I wanted was a dead simple gateway, that runs just about anywhere.  I’m sure there’s at least a few others out there with similar experiences.

So I finally decided to do something about it, and present to you Moco (either the Brazilian Rock Cavy, or Molybdenum-Hexacarbonyl depending on if you like chemicals or rodents).  Moco is a dead simple, no-nonsense OSC-MIDI gateway, written in Java.

Primarily it sends and receives note on, note off, and control change messages cause that’s usually all you want.  It also will send and receive raw midi messages with the flick of a switch (if you really want to get into more complex things).

I’m going to be trying something different with Moco too.  I’m distributing it for free, but if you find it useful I’d encourage you to contribute to its development on Flattr.  Also if you’d like to see new features, get quick help with it, take a peek at the source, etc. then I’d encourage you even more contribute, and I’ll see what I can do!

Now, for what you’ve all been waiting for:

Download Moco version 1.0

Contribute to Moco:

6 Responses to “Moco: MIDI – OSC Converter”

  1. Kasper says:

    Hi,

    Did you make this with Processing (I seem to recognize ControlP5). Do you mind sharing your code? I’d like to build something similar, so maybe I can build on your code.

    By the way if you need an OSC monitor I build this in Processing:
    http://code.google.com/p/osc-tools/

    Thank you in advance.

    Kasper

  2. Mike says:

    Hey, I ran across your project last week and I love it. I do have a quick question though- it seems like the raw midi messages I receive are being translated to decimal before being send out via osc. I’ve tried to see if there’s some type of conversion going on, but it’s almost inconsistent.

    I’m a programmer so I poked around the class files a bit but couldn’t find anything. Just wondering if you can shed some light on the situation.

  3. additv says:

    Hi Kasper, good eye, it *was* down in Processing with MidiBus, OSCP5, and ControlP5.
    Though more accurately it was done in Eclipse (pure java) using the processing libs.
    I’d be happy to share the code. It was just thrown together in an afternoon, so forgive me if it isn’t tidy.

    package moco;
    /*
    * Moco: A Midi-OSC OSC-Midi converter
    * By Rob King 2010
    */
    import javax.sound.midi.MidiMessage;

    import netP5.NetAddress;
    import oscP5.OscMessage;
    import oscP5.OscP5;
    import processing.core.PApplet;
    import themidibus.MidiBus;
    import controlP5.ControlEvent;
    import controlP5.ControlP5;
    import controlP5.RadioButton;
    import controlP5.Textfield;
    import controlP5.Toggle;

    public class Moco extends PApplet {

    /**
    *
    */
    private static final long serialVersionUID = 1816549495367020936L;
    MidiBus midi = null;
    OscP5 osc = null;
    int oscRecievePort = 5005;

    NetAddress oscOuptutAddress;
    NetAddress oscLoopbackAddress;
    NetAddress oscLoopbackAddress2;
    NetAddress oscSend2;
    NetAddress oscSend3;

    String[] midiInputs;
    String[] midiOutputs;
    ControlP5 controlP5;

    Textfield cOSCRecievePort;
    Textfield cOSCSendPort;
    Textfield cOSCIP;
    RadioButton cMIDIIn;
    RadioButton cMIDIOut;
    Toggle cSendRaw;

    boolean midiRecieved = false;
    boolean oscRecieved = false;

    boolean sendRawMidi = false;

    public void setup() {
    size(560,300);
    midi = new MidiBus(this);
    midiInputs = MidiBus.availableInputs();
    midiOutputs = MidiBus.availableOutputs();
    osc = new OscP5(this, oscRecievePort);
    oscOuptutAddress = new NetAddress("127.0.0.1", 5000);
    oscLoopbackAddress = new NetAddress("127.0.0.1", oscRecievePort);
    oscSend2 = new NetAddress("127.0.0.1", oscRecievePort+1);
    oscSend3 = new NetAddress("127.0.0.1", oscRecievePort+2);
    oscLoopbackAddress2 = new NetAddress("127.0.0.1", oscRecievePort+3);

    frame.setTitle("Moco");

    controlP5 = new ControlP5(this);

    controlP5.addTextlabel("MidiIns", "Midi Inputs", 10, 20);
    cMIDIIn = controlP5.addRadioButton("midiInput", 10, 35);
    for(int i = 0; i cMIDIIn.addItem(midiInputs[i], i);

    controlP5.addTextlabel("Title", "Moco : MIDI - OSC Converter : addi.tv ", 2, 2);

    controlP5.addTextlabel("OSC Output", "OSC Output", 150, 20);
    cOSCSendPort = controlP5.addTextfield("OSC Sending Port", 150, 65, 100, 15);
    cOSCSendPort.setText(Integer.toString(oscOuptutAddress.port()));
    cOSCSendPort.setAutoClear(false);
    cOSCIP = controlP5.addTextfield("OSC Sending Address", 150, 35, 100, 15);
    cOSCIP.setText(oscOuptutAddress.address());
    cOSCIP.setAutoClear(false);
    cSendRaw = controlP5.addToggle("Send Raw Midi", sendRawMidi, 150, 95, 100, 15);

    controlP5.addTextlabel("OSC Input", "OSC Input", 300, 20);
    cOSCRecievePort = controlP5.addTextfield("OSC Listening Port", 300, 35, 100, 15);
    cOSCRecievePort.setText(Integer.toString(oscRecievePort));
    cOSCRecievePort.setAutoClear(false);

    controlP5.addTextlabel("MidiOuts", "Midi Outputs", 420, 20);
    cMIDIOut = controlP5.addRadioButton("midiOutput", 420, 35);
    for(int i = 0; i cMIDIOut.addItem(midiOutputs[i], i);

    controlP5.addTextarea("description", "OSC Namespace:\n\n" +
    "/midi/noteon \n" +
    " int:channel\n" +
    " int:pitch\n" +
    " int:velocity\n\n" +
    "/midi/noteoff \n" +
    " int:channel\n" +
    " int:pitch\n" +
    " int:velocity\n\n" +
    "/midi/cc \n" +
    " int:channel\n" +
    " int:cc#\n" +
    " int:value\n\n" +
    "/midi/raw \n" +
    " ints:data\n" +
    "", 300, 90, 100, 200);

    }

    public void changeMIDIInput(String deviceName){
    midi.clearInputs();
    if(deviceName!=null){
    midi.addInput(deviceName);
    println("MidiDeviceChanged to "+deviceName);
    }
    }
    public void changeMIDIOutput(String deviceName){
    midi.clearOutputs();
    if(deviceName!=null){
    midi.addOutput(deviceName);
    println("MidiDeviceChanged to "+deviceName);
    }
    }

    public void changeOSCOutputAddress(String ip, int port){
    oscOuptutAddress = new NetAddress(ip, port);
    oscSend2 = new NetAddress(ip, port+2);
    oscSend3 = new NetAddress(ip, port+3);
    }
    public void changeOSCOutputAddress(String ip){
    oscOuptutAddress = new NetAddress(ip, oscOuptutAddress.port());
    oscSend2 = new NetAddress(ip, oscOuptutAddress.port()+2);
    oscSend3 = new NetAddress(ip, oscOuptutAddress.port()+3);
    println("Changing destination ip to " + ip);
    }
    public void changeOSCOutputPort(int port){
    oscOuptutAddress = new NetAddress(oscOuptutAddress.address(), port);
    }
    public void changeOSCInputPort(int port){
    osc.stop();
    osc = new OscP5(this, port);
    }

    public void draw() {
    background(150);
    fill(100);
    stroke(255);

    if(midiRecieved){
    stroke(0, 255,0);
    midiRecieved = false;
    }else{
    stroke(255);
    }
    rect(5,15,250,height-20);
    line(70,22,140,22);
    line(140,22, 137,25);
    line(140,22, 137,19);

    if(oscRecieved){
    stroke(0, 255,0);
    oscRecieved = false;
    }else{
    stroke(255);
    }
    rect(295,15,255,height-20);
    line(350,22,410,22);
    line(410,22, 407,25);
    line(410,22, 407,19);

    }

    public void controlEvent(ControlEvent theEvent) {
    if(theEvent.isController()){
    if(theEvent.controller() == cOSCRecievePort){
    boolean canParsePort = false;
    try{
    Integer.valueOf(cOSCRecievePort.getText());
    canParsePort = true;
    }catch(NumberFormatException exc){
    System.err.println("Couldn't parse recieve port");
    cOSCRecievePort.setText(Integer.toString(oscRecievePort));
    }
    if(canParsePort)
    changeOSCInputPort(Integer.valueOf(cOSCRecievePort.getText()));
    }
    if(theEvent.controller() == cOSCSendPort){
    boolean canParsePort = false;
    try{
    Integer.valueOf(cOSCSendPort.getText());
    canParsePort = true;
    }catch(NumberFormatException exc){
    System.err.println("Couldn't parse recieve port");
    cOSCSendPort.setText(Integer.toString(oscOuptutAddress.port()));
    }
    if(canParsePort)
    changeOSCOutputPort(Integer.valueOf(cOSCSendPort.getText()));
    }
    if(theEvent.controller() == cOSCIP)
    changeOSCOutputAddress(cOSCIP.getText());
    if(theEvent.controller() == cSendRaw)
    sendRawMidi = cSendRaw.getState();
    }
    if(theEvent.isGroup()){
    if(theEvent.group() == cMIDIIn){
    if(theEvent.group().value() == -1)
    changeMIDIInput(null);
    else
    changeMIDIInput(midiInputs[(int)theEvent.group().value()]);
    }
    if(theEvent.group() == cMIDIOut){
    if(theEvent.group().value() == -1)
    changeMIDIOutput(null);
    else
    changeMIDIOutput(midiOutputs[(int)theEvent.group().value()]);
    }
    }

    }

    public void noteOn(int channel, int pitch, int velocity) {

    OscMessage msg = new OscMessage("/midi/noteon");
    msg.add(channel);
    msg.add(pitch);
    msg.add(velocity);
    try{
    osc.send(msg, oscOuptutAddress);
    osc.send(msg, oscLoopbackAddress);
    osc.send(msg, oscLoopbackAddress2);
    osc.send(msg, oscSend2);
    osc.send(msg, oscSend3);

    println("Sent " + msg + " to " + oscOuptutAddress);
    }catch (Exception e) {
    // TODO: handle exception
    }
    }

    public void noteOff(int channel, int pitch, int velocity) {
    OscMessage msg = new OscMessage("/midi/noteoff");
    msg.add(channel);
    msg.add(pitch);
    msg.add(velocity);
    try{
    osc.send(msg, oscOuptutAddress);
    osc.send(msg, oscLoopbackAddress);
    osc.send(msg, oscLoopbackAddress2);
    osc.send(msg, oscSend2);
    osc.send(msg, oscSend3);
    }catch (Exception e) {
    // TODO: handle exception
    }
    }

    public void controllerChange(int channel, int number, int value) {
    OscMessage msg = new OscMessage("/midi/cc");
    msg.add(channel);
    msg.add(number);
    msg.add(value);
    try{
    osc.send(msg, oscOuptutAddress);
    osc.send(msg, oscLoopbackAddress);
    osc.send(msg, oscLoopbackAddress2);
    osc.send(msg, oscSend2);
    osc.send(msg, oscSend3);
    }catch (Exception e) {
    // TODO: handle exception
    }
    }

    public void midiMessage(MidiMessage message) {
    midiRecieved = true;
    OscMessage oscMessage = new OscMessage("/midi/raw");
    //print ("Sent: " );
    for(int i = 0; i oscMessage.add(message.getMessage()[i]);
    //print(message.getMessage()[i] + " ");
    }
    //println("");
    if(sendRawMidi){
    try{
    osc.send(oscMessage, oscOuptutAddress);
    osc.send(oscMessage, oscLoopbackAddress);
    osc.send(oscMessage, oscLoopbackAddress2);
    osc.send(oscMessage, oscSend2);
    osc.send(oscMessage, oscSend3);
    }catch (Exception e) {
    // TODO: handle exception
    }
    }

    }

    public void oscEvent(OscMessage theOscMessage) {
    /* print the address pattern and the typetag of the received OscMessage */
    //println(theOscMessage);
    oscRecieved = true;
    if(theOscMessage.checkAddrPattern("/midi/raw")){

    int rawLength = theOscMessage.typetag().length();
    byte[] midiData = new byte[rawLength];
    //print("Got: ");
    for(int i = 0; i midiData[i] = (byte) theOscMessage.get(i).intValue();
    //print((byte) theOscMessage.get(i).intValue() + " ");
    }
    println(" : RAW");

    midi.sendMessage(midiData);

    }

    if(theOscMessage.checkAddrPattern("/midi/noteon")){
    println("Sending noteon to "+midi);
    println(theOscMessage.get(0).intValue() + " "+ theOscMessage.get(1).intValue()+ " " + theOscMessage.get(2).intValue());
    midi.sendNoteOn(theOscMessage.get(0).intValue(), theOscMessage.get(1).intValue(), theOscMessage.get(2).intValue()) ;
    }
    if(theOscMessage.checkAddrPattern("/midi/noteoff")){
    midi.sendNoteOff(theOscMessage.get(0).intValue(), theOscMessage.get(1).intValue(), theOscMessage.get(2).intValue()) ;
    }
    if(theOscMessage.checkAddrPattern("/midi/cc")){
    midi.sendControllerChange(theOscMessage.get(0).intValue(), theOscMessage.get(1).intValue(), theOscMessage.get(2).intValue()) ;
    }
    }

    public static void main(String _args[]) {
    PApplet.main(new String[] { moco.Moco.class.getName() });
    }
    }

    Your OSC monitor looks cool, I've been building a monitor/recorder too so I'll have to check yours out.
    Cheers!

  4. additv says:

    Hi Mike,
    Glad you like it!
    I just threw up the code, so hopefully you can find what is causing you trouble!
    Cheers!

  5. Mike says:

    Thanks for getting back so quickly.

    The two for loops that added the controls for the midi ins and outs was a little strange.

    Also, how did you manage when the midi controls use the same name?

  6. Mike says:

    Never mind, I had an outdated version of controlP5.

Leave a Reply