Controlling our MIDI arpeggiator thru Java In the first article of this series we've built a simple MIDI arpeggiator using ChucK. This time we'll explore its integration capabilities, using the OSC protocol and Java. We'll use a nice java library to do this, JavaOSC by Illposed Sofware. We'll also build a java GUI to control our MIDI arpeggiator, showing how powerful could be the combination of these technologies.
Extending our MIDI arpeggiatorHere are some technicalities about what we're going to change on the original MIDI arpeggiator. If you're not interested, feel free to skip this section and go directly to the code below. I take for granted that you've already tested the midi arpeggiator illustrated in the previous article.You remember that we've used an array of integers to map the different MIDI notes of the arpeggio progression. For this article purposes though , we need to change a bit the progression algorithm. This was our old progression: [0,4,3,4,5,-5,-4,-3] @=> int scale[]; meaning that we start from the basic note (adding 0), then we add 2 tones (+4), and to the resulting note we add one tone and a half (+3), meaning that the third note wil be three tones and a half (+4 +3 = +7) higher than the first one, and so on. Simply explained, each note distance is relative to the previous MIDI note. This time, the corresponding progression will be: [0,4,7,11,16,11,7,4] @=> int scale[]; The difference is that in the former case, distances are all relative to the base(the one played on the MIDI keyboard) note. As we will see, this imposes minor modifications to the progression calculus (the 'for' loop inside the 'arpeggio' function). Implementing OSC events ChucK contains a library to cope with OSC messages. OSC (Open Sound Control) is, as stated on its site : "a protocol for communication among computers, sound synthesizers, and other multimedia devices that is optimized for modern networking technology. ". Together with MIDI control classes, ChucK also includes OSC communication classes, and this gives us the opportunity to integrate ChucK with other systems, in our case Java. But stop chattering and let's see the code of our complete "OSC controlled MIDI arpeggiator". Here it the code for our arpeggiator.ck file: 1:MidiIn min; 2:MidiOut mout; 3:MidiMsg msg; 4: 5:min.open(1); 6:mout.open(1); 7:[0,4,7,11,16,11,7,4] @=> int scale[]; 8:int originalNote; 9: 10:OscRecv recv; 11:6449 => recv.port; 12:recv.listen(); 13:recv.event( "/foo, i i i i i i i i" ) @=> OscEvent oe; 14:spork ~ pollOscEvent(); 15: 16:while(true){ 17: min => now; 18: min.recv(msg); 19: if(msg.data1 == 144){ 20: spork ~ arpeggio(msg); 21: } 22:} 23: 24: 25:fun void arpeggio(MidiMsg mymsg){ 26: MidiMsg message; 27: mymsg.data1 => message.data1; 28: mymsg.data2 => message.data2; 29: mymsg.data3 => message.data3; 30: 31: message.data2 => originalNote; 32: for ( 0 => int i; i < scale.cap(); i++){ 33: 144 => message.data1; 34: originalNote + scale[i] => message.data2; 35: mout.send(message); 36: 300::ms => now; 37: 128 => message.data1; 38: mout.send(message); 39: } 40:} 41:fun void pollOscEvent(){ 42: while( true ) 43: { 44: oe => now; 45: while( oe.nextMsg() ) 46: { 47: for ( 0 => int i; i < 8; i++){ 48: oe.getInt() => scale[i] ; 49: } 50: } 51: } 52:} 53:
We're going to concentrate only on the new/different parts of the code. Line 7 : we've moved here the declaration of the "scale" array(the one containing MIDI note distances) , outside of the function "arpeggio", as we need it scoped globally to the file. Line 8 : we define an int variable "originalNote", which we'll use to store the base note of the progression (see later). Line 10: here we create the OSC receiver object, set the listening port (line 11) and make it start listening for incoming messages. Line 13: we define a specific type of message, named "/foo" accepting 8 parameters of "int" type (the eight "i"), and if we receive one it will be hosted on "oe", an "OscEvent" object. Line 14: we "spork" a "shred" (launch a process), the function "pollOscEvent". Let's see what it does:
Line 44: as we already know, this line means we wait until an event (in this case an OscEvent) is raised. Line 45-50: We expect our OscEvent carries 8 int type parameters. We just take these and re-populate our note-distances array with them.
This infrastructure allows as to change the MIDI arpeggio progression from outside ChucK. Any system implementing OSC is able to interact with our arpeggiator. We use Java, the JavaOSC library, and Swing to build a minimal GUI.
The Java code First, here is a shot of what our simple gui will look like: Each one of the 8 JSpinners represents the distance between the corresponding MIDI note and the base note. After setting all the JSpinners, pressing Send will apply the new arpeggio progression to a running "arpeggiator.ck".
1:import com.illposed.osc.*; 2:import java.net.InetAddress; 3:import java.net.SocketException; 4:import java.net.UnknownHostException; 5: 6:/** 7: * 8: * @author mauro molino - beanizer.org - 2008 9: */ 10:public class ArpeggiatorController extends javax.swing.JFrame { 11: 12: private javax.swing.JButton jButton1; 13: private javax.swing.JSpinner[] jSpinner; 14: 15: OSCPortOut sender=null; 16: 17: public ArpeggiatorController() { 18: initComponents(); 19: try { 20: InetAddress addr=InetAddress.getLocalHost(); 21: sender = new OSCPortOut(addr,6449); 22: } catch (UnknownHostException ex) { 23: ex.printStackTrace(); 24: } catch (SocketException ex) { 25: ex.printStackTrace(); 26: } 27: } 28: 29: private void initComponents() { 30: setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); 31: setTitle("ChucK Arpeggiator Controller"); 32: setMinimumSize(new java.awt.Dimension(384, 44)); 33: setResizable(false); 34: jSpinner=new javax.swing.JSpinner[8]; 35: getContentPane().setLayout(new javax.swing.BoxLayout (getContentPane(), javax.swing.BoxLayout.LINE_AXIS)); 36: for(int t=0 ; t<8 ; t++){ 37: jSpinner[t] = new javax.swing.JSpinner(); 38: getContentPane().add(jSpinner[t]); 39: } 40: jButton1 = new javax.swing.JButton(); 41: jButton1.setLabel("Send"); 42: jButton1.addMouseListener(new java.awt.event.MouseAdapter() { 43: public void mouseClicked(java.awt.event.MouseEvent evt) { 44: sendSequence(); 45: } 46: }); 47: getContentPane().add(jButton1); 48: 49: pack(); 50: } 51: 52: public static void main(String args[]) { 53: java.awt.EventQueue.invokeLater(new Runnable() { 54: public void run() { 55: new ArpeggiatorController().setVisible(true); 56: } 57: }); 58: } 59: private void sendSequence(){ 60: System.out.println("sending"); 61: Object args[] = new Object[8]; 62: for(int t=0 ; t<8 ; t++){ 63: args[t] = ((Integer)jSpinner[t].getValue()).intValue(); 64: } 65: OSCMessage msg = new OSCMessage("/foo", args); 66: try { 67: sender.send(msg); 68: } catch (Exception e) { 69: e.printStackTrace(); 70: } 71: } 72:} 73:
The code should be quite self-explaining; we open an output OSC port (OSCPortOut), pointing to a ChucK VM active on the same host and on UDP port 6449. The "initComponents" method just builds the GUI and adds a listener to the only JButton so that, when pressed, the "sendSequence" method will be called. In this method, we'll put the 8 "int" values of the JSpinners in an array of objects. We then create an OSCMessage, passing as parameters the same "name/path" of the event ("/foo" , it must match the one defined in our ChucK code when creating the event), and the array of objects. Compile ArpeggiatorController.java putting javaosc.jar ( you can find it here) in the classpath, then launch "arpeggiator.ck" inside a ChucK VM (read the previous article to see how to map MIDI ports), and finally launch the java class to use the GUI. Hasta la proxima. |