class Transformation { 0 => static int O; 1 => static int I; 2 => static int R; 3 => static int IR; 4 => static int RANDOM; int type; // 0=O 1=I 2=R 3=IR int transposition; fun void set(int type, int transposition) { type => this.type; transposition => this.transposition; } fun void set(string type, int transposition) { if (type == "O") Transformation.O => this.type; else if (type == "I") Transformation.I => this.type; else if (type == "R") Transformation.R => this.type; if (type == "IR") Transformation.IR => this.type; if (type == "RAND") Transformation.IR => this.RANDOM; transposition => this.transposition; } fun void set(Transformation tranformation) { set(tranformation.type, tranformation.transposition); } } //////////////////////////////////////////////////////////////////////// class RowTransformer { Transformation transformation; float row[]; int index; fun void set(float row[], string transformationType, int transposition) { setTransformation(transformationType, transposition); setRow(row); } fun void set(float row[], int transformationType, int transposition) { setTransformation(transformationType, transposition); setRow(row); } fun void set(int row[], string transformationType, int transposition) { setTransformation(transformationType, transposition); setRow(row); } fun void set(int row[], int transformationType, int transposition) { setTransformation(transformationType, transposition); setRow(row); } fun void setTransformation(int transformationType, int transposition) { transformation.set(transformationType, transposition); } fun void setTransformation(string transformationType, int transposition) { transformation.set(transformationType, transposition); } fun void setRow(float row[]) { float newRow[row.cap()]; for (0 => int i; i < row.cap(); i++) { row[i] => newRow[i]; } newRow @=> this.row; } fun void setRow(int row[]) { float newRow[row.cap()]; for (0 => int i; i < row.cap(); i++) row[i] $ float => newRow[i]; newRow @=> this.row; } fun void setRowBySeqence(int minValue, int maxValue) { maxValue - minValue + 1 => int length; float rowSequence[length] @=> row; for (0 => int i; i < length; i++) { (minValue + i) $ float => row[i]; } } fun float next() { // iTransformType : P=0 I=1 R=2 IR(X) =3 // R4 : iTransformType=2, iTransposition=4 if (row == null) return -1.0; row.cap() => int rowLength; float element; // Set nextRowElement value, -1 if out of range if (index >= 0 && index <= rowLength - 1) row[index] => element; else -1 => element; if (transformation.type == Transformation.RANDOM) { Std.rand2(0, row.cap() -1) => index; } // Advance index, rotate if it has reached to the end range if (transformation.type == Transformation.O || transformation.type == Transformation.I) { if (index >= row.cap() - 1) 0 => index; //Rotate else index + 1 => index; } if (transformation.type == Transformation.R || transformation.type == Transformation.IR) { if (index <= 0) rowLength - 1 => index; //Rotate else index - 1 => index; } if (transformation.type == Transformation.I || transformation.type == Transformation.IR) { 127.0 => float minElement; for (0 => int i; i < rowLength; i++) { if (row[i] < minElement) row[i] => minElement; } minElement - (element - minElement) => element; } //return (element + transformation.transposition) % row.cap(); return (element + transformation.transposition); } fun void restartTransformation(int transformationType, int transposition) { transformation.set(transformationType, transposition); if (transformation.type == Transformation.O || transformation.type == Transformation.I) 0 => index; else if (transformation.type == Transformation.R || transformation.type == Transformation.IR && row != null) row.cap() - 1 => index; } } //////////////////////////////////////////////////////////////////////// // used as a shred resource between different ChucK shreds class Global { 0 => static int transposition; 1.0 => static float velocityFactor; 1.0 => static float durationFactor; 0 => static int maxShredId; 0 => static int maxReceiverShredId; 0 => static int recordMode; } //////////////////////////////////////////////////////////////////////// class InteractSynthEngine { 0 => static int C; 2 => static int D; 4 => static int E; 5 => static int F; 7 => static int G; 9 => static int A; 11 => static int B; 12 => static int OCT; int id; int minVelocity; int maxVelocity; int minPitch; int maxPitch; float minDurSeconds; float maxDurSeconds; MidiOut midiOut; RowTransformer pitchRT; if(!midiOut.open(0)) me.exit(); fun void set(float row[], string transformationType, int transposition) { pitchRT.set(row, transformationType, transposition); } fun void set(float row[], int transformationType, int transposition) { pitchRT.set(row, transformationType, transposition); } fun void set(int row[], string transformationType, int transposition) { pitchRT.set(row, transformationType, transposition); } fun void set(int row[], int transformationType, int transposition) { pitchRT.set(row, transformationType, transposition); } fun void setTransposition(int transposition) { transposition => pitchRT.transformation.transposition; } fun void setTransformation(int transformationType, int transposition) { pitchRT.setTransformation(transformationType, transposition); } fun void clearRow() { float emptyRow[]; emptyRow @=> pitchRT.row; } fun void addToRow(int n) { 0 => int size; if (pitchRT.row != null) { pitchRT.row.cap() => size; } float newRow[size +1]; for (0 => int i; i < size; i++) { pitchRT.row[i] => newRow[i]; } n => newRow[size]; newRow @=> pitchRT.row; pitchRT.row.cap() => size; } fun void setTransfromationMode(float row[], int transformationType, int transposition, int minVelocity, int maxVelocity, float minDurSeconds, float maxDurSeconds) { set(row, transformationType, transposition); minDurSeconds => this.minDurSeconds; maxDurSeconds => this.maxDurSeconds; minVelocity => this.minVelocity; maxVelocity => this.maxVelocity; } fun void setAutoMode(int minVelocity, int maxVelocity, int minPitch, int maxPitch, float minDurSeconds, float maxDurSeconds) { minVelocity => this.minVelocity; maxVelocity => this.maxVelocity; minDurSeconds => this.minDurSeconds; maxDurSeconds => this.maxDurSeconds; minPitch => this.minPitch; maxPitch => this.maxPitch; } fun void noteOut(int pitch, int velocity, dur duration) { Global.durationFactor * duration + 0.01::second => duration; if (duration <= 0::second) return; Global.transposition + pitch => pitch; (Global.velocityFactor * velocity) $ int => velocity; if (pitch <33) 33 => pitch; if (pitch > 96) 96 => pitch; if (velocity < 0) 0 => velocity; if (velocity > 127) 127 => velocity; <<<"pitch is", pitch, "velocity is", velocity, "duration is ", duration>>>; MidiMsg msg; // NOTE_ON is 144 144 => msg.data1; pitch => msg.data2; velocity => msg.data3; midiOut.send(msg); duration => now; // NOTE_OFF is 128 128 => msg.data1; pitch => msg.data2; 0 => msg.data3; midiOut.send(msg); } fun void playNextNote(int minVelocity, int maxVelocity, dur duration) { pitchRT.next() => float pitchElement; if (pitchElement == -1) return; pitchElement $ int => int pitch; Math.random2(minVelocity, maxVelocity) => int velocity; minVelocity => this.minVelocity; maxVelocity => this.maxVelocity; noteOut(pitch, velocity, duration); } fun void playNextNote(int minVelocity, int maxVelocity, float durationSeconds) { playNextNote(minVelocity, maxVelocity, durationSeconds::second); } fun void playNextNotes(int minVelocity, int maxVelocity, float durationList[]) { for (0 => int i; i < durationList.cap(); i++) { float durationSeconds; durationList[i] => durationSeconds; playNextNote(minVelocity, maxVelocity, durationSeconds); } } fun void playNextNotes(int pitchRow[], string transformationType, int transposition, int minVelocity, int maxVelocity, float durationSeconds[]) { pitchRT.set(pitchRow, transformationType, transposition); playNextNotes(minVelocity, maxVelocity, durationSeconds); } fun void playNextNote(int minVelocity, int maxVelocity, float minDurSeconds, float maxDurSeconds) { minDurSeconds => this.minDurSeconds; maxDurSeconds => this.maxDurSeconds; Std.rand2f(minDurSeconds, maxDurSeconds)=> float durSeconds; playNextNote(minVelocity, maxVelocity, durSeconds); } fun void playAutoMode(int minVelocity, int maxVelocity, int minPitch, int maxPitch, float minDurSeconds, float maxDurSeconds) { <<>>; minVelocity => this.minVelocity; maxVelocity => this.maxVelocity; minDurSeconds => this.minDurSeconds; maxDurSeconds => this.maxDurSeconds; minPitch => this.minPitch; maxPitch => this.maxPitch; Std.rand2f(minDurSeconds, maxDurSeconds)=> float durSeconds; Std.rand2(minPitch, maxPitch) => int pitch; Std.rand2(minVelocity, maxVelocity) => int velocity; noteOut(pitch, velocity, durSeconds::second); } fun void playNextNote() { playNextNote(minVelocity, maxVelocity, minDurSeconds, maxDurSeconds); } fun void playAutoMode() { <<>>; playAutoMode(minVelocity, maxVelocity, minPitch, maxPitch, minDurSeconds, maxDurSeconds); } fun void playAutoModesLoop(int minVelocity, int maxVelocity, int minPitch, int maxPitch, float minDurSeconds, float maxDurSeconds) { <<>>; setAutoMode(minVelocity, maxVelocity, minPitch, maxPitch, minDurSeconds, maxDurSeconds); while (true) { playAutoMode(minVelocity, maxVelocity, minPitch, maxPitch, minDurSeconds, maxDurSeconds); } } fun void playNextNotesLoop(float row[], int transformationType, int transposition, int minVelocity, int maxVelocity, float minDuration, float maxDuration) { if (row == null) return; InteractSynthEngine synthEngine; synthEngine.setTransfromationMode(row, transformationType, transposition, minVelocity, maxVelocity, minDurSeconds, maxDurSeconds); while (true) { synthEngine.playNextNote(minVelocity, maxVelocity, minDurSeconds, maxDurSeconds); } } fun int sporkAutoNotesShred(int minVelocity, int maxVelocity, int minPitch, int maxPitch, float minDurSeconds, float maxDurSeconds) { <<<"sporking auto notes mode">>>; <<>>; return (spork ~ playAutoModesLoop(minVelocity, maxVelocity, minPitch, maxPitch, minDurSeconds, maxDurSeconds)).id(); } fun int sporkTransformNotesShred(int transformationType, int transposition, int minVelocity, int maxVelocity, float minDuration, float maxDuration) { <<<"sporking next notes mode">>>; if (pitchRT == null || pitchRT.row == null) return -1; return (spork ~ playNextNotesLoop(pitchRT.row, transformationType, transposition, minVelocity, maxVelocity, minDuration, maxDuration)).id(); } } /////////////////////////////////////////////////////////////////////////////////////////////////// class MIDIControlReceiver { //Controller Device: Please change the number of the MIDI device to open if needed //@@ Controller 3 => int device; // MIDI input event MidiIn midiIn; MidiMsg midiMessage; if(!midiIn.open(device)) { <<<"No MIDI controller input device detected!">>>; //me.exit(); } // Print out device that was opened <<< "MIDI controller device:", midiIn.num(), " -> ", midiIn.name() >>>; OscSend xmit; xmit.setHost("localhost", 57120); fun void run() { while( true ) { midiIn => now; while( midiIn.recv(midiMessage) ) { midiMessage.data1 => int data1; midiMessage.data2 => int data2; midiMessage.data3 => int data3; <<< data1, data2, data3 >>>; if (data1 == 176) // control in { if (data2 == 2) { // controller #2 xmit.startMsg("/midi/durationFactor", "f"); xmit.addFloat(data3 $ float * 4 / 127); } if (data2 == 3) { xmit.startMsg("/midi/velocityFactor", "f"); xmit.addFloat(data3 $ float * 3 / 127); } if (data2 == 5) { xmit.startMsg("/midi/minVelocity", "i"); xmit.addInt(data3); } if (data2 == 6) { xmit.startMsg("/midi/maxVelocity", "i"); xmit.addInt(data3); } if (data2 == 8) { xmit.startMsg("/midi/minDuration", "f"); xmit.addFloat(data3 $ float * 3 / 127); } if (data2 == 9) { xmit.startMsg("/midi/maxDuration", "f"); xmit.addFloat(data3 $ float * 3 / 127); } if (data2 == 12) { xmit.startMsg("/midi/minPitch", "i"); xmit.addInt(data3); } if (data2 == 13) { xmit.startMsg("/midi/maxPitch", "i"); xmit.addInt(data3); } if (data2 == 14) { xmit.startMsg("/midi/rowTranspose", "i"); xmit.addInt(data3 / 2 - 32); } if (data2 == 15) { xmit.startMsg("/midi/transpose", "i"); xmit.addInt(data3 / 2 - 32); } else if ((data2 >= 44 && data2 <= 49) || (data2 >= 33 && data2 <= 37)) { //Map MIDI controller numbers to transformation types string buttonMap[127]; "CLEAR" => buttonMap[49]; "RECORD" => buttonMap[44]; "STOP" => buttonMap[46]; "O" => buttonMap[33]; "I" => buttonMap[34]; "R" => buttonMap[35]; "RI" => buttonMap[36]; "RAND" => buttonMap[37]; xmit.startMsg("/midi/button", "s, i"); xmit.addString(buttonMap[data2]); xmit.addInt(data3); } } } } } } /////////////////////////////////////////////////////////////////////////////////////////////////// public class InteractSynth extends InteractSynthEngine { 9997 => static int DEFAULT_PORT; DEFAULT_PORT => int port; // Keyboard - Please change the number of the MIDI evice to open if needed //@@ Keyboard 4=> int device; // MIDI input event MidiIn midiIn; MidiMsg midiMessage; if(!midiIn.open(device)) { <<<"No Keyboard input device detected!">>>; // me.exit(); } // Print out device that was opened <<< "Keyboard device:", midiIn.num(), " -> ", midiIn.name() >>>; fun void noteListener() { while( true ) { midiIn => now; while( midiIn.recv(midiMessage) ) { midiMessage.data1 => int data1; midiMessage.data2 => int data2; midiMessage.data3 => int data3; <<>>; if (Global.recordMode == 1 && data1 == 128) { //128 is note-off addToRow(data2); // pitch } } } } fun void setPort(int port) { port => this.port; } fun OscEvent makeEvent(string messagePattern) { OscRecv recv; OscEvent oscEvent; port => recv.port; recv.listen(); recv.event(messagePattern) @=> oscEvent; return oscEvent; } fun void receiveClear() { makeEvent("/clear") @=> OscEvent oscEvent; while ( true ) { oscEvent => now; while (oscEvent.nextMsg() != 0) { <<<"clear received">>>; clearRow(); } } } fun void receiveRecord() { makeEvent("/record") @=> OscEvent oscEvent; while ( true ) { oscEvent => now; <<<"record received">>>; while (oscEvent.nextMsg() != 0) { 1 => Global.recordMode; } } } fun void receiveStop() { makeEvent("/stop") @=> OscEvent oscEvent; while ( true ) { oscEvent => now; <<<"stop received">>>; while (oscEvent.nextMsg() != 0) { 0 => Global.recordMode; } } } fun void receiveAddToRow() { makeEvent("/addToRow, i" ) @=> OscEvent oscEvent; while ( true ) { oscEvent => now; <<<"addToRow recevied">>>; while ( oscEvent.nextMsg() != 0) { oscEvent.getInt() => int element; <<<"adding to row:", element>>>; addToRow(element); } } } fun void receiveGlobals() { makeEvent("/globals, iff" ) @=> OscEvent oscEvent; while ( true ) { oscEvent => now; <<<"globals received">>>; while ( oscEvent.nextMsg() != 0) { oscEvent.getInt() => Global.transposition; oscEvent.getFloat() => Global.velocityFactor; oscEvent.getFloat() => Global.durationFactor; } } } fun void receiveAutoMode() { makeEvent("/autoNoteMode, iiiiiff") @=> OscEvent oscEvent; while ( true ) { oscEvent => now; while ( oscEvent.nextMsg() != 0) { oscEvent.getInt() => int id; <<<"sporkAutoMode">>>; sporkAutoNotesShred(oscEvent.getInt(), oscEvent.getInt(), oscEvent.getInt(), oscEvent.getInt(), oscEvent.getFloat(), oscEvent.getFloat())=> Global.maxShredId; } } } fun void receiveTransformMode() { makeEvent("/transformMode, iiiiiff") @=> OscEvent oscEvent; while ( true ) { oscEvent => now; while (oscEvent.nextMsg() != 0) { oscEvent.getInt() => int id; <<<"sporkTransformedNotes">>>; sporkTransformNotesShred(oscEvent.getInt(), oscEvent.getInt(), oscEvent.getInt(), oscEvent.getInt(), oscEvent.getFloat(), oscEvent.getFloat()) => int shredId; if (shredId != -1) shredId => Global.maxShredId; } } } fun void receiveRemoveShreds() { makeEvent("/removeShreds") @=> OscEvent oscEvent; while ( true ) { oscEvent => now; while (oscEvent.nextMsg() != 0) { panic(); for (Global.maxReceiverShredId + 1 => int i; i <= Global.maxShredId; i++) { Machine.remove(i); } Global.maxReceiverShredId => Global.maxShredId; } } } fun void receiveRemoveLastShred() { makeEvent("/removeLastShred") @=> OscEvent oscEvent; while ( true ) { oscEvent => now; while (oscEvent.nextMsg() != 0) { if (Global.maxShredId > Global.maxReceiverShredId) { Machine.remove(Global.maxShredId); Global.maxShredId - 1 => Global.maxShredId; } } } } fun void panic() { for (0 => int i; i < 127; i++) { noteOff(i); } 1::second => now; } fun void noteOff(int pitch) { MidiMsg msg; // NOTE_OFF is 144 128 => msg.data1; pitch => msg.data2; 0 => msg.data3; midiOut.send(msg); } fun void receivePanic() { makeEvent("/panic") @=> OscEvent oscEvent; while ( true ) { oscEvent => now; <<<"panic received">>>; while (oscEvent.nextMsg() != 0) { panic(); } } } fun void sporkListenerShreds() { MIDIControlReceiver midiControlReceiver; spork ~ midiControlReceiver.run(); spork ~ noteListener(); spork ~ receiveClear(); spork ~ receiveRecord(); spork ~ receiveStop(); spork ~ receiveAddToRow(); spork ~ receiveGlobals(); spork ~ receiveAutoMode(); spork ~ receiveTransformMode(); spork ~ receivePanic(); spork ~ receiveRemoveLastShred(); (spork ~ receiveRemoveShreds()).id() => Global.maxReceiverShredId; } }