Friday, December 12, 2014

Last Week's Notes

Note: This is here temporarily for the benefit of some students. It will move to Moodle and disappear form here


/*
SuperCollider is an Object Oriented Language that is designed for music and audio. It is a Music V type language. that means, like MAX MSP, the sound generation runs on a different thread than the control stuff. In MAX / MSP, you see this represented by differented coloured patch connections. In SuperCollider, audio runs on a server.

Because we don't have much time, I'm going to ask you take notes and then try stuff out later, not in class.  I also will ask Pieman to put a tutorial up for you on Moodle.

Hopefully you all have SuperCollider installed. When you tried opening it, you saw the screen divided in two parts. One part was the post window, filled with strange text and the other part was just blank and called untitled. The untitled window is where you write your code.  When you want to run your code, you select it with your mouse and hit ctrl-enter if you are in windows or linux, or apple-enter if you are on a mac. On some computers, you may need to use the apple or control key on the right wide of your keyboard. Try this out later with a simple program:
*/

4 + 4

/*
When you highlight 4 + 4 and run it, 8 will print out in your post window.

We're going to start out with working with sound design. Sound runs on the server. Therefore, we need to fist boot the server.  There are a few ways to do this. If you look in the Language menu, one of the items is 'boot server'. Select that.

The text in the little status bar at the bottom should change and turn green. you will also see output in the post window.


Now that the server is booted, let's make some sound:

*/

(
SynthDef(\sine, {
 Out.ar(0,
  SinOsc.ar(440, 0, 0.2)
 )
}).play
)

/*
When you want it to stop, hit ctrl-period or apple-period.

We are defining a SynthDef, sending it to the server and telling it to play. a synthDef is a description of how the server makes a sound.

First it has the name, which starts with a \, then comman curly bracket, then all of the UGens.

Out.ar is where the sound should come out. The channels start with 0, so this is on the left. Next is what should come out.
SinOsc is a sinwav oscillator. It's running at 440 Hz, it's phase is 0, and it's amplitude is 0.2.
Notice that these items have commas between them.

Then we close all of our open parens and curly brackets

Then a .play tells the whole thing to play.


We can organise this better using variables.
A variable is a container with a name. Like a box named sine. It can hold an object.
*/

(
SynthDef(\sine, {
 var sine;
 sine = SinOsc.ar(440, 0, 0.2);
 Out.ar(0, sine);
}).play
)


/*

First we let SuperCollider know that we've got a variable named sine.
We end that with a semicolon, so it knows that statement is done.

Then we do an assignment. Don't read = as 'equals, read it as 'gets'.
sine gets SinOac.ar.  We've got a box named sine. We've put a sine oscillator in the box, to keep track of it.
Then a semi colon, so it knows we're done with the assignment.
Then Our.ar, saying what channel to go out on.  And we tell it, put out whatever's in sine. Take the contents of the box and send it to the left speaker.


Every line needs to be separated by semicolons. Later on, when you can't figure out why something isn't working, check the semi colons first. It's the most common mistake. I still forget them all the time.


These sines all last forever, so let's try an envelope:

*/


(
SynthDef(\sinenv, {
 var sine, env;
 sine = SinOsc.ar(440, 0, 0.2);
 env = EnvGen.kr(Env.perc, doneAction:2);
 Out.ar(0, sine * env);
}).play
)


/*
we have added an extra variable called env
We've put an EnvGen into it. env gets EnvGen.kr.  An EnvGen is an envelope generator. First we tell it what envelope shape, then we do this doneAction:2 thing.

When we're telling out what to play, we tell it to multiple the sine box by the env box.


Let's do some ring modulation:

*/

(
SynthDef(\sinerm, {
 var sine, ringmod, env;
 sine = SinOsc.ar(440, 0, 0.2);
 ringmod = SinOsc.ar(30) * sine;
 env = EnvGen.kr(Env.perc, doneAction:2);
 Out.ar(0, ringmod * env);
}).play
)


/*

We've added a new variable called ringmod.

Ring mod gets a new sinosc times the contents of the sine variable.
When SuperCollider is looking at your code and it sees an assignment - the equals sign - it does everything on the right hand side of the assingment and puts the result of everything into the variable on the left.

We've picked 30 Hz, because that's the frequency daleks use, but what if we want different frequencies?

We could keep changing the synthdef:
*/

