chapter 2

Examples with Routines and Loops

(
/*
Algorithmic composition paradigms
BA 181502
Objective:  create a sine wave and accelerate and decelarate its frequency nth times.
*/

fork{//this alternative syntax goes for Routine

10.do({//here goes the repetition (10 times)

a = {SinOsc.ar(XLine.kr(100, 500, 4), 0, 0.4)}.play;
4.wait;
a.release;
b = {SinOsc.ar(XLine.kr(500, 100, 4), 0, 0.4)}.play;
4.wait;
b.release;

});//loop ends here
}//fork (Routine) ends here
)

probability-stochastic

/* BA 181502
 Stochastic example 1
Objective: Create two sounds one after the other then free one of them (probability), wait a couple of seconds to free also the remain sounds (probability). (Rolling a Dice)
*/

(
fork{
	"sound 1".postln;
        ~a = {Out.ar(0, SinOsc.ar(440, 0, 0.5))}.play;
	2.wait;
	"sound 2".postln;
	~b = {Out.ar(1, SinOsc.ar(443, 0, 0.5))}.play;
		0.11.wait;

	"dice game".postln;
	0.1.wait;
	"one sound is going to 'die'".postln;
	0.5.wait;
	"sound 1 or sound 2 is dead?".postln;
        0.1.wait;
        "rolling the die...".postln;

	6.wait;
	~die =
	{

	fork{
	var x = [1,2, 3, 4, 5, 6].choose.postln; // if you use .coin example below comment this line
	if(x>3,{~a.free},{~b.free}); // if you use .coin example below comment this line


	if(x>3, {"sound 1 is dead".postln},{"sound 2 is dead".postln}); // if you use .coin example below comment this line
	4.wait; // if you use .coin example below comment this line

	/*

There are other options to experiment with probability such as .wchoose and .coin as well.

.wchoose
//[440,220,3350,660].wchoose([0.5, 0.25, 0.2, 0.05]) //the second array of .wchoose, weights the options of the first array.

.coin
//0.5.coin //fair coin, equal chance.  true or false as output

//0.2.coin //unfair coin, 20% chance true

//0.95.coin //unfair coin, 95% chance true
*/

//Comment out the bolow code to use .coin instead of .choose and don't forget to comment the above code
/*
		var x = 0.5.coin.postln;
		if(x = false, {~a.free},{~b.free});


	        "sound 1 or sound 2 is dead?".postln;

		if(x = false, {"sound 1 is dead".postln},{"sound 2 is dead".postln});
                4.wait;

*/
	"free the remain sound_n".postln;
	if(~a.isRunning, {~a.free}, {~b.free});
	if(~b.isRunning, {~b.free}, {~a.free});


};//Sub-routine ends here
}.value;// ~die ends here
};//Routine ends here
)

With complex sounds




/*
BA 181502
Stochastic example 2
Objective: Create two sounds one after the other, then free one of them and after a while free also the remain sounds using probability. (Rolling a Dice)
*/

(
fork{
	"sound 1".postln;
	~a = {Out.ar([0,1], Resonz.ar(SinOsc.ar(
Dust.kr(XLine.kr(10, 1500, 1500, 100, 40, 4), XLine.kr(100, 400, 400, 200)), 0, LFNoise1.kr(20))
, 440.rand, 0.7)*Saw.ar(XLine.kr(44, 20, 44, 20, 4)).dup)}.play;
	0.1.wait;
	~a.postln;
	4.wait;
	"sound 2".postln;
	~b = {Out.ar([0,1], SinOsc.ar(SinOsc.ar(XLine.kr(10, 1500, 150, 10, 40, 4), 10, XLine.kr(100, 400, 400, 200)), 0, LFNoise1.kr(20)*0.6)*Saw.ar(XLine.kr(44, 20, 440, 20, 4)).dup)}.play;
	0.1.wait;
	~b.postln;
	4.wait;

	"dice game".postln;
	"one sound is going to 'die'".postln;

        ~die =
	{

	fork{
	var x = [1,2, 3, 4, 5, 6].choose.postln;
	if(x>3,{~a.free},{~b.free});

	"sound 1 or sound 2 is dead?".postln;

	if(x>3, {"sound 1 is dead".postln},{"sound 2 is dead".postln});

        4.wait;

	"free the remain sound_n".postln;

	if(~a.isRunning, {~a.free}, {~b.free});
	if(~b.isRunning, {~b.free}, {~a.free});


};//Sub-routine ends here
}.value;// ~die ends here
};//Routine ends here
)

