19:
Via Reaktor tutorials I came across this video. I have already talked about generative systems that create rich patterns (see here, and here), and how simple rules can give rise to emergent complexity.
Watch the video to know what the simple rules are in this case (and to see why it is called “molecular” ), or look at the following SuperCollider code, which, I must say, took me a bit more than I thought
MIDIClient.init;
~mOut = MIDIOut.new(3);
(
var seed = 48;
var degrees, deg;
var length = Pseq([4, 3], inf).asStream;
var dur = length.next();
var bars = 25;
var quant = 16;
var notes = [];
var loop = [];
var pos = [];
var next = 0;
degrees = [];
//Building the MIDI values for the white keys
9.do({|i|
degrees = degrees++([0, 2, 4, 5, 7, 9, 11] + (12*i));
});
//Starting notes from the seed
deg = Pseq(degrees, inf, degrees.indexOf(seed)).asStream;
(bars * quant).do({|i|
var note;
if((i%quant == 0) && (notes != []),
{
loop = loop.add(notes);
notes = [];
});
if((i%quant == next) && (pos.includes(next) == false),{
notes = notes.add([deg.next(), dur/4]);
pos = pos.add(next);
next = (next + dur)%quant;
});
if ( (i%quant == next) && (pos.includes(next) == true),{
dur = length.next();
notes = notes.add([deg.next(), dur/4]);
next = (next + dur)%quant;
});
});
loop.do({|patt, i|
patt.postln;
patterns = patterns++([i * 4, Pbind(*[\type,\midi,\chan,0,
\midiout,~mOut,
[\midinote, \dur]: Pseq(patt, inf),
\legato: 1,
\amp: rrand(0.1,0.5)])]);
});
Ptpar(patterns, 1).trace.play;
)
Notice that you can very easily change any of the rules (duration length, scale used, etc.) with a few keyboard strokes: the power of a text based programming language!
I have sent the output of this to the Grand Piano instrument in Ableton Live 9.
Here is the result for 4C3
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.
and here is the one for 9C14½
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.
18:
In the last post, I wrote about some object oriented techniques in Processing which simplyfied our code, and gave the possibilty to reason more abstractly. Also SuperCollider is an object oriented programming language, and in this little example below I want to explore a situation where we can really take advantage of this: it somehow belongs to the category of “things one (or I?) can’t really do in Max/Msp, Reaktor, etc.”.
Here’s the simple situation: suppose we want to create 100 different percussive sounds with FM synthesis, with random waveforms as carrier, random harmonics for the modulating signal, etc., and play them randomly. We don’t want to change the sounds later, just play them. So, the first strategy we could adopt is the dirty and cheap no-brainer approach, which works along the following lines: well, what I will do is to create a SynthDef, i.e. an audio graph on the server, which is “big” enough to encompass the various possibilities of sounds, store the various randomly created combinations of waveforms, frequencies, etc. in an array, and poll these data when needed. The SynthDef would look like
SynthDef(\perc, { arg out = 0, freq = 0, decay = 0.5, pan = 0, depth = 1, waveform = 0, harm = 0;
var sig = Select.ar(waveform, [SinOsc.ar(freq + (SinOsc.ar(harm * freq) * depth)), Saw.ar(freq + (SinOsc.ar(harm * freq) * depth))]);
var env = EnvGen.kr(Env.perc(0.01, decay), doneAction: 2);
Out.ar(out, Pan2.ar(LPF.ar(sig * env * 0.1, 3000), pan));
}).add;
Setting the parameter waveform will choose among the waveforms, freq will determine the frequency, etc. A big array (or dictionary, if you prefer) with randomly generated values, and I’m done!
Well, yeah, this approach will work, but it’s quite ugly, and, more importantly, inefficient: indeed, for each Synth created, a copy is kept of the two waveforms, only one of which is heard. So, for each percussion sound, we allocate twice what we need. Remember that there is a difference between not having a sound generator on the audio server, and having one which is muted, since in the second case, the sample rate computations are performed anyways.
Let’s think about this situation from a different angle, which makes more use of the language capabilities of SuperCollider. What we really need are different percussion sounds: they are different enough to be considered not as different instances of the same SynthDefs, but rather as instances of different SynthDefs. Instead of having SuperCollider creating different Synth instances, we can tell it to create different SynthDefs! Indeed, consider the following code (assuming a given choice for the range of the parameters)
100.do({|i|
var name;
name = "perc"++i;
SynthDef(name, { arg out = 0;
var freq = [24, 57, 68, 29, 87, 39, 70].choose.midicps;
var decay = rrand(0.1, 0.5);
var pan = rrand(-1.0, 1.0);
var sig = [SinOsc, Saw].choose.ar(freq + (SinOsc.ar([0, 1, 2, 3, 4].choose*freq*rrand(1, 30))))*0.1;
var env = EnvGen.kr(Env.perc(0.01, decay), doneAction: 2);
Out.ar(out, Pan2.ar(LPF.ar(sig*env, 3000), pan));
}).add;
})
});
First, we are defining names with which we can refer to the SynthDef: this is easily done, since a name for a Synth is a string (or a symbol), and we can obtain each of them by concatenating a string (in this case “perc”) with the counter index. Next, we define different SynthDefs: the beautiful thing that makes this work in a nice way is contained in the part
[SinOsc, Saw].choose.ar()
Remember: SuperCollider is object oriented, and in most cases, even if we don’t think about it, we are dealing with objects. Indeed, when we write something like SinOsc.ar(440), we are actually creating an object of type SinOsc, and sending to it the value 440 via the method .ar (which stands for “audio rate”), which provides the necessary computations to produce audio on scsynth, the audio server. Since SinOsc, Saw, etc are an “extension” of the class UGen (more or less), we can make a list (or Collection) with objects of this type, and use the powerful collection manipulation methods provided by SuperCollider (in this case the method .choose). Since both SinOsc and Saw react in the same way to the method .ar (this is an example of “polymorphism”), we can have it follow the method .choose, and pass everything in the round brackets. In this way the SynthDef itself will only have one copy of the choosen waveform per name.
This way of reasoning might look a bit abstract at the beginning, but it’s based on a simple concept: everything that require repetitive actions is best left to a programming language! Moreover, since SuperCollider is heavily object oriented, it gives really serious manipulations possibilities, as we have seen. We used already an incarnation of this concept when we built graphical interfaces.
Finally, the percussive sounds can be played via something like this
fork{
inf.do({
i = rrand(0, 99);
Synth("perc"++i);
rrand(0.1, 0.4).wait;
})
}
Here’s the complete code
s.boot;
(
var reverb, delay;
reverb = Bus.audio(s, 2);
delay = Bus.audio(s, 2);
SynthDef(\reverb, {arg out = 0, in;
var sig = In.ar(in, 2);
sig = FreeVerb.ar(sig);
Out.ar(out, sig);
}).add;
SynthDef(\delay, {arg out = 0, in;
var sig = In.ar(in, 2) + LocalIn.ar(2);
LocalOut.ar(DelayL.ar(sig, 0.5, 0.1)*0.8);
Out.ar(out, sig);
}).add;
fork{
100.do({|i|
var name;
name = "perc"++i;
SynthDef(name, { arg out = 0;
var freq = [24, 57, 68, 29, 87, 39, 70].choose.midicps;
var decay = rrand(0.1, 0.5);
var pan = rrand(-1.0, 1.0);
var sig = [SinOsc, Saw].choose.ar(freq + (SinOsc.ar([0, 1, 2, 3, 4].choose*freq*rrand(1, 30))))*0.1;
var env = EnvGen.kr(Env.perc(0.01, decay), doneAction: 2);
Out.ar(out, Pan2.ar(LPF.ar(sig*env, 3000), pan));
}).add;
});
s.sync;
Synth(\reverb, [\in: reverb]);
Synth(\delay, [\in: delay]);
inf.do({
i = rrand(0, 99);
Synth("perc"++i, [\out: [reverb, delay].choose]);
rrand(0.1, 0.4).wait;
})
}
)
I have added some reverb and delay to taste.
Notice that this code illustrate only the tip of the iceberg concerning what you can achieve with an object oriented approach to structure your code.
No audio this time, since it is not that interesting.
17:
This code is a nice playground for some advanced aspects of Object Oriented Programming (OOP): it is simple enough to be interesting, and show the power and elegance of a more abstract approach to programming. What does the code do? It generates a number of bouncing particles which disappear when colliding with each other. Pretty simple. Now, in general, when you have to test a condition for an object against the totality of objects present, you may run soon in annoying cycles which can obscure the code itself, making it less readable, and which would be best avoided. To do this, I need to introduce one easy, but extremely important observation on how function in Processing (and Java, and many other languages) work: “Objects to a function are passed by reference“. Remember this, write it somewhere, do whatever but don’t forget it. “Oookay”, you say, “but, what does “passed by reference” even mean? What’s a “reference”?”. In OOP, objects are instantiated as initialized references, which means the following: suppose we have a class Ball (with empty constructor), and the following piece of code
In plain English, the code above says: create a label p which labels an object of type Ball, then create an object of type Ball, and attach to it the label p. In other words, p is *not* the object*, but it is just a label “referencing” the actual Ball object. Indeed, if we add the following piece of code
we are asking to create a new label q, referencing to the same object p is referencing. Now, as soon as we modify the actual object which is referenced, these modifications will reflect on the behaviour of p and q. For instance, suppose that the class Ball has a field a, of type integer, which is initialized to 10. Then the following code
println(q.a);
p.a = 15;
println(q.a);
will return 10, and then 15. In other words, changing p has changed *also* q, and the reason is that we are really not changing p, but rather the object p references to. A bit confusing, right? Neverthless I bet you have seen this behaviour before, if you have ever used (and who hasn’t) arrays.
Indeed, this piece of code would give the same results;
int[] p = new int[10];
int[] q;
q = p;
q[0] = 10;
p[0] = 15;
println(q[0]);
Okay, we are ready to talk about “passing by reference”**. Suppose you have a function
void f(Ball b){
do something;
}
and suppose we evaluate it on p, as in f(p). What happens is that b becomes a reference to the object referenced by p (as in b = p). So, if you modify b inside the scope of f, then you are really modifying the referenced object, and hence also p. Of course, the same is true for arrays, so be careful with what you do inside the scope of a function, when arrays are involved.
Supposing this is more or less, clear, the question remains: why caring about this for some stupid balls? Well, this simple observation allows to define something like this
class Ball{
int a;
Ball[] balls;
Ball(int _a, Ball[] _balls){
a = 10;
balls = _balls;
}
}
Wait, what’s going on here? Am I defining a class Ball in which one of its fields is an array of objects of type… Ball?! Like, isn’t there gonna be a nasty recursion type of end of the universe OMG!!
Calm down, nothing’s bad gonna happen. And the reason lies in the simple observations above: objects variable (and arrays) are defined as reference, and they need to be initialized. In the case above, we are asking for a label balls which is a reference to an array of type Ball: at this point we are *not* calling the constructor Ball( ). When it will be called, though, the array balls will reference to the same array referenced by the label (or variable) _balls. Here we are using that objects are passed to functions by reference (the constructor, in this case). Nice, uh?
Notice that, on the other hand, this piece of code
class Ball{
int a;
Ball p;
Ball(int _a){
a = 10;
p = new Ball();
}
}
will give a nasty recursion, since we are indeed calling the constructor inside itself.
Ok, still, what do we do with this?
Here’s the Processing code
int num = 50;
ArrayList<Particle> partls;
void setup(){
size(600, 600);
background(0);
partls = new ArrayList<Particle>();
for (int i = 0; i < num; i++){
partls.add(new Particle(random(0, width), random(0, height), partls));
}
}
void draw(){
ArrayList<Particle> remove = new ArrayList<Particle>();
background(0);
for (Particle par: partls){
par.update();
par.display();
}
for (Particle par: partls){
if (par.collide()) remove.add(par);
}
for (Particle par: remove){
partls.remove(par);
}
}
///Define the class Particle
class Particle{
float x, y, vx, vy, r;
ArrayList<Particle> others;
Particle(float _x, float _y, ArrayList<Particle> _others){
x = _x;
y = _y;
vx = random(-5, 5);
vy = random(-5, 5);
others = _others;
r = 20;
}
void update(){
if ( x <=0 || x >= width) vx = -vx;
if ( y <=0 || y >= height) vy = -vy;
x = (x + vx);
y = (y + vy);
}
void display(){
fill(255, 200);
ellipse(x, y, r, r);
}
boolean collide(){
boolean b = false;
for (Particle p: others){
if (dist(x, y, p.x, p.y) <= r & dist(x, y, p.x, p.y) > 0.0){
b = true;
}
}
return b;
}
}
Let’s look at the Particle class. Among other fields, we have others, an ArrayList of type Particle, which refers to the ArrayList _others passed to the constructor. The variable others is used in the method collide, which tests the distance between the instantiated object and all the particles in the list others, and returns the value true if a collision happens (the condition dist(x, y, p.x, p.y) > 0.0 ensures no self-collision). Also, notice the “enhanced” for loop, which is pretty elegant.
Now, the magic appears in the main code, namely in
partls = new ArrayList();
for (int i = 0; i < num; i++){
partls.add(new Particle(random(0, width), random(0, height), partls));
}
The first line initializes the variable partls as an (empty) ArrayList of type Particle. In the for loop, we add to partls some objects of type Particle by passing to the constructor the required fields: x, y, and others. Since objects are passed by reference to the constructor, this means that, at the end of the for loop, for each object instance of Particle the field others will reference to the same ArrayList referenced by partls, which, by construction, is an ArrayList in which each element is exactly the object Particle created in the for loop. The reference partls gets modified at each iteration of the cycle, and since we are passing it by reference, this modification will reflect also on the variable others. Pretty cool, uh?
This is quite a convenient way to manage collisions. Indeed, in the draw() function we have first the usual (enhanced) for loop which updates, and displays the particles. Then we check for collisions: if the particle collides with any other particle, we add it to a remove list. We need to do this, instead of removing it on the spot, because otherwise only one of the two particles involved in a collision would get removed. Finally, we have another for loop, in order to dispose of the colliding particles.
Everything is pretty elegant and compact, and most importantly, the code is more readable this way!
No video this time, since it’s pretty visually uninteresting.
*Not 100% true, but pretty close.
**There exists also the notion of “passing by value”.
13:
It has been a while since my last update, and in particular since my last Processing code! So, here’s something very simple. Basically, it’s a grid of points connected by lines, which form the edges of polygons. The points move almost periodically: each of them undergoes an oscillatory motion, to which I’ve added a bit of noise, to make things more interesting. The polygons, which during the motion change their shape as the vertices move, are colored. Here comes the nice part: the color of a single polygon is given by taking at its geometric center the color of the pixel of the image obtained by capturing a frame via the webcam. In this way, the colors are “smeared”, but you can still somehow discern the webcam input. Here’s the code.
import processing.video.*;
Capture cam;
int n = 30;
float t = 0;
Point[][] points = new Point[n][n];
void setup(){
size(320, 240);
cam = new Capture(this, 320, 240, 30);
cam.start();
background(0);
/* Setup the grid;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
points[i][j] = new Point(i*width/n, j*height/n);
}
}
}
void draw(){
if(cam.available()) {
cam.read();
}
background(0);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
points[i][j].move();
points[i][j].display();
}
}
grid(points);
t+= 0.1;
}
void grid(Point[][] gr){
int c_x, c_y;
for (int i = 0 ; i < n; i++){
for (int j = 0; j < n; j++){
if ( j < n -1 && i < n - 1){
stroke(255, 100);
/* Compute the geometric center;
c_x = constrain(int((gr[i][j].pos.x + gr[i][j + 1].pos.x + gr[i + 1][j + 1].pos.x + gr[i + 1][j].pos.x)/4), 0, width);
c_y = constrain(int((gr[i][j].pos.y + gr[i][j + 1].pos.y + gr[i + 1][j + 1].pos.y + gr[i + 1][j].pos.y)/4), 0, height);
/* Create the polygon;
fill(cam.pixels[constrain(c_x + width*c_y, 0, cam.pixels.length - 1)], 250);
beginShape();
vertex(gr[i][j].pos.x, gr[i][j].pos.y);
vertex(gr[i][j + 1].pos.x, gr[i][j + 1].pos.y);
vertex(gr[i + 1][j + 1].pos.x, gr[i + 1][j + 1].pos.y);
vertex(gr[i + 1][j].pos.x, gr[i + 1][j].pos.y);
endShape();
}
}
}
}
/* Define the class Point
class Point{
PVector pos;
float angle;
float depth;
PVector dir;
float phase;
float vel;
Point(float _x, float _y){
pos = new PVector(_x, _y);
angle = random(0.0, 2*PI);
depth = random(0.8, 2.4);
dir = new PVector(cos(angle), sin(angle));
vel = random(0.5, 1.5);
}
void display(){
noStroke();
fill(255, 255);
ellipse(pos.x, pos.y, 4, 4);
}
void move(){
/* Oscillatory motion with noise which depends on the "time" variable t;
pos.x = pos.x + dir.x * cos(vel*t + noise(t*angle)*0.1) * depth;
pos.y = pos.y + dir.y * cos(vel*t + noise(t*angle)*0.1) * depth;
}
}
Warning: Kids, don’t code the way I do! In this case I was particularly lazy: indeed, a more elegant way to do it would have been to create another class, say Grid, and hide there the polygon making, etc. The code would have been more readable and reusable, that way. But as I said, lazyness.
Here’s a little video of what you get
(If it doesn’t work, you can download or stream this )