(
SynthDef(\sinerm, {
 var sine, ringmod, env;
 sine = SinOsc.ar(470, 0, 0.2);
 ringmod = SinOsc.ar(30) * sine;
 env = EnvGen.kr(Env.perc, doneAction:2);
 Out.ar(0, ringmod * env);
}).play
)

(
SynthDef(\sinerm, {
 var sine, ringmod, env;
 sine = SinOsc.ar(810, 0, 0.2);
 ringmod = SinOsc.ar(30) * sine;
 env = EnvGen.kr(Env.perc, doneAction:2);
 Out.ar(0, ringmod * env);
}).play
)

(
SynthDef(\sinerm, {
 var sine, ringmod, env;
 sine = SinOsc.ar(752, 0, 0.2);
 ringmod = SinOsc.ar(30) * sine;
 env = EnvGen.kr(Env.perc, doneAction:2);
 Out.ar(0, ringmod * env);
}).play
)

/*

Or, we could use a special kind of variable called an argument

*/

(
SynthDef(\sinermarg, { arg freq;
 var sine, ringmod, env;
 sine = SinOsc.ar(freq, 0, 0.2);
 ringmod = SinOsc.ar(30) * sine;
 env = EnvGen.kr(Env.perc, doneAction:2);
 Out.ar(0, ringmod * env);
}).add
)

/*

We've declared the argument at the top. That uses the keyword arg. argument need to go before variables.
So first you have the SynthDef and the name of it and the culry bracked.
Then next is arguments, starting with the keyword arg.
then are the variables, starting with the keyword var.
Everything else is below that.


It does not make sound when we play this one, because we changed the .play at the end to .add.

Instead of playing this right away, we tell the server to remember it, so we can play it later.

*/

Synth(\sinermarg, [\freq, 440])


/*
there are a lot of ways we can play a SynthDef, and one of them is by via Synth.

The first argument to Synth is \sinermarg, which is the name of the synthdef.  The next is this square bracket thing.

The first thing in the square brackets is \freq, this is the name of our argument, but starting with a slash.
Then is the value we want to give it.

*/


Synth(\sinermarg, [\freq, 860])

/*

What if we want to have more arguments?

*/

(
SynthDef(\sinermargs, { arg freq, rm, dur, amp;
 var sine, ringmod, env;
 sine = SinOsc.ar(freq, 0, amp);
 ringmod = SinOsc.ar(rm) * sine;
 env = EnvGen.kr(Env.sine(dur), doneAction:2);
 Out.ar(0, ringmod * env);
}).add
)

Synth(\sinermargs, [\freq, 912, \rm, 123, \dur, 7, \amp, 0.3])



/*

For this morning, we're going to focus more on SynthDefs, so we'll get to do sequences of notes in the afternoon.


Let's try different waves, panned to centre
*/


(
SynthDef(\saw, { arg freq, dur, amp;
 var saw, env, panner;
 saw =  Saw.ar(freq, amp);
 env = EnvGen.kr(Env.sine(dur), doneAction:2);
 panner = Pan2.ar(saw, 0,env);
 Out.ar(0, panner);
}).add
)

Synth(\saw, [\freq, 550, \dur, 3, \amp, 0.2])


/*

Notice that saw and sign have different arguments in their list. the second thing in the list to sign is a 0 and the amplitude is third. But saw just goes directly from frequency to amplitude.

How would you know about how these are different (or even what the various oscillators are)?  the answer is in the help browser!

Under the Help menu, there is a 'show help browser' option. You can click on search there to start searching. Or just browse around. Get to know the help browser. It is your friend. There are lots of examples.

Let's try some filtered noise:

*/


(

SynthDef(\subtractive, { arg freq, dur, amp;
 var noise, filt, env, panner;
 noise = WhiteNoise.ar;
 filt = RLPF.ar(noise, freq);
 env = EnvGen.kr(Env.sine(dur), doneAction:2);
 panner = Pan2.ar(filt, 0,env * amp);
 Out.ar(0, panner);
}).add
)

Synth(\subtractive, [\freq, 1200, \dur, 3, \amp, 0.2])


/*
WhiteNoise is just noise, so there's no need to give it additional information
RLPF is a Resonant Low Pass Filter

You may have noticed some of these are .ar and some are .kr.
ar is audio rate
kr is control rate

The envelope doesn't need to change values as often as a 440 Hz sine oscillator in order to sound good. The oscillator needs to go through it's entire wave form 440 times a second, whereas, the envelope needs much less precision.

*/


