3.c. Transmission protocols and computer music systems
3.c. Transmission protocols and computer music systems
✓ ✓ ✓
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
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
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.
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
Example 1 Key
( MIDI port Channel number
MIDIIn.connect; number
// register functions: Velocity
Registering
};
~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);
)
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
//cleanup
MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
MIDIIn.removeFuncFrom(\noteOff, ~noteOff);
MIDIIn.removeFuncFrom(\bend, ~bend);
Interfacing with SuperCollider
MIDI communication
import midi
conn = MidiConnector('/dev/serial0')
no = NoteOn(47,102)
msg = Message(no, channel = 1)
conn.write(msg)
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
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
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
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
{
DelayLine
SuperCollider
{
Time depth
JUCE interface
SuperCollider interface
Interfacing with JUCE
OSC communication – Example 2: SuperCollider interface
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
}
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 guide
• Py-midi: https://pypi.org/project/py-midi/
• oscpy: https://pypi.org/project/oscpy/#description