0% found this document useful (0 votes)
4 views56 pages

3.c. Transmission protocols and computer music systems

The document discusses various transmission protocols (Serial, OSC, MIDI) and their application in Computer Music Systems like SuperCollider, Python, and JUCE. It emphasizes the importance of interfacing devices for musical and gestural information, detailing electronic platforms such as Arduino, Raspberry Pi, and Bela. Additionally, it provides examples of how to implement serial and MIDI communication in SuperCollider and Python, highlighting the necessary classes and methods for effective data transmission.

Uploaded by

Thomas Fayard
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views56 pages

3.c. Transmission protocols and computer music systems

The document discusses various transmission protocols (Serial, OSC, MIDI) and their application in Computer Music Systems like SuperCollider, Python, and JUCE. It emphasizes the importance of interfacing devices for musical and gestural information, detailing electronic platforms such as Arduino, Raspberry Pi, and Bela. Additionally, it provides examples of how to implement serial and MIDI communication in SuperCollider and Python, highlighting the necessary classes and methods for effective data transmission.

Uploaded by

Thomas Fayard
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 56

Interfacing transmission protocols

with Computer Music Systems


Learning objective

To understand the different solutions that can be adopted for


receiving and sending musical and gestural information on different
Computer Music Systems.
The considered transmission protocols are:
• Serial data
• OSC
• MIDI
The considered Computer Music Systems are:
• SuperCollider
• (Python)
• JUCE

Computer Music: Languages and Systems Fabio Antonacci


Motivations

Computer Music Systems often rely on interfacing different devices.


As an example, a Theremin-like instrument will consist in:
• an analog electronic device – antenna - to sense the position of the
hand;
• a CPU- or microcontroller-based electronic platform that acquires the
analog outputs of the antenna and converts them into digital signals;
• the computer music system that reads the digital signal, maps to
musical parameters and synthesizes the audio signal.
From the viewpoint of the interaction designer, the keypoints are:
• the measurement of the hand position through the antenna;
• the transmission of the digital signals from the electronic platform to
the CMS;
• the mapping of the gesture-related signals into musical parameters

Computer Music: Languages and Systems Fabio Antonacci


Electronic platforms
Arduino

• Almost all of them based on microcontrollers


(ATMega / Intel);

• low cost;
• Equipped with several I/O ports (analog and
• limited power requirements;
digital);
• availability of tons of projects;
• USB connection;
• Programming language: Wiring; ✗
• Supports serial communication; • limited computational power;
• Several models available that suit to different • asynchronous computation;
needs (e.g. Wi-Fi) • limited memory (both program and data
memory);

Computer Music: Languages and Systems Fabio Antonacci


Electronic platforms
Raspberry Pi

• Small form factor computer based on a


System On Chip including:

• many off-the-shelf sensors;
o ARM processor;
• high computational power;
o GPU;
o Up to 4 GB of memory ✗
• Ethernet Controller; • does not natively support some
• Up to 4 USB ports (both 2.0 and 3.0); multimedia formats;
• Operating system: Raspbian; • the cost is not so limited.
• Supports programming in different languages
(Python the most popular).

Computer Music: Languages and Systems Fabio Antonacci


Electronic platforms
Bela

Based on the Pocket Beagle computer, which ✓


features: • can be programmed in different
• Processor: Octavo Systems OSD3358 1GHz ARM® langagues (C/C++, PureData,
Cortex-A8 SuperCollider)
• 512MB DDR3 RAM integrated • computational power;
• Integrated power management • very low latency;
• 2×32-bit 200-MHz programmable real-time units
(PRUs) ✗
Purposely designed as an independent Computer • the cost is not so limited;
Music System • less popular than Arduino and
Raspberry → fewer projects online
Needs and requirements ✘ not explained but possible
A matrix to be filled ✓ discussed

Serial MIDI OSC

✓ ✓ ✓
management Programming Languages

SuperCollider
Computer Music

Python ✘ ✓ ✓

JUCE ✘ ✓ ✓

Processing TO ✘
BE DISCUSSED✘IN THE NEXT LAB

GUI

Arduino
TO✓ BE DISCUSSED
✘ IN✘
Electronic
platforms

Bela THE
✘ NEXT LECTURE
✓ ✓
Serial communication

Serial communication is the process of sending data one bit at a