Analysing methods

  • .normalizeSum


// first run the array with the message .normalizeSum and see the results
[16, 1.7, 38, 4.2, 21].normalizeSum //the method .normalizeSum makes a new array which results from the division of the sum of the entries

//We are going to use some maths to demonstrate how the method .normalizeSum works

//firts find the result of the summation of the array
[16, 1.7, 38, 4.2, 21].sum //80.9

//then add each value from the array seperately to the summation of the array \
// and divide the result with the result of the summation of the array
//the last thing to do is to substract the result of each equation by 1 i.e (-1)

//Below is an equation of the method .normalizeSum with the a collection of five items [16, 1.7, 38, 4.2, 21]

(
//run the code inside parentheses
n = (16+([16, 1.7, 38, 4.2, 21].sum)/ 80.9); // start with the first item of the array (16)
n = n-1;
o = (1.7+([16, 1.7, 38, 4.2, 21].sum)/ 80.9); // then the second,
o = o-1;
r = (38+([16, 1.7, 38, 4.2, 21].sum)/ 80.9); // the third, and so on
r = r-1;
m = (4.2+([16, 1.7, 38, 4.2, 21].sum)/ 80.9);
m = m-1;
a = (21+([16, 1.7, 38, 4.2, 21].sum)/ 80.9);
a = a-1;
[n,o,r,m,a].asStream;
)

// see the results on the post window and compare them with the results of the [16, 1.7, 38, 4.2, 21].normalizeSum

Using Patterns

Scales and Patterns examples

/*
BA 181602
Composing music with patterns paradigms
Objective: Create sound textures while you playing a scale.
Synthesis technique: (FM synthesis)
*/

//Midi notation

s.boot

(
SynthDef(\synth01, {|out = 0, gate = 1, freq = 220, amp = 0.4, pan = 0, mod = 440, ind = 1|

	var env, source;

	env = EnvGen.kr(Env.adsr(0.01, 0.4, 0.001, 0.0, 1), gate, doneAction: 2);
	source = SinOsc.ar(SinOsc.ar(freq.midicps, mod*freq, ind*freq), 0, amp*env);
	Out.ar(out, source)

//midicps convert MIDI note to cycles per second

}).add;

~pattPm1 = Pseq((60..80)++(79..60)++[\rest], 2);

// Prand((0..14)++(13..0)++[\rest], 2);

~pattPm2  =  Pseq((0..14)++(13..0)++[\rest], 2);

~pattPm3  = Pseq((1..18000), inf);

Pbind(
	\instrument, \synth01,
	\freq, Pseq([~pattPm1, ~pattPm2], 2),
	\dur, 0.25,
	\mod, ~pattPm3,
	\ind, ~pattPm3,
	\amp, Pseq((0.4..0.7), inf)
).play;
)

Example 2 Patterns

/*
BA 181602
Composing music with patterns paradigms.
Objective: a) Experiment with patterns and scales b) Change scale and duration after n time, c) play all scales, d) stop the music
*/

(
fork{

~scaleS = Scale.bartok;//experiment with other scales as well. See Scale.directory


~pattP = Prand((0..17)++ [\rest] ++ (16..0)++[\rest], inf);

~pattD = Pn(Pgeom(0.25, 1, inf), Pgeom(1, 0.25, inf), inf);

~pattD2 = Pn(Prand([0.025, 0.34, 0.16, 1], inf), Pseq([1, 0.34, 0.56, 0.25], inf), inf);

Pdef(\first_mov1,
 Pbind(\scale, ~scaleS,
	\degree, ~pattP,
	\dur, ~pattD)).play;

	"start with Bartok scale".postln;

	15.wait;

	Pdef(\first_mov1).stop;

	"change to Dorian".postln;

~scaleS = Scale.dorian;

Pdef(\first_mov2,
 Pbind(\scale, ~scaleS,
	\degree, ~pattP,
	\dur, ~pattD2)).play;

	14.wait;
	"now both".postln;

	Pdef(\first_mov1).play;

	3.wait;
        "8s to close".postln;

	8.wait;// change this to a bigger number in case you want more

	Pdef(\first_mov1).stop;
	Pdef(\first_mov2).stop;

	"thats it".postln;
};
)

Playing with Patterns more


