Tides on the Sea of Tranquillity

posted by on 2014.03.04, under Supercollider
04:

A quick post, since it is a while I’m not updating this.
Here’s a short track, “Tides on the Sea of Tranquillity”, built around one of my favourite UGens in SuperCollider: GrainBuf. GrainBuf is a buffer granulizer which is very flexible.
The idea for the track is quite simple: there are four granulizers, which get activated at a certain point, whose parameters are controlled by the loops in the Task. In particular, the position parameter for each of them is “lagged”, so that at each change it will start from the middle of the buffer, and reach the new value in a given time. Also, the spread, pitch, and pan are jittered, to give a spacey feeling. The other important part is that the rate for the grains are controlled via the .midiratio method, which allows to vary the pitch according to a temperate scale, hence giving some harmonic consistency. To end, everything is passed to a feedback delay line with a simple compressor.
One technical point: make sure you load buffers which are mono in GrainBuf, otherwise it will fail silently! Some times ago it took me ages to figure this out (I know, the documentation says so…).
Enjoy. ;)

s.boot;

(
fork({

//Define SynthDefs for the granulizers and delay

SynthDef(\granular, {arg out = 0, buff, pos = 0, spread = 0.05, dur = 0.40, p = 0.5, t_trig = 0, rate= 1, t = 300, a = 0.1, l = 1, pa = 0, att = 6, f = 100;
    var trigger = Impulse.ar(t);
    var sp = TRand.ar((-1)*spread, spread, trigger);
    var pan = TRand.ar((-1)*p, p, trigger);
    var sig = GrainBuf.ar(1, trigger, dur, buff, rate + LFNoise0.ar(f).range(-0.01, 0.01), Lag.kr(pos, l) + sp, pan:pan);
    var env = EnvGen.kr(Env.linen(att, 4, 8), gate: t_trig, doneAction:0);
    Out.ar(out, Pan2.ar(sig*env*a, pa));
}).add;

SynthDef(\del, {arg out = 0, in, feed = 0.93;
    var sig = In.ar(in, 2) + LocalIn.ar(2);
    var delL = DelayL.ar(sig[0], 2.0, 0.2);
    var delR = DelayL.ar(sig[1], 2.0, 0.5);
    LocalOut.ar([delL, delR]*feed);
    Out.ar(out, Compander.ar(sig, sig, 0.5, 1, 0.5, 0.01, 0.3));
}).add;


// Load samples in mono buffers and create a audio bus for delay
~path = PathName(thisProcess.nowExecutingPath).pathOnly;

~del = Bus.audio(s, 2);

~buff = Buffer.readChannel(s, ~path ++ "sample/sample1.wav", channels: [0]);
~buff2 = Buffer.readChannel(s, ~path ++ "sample/sample2.wav", channels: [0]);
~buff3 = Buffer.readChannel(s, ~path ++ "sample/sample3.wav", channels: [0]);
~buff4 = Buffer.readChannel(s, ~path ++ "sample/sample4.wav", channels: [0]);
~buff5 = Buffer.readChannel(s, ~path ++ "sample/sample5.wav", channels: [0]);

s.sync; //Wait for the buffers to be loaded;

y = Synth(\del, [\in: ~del]);


// Make an array of synths to be used as voices

~synths = Array.fill(3, {Synth(\granular, [\buff: ~buff, \t_trig:0, \pos: 0.5, \rate: ([0, 4, 5, 7, 9, 12, 16, 17] - 12).choose.midiratio, \spread: 0.05, \out:~del ])});

Task({

    10.do({
        ~synths[0].set(\buff, [~buff, ~buff2, ~buff3, ~buff4, ~buff5].wchoose([0.5, 0.3, 0.1, 0.08, 0.02]), \t_trig, 1, \t, rrand(10, 400), \dur, rrand(0.020, 0.40), \pos, rrand(0.0, 0.8), \l, rrand(0.4, 4), \rate, ([0, 4, 5, 7, 9, 12, 16, 17] - 12).choose.midiratio, \spread, rrand(0.005, 0.1), \p, rrand(-0.3, 0.3), \out, ~del, \pa, rrand(-0.5, 0.5), \att, rrand(6, 9), \a, rrand(0.05, 0.1), \f, rrand(50, 150));

        rrand(4, 9).wait;
    });

        30.do({
        ~synths[0].set(\buff, [~buff, ~buff2, ~buff3, ~buff4, ~buff5].wchoose([0.5, 0.3, 0.1, 0.08, 0.02]), \t_trig, 1, \t, rrand(10, 400), \dur, rrand(0.020, 0.40), \pos, rrand(0.0, 0.8), \l, rrand(0.4, 4), \rate, ([0, 4, 5, 7, 9, 12, 16, 17] - 12).choose.midiratio, \spread, rrand(0.005, 0.1), \p, rrand(-0.3, 0.3), \out, ~del, \pa, rrand(-0.5, 0.5), \att, rrand(6, 9), \a, rrand(0.05, 0.1), \f, rrand(50, 150));

        ~synths[1].set(\buff, [~buff, ~buff2, ~buff3, ~buff4, ~buff5].wchoose([0.5, 0.3, 0.1, 0.08, 0.02]), \t_trig, 1, \t, rrand(10, 400), \dur, rrand(0.020, 0.40), \pos, rrand(0.0, 0.8), \l, rrand(0.4, 4), \rate, ([0, 4, 5, 7, 9, 12, 16, 17]).choose.midiratio, \spread, rrand(0.005, 0.1), \p, rrand(-0.3, 0.3), \out, ~del, \pa, rrand(-0.5, 0.5), \att, rrand(6, 9), \a, rrand(0.01, 0.05), \f, rrand(50, 150));

        rrand(0.1, 4).wait;

    });

            20.do({
            ~synths[0].set(\buff, [~buff, ~buff2, ~buff3, ~buff4, ~buff5].wchoose([0.5, 0.3, 0.1, 0.08, 0.02]), \t_trig, 1, \t, rrand(10, 400), \dur, rrand(0.020, 0.40), \pos, rrand(0.0, 0.8), \l, rrand(0.4, 4), \rate, ([0, 4, 5, 7, 9, 12, 16, 17] - 12).choose.midiratio, \spread, rrand(0.005, 0.1), \p, rrand(-0.3, 0.3), \out, ~del, \pa, rrand(-0.5, 0.5), \att, rrand(6, 9), \a, rrand(0.05, 0.1), \f, rrand(50, 150));

            ~synths[1].set(\buff, [~buff, ~buff2, ~buff3, ~buff4, ~buff5].wchoose([0.5, 0.3, 0.1, 0.08, 0.02]), \t_trig, 1, \t, rrand(10, 400), \dur, rrand(0.020, 0.40), \pos, rrand(0.0, 0.8), \l, rrand(0.4, 4), \rate, ([0, 4, 5, 7, 9, 12, 16, 17]).choose.midiratio, \spread, rrand(0.005, 0.1), \p, rrand(-0.3, 0.3), \out, ~del, \pa, rrand(-0.5, 0.5), \att, rrand(6, 9), \a, rrand(0.05, 0.1), \f, rrand(50, 150));

            ~synths[2].set(\buff, [~buff, ~buff2, ~buff3, ~buff4, ~buff5].wchoose([0.5, 0.3, 0.1, 0.08, 0.02]), \t_trig, 1, \t, rrand(10, 400), \dur, rrand(0.020, 0.40), \pos, rrand(0.0, 0.8), \l, rrand(0.4, 4), \rate, ([0, 4, 5, 7, 9, 12, 16, 17] + 12).choose.midiratio, \spread, rrand(0.005, 0.1), \p, rrand(-0.3, 0.3), \out, ~del, \pa, rrand(-0.5, 0.5), \att, rrand(6, 9), \a, rrand(0.05, 0.1), \f, rrand(50, 150));

        rrand(0.1, 3).wait;

    });
                10.do({
            ~synths[0].set(\buff, [~buff, ~buff2, ~buff3, ~buff4, ~buff5].wchoose([0.5, 0.3, 0.1, 0.08, 0.02]), \t_trig, 1, \t, rrand(10, 400), \dur, rrand(0.020, 0.40), \pos, rrand(0.0, 0.8), \l, rrand(0.4, 4), \rate, ([0, 4, 5, 7, 9, 12, 16, 17] - 12).choose.midiratio, \spread, rrand(0.005, 0.1), \p, rrand(-0.3, 0.3), \out, ~del, \pa, rrand(-0.5, 0.5), \att, rrand(6, 9), \a, rrand(0.05, 0.1), \f, rrand(50, 150));

        rrand(1, 3).wait;

});



}).play(quant: 4);
})
)

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

