In this lab, you'll learn how to control the parameters of a synth running in ChucK using sensors connected to a microntroller (Teensy).
Download this lab's code here.
void setup() {
Serial.begin(9600); // initializing serial port
}
void loop() {
int sensorValue = analogRead(A0); // retrieving sensor value on Analog pin 0
Serial.println(sensorValue);
delay(30); // wait for 30ms
}
Bounce.h
must be included in your program:#include <Bounce.h>
usbMIDI.sendControlChange()
can be used to send MIDI control events (CC). This function takes three arguments: the MIDI CC number, the value to send, and the MIDI channel number (0-15). The first two values should be 7 bits integers (0-127), per the MIDI standard.sensorValue
must be scaled down to fit within this range:int midiValue = sensorValue*127/1024;
#include <Bounce.h>
void setup() {
}
void loop() {
int sensorValue = analogRead(A0); // retrieving sensor value on Analog pin 0
int midiCC = 0;
int midiValue = sensorValue*127/1024; // value between 0-127
int midiChannel = 0; // doesn't really matter
usbMIDI.sendControlChange(midiCC,midiValue,midiChannel); // sending on CC 0
delay(30); // wait for 30ms
}
midiChannel
doesn't really matter as it will be ignored by ChucK.Tools/USB Type/MIDI
.chuck --probe
// synth algorithm
SawOsc saw => dac;
// synth default params
50 => saw.freq;
// number of the device to open (see: chuck --probe)
1 => int device;
// get command line
if( me.args() ) me.arg(0) => Std.atoi => device;
// the midi event
MidiIn min;
// the message for retrieving data
MidiMsg msg;
// open the device
if( !min.open( device ) ) me.exit();
// print out device that was opened
<<< "MIDI device:", min.num(), " -> ", min.name() >>>;
// infinite time-loop
while( true ){
// wait on the event 'min'
min => now;
// get the message(s)
while( min.recv(msg) ){
if(msg.data2 == 0){ // 0 is the MIDI CC number
// formating sawtooth freq
msg.data3*10 + 50 => saw.freq;
}
}
}
int device
variable. Note that it might be different on your computer, make sure int device
matches the ID of your Teensy when executing chuck --probe
. When the received message is sent on MIDI CC 0 (see msg.data2 == 0
), the value of the message (msg.data3
, which is a 7 bits integer) is formatted to have the desired range to control the frequency of the sawtooth oscillator.vec3
:// synth algorithm
SawOsc saw => dac;
// oscillator base frequency
50 => float baseFreq;
// the interpolator
vec3 i;
// the interpolation rate
2::ms => dur irate;
// set initial .value, .goal, .slew
i.set( baseFreq, baseFreq, .05 * (second/irate) );
// function to drive interpolator(s) concurrently
fun void interpolate( dur delta )
{
while( true ){
// interpolate oscillator frequency
i.interp( delta ) => saw.freq;
// advance time by rate
delta => now;
}
}
// spork interpolate function
spork ~ interpolate( irate );
// number of the device to open (see: chuck --probe)
2 => int device;
// get command line
if( me.args() ) me.arg(0) => Std.atoi => device;
// the midi event
MidiIn min;
// the message for retrieving data
MidiMsg msg;
// open the device
if( !min.open( device ) ) me.exit();
// print out device that was opened
<<< "MIDI device:", min.num(), " -> ", min.name() >>>;
// infinite time-loop
while( true ){
// wait on the event 'min'
min => now;
// get the message(s)
while( min.recv(msg) ){
if(msg.data2 == 0){ // 0 is the MIDI CC number
// formating sawtooth freq
msg.data3*10 + baseFreq => i.update;
}
}
}
SawOsc saw => LPF lowpass => dac;
50 => saw.freq;
100 => lowpass.freq;
7 => lowpass.Q;
Gain
unit generator to your synth algorithm and change its value between 0 and 1:SawOsc saw => LPF lowpass => Gain g => dac;
1 => g.gain; // on
0 => g.gain; // off