Musical Patterns on SC Server
The SC Server is highly streamlined, small and functional piece of software. It does not have the whole of the SuperCollider language to do timing, data flow, etc., but it does have unit generators that can do much of the same.
Stepper and Select
The stepper is a pulse counter that outputs a signal.
A scale of frequencies from 500 to 1600 in steps of 100 (as it is multiplied by 100)
{SinOsc.ar( Stepper.kr(Impulse.kr(10), 0, 4, 16, 1) * 100, 0, 0.2)}.play;
And here the steps are -3 so there are more interesting step sequences
{SinOsc.ar(Stepper.kr(Impulse.kr(6), 0, 5, 15, -3).poll(6, "stepper") * 80, 0, 0.2)}.play;
We poll the Stepper to see the output
And here we use Lag (generating a line from the current value to the next in specified time) for the frequency
{SinOsc.ar(Lag.kr(Stepper.kr(Impulse.kr(6), 0, 5, 25, -4) * 90, 6.reciprocal), 0, 0.2)}.play;
// perhaps more understandable like this:
(
{
SinOsc.ar( // the sine
Lag.kr( // our lag
Stepper.kr(Impulse.kr(6), 0, 5, 25, -4) * 90, // the stepper
6.reciprocal),// the time of the lag
0, // phase of the sine
0.2) // amplitude of the sine
}.play;
)
NOTE: the lag time is the reciprocal of the Impulse frequency, i.e. the impulse happens 6 times per second, i.e. every 0.16666666666667 seconds. If you check the reciprocal of 6, you get that number. In this case it doesn't matter whether we use 0.16666666666667 or 6.reciprocal, but if Impulse frequency is in a variable, it could be useful, as in:
f = {arg rate;
{SinOsc.ar(Lag.kr(Stepper.kr(Impulse.kr(rate), 0, 5, 25, -4) * 90, rate.reciprocal), 0, 0.2)}.play;
}
f.(6)
f.(12)
f.(24)
Select
Blah
(
{
var scale, cycle;
//scale = Array.fill(12,{ arg i; 60 + i }).midicps; // we fill an array with a scale
scale = [60, 61, 63, 64, 65, 67, 68, 69, 70].midicps; // we fill an array with a scale
cycle = scale.size / 2;
SinOsc.ar(
Select.kr(
LFSaw.kr(0.4, 1, cycle, cycle),
scale
)
);
}.play;
)
Select and Stepper together
Here we use the Stepper to do what LFSaw did above, it is just stepping through the pitchArray and not generating the pitches like in the Stepper examples above.
(
var pitchArray; //Declare a variable to hold the array
//load the array with midi pitches
pitchArray = [60, 62, 64, 65, 67, 69, 71, 72].midicps;
{
SinOsc.ar(
Select.kr(
Stepper.kr(Impulse.kr(8), max: pitchArray.size-1), // try with Dust
pitchArray),
mul: 0.5)
}.play
)
PulseCount and PulseDivider
We could also use PulseCount to get at the items of the array
(
{
var scale, cycle;
//scale = Array.fill(12,{ arg i; 60 + i }).midicps; // we fill an array with a scale
scale = [60, 61, 63, 64, 65, 67, 68, 70].midicps; // we fill an array with a scale
cycle = scale.size / 2;
SinOsc.ar(
Select.kr(
PulseCount.ar(Impulse.ar(scale.size), Impulse.ar(1)), // we go through the scale in 1 sec
scale
)
);
}.play;
)
PulseDivider is also an interesting UGen, it outputs an impulse when it has received a certain numbers of impulses
Here we use it to create a drummer in one synthdefinition. (quite primitive, and just for fun, but look at the CPU : )
(
SynthDef(\drummer, { arg out=0, tempo=4;
var snare, base, hihat;
tempo = Impulse.ar(tempo); // for a drunk drummer replace Impulse with Dust !!!
snare = WhiteNoise.ar(Decay2.ar(PulseDivider.ar(tempo, 4, 2), 0.005, 0.5));
base = SinOsc.ar(Line.ar(120,60, 1), 0, Decay2.ar(PulseDivider.ar(tempo, 4, 0), 0.005, 0.5));
hihat = HPF.ar(WhiteNoise.ar(1), 10000) * Decay2.ar(tempo, 0.005, 0.5);
Out.ar(out,(snare + base + hihat) * 0.4!2)
}).add;
)
a = Synth(\drummer);
a.set(\tempo, 6);
a.set(\tempo, 18);
a.set(\tempo, 180); // check the CPU! no increase.
Demand UGens
In Tutorial 2 we saw how we could use Patterns to control the server. Patterns are language side streams used to control the server. The Demand UGens are server side and don't need the SC language. So you could use this from languages like Python, Java, etc.
The Demand UGens follow the logic of the Pattern classes of the SCLang - We will look further at Patterns in the next tutorial.
(
{
var freq, trig, reset, seq1;
trig = Impulse.kr(10);
seq1 = SinOsc.ar(2, mul: 200, add: 700);
freq = Demand.kr(trig, 0, seq1);
SinOsc.ar(freq + [0,0.7]).cubed.cubed * 0.1;
}.play;
)
Same as above, but here we Demand more frequently and the sine is slower and we poll the freq
(
{
var freq, trig, reset, seq1, trigrate;
trigrate = 20;
trig = Impulse.kr(trigrate);
seq1 = SinOsc.ar(1, mul: 200, add: 700).poll(trigrate.reciprocal, "freq");
freq = Demand.kr(trig, 0, seq1);
SinOsc.ar(freq + [0,0.7]).cubed.cubed * 0.1;
}.play;
)
Using LFSaw instead of a SinOsc
(
{
var freq, trig, reset, seq1, seq2;
trig = Impulse.kr(10);
seq1 = LFSaw.ar(1, mul: 200, add: 700);
freq = Demand.kr(trig, 0, seq1);
SinOsc.ar(freq + [0,0.7]).cubed.cubed * 0.1;
}.play;
)
Using LFTri and now we use the mouse to control the mul and add of the Freq osc.
(
{
var freq, trig, reset, seq1, seq2;
trig = Impulse.kr(10);
seq1 = LFTri.ar(1, mul: MouseX.kr(200,1000), add: MouseY.kr(200,1000)).poll(10.reciprocal, "freq");
freq = Demand.kr(trig, 0, seq1);
SinOsc.ar(freq + [0,0.7]).cubed.cubed * 0.1;
}.play;
)
There are useful Ugens like Dseq and Drand (compare to Pseq and Prand)
(
{
var freq, trig, reset, seq1, seq2;
trig = Impulse.kr(10);
seq1 = Drand([72, 75, 79, 82]-12, inf).midicps;
seq2 = Dseq([72, 75, 79, Drand([82,84,86])], inf).midicps;
freq = Demand.kr(trig, 0, [seq1, seq2]);
SinOsc.ar(freq + [0,0.7]).cubed.cubed * 0.1;
}.play;
)
Dseries
(
{
var a, freq, trig;
a = Dseries(0, 1.4, 20); // we build a series of values
trig = Impulse.kr(MouseX.kr(1, 40, 1));
freq = Demand.kr(trig, Impulse.kr(0.5), a) * 30 + 340;
SinOsc.ar(freq) * 0.1
}.play;
)
Dgeom
(
{
var a, freq, trig;
a = Dgeom(1, 1.4, 20); // we build a series of values
trig = Impulse.kr(MouseX.kr(1, 40, 1));
freq = Demand.kr(trig, Impulse.kr(0.5), a) * 30 + 340;
SinOsc.ar(freq) * 0.1
}.play;
)
The Dbrown and Dibrown Ugens are good for random walk (drunken walk)
(
{
var a, freq, trig;
a = Dibrown(0, 20, 2, inf);
trig = Impulse.kr(MouseX.kr(1, 40, 1));
freq = Demand.kr(trig, 0, a) * 30 + 340;
SinOsc.ar(freq) * 0.1
}.play;
)
Dwhite is whitenoise - not drunk anymore but jumping around madly
(
{
var a, freq, trig;
a = Diwhite(0, 15, inf);
trig = Impulse.kr(MouseX.kr(1, 40, 1));
freq = Demand.kr(trig, 0, a) * 30 + 340;
SinOsc.ar(freq) * 0.1
}.play;
)
Using TDuty to demand results from demand rate UGens
(
{
var minDur = 0.1, delta = 0.01;
var trig = TDuty.ar(Dbrown(minDur, minDur+delta), 0, Dwhite(0, 1));
Ringz.ar(trig, TRand.ar(2000, 4050, trig), 0.1)!2
}.play
)