A sequenced fx machine

posted by on 2014.01.19, under Supercollider
19:

Ever wondered how to make a sequenced fx machine, where the effects cut each other out (like dbGlitch, say)? Here’s a very simple example

SynthDef(\fx, {arg out = 0, in, trig = 0, lag = 0.1, grid = 1/2, bit = 7, sample = 5000;
    var sig, del;
    sig = In.ar(in,2)*0.5 + Select.ar(trig, [In.ar(in, 2)*0.5, LocalIn.ar(2), LPF.ar(Decimator.ar(In.ar(in,2)*0.5, sample, bit), 6000)]);
    del = DelayL.ar(sig, 1.0, Lag.kr(grid, lag));
    LocalOut.ar(HPF.ar(del*(trig.clip(0, 1)), 2000));
    Out.ar(out, Pan2.ar(sig[0], TRand.kr(-0.5, 0.5, trig)*trig));
}).add;

which uses the Ugen Select.ar, which functions as a “one-to-many” audio switch*. The delay part works as a simple beat repeater, and the other effect is a bit-crusher. After instantiating an audio bus, you can then delegate the sequencing to a Pmono

~rep = Bus.audio(s, 2);

Pmono(\fx, *[\trig : Pwrand([0, 1, 2], [0.4, 0.3, 0.3], inf),
    \grid: Prand([1/2, 1/4, 1/8, 1/16], inf),
    \bit: Prand([7, 10, 8, 24], inf),
    \sample: Pwhite(1000, 10100, inf),
    \lag: Pwhite(0, 0.1, inf),
    \in: ~rep,
    \dur: 1/8]).play(quant: 4);

