Controlliamo il nostro arpeggiatore MIDI con Java Nel primo articolo di questa serie abbiamo costruito un semplice arpeggiatore MIDI in ChucK. Questa volta esploreremo le capacità integrative di ChucK, mediante il protocollo OSC e Java. Utilizzeremo una semplice libreria, JavaOSC della Illposed Sofware. Progetteremo anche una interfaccia grafica in Java per controllare il nostro arpeggiatore MIDI per mostrare quanto la combinazione di queste tecnologie possa essere potente.
Estendiamo l'arpeggiatore MIDI Di seguito alcuni tecnicismi sulle modifiche che apporteremo all'arpeggiatore MIDI originale. Chi non fosse interessato può tranquillamente saltare questa sezione d andare direttamente al codice più sotto. Dò per scontato che abbiate già letto l'articolo precedente e provato l'arpeggiatore ivi illustrato. Ricorderete che ci siamo serviti di un array di interi per mappare le differenti note MIDI della progressione dell'arpeggio. Per gli scopi del presente articolo abbiamo però bisogno di apportare qualche modifica all'algoritmo di arpeggio. Questa era la nostra vecchia progressione: [0,4,3,4,5,-5,-4,-3] @=> int scale[]; a significare che iniziamo da una nota base ( aggiungendo 0), poi la alziamo di 2 toni(+4) e alla risultante nota aggiungiamo un tono e mezzo(+3), per cui la terza nota sarà 3 toni e mezzo (+4 +3= +7) più alta della prima, e così via.) In parole povere, indichiamo sempre la distanza relativa di una nota MIDI da quella precedente. Questa volta invece, la progressione corrispondente sarà: [0,4,7,11,16,11,7,4] @=> int scale[]; La differenza in questo caso è che le distanze sono sempre relative alla nota base (quella suonata sulla tastiera MIDI). Come vedremo, questo impone anche alcune piccole modifiche al calcolo della progressione (il ciclo "for" nella funzione "arpeggio"). Implementiamo gli eventi OSC ChucK contiene una libreria relativa ai messaggi OSC. OSC(Open Sound Control) è , come indicato sul relativo sito : "un protocollo di comunicazione tra sintetizzatori di suono computerizzati e altri dispositivi multimediali, ottimizzato per le moderne tecnologie di rete". Insieme alle classi di controllo per il MIDI, ChucK include classi di comunicazione OSC, e questo ci dà l'opportunità di integrare ChucK con altri sistemi, in questo caso Java. Ma basta chiacchiere e vediamo il codice del nostro completo "arpeggiatore MIDI controllato via OSC". Ecco il codice del file "arpeggiator.ck": 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:
Concentriamoci solo sulle porzioni di codice nuove e/o modificate. Riga 7 : abbiamo spostato qui la dichiarazione dell'array "scale" (contenente le distanze tra le note MIDI), fuori dalla funzione "arpeggio", perchè abbiamo bisogno che sia globale. Riga 8 : definiamo una variabile "int" con nome "originalNote", che ci servirà per memorizzare la nota base della progressione(vedere più sotto). Riga10: quì creiamo l'oggetto ricevente OSC, impostiamo la porta di ascolto(riga 11) e lo mettiamo in attesa di messaggi in ingresso. Riga 13: definiamo uno specifico tipo di messaggio, chiamato "/foo" , accettiamo 8 parametri di tipo "int" (le 8 "i") e se ne riceviamo uno verrà assegnato ad "oe", un oggetto di tipo "OSCEvent". Riga14: lanciamo un processo("spork" a "shred"), la funzione "pollOscEvent". Vediamo cosa fà: - Riga 44: come già sappiamo, questa riga significa che aspettiamo fino al verificarsi di un evento(in questo caso un OscEvent).
- Riga 45-50: Ci aspettiamo che l'evento OscEvent ricevuto contenga 8 parametri di tipo "int". Li prendiamo e ri-popoliamo il nostro array delle distanze delle note MIDI.
Questa infrastruttura permette di cambiare la progressione MIDI d'arpeggio dall'esterno di ChucK. Qualsiai sistema che implementi OSC è in grado di interagire con il nostro arpeggiatore. Noi usiamo Java, la libreria JavaOSC e Swing per costruire una minimale interfaccia grafica. Il codice Java
Intanto ecco un'immagine della nostra semplice interfaccia grafica: Ognuno degli otto JSpinner rappresenta la distanza tra la corrispondente nota MIDI e la nota base. Dopo aver valorizzato i JSpinner, premendo "Send" la nuova progressione MIDI verrà eseguita su un "arpeggiator.ck" attivo. 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: Il codice dovrebbe essere abbastanza auto-esplicativo; apriamo una porta di uscita OSC(OSCPortOut) che punti ad una VM ChucK attiva sulla stessa macchina sulla porta UDP 6449. Il metodo "initComponents" costruisce l'interfaccia grafica e aggiunge un "listener" all'unico JButton presente in modo che, se premuto, venga chiamato il metodo "sendSequence". In questo metodo, metteremo gli 8 valori "int" dei JSpinner in un array di oggetti. Creeremo un oggetto OSCMessage passando come parametri gli stessi "nome/percorso" dell'evento definito in ChucK ("/foo"), e l'array degli oggetti. Compileremo il file ArpeggiatorController.java mettendo in "classpath" javaosc.jar ( lo troviamo quì), poi lanceremo "arpeggiator.ck" in una VM ChucK (nel precedente articolo è indicato come mappare le porte MIDI), e finalmente potremo lanciare la classe java per vedere all'opera l'interfaccia grafica. Hasta la proxima. |