
robin.escalation wrote:
Scott Wheeler wrote:
The main thing this boils down to from my side is doing controlled randomization, parameter envelopes or event sequences that are too complicated or tedious to do directly in the sequencer.
I can see doing that myself. For the last couple of years I have been using Reaktor to build instruments that generate sound through simple interaction... no complex sequencing. For example I slow a drum machine to 2 BPM and run the hits through a resonant filter and delay, with one or two LFOs cycling some parameters. This might create odd popping an chirping sounds at randomish intervals.
This is all well and good, but the only algorithmic devices I've used have been made by other people, since Reaktor is not the best environment for writing equations. That said, I would rarely want to simply feed and equation and watch it run.
What is great about Reaktor is that it is easy for me to "play" these instruments in real time, since any of their parameters can be exposed to controls and mapped with MIDI. So I am able to jam with my creations with some sound factors under strict control, others wandering, and still others directly played.
Well, you can also map stuff to MIDI in ChucK, you just have to build a few classes to make the setup easy. I've attached an example built on my collection of classes. (Most of which are hacked out of a recent set.) Once the base classes are there (which shouldn't ever need to be changed, really), this gives you a way to map functionality to a hardware MIDI control either based on subclassing: class FooControl extends Control { 1 => cc; fun void set(int value) { <<< "Foo: ", value >>>; } } FooControl foo; Or events: EventControl bar; 2 => bar.cc; fun void listener() { while(true) { bar.changed => now; <<< "Bar: ", bar.changed.value >>>; } } spork ~ listener(); Combined with the dummy code I inserted below that to simulate a knob and then a button we get: localhost: /Users/scott/Documents/ChucK> chuck Control.ck Foo: 0 Bar: 127 Foo: 1 Foo: 2 Foo: 3 Foo: 4 Foo: 5 Foo: 6 Foo: 7 Foo: 8 Foo: 9 -Scott (aka Self Appointed ChucK Algorithms Wonk) Extra Credit: One thing that I've noticed in a couple of places is that sometimes sporking doesn't do what I would expect it to. For instance, if I change line 223 to "spork ~ node.item.set(value);" to make it non-blocking the program doesn't work. I don't see why. // MIDI Event IDs int codes[0]; 144 => codes["NoteOn"]; 128 => codes["NoteOff"]; 176 => codes["ControlChange"]; class MidiMessage { int id; fun int[] data() { return [ 0, 0 ]; } } class NoteMessage extends MidiMessage { int pitch; int velocity; fun int[] data() { return [ pitch, velocity ]; } } class NoteOnMessage extends NoteMessage { codes["NoteOn"] => id; 100 => velocity; } class NoteOffMessage extends NoteMessage { codes["NoteOff"] => id; 0 => velocity; } class ControlChangeMessage extends MidiMessage { codes["ControlChange"] => id; 8 => int control; 127 => int value; fun int [] data() { return [ control, value ]; } } class MidiHandler { // Members MidiIn input; MidiOut output; 0 => int inputDevice; 0 => int outputDevice; // Constructor if(!input.open(inputDevice)) { <<< "Could not open MIDI input device." >>>; me.exit(); } if(!output.open(outputDevice)) { <<< "Could not open MIDI output device." >>>; me.exit(); } fun void send(MidiMessage message) { message.data() @=> int data[]; if(data.cap() == 2) { MidiMsg out; message.id => out.data1; data[0] => out.data2; data[1] => out.data3; output.send(out); } else { <<< "Invalid data() for MidiMessage." >>>; } } fun void run() { // Now handle incoming events. MidiMsg message; while(true) { input => now; while(input.recv(message)) { message.data1 => int code; if(code == codes["NoteOn"]) { spork ~ noteOn(message.data2, message.data3); } else if(code == codes["NoteOff"]) { spork ~ noteOff(message.data2, message.data3); } else if(code == codes["ControlChange"]) { spork ~ controlChange(message.data2, message.data3); } else { <<< "Unhandled MIDI Message: ", message.data1, message.data2, message.data3 >>>; } } } } fun void noteOn(int pitch, int velocity) { <<< "Note On: ", pitch, velocity >>>; } fun void noteOff(int pitch, int velocity) { <<< "Note Off: ", pitch, velocity >>>; } fun void controlChange(int control, int value) { <<< "Control Change: ", control, value >>>; } } class Control { -1 => int cc; ControlDispatcher.register(this); fun void set(int value) { <<< "Control Changed: ", cc, ", ", value >>>; } } class ControlEvent extends Event { int control; int value; } class EventControl extends Control { ControlEvent changed; fun void set(int value) { cc => changed.control; value => changed.value; changed.broadcast(); } } class ControlNode { ControlNode @ next; Control @ item; } class ControlList { static ControlNode @ first; static ControlNode @ last; fun void append(Control control) { if(first == null) { new ControlNode @=> first; first @=> last; control @=> first.item; } else { new ControlNode @=> last.next; last.next @=> last; control @=> last.item; } } } class ControlDispatcher extends MidiHandler { static ControlList @ controls; fun void controlChange(int control, int value) { if(controls == null) { return; } controls.first @=> ControlNode @ node; while(node != null) { if(node.item.cc == control) { node.item.set(value); } node.next @=> node; } } fun static void register(Control control) { if(controls == null) { new ControlList @=> controls; } controls.append(control); } } ControlDispatcher controller; // Two demos here: one with subclassing, one with events: class FooControl extends Control { 1 => cc; fun void set(int value) { <<< "Foo: ", value >>>; } } FooControl foo; // And now with events. EventControl bar; 2 => bar.cc; fun void listener() { while(true) { bar.changed => now; <<< "Bar: ", bar.changed.value >>>; } } spork ~ listener(); // And now let's create some fake hardware controls to test things. fun void fakeKnob() { ControlChangeMessage message; 1 => message.control; for(0 => int i; i < 10; i++) { i => message.value; controller.send(message); 10::ms => now; } } fun void fakeButton() { ControlChangeMessage message; 2 => message.control; 127 => message.value; controller.send(message); } spork ~ fakeKnob(); spork ~ fakeButton(); controller.run();