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
// 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
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)
#
#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)
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;
};
)