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!” :)

pagetop