Saturday, January 23, 2010

Max for Live, Java, MIDI, Windows and Sysex

Recently, I have spent some of my free time hacking some Java to provide Max for Live patchers direct access to the MIDI ports of my machine. Usually, one would be restricted to the MIDI data that Live has previously processed, which restricts it in many ways.

I have not spent too much time hacking Java in my life, but the times I have done it for various smaller hacks, it was a nice overall experience. This time, I was particularily impressed by Eclipse for its automated source code formatting facility that is widely configurable. I tweaked the settings a little to match my prefered squirly brace style, and then had Eclipse beautify my (and imported) source code with a single key stroke. Also, I like Eclipse's ability to automatically locate unknown classes and add required import statements with a single click. This makes working with examples from documentation rather easy. I still like Emacs, but I must admit that it is rather baroque when compared to what Eclipse can do.

Getting the Max external to interface to the MIDI subsystem that Java provides was rather easy, and I was mostly done after 2-3 hours of hacking. Sadly, sending Sysex strings to the MIDI controller that I used for testing did not work reliably. Naturally, I thought that the problem was with my lack of MIDI, Java and Max skills, so I spent quite some time trying out various implementation strategies, without success.

Finally, I figured that what did not work was sending Sysex messages, and only sometimes (i.e. it appeared as if the first message came through and then some following messages where not transmitted). Google finally pointed me to a web page by Thorsten Klose where, at the end, he describes the behavior as being a bug in the Java MIDI library. The problem is that when sending Sysex messages, the Java library remembers the size of largest Sysex message sent and uses that for all subsequent Sysex messages, even if they are shorter. My workaround is to close and re-open the MIDI device if a Sysex string to be sent is shorter than the previous one. This is kind of ugly, but it works for me. The workaround that Thorsten suggest has the problem that it does not stop Java from sending long messages, which reduces MIDI throughput to a point that was unacceptable for me.

I'm posting this in the hopes that it saves the next person seeing the problem some time. If you are interested in the Max for Live MIDI external, it is available here.

Share:

10 comments:

  1. Hiya,
    I am interested in what you are doing with Java and Max for Live. I use to programme in java many moons ago. I still maintain a few sites in java so i kinda know what i am doing. I am realatively new to Ableton live and max though. I tried to load up your patch and Max 5 just keeps crashing. If you wouldn't mind i would like to see the java for your project. Every time i try something simple like
    MidiDevice.Info[] devices = MidiSystem.getMidiDeviceInfo();
    through a patch (using a MXJ object) my java class causes max to crash. If i run the same command in a stand alone app i generally dont get this problem.

    I am hoping to get some more functionality out of midi than what ableton offers. I would like to save and recall patches for hardware synths. So i am expecting to do sysex, bank and programme changes etc..

    any help is greatly appreciated.
    cheers
    kle

    ReplyDelete
  2. The sources are contained in the jar file, so you can have a look at them right away. I am using the current Sun JDK on Windows, maybe it is a version problem? On Macs, you need a current OS X version with the latest patches and JDK installed, as MIDI support for Java was added very recently. You may want to prefer sending email for further discussion (hans.huebner@gmail.com)

    ReplyDelete
  3. Hello Mr. Hübner!

    I'm very interested in using your patch, but I don't know how to make max for live to see it. I moved it to "m4l-externals" folder, but that doesn't seems to work. Could you help me?

    Thanks for your hard work!

    ReplyDelete
  4. Kambor, you need to put the jmidi.jar file into a directory that is in the Java classpath. A suitable location would be

    C:\Program Files (x86)\Cycling '74\Max 5.0\Cycling '74\java\lib

    on my Windows box. You'll figure it out from that.

    The patch can then be used from any location.

    Good luck!
    Hans

    ReplyDelete
  5. Hi Hans,

    I am having issues using the mxj objects in your example patch. Not sure if it is a comatibility issue (I am using Max/MSP/Jitter 5 running on OSX Leopard)? Below is an extract of the errors I am getting. Any help would be much appreciated as I have no java skills!

    Thanks,
    Trevor


    MXJ System CLASSPATH:
    /Applications/Max5/Cycling '74/java/lib/jitter.jar
    /Applications/Max5/Cycling '74/java/lib/jmidi.jar
    /Applications/Max5/Cycling '74/java/lib/jode-1.1.2-pre-embedded.jar
    /Applications/Max5/Cycling '74/java/lib/max.jar
    /Applications/Max5/Cycling '74/java/lib/midiIn.jar
    MXJClassloader CLASSPATH:
    /Applications/Max5/Cycling '74/java/classes/
    /Applications/Max5/Cycling '74/java/classes
    Jitter 1.7.0 installed
    Jitter Java support installed
    java.lang.UnsupportedClassVersionError: Bad version number in .class file
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:676)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:317)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:280)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
    at java.lang.ClassLoader.findSystemClass(ClassLoader.java:938)
    at com.cycling74.max.MXJClassLoaderImpl.doLoadClass(MXJClassLoaderImpl.java:103)
    at com.cycling74.max.MXJClassLoader.loadClazz(MXJClassLoader.java:88)
    Could not load class 'net.bknr.max.MidiInfo'

    ReplyDelete
  6. Hi Trevor,

    I'm going to port the MIDI extension to MacOS X during the coming week. Please contact me by email (hans.huebner@gmail.com) so that I can send you a version to test.

    Cheers,
    Hans

    ReplyDelete
    Replies
    1. If You have devoted some time to a MacOS X port I would be very interested in testing it!

      Nikola

      Delete
    2. Nikola,

      the jmidi.jar file that is on http://netzhansa.com/jmidi.jar works with OSX (Snow Leopard). I have not tried it with Lion. An updated Java release is required. Let me know if you can't get it to run and I'll see if I can help.

      -Hans

      Delete
  7. Hi Hans,

    thank you very much for your post - it DID save me time with Midi sysex messages from Java!
    The problems were very hard to understand and the workaround to close and reopen the midi device solved it completely...

    Best regards from Berlin
    Karsten

    ReplyDelete
  8. I know this is old, but can anyone confirm whether this works ONCE THE MAX EDITOR IS CLOSED? On Win7 Live 9.17, It stops passing MIDI data once the Max editor is closed, i.e it works in Max, but not Max4Live,

    ReplyDelete