# Using Arrays in SuperCollider

## Simple Arrays

In SC, arrays are collections of objects of any kind. They can be defined and accessed using brackets:

// define simple arrays:
a = [0,1,2,3];
b = [0,1,2,"last_value"];

// access indices:
a[3];


## Dynamic Creation

The array class offers numerous methods for creating arrays, including fill():

c = Array.fill(4,{arg i; 10/(i+1) });


## Arrays of Buses

Especially in multichannel projects and larger mixing setups, arrays of buses can be helpful. Make sure to boot the server to actually use (scope) the buses:

// an array of 16 buses, each with 4 channels:
~busArray = Array.fill(16,{Bus.control(s, 4)})

// scope the second bus in the array:
~busArray[1].scope

// set the third bus of the second bus in the array:
~busArray[1].setAt(2,0.5);


## Array of Nodes/UGens

The same array approach can be used to generate multiple nodes, for example sine waves at different frequencies and amplitudes:

// an array of 16 sine oscillators:
~sineArray = Array.fill(16,{arg i;{SinOsc.ar(200*i)}.play})


## Array of Synths

The previous example can also be used with SynthDefs, which is a good starting point for additive synthesis:

// a simple synthdef
(
SynthDef(\sine,
{|f = 100, a = 1|

Out.ar(0, a * SinOsc.ar(f));

}).send(s);
)

~busArray = Array.fill(16,{arg i;Synth.new(\sine,[f:200*(i+1),a:0.2])})


Warning

The second argument of fill has to be a function in curly brackets. If not, the array will contain multiple pointers to the same object (try)!

# Wavetable Oscillator with Phase Reset

The Faust oscillators.lib comes with many different implementations of oscillators for various waveforms. At some point one might still need a behavior not included and lower level approaches are necessary.

This example shows how to use a phasor to read a wavetable with a sine waveform. This implementation has an additional trigger input for resetting the phase of the oscillator on each positive zero crossing. This can come handy in various applications, especially for phase-sensitive transients, as for example in kick drums.

The example is derived from Barkati et. al (2013) and part of the repository:

import("stdfaust.lib");

// some basic stuff
sr = SR;
twopi = 2.0*ma.PI;

// define the waveform in table
ts =  1<<16; // size = 65536 samples (max of unsigned short)
time = (+(1) ~ _ ) , 1 : - ;
sinewave =  ((float(time) / float(ts)) * twopi) : sin;

phase = os.hs_phasor(ts,freq,trig);

sin_osc( freq) = rdtable(ts ,sinewave , int(phase)) ;

// generate a one sample impulse from the gate
trig =  pm.impulseExcitation(reset);

reset = button ("reset");
freq = hslider("freq", 100, 0, 16000, 0.00001);

// offset = hslider("offset", 0, 0, 1, 0.00001);

process = sin_osc(freq);


# Sending OSC from SuperCollider

For sending OSC from SuperCollider, a NetAddr object needs to be generated. It needs an IP address and a port:

~out_address  = NetAddr("127.0.0.1", 6666);


## Sending Values Once

This first example sends an OSC message once when the following line is evaluated. The previously created NetAddr object can be used to send OSC messages with its sendMsg method:

~out_address.sendMsg('/test/message', 1);


## Sending Values Continuously

Based on the previous example, a routine can be created which continuously reads values from control rate buses to send their instantaneous value via OSC. The osc_routine runs an infinite loop with a short wait interval to limit the send rate and the CPU load:

  ~cBus = Bus.control(s,1);

~osc_routine = Routine({

inf.do({

var value      = ~cBus.getSynchronous(~nVbap);

// send value

// wait
0.05.wait;

});
});


Once created, the routine can be started and stopped with the methods play() and stop(). While running, bus values can be changed to test the functionality:

~osc_routine.play();

~cBus.set(300);

~cBus.set(700);

~osc_routine.stop();


## Exercise

Exercise

Run the PD patch osc-receive.pd to receive values from SuperCollider via OSC and control the pitch.

# Additive & Spectral: IFFT Synthesis

The calculation of single sinusoidal components in the time domain can be very inefficient for a large number of partials. IFFT synthesis can be used to compose spectra in the frequency domain.

Main lobe kernel for $\varphi = 0$

Main lobe kernel for $\varphi = \pi/2$

Main lobe kernel for $\varphi = \pi/4$

Main lobe kernel for $\varphi =c3 \pi/4$

# Receiving OSC in SuperCollider

## OSCFunc

By default, a running instance of sclang listens to incoming OSC messsages on the port 57120. For listening to a specific OSC message, an OSC function can be defined with a specific path. SC will then evaluate the defined function when OSC messages are received at the default port with the matching path:

~osc_receive = OSCFunc(

{ arg msg, time, addr, recvPort;

post('Revceived message to path: ');
msg[0].postln;

post('With value: ');
msg[1].postln;

}, '/test/message');


## OSCdef

OSCdef is slightly more flexible and allows to change definitions on the fly, without deleting nodes:

OSCdef(\tester,

post('Revceived message to path: ');
msg[0].postln;

post('With value: ');
msg[1].postln;

},'/test/another', n);


### Exercises

Exercise I

Use a SuperCollider OSC receiver with the first PD example on sending OSC for sending OSC to change the value of control rate bus and monitor the bus with a scope. The section on buses is helpful for this. Keep in mind to set the correct port (57120) and path in the PD patch.

Exercise II