Whatever you send to ~rep will be processed by the fx SynthDef (be careful with the order of execution, or use Group to be on the safe side). If you have already SynthDefs for the single effects you would like to use, it is more convenient to send the audio to single busses, and then add a SynthDef at the tail of the synth chain containing a Select.ar Ugen which will choose among the different effects. You will have to run additional Pmonos to control the various parameters, though.
Notice that this approach to sequenced fx machines is quite expensive, since the various effects will run constantly on the server.
Anyway, in this simple case it sounds like

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

*You can use a similar approach in Max/Msp, Reaktor, etc.

First L!vE K@ding session!

posted by on 2014.01.13, under Supercollider
13:

As promised, here’s my first live coding screencast! The video unfortunately turned out to be not very good: ffmpeg on Linux failed me miserably. I thought it was worth sharing anyways, so, here it is. ;)
Some comments: I have used some SynthDefs that I have already prepared and some samples that I have loaded beforehand. This is achieved by the tabs “initialize” and “synths” that you see in the video. This is not a “blank page” approach to live coding, but it’s what I have realized works for me, since I am more interested in improvising patterns than the actual sound synthesis (which I also did in this session, by the way). In particular, one of the SynthDefs which I am really liking is \looper, a custom made looper which allows me to capture audio from other synths and control its parameters to get nice glitchy patterns. I’m really liking it. :) On the other end, \pad_fm is a very simple pad with fm modulation. Of course, if anybody is interested in these SynthDefs I will certainly share.
Oh, also almost everything happens in ProxySpace.
Enjoy ;)

New Year Resolutions

posted by on 2013.12.31, under Uncategorized
31:

Here’s that time of the year when you make those resolutions you’ll never gonna keep. I’ll make mine and I’ll try to stick to it, hopefully. Well, what is it? Very simple: I want to practice more and more regularly live coding in SuperCollider AND record audio sessions, and post them here in a section called “L!vE K@ding”. I’m not sure I will be posting codes about it, though, ’cause when I live code I get very messy. But I’ll be happy to discuss techniques, etc., what I do, and don’t. Let me add I’m not an expert at all, and there’s some great people out there doing excellent things. But you know what? If you never start, you’ll never learn. The session is not perfect? Who cares, that’s how “live” is. ;)
So, watch this space, if you are interested.
Meanwhile, Happy New Coding Year!

Finite fields and musical phrasings

posted by on 2013.12.20, under Supercollider
20:

In this post I want to talk about musical phrasings in algorithmic composition and algebra over finite fields. Yep, algebra, so brace yourself  :-).
I am pretty sure you have been exposed to clock arithmetic: basically, you consider integer numbers modulo a natural number p. Now, in the case p is a prime, the commutative ring \mathbb{Z}_{p} is actually a field with finite elements. There is a name for that, and it is finite field, or Galois* field.
Now consider a pxp-matrix A with entries in \mathbb{Z}_{p}\:: A will give us a map \varphi_{A} from \mathbb{Z}_{p} to itself via