/*

This file is a SuperCollider file.  The english text is in a comment.  Programmers sometimes want to put human-language notes in their files.  This makes the files more readable to humans, but the text is not meaningful to SuperCollider. They mark the text as not being code by putting comment markers around it. Every one of these block of text starts with a slash star and ends with a star slash.
*/


/* <-- this is the starts of a comment
this is the end of a comment ---> */

// <- this marks a comment that only lasts for one line (and thus does not need an end marker)




Pbind Notes from this morning

Note: This is here temporarily for the benefit of some students. It will go up on Moodle and then be removed from here.


/*

Now what we want is a way to play notes in sequence. Let's use an earlier synthdef:

*/

(
SynthDef(\sin, { arg freq = 440, dur = 1, amp= 0.5, pan=0;
 var sine, env, panner;
 sine = SinOsc.ar(freq, 0, amp);
 env = EnvGen.kr(Env.perc(0.01, dur), doneAction:2);
 panner = Pan2.ar(sine, pan, env);
 Out.ar(0, panner);
}).add
)


(

Synth(\sin, [\freq, 440, \dur, 1, \amp, 0.3]);
Synth(\sin, [\freq, 550, \dur, 1, \amp, 0.3]);
Synth(\sin, [\freq, 660, \dur, 1, \amp, 0.3]);

)

/*

This plays all at once!

Fortunately, there are several ways to add timings.  One of these is Pbind.

*/


Pbind.new.play


/*

As before, press cntl-. or apple-. to stop

Pbind makes a LOT of assumptions on your behalf, so you can run it with no arguments at all.  Or you can specify as much as you want.

Let's tell it to use our synthdef:

*/

(

Pbind(
 \instrument, \sin
).play


)


/*

Let's tell it to play louder

*/

(

Pbind(
 \instrument, \sin,
 \amp, 0.5
).play


)


// Change the freq and dur

(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \freq, 770,
 \dur, 0.5
).play


)

/*

  This is better, but let's make the notes change over time


*/


(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \freq, Pseq([440, 550, 660, 770, 880], 1),
 \dur, 0.5
).play


)


/*

Pseq plays all the array contents in order, then repeats the number of times listed

*/


(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \freq, Pseq([440, 550, 660, 770, 880], 3),
 \dur, 0.4
).play


)


/*
To repeat forever, use the keyword inf

*/

(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \freq, Pseq([440, 550, 660, 770, 880], inf),
 \dur, 0.2
).play


)


/*


We can use Pseqs for ANY item in a Pbind

*/


(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \freq, Pseq([440, 550, 660, 770, 880], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 2)
).play


)


/*

The first Pseq that ends, ends the entire Pbind



Instead of specifying frequencies let's switch to scale degrees

*/


(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \degree, Pseq([0, 2, 4, 6, 7], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 2)
).play


)



/*

The Pbind does the maths for us to map the scale degrees to the frequency.  This works because our synth uses the argument freq for frequency. the Pbind assumes this is what the frequency argument will be called and acts accordingly.


We can change to a different number of scale steps

*/

(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \stepsPerOctave, 10,
 \degree, Pseq([0, 2, 4, 6, 7], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 2)
).play


)



/*

We can change scales

*/

(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \scale, Scale.minor,
 \degree, Pseq([0, 2, 4, 6, 7], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 2)
).play

)
/*

Look up scales in the help browser for more

(incidentally, when you want to search, the search box at the top right searches only the currently displayed page.  to search everything, click on the word 'search' on the top.



We can do maths with Pseqs

*/

(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \scale, Scale.minor,
 \degree, Pseq([0, 2, 4, 6, 7], inf) + Pseq ([0, 0, 0, 0, 0, 5, 5, 5, 5], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10)
).play

)


/*

We can nest Pseqs

*/


(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \scale, Scale.minor,
 \degree, Pseq([0, 2, 4, 6, 7], inf) +
 Pseq ([
  Pseq([0], 5),
  Pseq([5], 4)
 ], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10)
).play

)

/*

They can also be multiplied, divided, etc.



We can play multiple notes at once:

*/