Use a SuperCollider OSC receiver with the PD example for controlling the subtractive synth in the previous example. This can be done with control rate buses or by a direct set() to the synth nodes.

# Managing Jack Connections

When JackTrip clients connect corresponding jack clients are created on the server side that may have a name defined by the connecting client. In the SPRAWL system every client connects with a remote name that starts with AP_ followed by the user's name. Those jack clients must be connected to the right inputs and outputs of the sprawl system. There are several solution to do this automatically. With aj-snapshot you can create a snapshot of all current jack connections and reconnect that state later. You can even running aj-snapshot as a daemon to constantly watch that all connections of a snapshot are set.

In the sprawl system we don't know the full name of connecting jacktrip clients. With jack-matchmaker we are able to write pattern files that use regular expressions to connect jack clients:

#
# Direct Input

SPRAWL_Server:in_1
SPRAWL_Server:in_2
SPRAWL_Server:in_3
SPRAWL_Server:in_4


Those regexes in slashes are conforming python's regex syntax <https://docs.python.org/3/library/re.html>'_. Jack-Matchmaker can show you all current connections:

System Message: WARNING/2 (<string>, line 30); backlink

Inline interpreted text or phrase reference start-string without end-string.

\$ jack-matchmaker -c
SPRAWL_Server:in_1

SPRAWL_Server:out_1
AP_Nils_1:send_1

SPRAWL_Server:out_2
AP_Nils_1:send_2

SPRAWL_Server:out_33
AP_Nils_1:send_1

SPRAWL_Server:out_34
AP_Nils_1:send_2
`

As you see I'm sending one audio channel to the server that connects to the first input of the SPRAWL_Server SuperCollider application. The next two connections are the direct outputs to my receiving channels. The last two connections are the binaural rendered spatialised mix.

## Jack-Matchmaker user service

Right now the jack-matchmaker user service loads the pattern file located in /home/student/SPRAWL/matchmaker/sprawl_server_stereo.pattern. This might be changed in the future with a template instantiated service.

More advanced physical models can be designed, based on the principles explained in the previous sections.

## Resonant Bodies & Coupling

The simple lowpass filter in the example can be replaced by more sophisticated models. For instruments with multiple strings, coupling between strings can be implemented.

Model of a wind instrument with several waveguides, connected with scattering junctions (de Bruin, 1995):

# FM Synthesis: DX7

FM synthesis was not only an outstanding method for experimental music but landed a major commercial success. Although there are many more popular and valuable synthesizers from the 80s, no other device shaped the sound of pop music in that era like the DX7 did. It was not the first ever, but the first affordable FM-capable synth and can generate a wide variety of sounds -- bass, leads, pads, strings, ... -- with extensive (but complicated) editing opportunities. It was also the breakthrough of digital sound synthesis, using the full potential with MIDI.

Fig.1

Yamaha DX7.

## Specs

• released in 1983

• 16 Voices Polyphony

• 6 sine wave 'operators' per voice

• velocity sensitive

• aftertouch

• LFO

• MIDI

## The DX7 in 80s Pop

Tina Turner - What's Love Got To Do With It

• 1984

• blues harp preset

• starting 2:00

https://youtu.be/oGpFcHTxjZs

Laura Branigan - Self Control

• 1984

• the bells

https://youtu.be/WqiCQA8ROXU

Harold Faltenmeyer - Axel F

• 1986

• marimbas

• starting 1:40

https://youtu.be/V4kWpi2HnPU

Kenny Loggins - Danger Zone

• 1986

• FM bass

https://youtu.be/siwpn14IE7E

A Comprehensive List

Find a comprenesive list of famous examples, here:

http://bobbyblues.recup.ch/yamaha_dx7/dx7_examples.html

## Programming the DX7

The DX7 can be fully programmed using membrane buttons. Alternatively, Sysex messages can be used to work with external programmers, like a laptop, over MIDI. For users new to FM synthesis, it may be confusing not to find any filters. Timbre is solely controlled using the FM parameters, such as operator freuqncy ratios and modulation indices.

### Algorithms

The configuration of the six operators, respectively how they are connected, is called algorithm in the Yamaha terminology. In contrast do some of its successors, the DX7 does not allow the free editing of the operator connections but provides a set of 32 pre-defined algorithms, shown in [Fig.2].

Fig.2

Yamaha DX7 manual: algorithm selection.

### Envelopes

For generating sounds with evolving timbres, each operator's amplitude can be modulated with an individual ADHSR envelope, shown in [Fig.3]. Depending on the algorithm, this directly influences the modulation index and thus the overtone structure.

Fig.3

Yamaha DX7 manual: envelope editing.

### Velocity

The level of each operator, and therefor modulation indices, can be programmed to depend on velocity. This allows the timbre to depend on the velocity, as in most physical instruments, which is crucial for expressive performances.

# FM Synthesis: Pure Data Example

The following Pure Data example shows a 2-operator FM synthesis with two temporal envelopes. This allows the generation of sounds with a dynamic spectrum, for example with a sharp attack and a decrease during the decay, as it is found in many sounds of musical instruments. The patch is derived from the example given by John Chowning in his early FM synthesis publication:

Fig.1

Flow chart for dynamic FM with two operators (Chowning, 1973).

The patch fm_example_adsr.pd can be downloaded from the repository. For the temporal envelopes, the patch relies on the abstraction adsr.pd, which needs to be saved to the same directory as the main patch. This ADSR object is a direct copy of the one used in the examples of the PD help browser.

Fig.2

PD FM Patch.