Wednesday, August 07, 2019

Set up ChucK architecture

More code today. This time I’m setting up the core architecture of my ChucK layer. This will provide the necessary flexibility to allow me to receive OSC messages and use the data to change parameters of my ChucK instruments using the built-in event system.

First my directory hierarchy:

There’s a few interesting things going on here. Starting with initialize.ck which is the ‘Main’ file for our ChucK project. Here it is:

 // initialize.ck
 
 Machine.add(me.dir() + "/events/hrmEvent.ck");
 Machine.add(me.dir() + "/events/eventBus.ck");
 
 Machine.add(me.dir() + "/BPM.ck");
 Machine.add(me.dir() + "/score.ck");
 Machine.add(me.dir() + "/osc.ck");

It’s pretty simple - it just loads all the other required files. Note that hrmEvent.ck has to load before eventBus.ck to satisfy some dependency requirements. Let’s take a look at eventBus.ck:

//eventBus.ck

public class eventBus{
    static HrmEvent @ hrmEvent;
}
new HrmEvent @=> eventBus.hrmEvent;

This will get filled with all the events needed to control the app but at the moment there is just one event - the hrm (heart rate monitor) event. Created like this it allows us to broadcast the event across the entire app so any of the other classes/shreds can register to receive notifications when this event fires.

And HrmEvent looks like this:

//hrmEvent.ck

public class HrmEvent extends Event
{
    float val;
}

It simply takes a single float value (though of course it could take many more).

Now, we want to fire this event every time we get an incoming OSC message from (in this case) the heart rate monitor. This gets set up in osc.ck:

//osc.ck
// create our OSC receiver
OscRecv oscin;

3001 => oscin.port;

// start listening (launch thread)
oscin.listen();

// ..or listen to all messages
//oscin.listenAll();

// create an address in the receiver 
// and store it in a new variable.
oscin.event("/hello,f") @=> OscEvent HelloEvent; 

while ( true ){
    
    HelloEvent => now;
    
    while ( HelloEvent.nextMsg() != 0 ){
        
        //chuck the value from the osc message into the hrmEvent
        HelloEvent.getFloat() => eventBus.hrmEvent.val;
        
        //this event will be broadcast to all shreds that are listening
        eventBus.hrmEvent.broadcast();
    }
    
}

So the value from the incoming OSC message will be broadcast using the static hrmEvent which can be listened to by any shred that is interested. In this way we can receive a single value and have it affect multiple properties of multiple instruments, effects etc.