(

Pbind(
 \instrument, \sin,
 \amp, 0.3,
 \scale, Scale.minor,
 \degree, Pseq([[1, 3], [3, 5, 7], [2, 5], 7, [1,8]], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 2)
).play

)


/*

If a pseq gets an array back for any value, it will make as many notes as are in the array, each with one of the values in the array.

So when it gets [3, 5, 7] for the degree, it makes one note with the 3rd, one with the 5th and one with the 7th.




We can not always use direct sequences

*/


(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \scale, Scale.minor,
 \degree, Prand([0, 2, 4, 6, 7], 3),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 2)
).play

)


/*

Prand picks an item randomly from the array, the amount of times you tell it to. So rather than playing through everything 3 times, it picks 3 items

*/

(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \scale, Scale.minor,
 \degree, Prand([0, 2, 4, 6, 7], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 2)
).play

)


/*

Prands can do all the same maths as Pseqs and work in combination with them

*/




(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \scale, Scale.minor,
 \degree, Prand([0, 2, 4, 6, 7], inf)  + Pseq ([0, 0, 0, 0, 0, 5, 5, 5, 5], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10)
).play

)

(

Pbind(
 \instrument, \sin,
 \amp, 0.3,
 \scale, Scale.minor,
 \degree, Prand([0, 2, 4, 6, 7, [1, 3, 5]], inf)  + Pseq ([0, 0, 0, 0, 0, 5, 5, 5, 5], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10)
).play

)



(

Pbind(
 \instrument, \sin,
 \amp, 0.3,
 \scale, Scale.minor,
 \degree, Prand([0, 2, 4, 6, 7, [1, 3, 5]], inf)  + Pseq ([
  Pseq([0], 5),
  Pseq([5], 4),
  Prand([0, 2, 5], 1)
 ], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10)
).play

)

/*

Pwhite varies randomly between a low and a high number

*/

(

Pbind(
 \instrument, \sin,
 \amp, 0.3,
 \scale, Scale.minor,
 \degree, Prand([0, 2, 4, 6, 7, [1, 3, 5]], inf)  + Pseq ([
  Pseq([0], 5),
  Pseq([5], 4),
  Prand([0, 2, 5], 1)
 ], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10),
 \pan, Pwhite(-1, 1, inf)
).play

)

/*
The panning is hard left, hard right or center, because it's only picking whole number. If we want it to pick numbers between the whole numbers, we need to specify that

*/

(

Pbind(
 \instrument, \sin,
 \amp, 0.3,
 \scale, Scale.minor,
 \degree, Prand([0, 2, 4, 6, 7, [1, 3, 5]], inf)  + Pseq ([
  Pseq([0], 5),
  Pseq([5], 4),
  Prand([0, 2, 5], 1)
 ], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10),
 \pan, Pwhite(-1.0, 1.0, inf)
).play

)




/*

The whole numbers are called intergers and the numbers with decimal points are called floating point or floats, which are terms you might see in the help files.

*/

/*

This behaviour is actually useful, if we want to pick scale degrees, we may not what to pick ones in between

*/

(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \scale, Scale.minor,
 \degree, Pwhite(0, 8, 10),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10),
 \pan, Pwhite(-1.0, 1.0, inf)
).play

)


(

Pbind(
 \instrument, \sin,
 \amp, 0.5,
 \scale, Scale.minor,
 \degree, Pwhite(0.0, 8.0, 10),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10),
 \pan, Pwhite(-1.0, 1.0, inf)
).play

)

/*

What if we want moments of silence?

we use the symbol \rest for the freq or degree

*/

(

Pbind(
 \instrument, \sin,
 \amp, 0.3,
 \scale, Scale.minor,
 \degree, Pseq([0, 2, \rest], 3),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10),
 \pan, Pwhite(-1.0, 1.0, inf)
).play
)
(

Pbind(
 \instrument, \sin,
 \amp, 0.3,
 \scale, Scale.minor,
 \degree, Prand([1, 3, 5, 7, 8, [1, 3, 5], \rest], inf)  + Pseq ([
  Pseq([0], 5),
  Pseq([5], 4),
  Prand([0, 2, 5], 1)
 ], inf),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10),
 \pan, Pwhite(-1.0, 1.0, inf)
).play

)

/*

What if we want to do one thing and go on to another thing?

*/