time over a communication channel or computer bus.
• Examples of common serial buses that can be found in
integrated circuits include RS-232, SPI, I²C, UNI/O, 1-Wire and
PCI Express.
• Serial communication can be used also over long distances to
decrease the cost of cables wrt the parallel communication.
• Available bitrates go from 75 bits/s to 256 kbit/s. Most common
is 9.6 kbit/s
• In Computer Music systems serial communication is popular
as many low-cost electronic platforms primarily support serial
communication interface.

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with SuperCollider
Interfacing with SuperCollider
Serial connection

Serial communication in SuperCollider is enabled by:


• SerialPort, which is a class that creates and manages
serial port objects;
• the use of a Routine for reading from the serial port at a
predefined rate.

Many devices transmit through the serial port in the form of ASCII
characters, i.e. numbers are converted into ASCII characters. A
separator character/string must be inserted in order to separate
different transmission packets. An analysis of the serial stream is
required in this case.
Interfacing with SuperCollider
Serial communication

• SerialPort.devices: queries the system on the available


serial ports;
• ~port = SerialPort.new(port, baudrate: 9600,
databits: 8, stopbit: true, parity, crtscts:
false, xonxoff: false, exclusive: false)
creates a new serial port object. port is the variable
associated to the serial port. Suggestion: do not modify
arguments from databits onward.
• ~port.read: reads data from the serial port as integer
numbers.
• ~port.read.asAscii: converts the integer received into a
char.

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with SuperCollider
Serial Communication

Scenario:
Serial communication in the
Sensor (e.g. Acquisition board form of SuperCollider
photoresistor) (e.g. Arduino) 123a234a567a...

Measurement Separator
converted into a between
stream of different
charactarers measurements

Needs:
1. Convert characters into digits;
2. Detect the separator between different measurements;
3. Convert the collected digits belonging to a single
measurements into a number.

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with SuperCollider
Serial communication

Example:
~port = SerialPort.new("/dev/tty.usbmodem1434201", 9600);
(
~charArray = [ ];
~getValues = Routine.new({
var ascii; isDecDigit verifies if the char is a
{ digit. If so, adds the char into an array
ascii = ~port.read.asAscii;
if(ascii.isDecDigit,{~charArray =
~charArray.add(ascii)});
if(ascii == $a,{
~val1= ~charArray.collect(_.digit).convertDigits;
~charArray = [ ];
~val1.postln(); collect maps the element in the array onto a new array
}); that evaluates for every item the function digit.
}.loop; convertDigits finally converts the items in the array
}).play; into a number.
) Example:
~charArray = [$3,$5,$1,$2].post;
~val1 = ~charArray.collect(_.digit).post;
~val1.convertDigits.post;
Interfacing with SuperCollider
Serial communication

In summary:
• In order to work with serial data, SuperCollider provides the
SerialPort class.
• SerialPort provides low level access to a serial stream, but does not
provide methods for processing it.
• Reading from the SerialPort object ~sp: ~sp.read
• Writing onto the SerialPort object ~sp: ~sp.put(byte), where
byte is an integer or a character. If an integer it is converted into the
corresponding ASCII character.
• Alternative for writing: ~sp.putAll(array), where array is an
array of integer or characters.
• As an example, when the stream consists of characters, in which
measurements are separated by special characters, we need to
define a routine that converts sequences of characters into numbers,
as shown before.
Interfacing with SuperCollider
MIDI communication

In order to enable receiving MIDI in SuperCollider the following


classes can be used:
• MIDIIn: when MIDI events come into SuperCollider, MIDIIn
evaluates simple handler functions.
• MIDIFunc registers one or more functions to respond to an
incoming MIDI message.
• MIDIdef same as before but extends some functionalities
Interfacing with SuperCollider
MIDI communication

Example 1 Key
( MIDI port Channel number
MIDIIn.connect; number
// register functions: Velocity
Registering

~noteOff = { arg src, chan, num, vel;[chan,num,vel / 127].postln;


functions

};
~noteOn = { arg src, chan, num, vel; [chan,num,vel /
127].postln; };
MIDIIn.addFuncTo(\noteOn, ~noteOn);
MIDIIn.addFuncTo(\noteOff, ~noteOff);
)
(
functions
Removing

MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
MIDIIn.removeFuncFrom(\noteOff, ~noteOff);
)

Analogously, functions can be registered for the following MIDI messages:


• Polyphonic aftertouch: \polytouch
• Control change: \control,
• Program change: \program,
• Channel aftertouch: \touch,
• Pitch bend: \bend,
• System exclusive: \sysex,
Interfacing with SuperCollider
MIDI communication

Example 2 (first part, creating the Synth):

MIDIIn.connect;
s.boot;

(
SynthDef("sik-goo", { |out, freq = 440, formfreq = 100, gate =
0.0, bwfreq = 800|
var x; Latch.kr(in:0, trig: 0)
x = Formant.ar( Holds input signal in value
SinOsc.kr(0.02, 0, 10, freq), when triggered by trig > 0 and
formfreq, keep the signal until trig
bwfreq becomes 0.
);
x = EnvGen.kr(Env.adsr, gate, Latch.kr(gate, gate)) * x;
Out.ar(out, x);
}).add;
)

x = Synth("sik-goo");
Interfacing with SuperCollider
MIDI communication

Example 2 (second part, setting the action):


(
~noteOn = {arg src, chan, num, vel;
x.set(\freq, num.midicps / 4.0);
x.set(\gate, vel / 200 );
x.set(\formfreq, vel / 127 * 1000);
};
MIDIIn.addFuncTo(\noteOn, ~noteOn);

~noteOff = { arg src,chan,num,vel;


x.set(\gate, 0.0);
};
MIDIIn.addFuncTo(\noteOff, ~noteOff);

~bend = { arg src,chan,val;


//(val * 0.048828125).postln;
x.set(\bwfreq, val * 0.048828125 );
};
MIDIIn.addFuncTo(\bend, ~bend);
)

//cleanup
MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
MIDIIn.removeFuncFrom(\noteOff, ~noteOff);
MIDIIn.removeFuncFrom(\bend, ~bend);
Interfacing with SuperCollider
MIDI communication

MIDIdef and MIDIFunc were developed to be faster than MIDIIn.


For MIDIdef, the main functions are:
• MIDIdef.new(key, func, msgNum, chan, msgType,
srcID, argTemplate, dispatcher): constructor for a new
MIDIdef. Not used commonly, as the key-specific functions are
normally used.
• MIDIdef.cc(key, func, ccNum, chan, srcID,
argTemplate, dispatcher): create a new MIDIdef that
responds to MIDI control change messages.
• MIDIdef.noteOn(key, func, noteNum, chan, srcID,
argTemplate, dispatcher): create a new MIDIdef to respond to
MIDI note on messages.
• Other functions can be found for the other MIDI messages

The MIDIOut class can be used to send MIDI messages from


SuperCollider. It works analogously to MIDIIn.

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with SuperCollider
OSC communication

• OSC is natively supported in SuperCollider, as it is the transmission


protocol adopted for the communication between server and client.
• The client also accepts receiving and sending OSC messages
from/to other hosts than the server.
• The main classes used for OSC communication are:
– OSCFunc: registers one or more functions as responders to
incoming OSC messages.
– OSCdef: extends the functionality of OSCFunc, of which it is a
subclass.
The use of both OSCFunc and OSCdef requires to work with network
addresses. The class that manages network addresses is NetAddr,
which also manages sending OSC messages. See next examples for
reference.

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with SuperCollider
OSC communication

Example 1: sending an OSC message