\varphi_{A}(i):=\sum_{j=0\ldots p-1}(A_{ij}\:j)\:{\rm mod}\:p

where A_{ij} are the entries of A. Now, consider a function f from \mathbb{Z}_{p} to a finite set S, and consider the subgroup P of the group of permutations of p objects given by elements \pi satifying

f(\pi(i)) = f(i)

If the function f is not injective, this subgroup will be not empty. Notice that for any function f of the type above, the matrix A gives us another function A^{*}f defined as**

A^{*}f(i):=f(\varphi_{A}(i))

Now we can ask ourselves: given a function f, can we find a matrix A such that

(A^{*})^{k}f = f

for some natural number k? What this means is that we want to find a matrix such that applied k-times to the function f returns the function itself. This problem is equivalent to finding a matrix A such that

A^{k} = \pi,\quad\pi\in{P}

In the case in which the function f is injective, this coincides with the problem of finding idempotents matrices in a finite field: the fantastic thing is that they are known to exist***, and even better their number is known in many situations!
“Ok, now, what all of this has to do with musical phrasing?! No, seriously, I’m getting annoyed, what really? ”
I hear your concern, but as with almost everything in life, it’s just a matter of perspective. So, consider the set S as a set of pairs (pitch, duration) for a note. A function f above will tell us in which order we play a note and what is the duration of each note: in other words, it is a musical phrasing. By picking a matrix A, we can generate from f a new phrasing, given by the function A^{*}f, and we can reiterate the process. If A is such that it satisfies the condition above, after a finite number of steps, the phrasing will repeat. Hence, the following Supercollider code

s.boot;

SynthDef(\mall,{arg out=0,note, amp = 1;
    var sig=Array.fill(3,{|n| SinOsc.ar(note.midicps*(n+1),0,0.3)}).sum;
    var env=EnvGen.kr(Env.perc(0.01,1.2), doneAction:2);
    Out.ar(out, sig*env*amp!2);
}).add;

(
var matrix, index, ind, notes, times, n, a;

notes = [48, 53, 52, 57, 53, 59, 60] + 12;
times = [1/2, 1/2, 1, 1/2, 1/2, 1, 1]*0.5;
n = 7;
matrix = Array.fill(n,{Array.fill(n, {rrand(0, n-1);})});
index = (0..(n-1));

a = Prout({
     inf.do({
            ind = [];
           
            matrix.collect({|row|
               ind = ind ++ [(row * index).sum % n];
               ((row * index).sum % n).yield;
            });
           index = ind;
         
     });
   }); 
       
   

Pbind(*[\instrument: \mall, \index: a, \note: Pfunc({|ev| notes[ev[\index]];}), \dur:Pfunc({|ev|
 times[ev[\index]];})]).trace.play;
)

s.quit;

represents a “sonification” of the probability distribution of finding a matrix A with the properties above, for p=7. We are assuming that the various matrices have equal probability to be generated in the code above.
So, how does this sound?
Like this

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

*By the way, you should check out the life of Galois, just to crush all your stereotypes about mathematicians. ;)
**Mathematicians love these “dual” definitions.
***Apart from the identity matrix and elements of P themselves, clearly.

On L-systems, dictionaries and all that

posted by on 2013.11.13, under Supercollider
13:

It’s quite a while I have not written anything here: Python, ChucK and functional programming are keeping me quite busy. ;)
Here I want to tell you about Lindenmayer systems (or L-systems) and how to use dictionaries in SuperCollider to sonify them. So, first of all, what is an L-system? An L-system is a recursive system which is obtained from an alphabet, an axiom word in the alphabet, and a set of production rules. Starting from the axiom word, one applies iteratively the production rules to each character in the word at the same time, obtaining a new word which allows the procedure to be repeated.
A classical example is the following: consider the alphabet L={A,B}, the set of rules {A -> AB, B -> A}, and axiom word W=A. This will give the following words by iteration

A
AB
ABA
ABAAB
ABAABABA