(
Pseq([
 Pbind(
  \instrument, \sin,
  \amp, 0.3,
  \scale, Scale.minor,
  \degree, Prand([1, 3, 5, 7, 8, [1, 3, 5], \rest], inf)  + Pseq ([
   Pseq([0], 5),
   Pseq([5], 4),
   Prand([0, 2, 5], 1)
  ], inf),
  \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 2),
  \pan, Pwhite(-1.0, 1.0, inf)
 ),
 Pbind(
  \instrument, \sin,
  \amp, 0.3,
  \stepsPerOctave, 10,
  \degree, Pwhite(1, 10, 10),
  \dur, Prand([0.4, 0.6, 0.8], inf)
 )
]).play
)

(
Pseq([
Pbind(
 \instrument, \sin,
 \amp, 0.3,
 \scale, Scale.minor,
 \degree, Pseq([0, 2, \rest], 3),
 \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10),
 \pan, Pwhite(-1.0, 1.0, inf)
),
 Pbind(
  \instrument, \sin,
  \amp, 0.3,
  \stepsPerOctave, 10,
  \degree, Pwhite(1, 10, 10),
  \dur, Prand([0.4, 0.6, 0.8], inf)
 )
]).play


/*

What if we don't just want chords, but want two streams going at once?

*/
(
SynthDef(\crapSnare, { arg amp, pan;
 var noise, env, panner;

 noise = WhiteNoise.ar;
 env = EnvGen.kr(Env.perc, doneAction:2);
 panner = Pan2.ar(noise, pan, env);
 Out.ar(0, panner * amp)
}).add
)

(
Ppar([
 Pbind(
  \instrument, \sin,
  \amp, 0.3,
  \scale, Scale.minor,
  \degree, Prand([1, 3, 5, 7, 8, [1, 3, 5], \rest], inf)  + Pseq ([
   Pseq([0], 5),
   Pseq([5], 4),
   Prand([0, 2, 5], 1)
  ], inf),
  \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10),
  \pan, Pwhite(-1.0, 1.0, inf)
 ),
 Pbind(
  \instrument, \crapSnare,
  \amp, 0.2,
  \dur, 0.8
 )
]).play
)

// note the snare never stops!

// or moving the snare to the offbeat:




(
Ppar([
 Pbind(
  \instrument, \sin,
  \amp, 0.3,
  \scale, Scale.minor,
  \degree, Prand([1, 3, 5, 7, 8, [1, 3, 5], \rest], inf)  + Pseq ([
   Pseq([0], 5),
   Pseq([5], 4),
   Prand([0, 2, 5], 1)
  ], inf),
  \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10),
  \pan, Pwhite(-1.0, 1.0, inf)
 ),
 Pbind(
  \instrument, \crapSnare,
  \amp, 0.2,
  \dur, Pseq([0.4, Pseq([0.8], inf)], 1)
 )
]).play
)


/*
to get rid of the first beat, we need to give it a rest for the freq.
It does not matter that the synthDef doesn't take freq as an argument. This is just for the safe of the pbind

*/


(
Ppar([
 Pbind(
  \instrument, \sin,
  \amp, 0.3,
  \scale, Scale.minor,
  \degree, Prand([1, 3, 5, 7, 8, [1, 3, 5], \rest], inf)  + Pseq ([
   Pseq([0], 5),
   Pseq([5], 4),
   Prand([0, 2, 5], 1)
  ], inf),
  \dur, Pseq([0.2, 0.2, 0.4, 0.2, 0.6], 10),
  \pan, Pwhite(-1.0, 1.0, inf)
 ),
 Pbind(
  \instrument, \crapSnare,
  \amp, 0.2,
  \dur, Pseq([0.4, Pseq([0.8], inf)], 1),
  \freq, Pseq([\rest, Pseq([440], inf)], 1),
 )
]).play
)

/*

Ppars can also be nested in Pseqs.
The helpfiles for streams patterns and events go into loads of detail about how to use p-things


When you are making your own pbinds, watch out for forgetting commas in the middle of them, or dangling commas at the end. commas should go between items.

*/


/* Finally, we might have a synth that lasts for a really long time, that we want to change, but not end
*/

(
SynthDef(\sinerm, {arg freq, amp;
 var sine, ringmod, env;
 sine = SinOsc.ar(freq, 0, 0.2);
 ringmod = SinOsc.ar(30) * sine;
 Out.ar(0, ringmod * amp);
}).play
)