/*
BA 181802
Playing with Patterns Harmony
Objective: Create Chord progressions
*/
s.boot;

//Example with \note notation simple major chords: I-V-IV-V
(
~soprano = Pbind(\scale, Scale.major, \note,  Pseq([7, 11, 9, 11], inf)).play;

~alto = Pbind(\scale, Scale.major, \note,  Pseq([0, 7, 5, 7], inf)).play;

~tenor = Pbind(\scale, Scale.major, \note,  Pseq([4, 2, 5, 2], inf)).play;

~bass = Pbind(\scale, Scale.major, \note,  Pseq([0, 7, 0, 7], inf)).play;
)

//another example free style minor
//Objective: Create Chord progressions with random delta time

(
~scalem = Scale.minor;
~soprano= Pseq([Pseq([5, 2, 1, 7], 1), Pseq([3, 1, 5, 4], 1)], inf).asStream;
~alto = Pseq([Pseq([3, 11, 9, 7], 1), Pseq([0, 7, 5, 7], 1)], inf).asStream;
~tenor = Pseq([Pseq([4, 2, 0, 9], 1), Pseq([2, 3, 5,  9], 1)], 1).asStream;
~bass = Pseq([Pseq([0, 7, 5,  7], 1), Pseq([0, 3, 6,  7], 1)], 1).asStream;

"n movement".postln;

Pdef(\nmove,
	Pbind(\scale, ~scalem,
		\note, Pn([~soprano, ~alto, ~tenor, ~bass], inf),
		\dur, Pxrand([0.5, 0.75, 1, 2, 0.25, 0.15], inf))).play;
//play forever
)

// see also interval ratios i.e I-V-I-V7: 1/1, 3/2 etc.

~bassoContinuo = Pbind(\freq,  Pseq([440, 440*3/2, 440, 440*15/8], inf)).play
//create the rest voices
~tenor = ...
~alto = ...
~soprano = ...

see more on Streams-Patterns-Events

Streams

Data-driven

/*
BA 180205
Create data and collect
Objective: Create data (an array of values i.e integers or floats), and map them to the arguments of the synth
*/

//Create and collect data as stream (array)
//iterate over a collection
(
a = 10.do{|n|
    n.postln;
};
)
//collect items between 0-9 
k = a.collect({ |item| item.rand.asStream });
// play the collection in Event pattern
Pbind(\dur, 0.09, \degree, Pseq(k)).play;



//with Array.fill

 s.waitForBoot{

fork{
//create a synthdef
b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");

1.wait;

SynthDef(\tgrain, {| gate = 1, trate = 1, dur = 1, rate = 1, bufdur = 1, amp = 0.5 |

	var source, env;

	env = EnvGen.kr( Env.asr, gate, doneAction: 2);

source = TGrains.ar(2, Impulse.ar(trate), b, rate, BufDur.kr(b)*bufdur, dur, Dseq([-1, 1], inf), 0.1, 2);


	Out.ar(0, source * env * amp);
}).add;


0.5.wait;

// run the synth
//x = Synth(\tgrain);

0.5.wait;

// create an Array of random values and set them to the synth

	e = Array.fill(100, { rrand(0, 20)});
	e.postln;



Pbind(\instrument, \tgrain, \dur, Pseq(e.sqrt), \trate, Pseq(e), \rate, 1, \amp, 1, \bufdur, Pseq(e.reciprocal)).play;


};
}

Kolakoski sequence