and so on.
You are probably thinking “Wait a minute: alphabets, words, formation rules? Has this all anything to do with languages?” And the answer is: “Yes! But…”, the main difference with (formal) languages being that the production rules in a formal grammar don’t need to be applied all at the same time.
Ok, all well and good, but you may ask “What is this all for?! I mean, why?!?”, and I suspect the fact that, for instance, the sequence of the length of the words in the above example gives you the Fibonacci sequence is not an appropriate answer. :) The point is that Lindenmayer came to realize that these iterative systems can describe nicely the growth of many types of plants and their branching structures. For instance, the example above describes the growth of algae. The truly surprising thing, also, is that L-systems can be used to generate all* the types of known fractals, like Mandelbrot, Koch curve, the Sierpinski triangle, etc., when the alphabet is appropriately geometrized. So. in a nutshell, L-systems are very cool things. ;)
What about dictionaries, then? Dictionaries are powerful objects in many programming languages, and are sort of arrays in which elements can be indexed with keys which are different from the index position of the element itself. They can be, for instance, strings or symbols**. Basically, they are perfect to work as mappings.
So, let’s look at the code

s.boot;

(
~path = "pathtofilefolder";

~kick = Buffer.read(s,~path ++ "sampleaddress");
~hhat = Buffer.read(s,~path ++ "sampleaddress");
~clap = Buffer.read(s,~path ++ "sampleaddress");

SynthDef(\playbuf,{arg out = 0, r = 1, buff = 0, amp = 1, t = 0;
    var sig = PlayBuf.ar(1, buff, r);
    var env = EnvGen.kr( Env.perc(0.01, 0.8), doneAction: 2);
    Out.ar(out, sig*env*amp*t!2);
}).add;

SynthDef(\mall,{arg out=0,note, amp = 1;
    var sig=Array.fill(3,{|n| SinOsc.ar(note.midicps*(n+1),0,0.3)}).sum;
    var env=EnvGen.kr(Env.perc(0.01,0.8), doneAction:2);
    Out.ar(out, sig*env*amp!2);
}).add;
)

(



var dict = IdentityDictionary[\A -> "AB", \B -> "A", \C -> "DB", \D -> "BC"]; //These are the production rules of the L-system
var word = "AC"; //Axiom word
var string_temp = "";
var iter = 10;

//These are diction for the mapping of the alphabet to "artistic" parameters: degrees in a scale, beat occurrence, etc.

var dictnotes = IdentityDictionary[\A -> 50, \B -> 55, \C -> 54, \D -> 57];
var dictkick = IdentityDictionary[\A -> 1, \B -> 0, \C -> 1, \D -> 0];
var dicthat = IdentityDictionary[\A -> 1, \B -> 0, \C -> 1, \D -> 1];
var notes=[];
var beat=[];
var beat2=[];

//This iteration generates the system recursively

iter.do({

word.asArray.do({|i|
    string_temp = string_temp ++ dict[i.asSymbol];
});

word = string_temp;
});

word.postln;

//Here we map the final system to the parameters as above

word.do({|i| notes = notes ++ dictnotes[i.asSymbol];});
word.do({|i| beat = beat ++ dictkick[i.asSymbol];});
word.do({|i| beat2 = beat2 ++ dicthat[i.asSymbol];});

notes.postln;
beat.postln;


s.record;

Pbind(*[\instrument: \mall, \note: Pseq(notes,inf), \amp: Pfunc({rrand(0.06,0.1)}), \dur: 1/4]).play(quant:32);
Pbind(*[\instrument: \mall, \note: Pseq(notes + 48,inf), \amp: Pfunc({rrand(0.06,0.1)}),\dur: 1/8]).play(quant:32 + 16);
Pbind(*[\instrument: \playbuf, \t: Pseq(beat,inf), \buff: ~kick, \dur: 1/8]).play(quant:32 + 32);
Pbind(*[\instrument: \playbuf, \t: Pseq(beat2,inf), \buff: ~hhat, \r:8, \dur: 1/8]).play(quant:32 + 32 + 8);
Pbind(*[\instrument: \playbuf, \t: Prand([Pseq([0,0,1,0],4), Pseq([0,1,0,0],1)],inf), \buff: ~clap, \r: 1,\dur: 1/4]).play(quant:32 + 32 + 8);

)

The code above is in a certain sense a “universal L-systems generator”, in the sense that you can change the alphabet, the production rules and the axiom and get your own system. A generalisation of L-systems are “probabilistic L-systems”, in which the production rules are weighted with a certain probability to happen. This is maybe the topic for another post, though. ;)
While listening to this, notice how the melodies and the beats tend to self replicate, but with “mutations”.

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

*I don’t know any mathematical proof of this statement.
**I have used Identity Dictionaries here, where keys are symbols.

Fun with additive synthesis and interfaces

posted by on 2013.10.01, under Supercollider
01:

When I started learning SuperCollider, I was motivated more by algorithmic and generative music. I was (and still am) fascinated by bits of code that can go on forever, and create weird soundscapes. Because of this, I never payed attention to build graphical interfaces. Another reason why I stayed away from interfaces was that I was using Pure Data before, which is an environment in which building the interfaces and coding are, from the user’s perspective, essentially the same thing. While it was fun for a while, I always got frustrated when, after spending some time building a patch, I wanted to scale it up: in most of the cases, I ended up with the infamous spaghetti monster. Studying additive synthesis was such a situation: once you understand the concept of a partial, and how to build a patch say with 4 of them, then extending it is only a matter of repeating the same job*. It’s almost a monkey job: enter the computer! :)
The code here shows exactly this paradigm: yes, you’ll have to learn a bunch of new classes, like Window, to even be able to see a slider. But after that, it’ll scale up easy peasy, i.e. in this case via the variable ~partials. So, as a sort of rule of thumb: if you think you will need to scale up your patch/code, then go for a text oriented programming language as opposed to a graphical environment. The learning curve is higher, but it’ll pay off.

s.boot;

(

//Setting the various Bus controls;
~freq=Bus.control(s);
~fm=Bus.control(s);
~ring=Bus.control(s);

~partials=16; //Number of partials;

~partials.do{|n|
~vol=~vol.add(Bus.control(s));
};

//Define the main Synth;

SynthDef(\harm,{arg out=0;
var vol=[];
var sig;
~freq.set(60);
sig=Array.fill(~partials,{|n| SinOsc.ar(Lag.kr(~freq.kr,0.1)*(n+1)+(SinOsc.kr(~freq.kr,0,1).range(0,1000)*(~fm.kr)),0,1)});
~partials.do{|n|
vol=vol.add(Lag.kr(~vol[n].kr,0.05));
};
Out.ar(out,(sig*vol).sum*(1+(SinOsc.ar(10,0,10)*Lag.kr(~ring.kr,0.1)))*0.1!2);
}).add;

//Build the Graphical interface;
w = Window(bounds:Rect(400,400,420,250)); //Creates the main window
w.front;
w.onClose_({x.free});

//Add the multislider;
n=~partials+1;
m = MultiSliderView(w,Rect(10,10,n*23+0,100));
m.thumbSize_(23);
m.isFilled_(true);
m.value=Array.fill(n, {|v| 0});
m.action = { arg q;
~partials.do{|n|
~vol[n].set(q.value[n]);
};
q.value.postln;
};
//Add sliders to control frequency, fm modulation, and ring modulation;

g=EZSlider(w,Rect(10,150,390,20),"Freq",ControlSpec(20,2000,\lin,0.01,60),{|ez| ~freq.set(ez.value);ez.value.postln});
g.setColors(Color.grey,Color.white);
g=EZSlider(w,Rect(10,180,390,20),"Fm",ControlSpec(0,1,\lin,0.01,0),{|ez| ~fm.set(ez.value);ez.value.postln});
g.setColors(Color.grey,Color.white);
g=EZSlider(w,Rect(10,210,390,20),"Ring",ControlSpec(0,0.1,\lin,0.001,0),{|ez| ~ring.set(ez.value);ez.value.postln});
g.setColors(Color.grey,Color.white);

//Create the synth
x=Synth(\harm);
)

s.quit;

If everything went according to plan, you should get this window :)

*Yes, I know the objections: you should have used encapsulations, macros, etc., but this didn’t do for me, because I rarely know what I’m going to obtain. “An LFO here? Sure I want it! Oh, wait, I have to connect again all these lines, for ALL partials?! Damn!” :)

Ambient soundscapes – A variation on theme

posted by on 2013.08.22, under Supercollider
22:

It’s always a good practice to take a simple idea, and look at it from a different perspective. So, I went back to the ambient code of my last post, and built a little variation. If you check that code, you’ll see that the various synths are allocated via a routine in a scheduled way, and it would go on forever. The little variation I came up with is the following: instead of allocating a synth via an iterative procedure which is independent of the synths playing, we will allocate new synths provided the amplitude of the playing synth exceeds a certain value. In plain English: “Yo, synth, every time your amplitude is greater than x, do me a favor darling, allocate a new synth instance. Thanks a lot!”. But how do we measure the amplitude of a synth? Is that possible? Yes, it is: the UGen Amplitude does exactly this job, being an amplitude follower. Now we are faced with a conceptual point, which I think it’s quite important, and often overlooked. SuperCollider consists a two “sides”: a language side, where everything related to the programming language itself takes place, and a server side, where everything concerning audio happens, and in particular UGens live. So, when you allocate a synth, the client side is sending a message to the audio server with instructions about the synth graph (i.e. how the various UGens interact with each others) to create. Since Amplitude lives on the server side, we need to find a way to send a message from the server side to the client side. Since in this case the condition we have, i.e. amplitude > x, is a boolean condition*, and we want to allocate a single synth every time the condition holds, we can use the UGen SendTrig, which will send an OSC message to the client side, which in turn will trigger the allocation of a new synth via an OSC function. We have almost solved the problem: indeed, SendTrig will send triggers continuosly (either at control rate or audio rate), while we need only 1 trigger per condition. For this, we can use a classic “debouncing” technique: we measure the time from a trigger to the previous one via the Timer UGen, check if “enough” time has passed, and only then allow a new trigger. The value chosen here is such that only one trigger per synth should happen. I say should, because in this case I end up getting two triggers per synth, which is not what I want… I don’t know really why this happens, but in the code you find a shortcut to solve this problem: simply “mark” the two triggers, and choose only the second one, which is accomplished by the “if” conditional in the OSC function. Of course, this little example opens the way to all cool ideas, like controlling part of synths or generate events via a contact microphone, etc. etc.

s.boot;
(
~out=Bus.audio(s,2);

//Synth definitions

SynthDef(\pad,{arg out=0,f=#[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],d=0,a=0.1,amp=0.11;
    var sig=0;
    var sig2=SinOsc.ar(f[15],0,0.08);
    var env=EnvGen.kr(Env.linen(6,7,10),doneAction:2);
    var amplitude;
    var trig;
    var trigCon;
    var timer;
   
    sig=Array.fill(16,{|n| [SinOsc.ar(f[n]+SinOsc.ar(f[n]*10,0,d),0,a),VarSaw.ar(f[n]+SinOsc.ar(f[n]*10,0,d),0,0.5,a),LFTri.ar(f[n]+SinOsc.ar(f[n]*10,0,d),0,a),WhiteNoise.ar(0.001)].wchoose([0.33,0.33,0.33,0.01])});
    sig=Splay.ar(sig,0.5);

    amplitude=Amplitude.kr((sig+sig2)*env);
    trig=amplitude > amp;
    timer=Timer.kr(trig);
    trigCon=(timer>4)*trig;
    SendTrig.kr(trigCon,0,Demand.kr(trigCon,0,Dseq([0,1])));

    Out.ar(out,((sig+sig2)*env)!2);
}).add;

SynthDef(\out,{arg out=0,f=0;
    var in=In.ar(~out,2);
    in=RLPF.ar(in,(8000+f)*LFTri.kr(0.1).range(0.5,1),0.5);
    in=RLPF.ar(in,(4000+f)*LFTri.kr(0.2).range(0.5,1),0.5);
    in=RLPF.ar(in,(10000+f)*LFTri.kr(0.24).range(0.5,1),0.5);
    in=in*0.5 + CombC.ar(in*0.5,4,1,14);
    in=in*0.99 + (in*SinOsc.ar(10,0,1)*0.01);
    Out.ar(out,Limiter.ar(in,0.9));
}).add;

//Choose octaves;
a=[72,76,79,83];
b=a-12;
c=b-12;
d=c-12;

//Define the function that will listen to OSC messages, and allocate synths

o = OSCFunc({ arg msg, time;
    [time, msg].postln;
    if(msg[3] == 1,{Synth(\pad,[\f:([a.choose,b.choose] ++ Array.fill(5,{b.choose}) ++ Array.fill(5,{c.choose}) ++ Array.fill(4,{d.choose})).midicps,\a:0.1/rrand(2,4),\d:rrand(0,40),\amp:rrand(0.10,0.115),\out:~out]);});
},'/tr', s.addr);
)

//Set the main out
y=Synth(\out);

Synth(\pad,[\f:([a.choose,b.choose] ++ Array.fill(5,{b.choose}) ++ Array.fill(5,{c.choose}) ++ Array.fill(4,{d.choose})).midicps,\a:0.1/rrand(2,4),\d:rrand(0,40),\amp:rrand(0.10,0.115),\out:~out]);

o.free;
y.free;

s.quit;

Here’s how it sounds. Notice that the audio finished on itself, there was no interaction on my side.

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

*Technically speaking, it’s not a boolean condition, since there are no boolean entities at server side. Indeed, amplitude > amp is a Binary Operator UGen… For this type of things, the Supercollider Book is your dearest friend.