(

Pmono(\sinerm,
 \degree, Pwhite(0, 12, inf),
 \amp, 0.5,
 //\stepsPerOctave, 10,
 \db, -3
).play

)


// We can change the synthdef a bit to add some lag

(
SynthDef(\sinermlag, {arg freq, amp;
 var sine, ringmod, env, lag;
 lag = Lag.kr(freq);
 sine = SinOsc.ar(lag, 0, 0.2);
 ringmod = SinOsc.ar(30) * sine;
 Out.ar(0, ringmod * amp);
}).play
)



(

Pmono(\sinermlag,
 \degree, Pwhite(0, 10, inf),
 \amp, 0.5,
 \stepsPerOctave, 10,
 \db, -3 // Pmono and Pbind will translate db to amp for us
).play

)


/*

Algorave:  uses algorythms to generate dance music
Can be live-coded, but need not be

Examples:

Microrex https://soundcloud.com/micorex
Yee-king http://open.spotify.com/track/1UTiLOe4LwarRDWR5pmjZg
Norah Lorway http://earthrid.bandcamp.com/album/ive-had-dreams-like-this

*/

Some Notes for Students Today

Note: This is literally for some students I have today. It will appear on Moodle some time soon and then disappear from this space.


/*

Extra Things!



Q: How do you make a Pmono stop?

A: A Pmono stops when it runs out of values, to send, just like a Pbind. However, unless we tell a synth to stop, it will keep going forever.



Q: How do you tell a synth to stop?

A: Envelopes!  We've seen this already with fixed duration envelopes. But if we don't know from the start how long we want the synth to go for, when need to use a different kind of envelope.

For example, and ASR.

ASR stands for 'attack, sustain, release and is a very common envelope in electronic music.  It uses a 'gate' to know when to start and stop. When the gate is 1, the envelope starts doing the attack, then it sustains indefinitely. As soon as the gate changes to 0, it immeditately stops whaever its doing to do the release portion.

Ex:

*/
(
SynthDef(\blipGate, {|freq, amp, gate=1, pan|  // the vertical bars are equvalent to using the keyword arg
 var blip, panner, env;
 blip = Blip.ar(freq, 10, 0.2);
 env = EnvGen.kr(Env.asr, gate, doneAction:2); // look at the helpfile for Env for more
 panner = Pan2.ar(blip, pan, env);
 Out.ar(0, panner * amp);
}).add
)

(
Pmono(
 \blipGate,
 \pan, Pwhite(0.0, 1.0, 5),
 \degree, Pwhite(1, 4, 5)
).play
)


// gates also work with Pbinds

(
Pbind(
 \instrument, \blipGate,
 \pan, Pwhite(0.0, 1.0, 5),
 \degree, Pwhite(1, 4, 5)
).play
)

/*

Q: How can I make frequency changes less abrupt?

A:  Add in some lag:

*/

(
SynthDef(\blipLag, {|freq, amp, gate=1, pan|  // the vertical bars are equvalent to using the keyword arg
 var lag, blip, panner, env;
 lag = Lag.kr(freq);
 blip = Blip.ar(lag, 10, 0.2);
 env = EnvGen.kr(Env.asr, gate, doneAction:2); // look at the helpfile for Env for more
 panner = Pan2.ar(blip, pan, env);
 Out.ar(0, panner * amp);
}).add
)

(
Pmono(
 \blipLag,
 \pan, Pwhite(0.0, 1.0, 5),
 \degree, Pwhite(1, 4, 5)
).play
)




/*

Q: If you're live coding and want to change a pbind without stopping it, can you do that?

A: Yes, with Pdef

*/

(
Pdef(\blips,
 Pbind(
  \instrument, \blipGate,
  \pan, Pwhite(0.0, 1.0, inf),
  \degree, Pwhite(1, 4, inf),
  \dur, 0.4
 )
).play
)

// just put a Pdef around the pbind



// now, let's change without stopping

(
Pdef(\blips,
 Pbind(
  \instrument, \blipGate,
  \pan, Pwhite(0.0, 1.0, inf),
  \degree, Pwhite(1, 4, inf),
  \dur, Prand([0.4, 0.2, 0.2, 0.8], inf)
 )
).play
)


// another change