In mathematics, the Kolakoski sequence, sometimes also known as the Oldenburger-Kolakoski sequence,[1] is an infinite sequence of symbols {1,2} that is its own run-length encoding[2] and the prototype for an infinite family of related sequences. It was initially named after the recreational mathematician William Kolakoski (1944–97), who discussed it in 1965,[3] but subsequent research has revealed that it first appeared in a paper by Rufus Oldenburger in 1939.(Wikipedia) (https://en.wikipedia.org/wiki/Kolakoski_sequence)

  • Python
#
#kolakoski seq
# Builds the list l as it's being
#iterated through.
#For each entry x of l,
#appends x copies of 1 or 2,
#whichever is opposite
#the current last element.

import argparse
import random
import time
from pythonosc import osc_message_builder
from pythonosc import udp_client

client = udp_client.SimpleUDPClient("127.0.0.1", 57120)

l=[2, -2]
##print(1,2)
while True:

    for x in l:
        print(x);l+=x*[l[-1]^3]
        client.send_message("/filter", x)
        time.sleep(1)
  • SuperCollider
s.waitForBoot{


	
	fork{
		~bufferk = Buffer.read(s,  "/Users/sounds/kick03.wav"); //
		//remember to free the buffer later.
		~buffersn = Buffer.read(s,  "/Users/sounds/snare01Stereo.wav");
		~bufferhi = Buffer.read(s,  "/Users/sounds/hihat01Stereo.wav");
1.wait;
SynthDef(\kick, {| out = 0, bufnum = 0, amp = 0.3, rate = 1, cutoff = 2000 |
	var source;
	source =  PlayBuf.ar(2, bufnum, BufRateScale.kr(bufnum)*rate, doneAction:2, loop:1);
	source = LPF.ar(source, cutoff+2000);
	    Out.ar(out, source*amp)
}).add;

0.5.wait;

		SynthDef(\snare, {| out = 0, bufnum = 1, amp = 0.3, rate = 1, cutoff = 2000 |
			var source;
			source =  PlayBuf.ar(2, bufnum, BufRateScale.kr(bufnum)*rate, doneAction:2, loop:1);
			source = LPF.ar(source, cutoff+2000);
    Out.ar(out, source*amp)
}).add;

SynthDef(\hihat, {| out = 0, bufnum = 2, amp = 0.3, rate = 1, cutoff = 2000 |
			var source;
			source =  PlayBuf.ar(2, bufnum, BufRateScale.kr(bufnum)*rate, doneAction:2, loop:1);
			source = LPF.ar(source, cutoff+2000);
    Out.ar(out, source*amp)
}).add;
		
		~kickx = Synth(\kick, [\out, 0, \bufnum, ~bufferk, \rate, 1]); 
		1.wait;
			~snarex = Synth(\snare, [\out, 0, \bufnum, ~buffersn, \rate, 1]);
			1.wait;
		~hihatx = Synth(\hihat, [\out, 0, \bufnum, ~bufferhi, \rate, 1]);

1.wait;
		OSCdef(\osctest,
                    { | args |
					
						fork{
							"rate and amp for kick:".postln;
					 ~kickx.setn(\rate, args[1].postln, \amp, args[1].reciprocal.postln, \cutoff, args[1].pow(3));
							
							1.wait;
							"rate and amp for snare:".postln;
							~snarex.setn(\rate, args[1].postln, \amp, args[1].reciprocal.postln, \cutoff, args[1].pow(2));
							1.wait;
										"rate and amp for hihat:".postln;
					~hihatx.setn(\rate, args[1].postln, \amp, args[1].reciprocal.postln, \cutoff, args[1].pow(4));
						};
    },
                    '/filter'
    );


  };
}

Scheduling Tempo

(

//create SynthDef
SynthDef(\blipClock, {|out = 0, gate = 1, freq = 440, numharm = 200,  freq2 = 220, cutoff = 444,  amp = 0.8, rq = 1, pan = 0 |
	var env, source, filter;

		env = EnvGen.kr(Env.perc, doneAction: 2);
	//	env = EnvGen.kr(Env.adsr(0.6, 0.3, 0.4, 1), gate,   doneAction: 2);

	source =
	Mix.fill(2, {Blip.ar(SinOsc.ar(freq*2, freq2*3, freq*0.6), numharm, amp*env)+SinOsc.ar(freq, 0,  amp*env)});
	//	filter = RLPF.ar(source, cutoff, rq, amp).softclip;
		filter = LPF.ar(source, cutoff, amp).softclip;

	Out.ar(out, Pan2.ar(filter, pan))*0.4;
	
}).add;
)

//create a new Tempoclock
(
~t = TempoClock(2); // make a new tempoclock of 2 beats per second means 12bpm

~t.schedAbs(0, { | ... args |	// start at absolute beat 0 immediately
args.postln;	//  post the input arguments to our event function 
							//  (will post logical time in beats, elapsed time 
							//  in seconds of enclosing thread and this clock)


Synth(\blipClock, [\freq, 60, \numharm, 120.rand2, \amp, 0.6]);// make a bleep
	1.0	// reschedules every beat
})

)


(
//Scheduling tempo with Routines

~f = fork{
~ev = Event.new( [
	~t.tempo_(4);
	6.wait;
	~t.tempo_(2);
	6.wait;
	~t.tempo_(1);
	6.wait;
]).play;
	
	12.wait;
	

	~t.clear;
};
)