Ambient soundscapes

posted by on 2013.08.09, under Supercollider
09:

It’s been a while since my last post, but I’ve been busy with the release of an Ep. And work. And other things. ;)
Here’s a code for all ambient soundscape lovers, which shows the power of code based programming languages. The code is not the cleanest, but I hope you get the idea, i.e. tons of oscillators, and filters! :)
I like here that the long drones are different in textures due to the fact that the single oscillators are randomly chosen.

s.boot;

~out=Bus.audio(s,2);

//Synth definitions

SynthDef(\pad,{arg out=0,f=#[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],d=0,a=0.1;
    var sig=0;
    var sig2=SinOsc.ar(f[15],0,0.08);
    var env=EnvGen.kr(Env.linen(6,7,10),doneAction:2);
    sig=Array.fill(16,{|n| [SinOsc.ar(f[n]+SinOsc.ar(f[n]*10,0,d),0,a),VarSaw.ar(f[n]+SinOsc.ar(f[n]*10,0,d),0,0.5,a),LFTri.ar(f[n]+SinOsc.ar(f[n]*10,0,d),0,a),WhiteNoise.ar(0.001)].wchoose([0.33,0.33,0.33,0.01])});
    sig=Splay.ar(sig,0.5);
    Out.ar(out,((sig+sig2)*env)!2);
}).add;

SynthDef(\out,{arg out=0,f=0;
    var in=In.ar(~out,2);
    in=RLPF.ar(in,(8000+f)*LFTri.kr(0.1).range(0.5,1),0.5);
    in=RLPF.ar(in,(4000+f)*LFTri.kr(0.2).range(0.5,1),0.5);
    in=RLPF.ar(in,(10000+f)*LFTri.kr(0.24).range(0.5,1),0.5);
    in=in*0.5 + CombC.ar(in*0.5,4,1,14);
    in=in*0.99 + (in*SinOsc.ar(10,0,1)*0.01);
    Out.ar(out,Limiter.ar(in,0.9));
}).add;

//Choose octaves;
a=[72,76,79,83];
b=a-12;
c=b-12;
d=c-12;

//Set the main out

y=Synth(\out);

//Main progression

t=fork{
    inf.do({
        y.set(\f,rrand(-1000,1000));
        Synth(\pad,[\f:([a.choose,b.choose] ++ Array.fill(5,{b.choose}) ++ Array.fill(5,{c.choose}) ++ Array.fill(4,{d.choose})).midicps,\a:0.1/rrand(2,4),\d:rrand(0,40),\out:~out]);
        rrand(5,10).wait;})

};


t.stop;

s.quit;

Of course, you could easily generalize this to a larger number of oscillators, filters, etc.
And even more, consider this just as a base for further sound manipulation, i.e. stretch it, granulize it, etc.
Experiment. Have fun. Enjoy. :)

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Wind Chimes

posted by on 2013.06.23, under Supercollider
23:

It has been a while, but I have been busy with a new work to be released in July, and recently I have spent some time fiddleing with Reaktor, which I have to say is quite good fun.
So, here’s something very simple and a classic in generative music, i.e. a wind chimes generator. I have decided to use only one graph synth in Supercollider (everything happens at “server side”), using a geiger generator (Dust) with varying density as a master clock, and a Demand Ugen as pitch selector. There is also a bit of FM synthesis, which is a nice way to get “tubular” type of sounds in this case. Finally, some delay and reverb.

s.boot;
{
    var pitch,env,density,scale,root,sig,mod,trig,pan,volenv;
    volenv=EnvGen.kr(Env.linen(10,80,20),1,doneAction:2);
    density=LFTri.kr(0.01).range(0.01,2.5);
    trig=Dust.kr(density);
    root=Demand.kr(trig,0,Drand([72,60,48,36],inf));
    pan=TRand.kr(-0.4,0.4,trig);
    scale=root+[0,2,4,5,7,9,11,12];
    pitch=Demand.kr(trig,0,Drand(scale,inf));
    env=EnvGen.kr(Env.perc(0.001,0.3),trig);
    mod=SinOsc.ar(pitch.midicps*4,0,800)*env;
    sig=SinOsc.ar(pitch.midicps+mod,0,0.2);
    FreeVerb.ar(CombL.ar(Pan2.ar(sig*env,pan),5,0.2,5))*volenv;
}.play;
s.quit;

Here’s how it sounds

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

pagetop