(
Pdef(\blips,
 Pbind(
  \instrument, \blipGate,
  \pan, Pwhite(0.0, 1.0, inf),
  \degree, Pwhite(1, 4, inf) * Pwhite(1, 2),
  \dur, Prand([0.4, 0.2, 0.2, 0.8], inf)
 )
).play
)



Pdef(\blips).fadeTime = 5; // tell it to fade changes in and out over 5 seconds

Pdef(\blips).stop; // tell it to stop (but don't stop anything else, including other Pdefs)


/*

Q: what are some good oscillators?

A:

SinOsc
Blip
Formant
Pulse
Saw
Klank

// look at the help files for those


Ok, now I want you folks to get to work making some sounds and patterns. I'll come around to answer questions, or you can pair up if you want.

*/



Thursday, May 22, 2014

How to keep an installation running

Writing a program that can make it through the length of a single 20 minute performance can sometimes be challenging, but installation code needs to run for a full day or sometimes weeks and keep going. The first step, of course, is to make your code as bug-free as possible. However, even in this case, your code will eventually crash, though no wrong doing of your own. Therefore, the thing to do is to recover gracefully.

The latest versions of SuperCollider are actually three programs running at once - the IDE; sclang, the language; and scserver, the audio server. Any one of these things can crash.

SkipJack

SkipJack is an object that is like a Task, except that it survives cmd-period. It takes several arguments, the first of which is a function and the second of which is a number indication the wait time in between executions of the function.

SkipJack({"SkipJack".postln}, 2)

This code would print out SkipJack once every two seconds. The advantage of using SkipJack in this case is not that it will keep going after a comand-periiod, but rather that it's on a different thread than the rest of your program. If your main execution loop gets stuck some place and effectively dies, SkipJack will likely carry on. Therefore, we can use SkipJack to check on your main loop and try to revive it.

Sclang

How can we tell your main thread is still running without also stopping if it stops? One way to check is by looking at a shared variable. Let's have a number. Every time we go through the loop, we can set it to 3. Meanwhile, SkipJack could be running with a duration roughly equivalent to how long it should take to get through our loop. It could subtract one from our number. If our main execution loop stops, that number will count down towards zero and then go negative.


var alive, click, dur, task;

dur = 2;
click = { alive = 3 };

task = Task({
inf.do({

"still allive".postln;
click.value;
dur.wait;
})
}).play;

SkipJack({
"are we alive?".postln;
(alive <=0).if({
task.resume;
task.play;
"play!".postln;
(alive <= -2).if({
1.exit;
})
});
alive = alive -1;
}, dur);

If alive gets to zero, first we try to get the task running again. This sometimes works. If it fails, however, we call 1.exit, which causes all of sclang to exit. If we can't recover inside sclang, we can recover outside it.

The Server

We'll need a separate loop to check on the server.


SkipJack({
Server.default.boot(onFailure:{});
Server.default.doWhenBooted({}, onFailure:{1.exit});

}, dur);

This may look odd because it changes the 'onFailure' argument, but the effect of it is that if the server is not already booted, it will take a moment and may struggle to boot. If it fails, all of SuperCollider exits.

Keeping a Synth Alive

If your loop is firing off new Synths, you don't need to worry about whether each individual synth keeps going, but if you're just changing parameters on an individual synth that keeps running, you also need to watch out for it perishing. there are a few ways to do this. Maybe you want to check if it has gone silent?