b = NetAddr.new("127.0.0.1", 5112); // create the NetAddr
//on the localhost on
// the port 5112
b.sendMsg("/noteOn", ”123"); // send the application the
// message ”noteOn" with the
// parameter ”123”

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with SuperCollider
OSC communication

OSCFunc.new(func, path, srcID, recvPort, argTemplate,


dispatcher): creates a new OSCFunc
• func: the function that responds to the incoming message. When
evaluated it will be passed the arguments
– msg: message as an Array in the form [OSCAddress,
...otherArgs];
– time: the time that the message was sent (plus the latency if
the message was in a bundle);
– addr: NetAddr corresponding to the IP address of the sender,
– recvPort: an Integer corresponding to the port on which the
message was received.
• path: a Symbol indicating the path of the OSC address of this
object.
• srcID: (optional) NetAddr indicating the IP address of the sender. If
set this object will only respond to messages from that source.
• recvPort: an optional Integer indicating the port on which
messages will be received.

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with SuperCollider
OSC communication

Example 2: receiving OSC messages from another application


The other application must send messages to the port SC is listening to.
NetAddr.localAddr // retrieve the current IP and port

n = NetAddr.new("127.0.0.1", 7771); // create the


// NetAddr
// create the OSCFunc
o = OSCFunc({ arg msg, time, addr, recvPort; [msg, time,
addr, recvPort].postln; }, ‘/message received', n);
o.free; // remove the OSCFunc

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with Python
Interfacing with Python
MIDI communication: py-midi

There are a few libraries in Python that enable communication


using the MIDI protocol. We consider here the py-midi (v.2.0.1),
available for Python 3.
• py-midi manages all kind of MIDI messages, both channel and
status;
• the key feature is that the user does not take care of the
correct formatting of the messages, as this aspect is handled
by the library.

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with Python
MIDI communication: py-midi

Create a MIDI interface with the serial port:


from midi import MidiConnector
conn = MidiConnector('/dev/serial0’)

Comment: MidiConnector is blocking, i.e. the processing is


blocked until data is received. In order to create a non-blocking
MIDI interface, a timeout can be specified:
conn = MidiConnector('/dev/serial0', timeout=5)

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with Python
MIDI communication: py-midi

To send a MIDI message, a MIDIMessageType object must be


created. Different types of messages:
• NoteOff(note_number, velocity)
• NoteOn(note_number, velocity)
• PolyphonicAftertouch(note_number, pressure)
• ChannelAftertouch(pressure)
• ControlChange(control_number, value)
• ProgramChange(program_number)
• PitchWheel(lsbyte, msbyte)
• SysEx(manufacturer_id, data1, data2..., dataN)

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with Python
MIDI communication: py-midi

Example: create a NoteOn message

import midi
conn = MidiConnector('/dev/serial0')
no = NoteOn(47,102)
msg = Message(no, channel = 1)
conn.write(msg)

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with Python
MIDI communication: py-midi

Example: reading a MIDI message


conn = MidiConnector('/dev/serial0’)
msg = conn.read() # read on any channel
• The message can be accessed directly typing the name of the
variable
msg → Message(NoteOn(44,100), 2)
i.e. we received a NoteOn message with note number 44, velocity
100 on the channel number 2
• Information can be obtained also for each field in the message
msg.channel → 2
msg.type → NoteOn(44,100)
Note: in the previous code the messages are read for all the channels.
In order to read from a single channel, we can write
msg = conn.read(8) # read on channel 8

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with Python
OSC communuication

Among the libraries available for OSC, we analyze here oscpy.


Setting up an OSC server:

from oscpy.server import OSCThreadServer


Callback function that
from time import sleep answers to incoming
def callback(values): messages
print("got values: {}".format(values))
osc = OSCThreadServer()
sock = osc.listen(address='0.0.0.0', port=8000, default=True)
osc.bind(b'/address', callback)
sleep(1000)
binding messages received
osc.stop()
on /address to the callback

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with Python
OSC communication

Two servers that interact in sending and answering messages:


from oscpy.server import OSCThreadServer
from time import sleep
osc_1 = OSCThreadServer()
osc_1.listen(default=True)
@osc_1.address(b'/ping’)
Callback function that
def ping(*values): answers to incoming
print("ping called") messages
if True in values:
cont.append(True)
else:
osc_1.answer(b'/pong’) Callback function that
osc_2 = OSCThreadServer()
osc_2.listen(default=True) answers to incoming
@osc_2.address(b'/pong’) messages and pong to them
def pong(*values):
print("pong called")
osc_2.answer(b'/ping', [True]) Send messages to the
osc_2.send_message(b'/ping', [], *osc_1.getaddress()) address /ping on the
timeout = time() + 1
while not cont: server osc_1
if time() > timeout:
raise OSError('timeout while waiting for success message.')
Interfacing with JUCE
Interfacing with JUCE
OSC communication

JUCE hosts native libraries for OSC bidirectional communication.


The two classes are OSCSender and OSCReceiver.
In the next few slides we are going to examine a couple of examples, one
for sending and one for receiving OSC messages.
They elaborate on examples discussed during labs, with the aim of
showing how two environments (SuperCollider and Juce) can
communicate.
• Example 1: modify the MIDI velocity processor discussed during the
labs: in the modified version, it receives MIDINoteOn messages and
outputs OSC messages containing the pitch in Hertz. This information
is read from a SuperCollider patch that plays a note, synthesized
through an FM synth, and adds a reverb. The JUCE interface also
sends the desired reverberation parameter.
• Example 2: The parameters of a delay line effect are controlled
through OSC messages sent by SuperCollider.

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with JUCE
OSC communication – Example 1: Interaction scheme

Conceptual scheme of the interaction

OSC method
MIDI note FMSynth/freq
Note frequency FM synthesis
number in Hertz

{
JUCE SuperColllider
{

wet/dry mixing
coefficient
OSC method
FMSynth /reverb
{ Reverb
Interfacing with JUCE
OSC communication – Example 1: JUCE

Tasks of the JUCE plugin:


1. Frequency conversion and sending
• The plugin reads the note number from MIDI messages;
• Converts the note in Hertz;
• Sends the frequency value through OSC.
2. Dry / mix coefficient reading and sending:
• Setup a slider for the dry / mix coefficient;
• Read the value of the slider when it is changed;
• Send the dry/mix coefficient through OSC.
Interfacing with JUCE
OSC communication – Example 1: JUCE

Preliminary operations:
• create an OSCSender object, which takes care of sending messages.
The class OSCSender is included in the juce_osc module (to be added
in Projucer) module
Declaration of the class:
...
private:
OSCSender sender; // Declaration of the
// OSC sender
...
• Setting up the connection in the processor constructor:
...
sender.connect("127.0.0.1", 57120);
...
Note: the first argument is the IP address of the recipient, the second
argument is the port on the recipient side. The port of the sender,
instead, is selected automatically. This impacts on SuperCollider
coding. In order to know the port on which messages have to be sent,
the solution is to use packet sniffers programs, such as Wireshark.
Interfacing with JUCE
OSC communication – Example 1: JUCE

1. Frequency conversion and sending


– The plugin reads the note number from MIDI messages
– Converts the note number in Hertz
– Send the frequency value through OSC.
void Send_osc_messageAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer&
midiMessages)
{
buffer.clear();
MidiBuffer processedMidi;
int time;
MidiMessage m;
for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);)
{
if (m.isNoteOn())
{
freq = (float) MidiMessage::getMidiNoteInHertz
(m.getNoteNumber());
sender.send("/FMSynth/freq", freq);
}
}
}
• The first argument is the name of the OSC method;
• The second argument is the message.
Interfacing with JUCE
OSC communication – Example 1: JUCE

1. Dry / mix coefficient reading and sending:


• Setup a slider for the dry / mix coefficient;
This is accomplished as with an usual slider in JUCE
GUIs.
• Read the value of the slider when it is changed;
• Send the dry/mix coefficient through OSC
a) In the editor class:
void
Send_osc_messageAudioProcessorEditor::sliderValueChanged(Slider
*slider)
{
processor.sendReverb(reverb.getValue());
}
Interfacing with JUCE
OSC communication – Example 1: JUCE

b) In the processor class:


void Send_osc_messageAudioProcessor::sendReverb(float
reverbValue)
{
sender.send("/FMSynth/reverb",(1.0f-reverbValue));
}
Interfacing with JUCE
OSC communication – Example 1: SuperCollider

Synth definition:
(
SynthDef(\fmSynth,
{
arg freq = 440, modIndex = 3, modPartial = 1,
carPartial = 1, level = 0.05, outBus = 0,
effectBus, direct = 0.33;
Frequency var modSig, outSig;
modulation modSig = SinOsc.ar(freq*modPartial,
0,
signal
freq*modIndex);
Output signal outSig = SinOsc.ar(freq * carPartial + modSig,
0,
level);
Effect and
direct sends {}
Out.ar(effectBus,outSig * (1 - direct));
Out.ar(outBus,(outSig * direct).dup);

).add
) dup: Makes a
stereo signal from a
monophonic one
Interfacing with JUCE
OSC communication – Example 1: SuperCollider

Reverb synth definition:


(
SynthDef(\reverb,
{
Reading signal from arg inBus, outBus;
the effect bus var dry, wet;
dry = In.ar (inBus);
Delay line wet = DelayN.ar(dry,0.048,0.048);
wet = Mix.arFill(7,{
Multiple comb CombL.ar(wet,0.1,LFNoise1.kr(Rand(0,0.1),
filtering 0.04,0.05),1 )});
Multiple allpass 4.do({wet = AllpassC.ar(wet,0.05,
filtering [Rand(0,0.05),Rand(0,0.05)],15) });
Out.ar(outBus, wet);
}
).add;
)
Multichannel
expansion
Interfacing with JUCE
OSC communication – Example 1: SuperCollider

Synth instantiation:
(
var bus = Bus.audio(s);
x =
Synth(\fmSynth,[\freq,440,\modIndex,2,\modPartia
l,2.4,\outBus,0,\level,0.1, \effectBus, bus ]);
y =
Synth.after(x,\reverb,[\inBus,bus,\outBus,0]);
)
Interfacing with JUCE
OSC communication – Example 1: SuperCollider

Reading incoming OSC messages and setting up


methods for answering
Network Function to be called for
address n = NetAddr("127.0.0.1"); incoming messages
object

Responder function OSCFunc.newMatching({|msg, time, addr,


for /FMSynth/freq recvPort| x.set(\freq,msg[1]).postln},
method '/FMSynth/freq’, n);

Name of the OSC method

Responder function OSCFunc.newMatching({|msg, time, addr,


for recvPort|x.set(\direct,msg[1]).postln},
/FMSynth/reverb '/FMSynth/reverb', n);
method
Interfacing with JUCE
OSC communication – Example 1: Notes

For debugging it is important to visualize the OSC packets.


1. If SuperCollider does not receive incoming OSC packets, use
WireShark. It is a packet sniffer program that visualizes
inbound and outbound packets on the network interfaces. In
our case, we need to use the localhost;
2. If SuperCollider does receive incoming OSC packets, but
does not work, the OSCFunc.trace method can be used.
OSCFunc.trace(bool: true, hideStatusMsg:
false)
– The first parameter enables (true) or disables (false) the
dump of the OSC messages
– The second parameter indicates whether server status
messages are excluded from the dump or not.

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with JUCE
OSC communication: Example 2 – interaction scheme

Dry and wet JUCE


coefficients

{
DelayLine
SuperCollider
{
Time depth

Computer Music: Languages and Systems Fabio Antonacci


Interfacing with JUCE
OSC communication – Example 2: intereaction scheme

The SuperCollider interface consists of three knobs, whose


position encodes the dry/wet coefficient and the depth of a delay
line. When the position of the knobs is modified, an OSC signal
will be sent through OSC.
JUCE will read OSC messages and modify consequently the
sliders in its own GUI and the delay line parameters

JUCE interface

SuperCollider interface
Interfacing with JUCE
OSC communication – Example 2: SuperCollider interface

Variable definition and communication setup:


(

var window,
knobDry, knobWet, knobTime,
textDry, textWet, textTime,
senderDry,senderWet,senderTime; Opens the UDP port
57109 on which SC will
send data
thisProcess.openUDPPort(57109);
b = NetAddr.new("127.0.0.1",57109);

Network object to be
used to send data
Interfacing with JUCE
OSC communication – Example 2: SuperCollider interface

Window and knobs creation:

window = Window.new("Delay Line Controller",


Rect.new(300,300, 450, 150)) ;
window.background = Color.white ;
knobDry = Knob.new(window, Rect(50, 25, 50, 50)) ;
knobDry.value = 0.0 ;
knobWet = Knob.new(window, Rect(200, 25, 50, 50)) ;
knobWet.value = 0.0 ;
knobTime = Knob.new(window, Rect(350, 25, 50, 50)) ;
knobTime.value = 0.0 ;
Interfacing with JUCE
OSC communication – Example 2: SuperCollider interface

Creation of labels for the knobs:

textDry = StaticText.new(window, Rect(50, 90, 50, 30));


textDry.string = "Dry";
textDry.align = \center;
textWet = StaticText.new(window, Rect(200, 90, 50, 30));
textWet.string = "Wet";
textWet.align = \center;
textTime = StaticText.new(window, Rect(350, 90, 50, 30));
textTime.string = "Time";
textTime.align = \center;
Interfacing with JUCE
OSC communication – Example 2: SuperCollider interface

Definition of the callback functions for the knobs:


knobDry.action_({ arg me;
var val; Reading the position of the knob
val = me.value; Sending OSC message
b.sendMsg("/reverb/dry", val);
});
knobWet.action_({ arg me;
var val; Reading the position of the knob
val = me.value; Sending OSC message
b.sendMsg("/reverb/wet", val);
});
knobTime.action_({ arg me;
var val; Reading the position of the knob
val = me.value;
b.sendMsg("/reverb/time", val);
});
window.front ; Sending OSC message
)
Interfacing with JUCE
OSC communication – Example 2: setup in JUCE

In order to receive messages in JUCE, we need to use the OSCReceiver


class, included in the juce_osc module.
In particular, the editor class inherits from OSCReceiver and subclasses.
Declaration of the editor class:
class DelayLineAudioProcessorEditor
: public AudioProcessorEditor,
Inherits from the class
private Slider::Listener, OSCReceiver. This allows
private OSCReceiver, us to use public methods
private defined in OSCReceiver.
OSCReceiver::ListenerWithOSCAddress<OSCReceiver::MessageLoopCallback>

Inherits from the class ListenerWithOSCAddress contained in OSCReceiver. This class is a


template class, i.e. there are members of this class whose type is not defined at the time
when the class is declared. The type of this member is specified within angled brackets.
In this case, using MessageLoopCallback , we specify that the reaction to messages is not
realtime critical and whose workload is relevant. Alternative: RealtimeCalllback.
Interfacing with JUCE
OSC communication – Example 2: setup in JUCE

Declaration of the editor class (private members):


private:
// This reference is provided as a quick way for your editor to
// access the processor object that created it.
DelayLineAudioProcessor& processor;
Slider wetSlider;
Label wetLabel;
Slider drySlider;
Label dryLabel;
Slider timeSlider;
Label timeLabel;
DatagramSocket ds;
void sliderValueChanged(Slider* slider) override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DelayLineAudioProcessorEditor)

void showConnectionErrorMessage (const String& messageText);


void oscMessageReceived(const OSCMessage& message) override;

virtual function present in ListenerWithOSCAddress that


needs to be implemented here
Interfacing with JUCE
OSC communication – Example 2: setup of the connection

In the constructor of the editor class, we need to


a) create a socket and connect to it;
b) initialize the listeners to all the OSC messages that need to
be processed. Socket creation and
connection. The integer
is the port number we
ds.bindToPort(57109,"127.0.0.1"); are listening to
if (! connectToSocket(ds))
showConnectionErrorMessage ("Error");

addListener(this, "/reverb/dry"); Listeners to the OSC


addListener(this, "/reverb/wet"); methods
addListener(this, "/reverb/time");
Note: for the connection to the host, an alternative is to use the
connect method: bool connect (int portNumber)
Interfacing with JUCE
OSC communication – Example 2: setup of the connection

OSC messages dispatching:


void DelayLineAudioProcessorEditor::oscMessageReceived (const OSCMessage& message)
{
if(message.getAddressPattern() == "/reverb/dry") Set the value of the slider and set the
{ parameters of the delay line
drySlider.setValue (jlimit (0.0f, 1.0f, message[0].getFloat32()));

}
processor.set_dry(drySlider.getValue());
}
if (message.getAddressPattern() == "/reverb/wet") Set the value of the slider and set
{
the parameters of the delay line
wetSlider.setValue (jlimit (0.0f, 1.0f, message[0].getFloat32()));

}
processor.set_wet(wetSlider.getValue());
}
if (message.getAddressPattern() == "/reverb/time")
{ Set the value of the slider and set
the parameters of the delay line
timeSlider.setValue (jlimit(500.0f,49500.0f,
message[0].getFloat32()*49500.0f+500));

}
processor.set_ds(timeSlider.getValue());
}
}
Note: knobs in SuperCollider return values from 0 to 1. When needed, we have
to interpolate on the desired range, as for the listener of the /reverb/time
messages.
Conclusions

• SuperCollider and JUCE provide full support for serial


communication (only SuperCollider has been analyzed), MIDI
and OSC.
• In SuperCollider the key task is to define callback functions
that are executed when a MIDI / OSC message is received. As
for the serial messages, they are managed through routines.
• In JUCE MIDI messages are natively supported, as in the
processBlock method the MIDI buffer is one of the arguments.
• OSC messages are managed through the use of senders and
receivers.
References

• SuperCollider guide
• Py-midi: https://pypi.org/project/py-midi/
• oscpy: https://pypi.org/project/oscpy/#description

Computer Music: Languages and Systems Fabio Antonacci

You might also like