On 9/19/07, Scott Wheeler
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.
I'll share my thoughts on livecoding practice and so on too, later. I just wanted to say now that bug reports benefit from a bit more exposure then this paragraph got in your mail, hence this rather explicitly topic-changing reply. I don't see why that would affect matters either, this is in reference to the "set" command that also broadcasts a event, right? The one thing I could think of would be a name space issue. Normally sporked shreds have their parent's namespace but this wouldn't be the first time things went a little odd when inside of classes and you certainly have no shortage of those here. Also; if it's namespace that should yield a complaint. Strange. :¬) Cheers, Kas. // 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();
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
Kassen wrote:
[...] I don't see why that would affect matters either, this is in reference to the "set" command that also broadcasts a event, right? [...]
It does so in one of the subclasses (the generic one for event broadcasting). Just explaining a little of the logic there: Every time a control is instantiated it registers itself with the ControlDispatcher, which itself is a specialization of the MIDI handler. The base implementation doesn't do anything interesting when the control is modified, but in event subclass it broadcasts an event. Since all overloads are implicitly polymorphic in ChucK, this Does The Right Thing. In the FooControl it does whatever action is specified in the overloaded version. (This explanation assumes familiarity with OO basics -- if any of it isn't clear I can give more details.)
[...] this wouldn't be the first time things went a little odd when inside of classes and you certainly have no shortage of those here.
Aside from being generally an OOP fan, it certainly makes a lot of the things in there a lot easier. Cheers, -Scott
On 9/20/07, Scott Wheeler
[...] this wouldn't be the first time things went a little odd when
inside of classes and you certainly have no shortage of those here.
Aside from being generally an OOP fan, it certainly makes a lot of the things in there a lot easier.
I wasn't criticising you; I could tell you are a OOP fan and that's cool. I also know (because I too use classes if not always with as much enthusiasm as you do) that with ChucK some things that normally work fine act weird when done inside of classes, just this summer we had some especially obnoxious cases of that. So; I was trying to help pinpoint the source of the issue because as far as I could see after a quick look what you are trying should work just fine. Yours, Kas.
While I haven't thoroughly gone through the code Scott posted, one thing did catch my eye: if(node.item.cc == control) { node.item.set(value); } node.next @=> node; This looks like a nice corner case that, if spork was used, the spork- handling code might not anticipate. Investigating further I was able to come up with a minimal example that crashes chuck 1.2.1.0b on OS X intel: class Someclass { int i; fun void f() { 0 => i; } } new Someclass @=> Someclass @ someclass; spork ~ someclass.f(); // placing me.yield() here prevents the crash new Someclass @=> someclass; me.yield(); // necessary to let the sporked shred execute and cause crash While Scott's code doesn't seem to be causing any crashes, the spottiness of using spork as he described and the crashing caused by the above code seems to indicate that something is not quite right with the spork member function implementation. Scott, maybe sticking a me.yield() after spork ~ node.item.set (value); would make things work a little better. Although, unless you are trying specifically to control shred execution order, I don't actually see the utility of spork in this particular case, as the set () function doesn't allow time to pass. spencer On Sep 20, 2007, at 3:16 AM, Kassen wrote:
On 9/20/07, Scott Wheeler
wrote: [...] this wouldn't be the first time things went a little odd when inside of classes and you certainly have no shortage of those here.
Aside from being generally an OOP fan, it certainly makes a lot of the things in there a lot easier.
I wasn't criticising you; I could tell you are a OOP fan and that's cool. I also know (because I too use classes if not always with as much enthusiasm as you do) that with ChucK some things that normally work fine act weird when done inside of classes, just this summer we had some especially obnoxious cases of that.
So; I was trying to help pinpoint the source of the issue because as far as I could see after a quick look what you are trying should work just fine.
Yours, Kas. _______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
Spencer Salazar wrote:
Scott, maybe sticking a me.yield() after spork ~ node.item.set (value); would make things work a little better.
Yes, that does make it work.
Although, unless you are trying specifically to control shred execution order, I don't actually see the utility of spork in this particular case, as the set () function doesn't allow time to pass.
Well, it doesn't advance time in the base class, but it does in non-toy specializations. In real usage, I mostly use that class to map buttons to fade in/outs or to modulate parameters. (Or another interesting one -- a control smoother, which doesn't allow the control to change more than a certain threshhold over a certain time and then catches up to the end value.) With the current code I wasn't able to control more than one (time-advancing) thing at a time. -Scott
participants (3)
-
Kassen
-
Scott Wheeler
-
Spencer Salazar