(
var syn, lastrms, max_silence;

SynthDef(\stereoListenForSilence, {|in=0, out=0|
var input;
input = In.ar(in, Server.default.options.numOutputBusChannels);
SendPeakRMS.kr(input, 1, 3, '/loudnessMonitoring'); // send the RMS once per second
ReplaceOut.ar(0, LeakDC.ar(input).tanh); // Optional line to get rid of offset and peaking
}).add;

/* ... */


Synth(\stereoListenForSilence, nil, RootNode(s), \addToTail);

max_silence = 10; // 10 seconds


lastrms=Array.fill(max_silence, {1});

osc_listener = OSCFunc({ |msg|
var rms;
rms = msg[4].asFloat.max(msg[6].asFloat);
lastrms.removeAt(0);
lastrms.add(rms);
(lastrms.sum <= 0.0001).if ({
"too quiet".postln;
// retsart your synths
s.freeAll;
Synth(\myAmazingSynthDef);
Synth(\stereoListenForSilence, nil, RootNode(s), \addToTail);
});
}, '/loudnessMonitoring');


You can put a monitoring Synthdef on the server's root node and use SendPeakRMS to send OSC messages with the overall amplitude of all running synthdefs. Then, set up an OSCFunc to check if the peak amplitude has been near zero for too long. If it has, free everything and put up new synths. This will not tell you if your server freezes or if your monitoring synth stops sending OSC messages.

Or if you just want to check if an individual Synth is still running, you can use OSCFuncs and SkipJack together.


(

var syn_alive, dur;

dur =1;
syn_alive = 3;

SynthDef(\myAmazingSynthDef, {

var sin, trig;
SendTrig.kr(Impulse.kr(dur.reciprocal));
sin = SinOsc.ar;
Out.ar(0, sin);

}).add;


/* ... */

Synth(\myAmazingSynthDef);

OSCFunc({ arg msg, time;
syn_alive = 3;
},'/tr', s.addr);


SkipJack({
(syn_alive <=0).if({

s.freeAll;
Synth(\myAmazingSynthDef);

(syn_alive <= -2).if({
1.exit;
})
});
syn_alive = syn_alive -1;
}, dur);


SkipJack({
Server.default.boot(onFailure:{});
Server.default.doWhenBooted({}, onFailure:{1.exit});

}, dur);
)

Try quitting the Server via the gui and everything gets back to where it was in under 3 seconds.

No IDE

Previously in this document, we've intentionally made sclang crash, which, if you're running the IDE, is no fun. However, we will not be running sclang through the IDE. Instead, we'll run it from a BASH script. On your computer (if you have a mac or unix), when you open a terminal, what's running in it is a Bash shell. You can write scripts for this shell, which you edit in a plain text editor.


#!/bin/bash


while true
do

/path/to/sclang installation.scd

sleep 1

killall scsynth

sleep 1

done


Ok, so first things first, open a terminal and type:

which bash

It may be /bin/bash or /usr/bin/bash. It's what you want in the first line of the bash script. So if you get back /usr/bin/bash, change the first line to #!/usr/bin/bash.

To find the path to sclang, you can try typing 'which sclang', but if you're on a mac, this is not going to work. Instead, you will need to find the SuperCollider application on you hard drive. Right click on it to examine package contents. If you poke around in there in folders called things like Resources or MacOs, you will eventually find sclang. Drag that file to your SuperCollider IDE to find out the path for it. Put that path into your bash script in the place of '/path/to/sclang'.

Save the script as installation.sh and save your supercollider file as installation.scd. Put them together in the same folder or directory. In your terminal window, cd to that directory and type:

chmod +x installation.sh

Or, alternately, if you'd prefer to use a GUI, get information about the installation.sh file and click a checkbox to make it executable.

What this script does is loop forever. It runs your program. When your program exists, it kills whatever server might still be running and then restarts your program. If your program crashes out entirely, this script will restart it.

Helper Apps

If your installation relies on a helper application, like let's say you're using PD to manage HID communications, you'll want that in your loop also, but as both applications are running at the same time, you'll need to run the helper application in the background, but keep track of the PID so you can kill it when your program crashes.


#!/bin/bash


while true
do

/path/to/pd hid_helper.pd &
pid=$!
/path/to/sclang installation.scd

sleep 1

killall scsynth
kill $pid

sleep 1

done

Make sure your hid_helper is in the same folder as as your other files. Find the path to pd in the same way you got the path to sclang. The & makes it run in the background and the next line tracks the PID, so you can kill it later.

Obviously, you'll also want to keep track of your helper application, which you can do via OSC in the same way you keep track of your synthdefs. If your helper application quits, you'll need to do a 1.exit to force the bash script to restart everything.

Making it all go

This is an installation, so if you're using your own laptop, don't run it as yourself. Make a new user account and don't give that user administrative rights. Make all your files READABLE by that user (or everyone on the system), but don't give that user write access. Set the system so that when it boots up, it automatically logs in as that user.

Log in to the system as the new user. Go to the settings and say you want to autostart an application on login. The application you want to autostart is installation.sh

Try booting your computer. Does it start your installation? Once you've got that sorted out, leave it running in your living room for days and days until you think you're losing your mind. Every so often, use the process manager to kill the server or the helper application or wiggle a wire or otherwise create a bit of problems and see if your installation survives.