2 summary:: Signal Processing in SuperCollider
4 categories:: UGens, Server>Nodes
7 ## 1. A Tour of available Unit Generators.
8 || SuperCollider has over 250 unit generators.
9 If you count the unary and binary operators, there are over 300.
10 This tour covers many, but not all of them.
12 categories of unit generators:
14 ## sources: periodic, aperiodic
19 ## delays and buffer ugens
21 ## control: envelopes, triggers, counters, gates, lags, decays
28 ## artificial space - decorrelation, beat frequencies, delays.
29 ## series and parallel structures.
33 Note: The link::Browse#UGens#category browser:: contains a category for UGens, which provides another useful way to get an overview of the available UGens, including those which were created since this tour was written.
43 section:: Periodic Sources: Oscillators.
45 subsection:: LF - "Low Frequency" Unit Generators.
47 link::Classes/LFPar::, link::Classes/LFCub::, link::Classes/LFTri::, link::Classes/Impulse::, link::Classes/LFSaw::, link::Classes/LFPulse::, link::Classes/VarSaw::, link::Classes/SyncSaw:: - has geometric waveforms, not band limited.
48 will cause aliasing at higher frequencies.
51 ## link::Classes/LFPar::, link::Classes/LFCub::, link::Classes/LFTri::, link::Classes/LFSaw::, link::Classes/Impulse::
52 || arguments: code::frequency, phase, mul, add::
55 // parabolic approximation of sine
56 { LFPar.ar(LFPar.kr(LFPar.kr(0.2,0,8,10),0, 400,800),0,0.1) }.scope(1, zoom: 4);
57 { LFPar.ar(LFPar.kr(0.2, 0, 400,800),0,0.1) }.scope(1, zoom: 4);
58 { LFPar.ar(800,0,0.1) }.scope(1, zoom: 4);
59 // since it is not band limited, there are aliasing artifacts
60 { LFPar.ar(XLine.kr(100,15000,6),0,0.1) }.scope(1, zoom: 4);
63 // cubic approximation of sine
64 { LFCub.ar(LFCub.kr(LFCub.kr(0.2,0,8,10),0, 400,800),0,0.1) }.scope(1, zoom: 4);
65 { LFCub.ar(LFCub.kr(0.2, 0, 400,800),0,0.1) }.scope(1, zoom: 4);
66 { LFCub.ar(800,0,0.1) }.scope(1, zoom: 4);
67 { LFCub.ar(XLine.kr(100,15000,6),0,0.1) }.scope(1, zoom: 4);
70 { LFTri.ar(LFTri.kr(LFTri.kr(0.2,0,8,10),0, 400,800),0,0.1) }.scope(1, zoom: 4);
71 { LFTri.ar(LFTri.kr(0.2, 0, 400,800),0,0.1) }.scope(1, zoom: 4);
72 { LFTri.ar(800,0,0.1) }.scope(1, zoom: 4);
73 { LFTri.ar(XLine.kr(100,15000,6),0,0.1) }.scope(1, zoom: 4);
76 { LFSaw.ar(LFSaw.kr(LFSaw.kr(0.2,0,8,10),0, 400,800),0,0.1) }.scope(1, zoom: 4);
77 { LFSaw.ar(LFSaw.kr(0.2, 0, 400,800),0,0.1) }.scope(1, zoom: 4);
78 { LFSaw.ar(100,0,0.1) }.scope(1, zoom: 4);
79 { LFSaw.ar(XLine.kr(100,15000,6),0,0.1) }.scope(1, zoom: 4);
82 { Impulse.ar(LFTri.kr(LFTri.kr(0.2,0,8,10),0, 400,800),0,0.1) }.scope(1, zoom: 4);
83 { Impulse.ar(LFTri.kr(0.2, 0, 400,800),0,0.1) }.scope(1, zoom: 4);
84 { Impulse.ar(100,0,0.1) }.scope(1, zoom: 4);
85 { Impulse.ar(XLine.kr(100,15000,6),0,0.1) }.scope(1, zoom: 4);
88 ## link::Classes/LFPulse::, link::Classes/VarSaw::
89 || arguments: code::frequency, phase, width, mul, add::
92 { LFPulse.ar(LFPulse.kr(LFPulse.kr(0.2,0,0.5,8,10),0,0.5, 400,800),0,0.5,0.1) }.scope(1, zoom: 4);
93 { LFPulse.ar(LFPulse.kr(3, 0, 0.3, 200, 200), 0, 0.2, 0.1) }.scope(1, zoom: 4);
94 { LFPulse.ar(XLine.kr(100,15000,6),0,0.5,0.1) }.scope(1, zoom: 4);
97 // pulse width modulation
98 { LFPulse.ar(100,0,MouseY.kr(0,1),0.1) }.scope(1, zoom: 4);
99 { LFPulse.ar(100,0,LFTri.kr(0.2,0,0.5,0.5),0.1) }.scope(1, zoom: 4);
102 { VarSaw.ar(VarSaw.kr(VarSaw.kr(0.2,0,0.2,8,10),0,0.2, 400,800),0,0.2,0.1) }.scope(1, zoom: 4);
103 { VarSaw.ar(VarSaw.kr(0.2, 0, 0.2, 400,800),0,0.2,0.1) }.scope(1, zoom: 4);
104 { VarSaw.ar(XLine.kr(100,15000,6),0,0.2,0.1) }.scope(1, zoom: 4);
107 // pulse width modulation
108 { VarSaw.ar(100,0,MouseY.kr(0,1),0.1) }.scope(1, zoom: 4);
109 { VarSaw.ar(100,0,LFTri.kr(0.2,0,0.5,0.5),0.1) }.scope(1, zoom: 4);
112 ## link::Classes/SyncSaw::
113 || arguments: code::syncFreq, sawFreq, mul, add::
116 { SyncSaw.ar(100, MouseX.kr(100, 1000), 0.1) }.scope(1, zoom: 4);
117 { SyncSaw.ar(100, Line.kr(100, 800, 12), 0.1) }.scope(1, zoom: 4);
121 subsection:: Band Limited Oscillators
123 link::Classes/SinOsc::, link::Classes/FSinOsc::, link::Classes/Blip::, link::Classes/Saw::, link::Classes/Pulse:: - will not alias.
126 ## link::Classes/SinOsc::, link::Classes/FSinOsc::
127 || arguments: code::frequency, phase, mul, add::
129 { SinOsc.ar(SinOsc.kr(SinOsc.kr(0.2,0,8,10),0, 400,800),0,0.1) }.scope(1, zoom: 4);
130 { SinOsc.ar(SinOsc.kr(0.2, 0, 400,800),0,0.1) }.scope(1, zoom: 4);
131 { SinOsc.ar(800,0,0.1) }.scope(1, zoom: 4);
132 { SinOsc.ar(XLine.kr(100,15000,6),0,0.1) }.scope(1, zoom: 4);
135 { FSinOsc.ar(800,0,0.1) }.scope(1, zoom: 4);
136 // FSinOsc should not be frequency modulated.
137 // Since it is based on a filter at the edge of stability, it will blow up:
138 { FSinOsc.ar(FSinOsc.kr(FSinOsc.kr(0.2,0,8,10),0, 400,800),0,0.1) }.scope(1, zoom: 4);
141 ## link::Classes/Blip::
142 || arguments: code::frequency, numHarmonics, mul, add::
144 { Blip.ar(XLine.kr(20000,200,6),100,0.2) }.scope(1);
145 { Blip.ar(XLine.kr(100,15000,6),100,0.2) }.scope(1); // no aliasing
146 // modulate number of harmonics
147 { Blip.ar(200,Line.kr(1,100,20),0.2) }.scope(1);
150 ## link::Classes/Saw::
151 || arguments: code::frequency, mul, add::
153 { Saw.ar(XLine.kr(20000,200,6),0.2) }.scope(1);
154 { Saw.ar(XLine.kr(100,15000,6),0.2) }.scope(1); // no aliasing
157 ## link::Classes/Pulse::
158 || arguments: code::frequency, width, mul, add::
160 { Pulse.ar(XLine.kr(20000,200,6),0.3,0.2) }.scope(1);
161 { Pulse.ar(XLine.kr(100,15000,6),0.3,0.2) }.scope(1); // no aliasing
163 // modulate pulse width
164 { Pulse.ar(200, Line.kr(0.01,0.99,8), 0.2) }.scope(1);
166 // two band limited square waves thru a resonant low pass filter
167 { RLPF.ar(Pulse.ar([100,250],0.5,0.1), XLine.kr(8000,400,5), 0.05) }.scope(1);
170 ## link::Classes/Klang:: - sine oscillator bank
171 || arguments: code:: `[ frequencies, amplitudes, phases ], mul, add ::
173 { Klang.ar(`[ [800, 1000, 1200],[0.3, 0.3, 0.3],[pi,pi,pi]], 1, 0) * 0.4}.scope(1);
175 { Klang.ar(`[ {exprand(400, 2000)}.dup(16), nil, nil ], 1, 0) * 0.04 }.scope(1);
179 subsection:: Table Oscillators
181 link::Classes/Osc::, link::Classes/COsc::, link::Classes/VOsc::, link::Classes/VOsc3:: - uses a buffer allocated on the server.
185 b = Buffer.alloc(s, 2048, 1, bufnum: 80);
186 b.sine1(1.0/(1..6), true, true, true);
191 ## link::Classes/Osc::
192 || arguments: code:: buffer number, frequency, phase, mul, add ::
195 { Osc.ar(80, 100, 0, 0.1) }.scope(1, zoom:4);
197 b.sine1(1.0/(1..12));
198 b.sine1(1.0/(1..24));
199 b.sine1(1.0/(1..32));
200 b.sine1([1.0/(1,3..12), 0].flop.flat.postln);
201 b.sine1([1.0/(1,3..32).squared, 0].flop.flat.postln);
202 b.sine1((1.dup(4) ++ 0.dup(8)).scramble.postln);
203 b.sine1((1.dup(4) ++ 0.dup(8)).scramble.postln);
204 b.sine1((1.dup(4) ++ 0.dup(8)).scramble.postln);
205 b.sine1((1.dup(4) ++ 0.dup(8)).scramble.postln);
206 b.sine1({1.0.rand2.cubed}.dup(8).round(1e-3).postln);
207 b.sine1({1.0.rand2.cubed}.dup(12).round(1e-3).postln);
208 b.sine1({1.0.rand2.cubed}.dup(16).round(1e-3).postln);
209 b.sine1({1.0.rand2.cubed}.dup(24).round(1e-3).postln);
212 ## link::Classes/COsc:: - two oscillators, detuned
213 || arguments: code:: buffer number, frequency, beat frequency, mul, add ::
215 b.sine1(1.0/(1..6), true, true, true);
217 { COsc.ar(80, 100, 1, 0.1) }.scope(1, zoom:4);
218 // change buffer as above.
221 ## link::Classes/VOsc:: - multiple wave table crossfade oscillators
222 || arguments: code:: buffer number, frequency, phase, mul, add ::
225 // allocate tables 80 to 87
226 8.do {|i| s.sendMsg(\b_alloc, 80+i, 1024); };
230 // fill tables 80 to 87
233 // generate array of harmonic amplitudes
234 n = (i+1)**2; // num harmonics for each table: [1,4,9,16,25,36,49,64]
235 a = {|j| ((n-j)/n).squared }.dup(n);
237 s.listSendMsg([\b_gen, 80+i, \sine1, 7] ++ a);
241 { VOsc.ar(MouseX.kr(80,87), 120, 0, 0.3) }.scope(1, zoom:4);
244 // allocate and fill tables 80 to 87
246 // generate array of harmonic amplitudes
247 a = {1.0.rand2.cubed }.dup((i+1)*4);
249 s.listSendMsg([\b_gen, 80+i, \sine1, 7] ++ a);
254 ## link::Classes/VOsc3:: - three VOscs summed.
255 || arguments: code:: buffer number, freq1, freq2, freq3, beat frequency, mul, add ::
258 { VOsc3.ar(MouseX.kr(80,87), 120, 121.04, 119.37, 0.2) }.scope(1, zoom:4);
261 { VOsc3.ar(MouseX.kr(80,87), 120, 151.13, 179.42, 0.2) }.scope(1, zoom:4);
265 section:: Aperiodic Sources: Noise.
267 subsection:: LF "Low Frequency" Noise Generators.
270 ## link::Classes/LFNoise0::, link::Classes/LFNoise1::, link::Classes/LFNoise2::, link::Classes/LFClipNoise::
271 || arguments: code:: frequency, mul, add ::
273 { LFClipNoise.ar(MouseX.kr(200, 10000, 1), 0.125) }.scope(1);
274 { LFNoise0.ar(MouseX.kr(200, 10000, 1), 0.25) }.scope(1);
275 { LFNoise1.ar(MouseX.kr(200, 10000, 1), 0.25) }.scope(1);
276 { LFNoise2.ar(MouseX.kr(200, 10000, 1), 0.25) }.scope(1);
279 { LFPar.ar(LFClipNoise.kr(MouseX.kr(0.5, 64, 1), 200, 400), 0, 0.2) }.scope(1, zoom:8);
280 { LFPar.ar(LFNoise0.kr(MouseX.kr(0.5, 64, 1), 200, 400), 0, 0.2) }.scope(1, zoom:8);
281 { LFPar.ar(LFNoise1.kr(MouseX.kr(0.5, 64, 1), 200, 400), 0, 0.2) }.scope(1, zoom:8);
282 { LFPar.ar(LFNoise2.kr(MouseX.kr(0.5, 64, 1), 200, 400), 0, 0.2) }.scope(1, zoom:8);
286 subsection:: Broad Spectrum Noise Generators
289 ## link::Classes/ClipNoise::, link::Classes/WhiteNoise::, link::Classes/PinkNoise::, link::Classes/BrownNoise::, link::Classes/GrayNoise::
290 || arguments: code:: mul, add ::
292 { ClipNoise.ar(0.2) }.scope(1);
293 { WhiteNoise.ar(0.2) }.scope(1);
294 { PinkNoise.ar(0.4) }.scope(1);
295 { BrownNoise.ar(0.2) }.scope(1);
296 { GrayNoise.ar(0.2) }.scope(1);
300 subsection:: Impulse Noise Generators
303 ## link::Classes/Dust::, link::Classes/Dust2::
304 || arguments: code:: density, mul, add ::
306 { Dust.ar(MouseX.kr(1,10000,1), 0.4) }.scope(1, zoom:4);
307 { Dust2.ar(MouseX.kr(1,10000,1), 0.4) }.scope(1, zoom:4);
311 subsection:: Chaotic Noise Generators
314 ## link::Classes/Crackle::
315 || arguments: code:: chaosParam, mul, add ::
317 { Crackle.ar(MouseX.kr(1,2), 0.5) }.scope(1);
323 subsection:: Low Pass, High Pass
326 ## link::Classes/LPF::, link::Classes/HPF:: - 12 dB / octave
327 || arguments: code:: in, freq, mul, add ::
329 { LPF.ar(WhiteNoise.ar, MouseX.kr(1e2,2e4,1), 0.2) }.scope(1);
330 { HPF.ar(WhiteNoise.ar, MouseX.kr(1e2,2e4,1), 0.2) }.scope(1);
331 { LPF.ar(Saw.ar(100), MouseX.kr(1e2,2e4,1), 0.2) }.scope(1);
332 { HPF.ar(Saw.ar(100), MouseX.kr(1e2,2e4,1), 0.2) }.scope(1);
336 subsection:: Band Pass, Band Cut
339 ## link::Classes/BPF::, link::Classes/BRF:: - 12 dB / octave
340 || arguments: code:: in, freq, rq, mul, add ::
342 rq is the reciprocal of the Q of the filter,
343 or in other words: the bandwidth in Hertz = rq * freq.
345 { BPF.ar(WhiteNoise.ar, MouseX.kr(1e2,2e4,1), 0.4, 0.4) }.scope(1);
346 { BRF.ar(WhiteNoise.ar, MouseX.kr(1e2,2e4,1), 0.4, 0.2) }.scope(1);
347 { BPF.ar(Saw.ar(100), MouseX.kr(1e2,2e4,1), 0.4, 0.4) }.scope(1);
348 { BRF.ar(Saw.ar(100), MouseX.kr(1e2,2e4,1), 0.4, 0.2) }.scope(1);
350 // modulating the bandwidth
351 { BPF.ar(WhiteNoise.ar, 3000, MouseX.kr(0.01,0.7,1), 0.4) }.scope(1);
355 subsection:: Resonant Low Pass, High Pass, Band Pass
358 ## link::Classes/RLPF::, link::Classes/RHPF:: - 12 dB / octave
359 || arguments: code:: in, freq, rq, mul, add ::
361 { RLPF.ar(WhiteNoise.ar, MouseX.kr(1e2,2e4,1), 0.2, 0.2) }.scope(1);
362 { RHPF.ar(WhiteNoise.ar, MouseX.kr(1e2,2e4,1), 0.2, 0.2) }.scope(1);
363 { RLPF.ar(Saw.ar(100), MouseX.kr(1e2,2e4,1), 0.2, 0.2) }.scope(1);
364 { RHPF.ar(Saw.ar(100), MouseX.kr(1e2,2e4,1), 0.2, 0.2) }.scope(1);
367 ## link::Classes/Resonz:: - resonant band pass filter with uniform amplitude
368 || arguments: code:: in, freq, rq, mul, add ::
370 // modulate frequency
371 { Resonz.ar(WhiteNoise.ar(0.5), XLine.kr(1000,8000,10), 0.05) }.scope(1);
373 // modulate bandwidth
374 { Resonz.ar(WhiteNoise.ar(0.5), 2000, XLine.kr(1, 0.001, 8)) }.scope(1);
376 // modulate bandwidth opposite direction
377 { Resonz.ar(WhiteNoise.ar(0.5), 2000, XLine.kr(0.001, 1, 8)) }.scope(1);
380 ## link::Classes/Ringz:: - ringing filter.
381 || arguments: code:: in, frequency, ring time, mul, add ::
383 Internally it is the same as Resonz but the bandwidth is expressed as a ring time.
385 { Ringz.ar(Dust.ar(3, 0.3), 2000, 2) }.scope(1, zoom:4);
387 { Ringz.ar(WhiteNoise.ar(0.005), 2000, 0.5) }.scope(1);
389 // modulate frequency
390 { Ringz.ar(WhiteNoise.ar(0.005), XLine.kr(100,3000,10), 0.5) }.scope(1, zoom:4);
392 { Ringz.ar(Impulse.ar(6, 0, 0.3), XLine.kr(100,3000,10), 0.5) }.scope(1, zoom:4);
394 // modulate ring time
395 { Ringz.ar(Impulse.ar(6, 0, 0.3), 2000, XLine.kr(0.04, 4, 8)) }.scope(1, zoom:4);
399 subsection:: Simpler Filters
401 ## link::Classes/OnePole::, link::Classes/OneZero:: - 6 dB / octave
404 { OnePole.ar(WhiteNoise.ar(0.5), MouseX.kr(-0.99, 0.99)) }.scope(1);
405 { OneZero.ar(WhiteNoise.ar(0.5), MouseX.kr(-0.49, 0.49)) }.scope(1);
409 subsection:: NonLinear Filters
411 ## link::Classes/Median::, link::Classes/Slew::
414 // a signal with impulse noise.
415 { Saw.ar(500, 0.1) + Dust2.ar(100, 0.9) }.scope(1);
416 // after applying median filter
417 { Median.ar(3, Saw.ar(500, 0.1) + Dust2.ar(100, 0.9)) }.scope(1);
419 // a signal with impulse noise.
420 { Saw.ar(500, 0.1) + Dust2.ar(100, 0.9) }.scope(1);
421 // after applying slew rate limiter
422 { Slew.ar(Saw.ar(500, 0.1) + Dust2.ar(100, 0.9),1000,1000) }.scope(1);
426 subsection:: Formant Filter
428 ## link::Classes/Formlet:: - A filter whose impulse response is similar to a FOF grain.
431 { Formlet.ar(Impulse.ar(MouseX.kr(2,300,1), 0, 0.4), 800, 0.01, 0.1) }.scope(1, zoom:4);
434 ## link::Classes/Klank:: - resonant filter bank
435 || arguments: code:: `[ frequencies, amplitudes, ring times ], mul, add ::
438 { Klank.ar(`[[200, 671, 1153, 1723], nil, [1, 1, 1, 1]], Impulse.ar(2, 0, 0.1)) }.play;
440 { Klank.ar(`[[200, 671, 1153, 1723], nil, [1, 1, 1, 1]], Dust.ar(8, 0.1)) }.play;
442 { Klank.ar(`[[200, 671, 1153, 1723], nil, [1, 1, 1, 1]], PinkNoise.ar(0.007)) }.play;
444 { Klank.ar(`[ {exprand(200, 4000)}.dup(12), nil, nil ], PinkNoise.ar(0.007)) }.scope(1);
446 { Klank.ar(`[ (1..13)*200, 1/(1..13), nil ], PinkNoise.ar(0.01)) }.scope(1);
448 { Klank.ar(`[ (1,3..13)*200, 1/(1,3..13), nil ], PinkNoise.ar(0.01)) }.scope(1);
455 ## abs, max, squared, cubed
458 { SinOsc.ar(300, 0, 0.2) }.scope(1);
459 { SinOsc.ar(300, 0, 0.2).abs }.scope(1);
460 { SinOsc.ar(300, 0, 0.2).max(0) }.scope(1);
461 { SinOsc.ar(300, 0).squared * 0.2 }.scope(1);
462 { SinOsc.ar(300, 0).cubed * 0.2 }.scope(1);
465 ## distort, softclip, clip2, fold2, wrap2,
468 { SinOsc.ar(300, 0, MouseX.kr(0.1,80,1)).distort * 0.2 }.scope(1);
469 { SinOsc.ar(300, 0, MouseX.kr(0.1,80,1)).softclip * 0.2 }.scope(1);
470 { SinOsc.ar(300, 0, MouseX.kr(0.1,80,1)).clip2(1) * 0.2 }.scope(1);
471 { SinOsc.ar(300, 0, MouseX.kr(0.1,80,1)).fold2(1) * 0.2 }.scope(1);
472 { SinOsc.ar(300, 0, MouseX.kr(0.1,80,1)).wrap2(1) * 0.2 }.scope(1);
473 { SinOsc.ar(300, 0, MouseX.kr(0.1,80,1)).wrap2(1) * 0.2 }.scope(1);
479 { SinOsc.ar(200, 0, 0.2).scaleneg(MouseX.kr(-1,1)) }.scope(1);
482 ## waveshaping by phase modulating a 0 Hz sine oscillator
483 || (currently there is a limit of 8pi)
488 in = SinOsc.ar(300, 0, MouseX.kr(0.1,8pi,1));
489 SinOsc.ar(0, in, 0.2); // 0 Hz sine oscillator
494 ## link::Classes/Shaper:: - input is used to look up a value in a table.
495 || Chebyshev polynomials are typically used to fill the table.
498 s.sendMsg(\b_alloc, 80, 1024); // allocate table
499 // fill with chebyshevs
500 s.listSendMsg([\b_gen, 80, \cheby, 7] ++ {1.0.rand2.squared}.dup(6));
502 { Shaper.ar(80, SinOsc.ar(600, 0, MouseX.kr(0,1))) * 0.3; }.scope(1);
504 s.listSendMsg([\b_gen, 80, \cheby, 7] ++ {1.0.rand2.squared}.dup(6));
505 s.listSendMsg([\b_gen, 80, \cheby, 7] ++ {1.0.rand2.squared}.dup(6));
515 s.options.numOutputBusChannels = 8;
516 s.options.numInputBusChannels = 8;
522 ## link::Classes/Pan2:: - equal power stereo pan a mono source
523 || arguments: code:: in, pan position, level ::
525 pan controls typically range from -1 to +1
528 { Pan2.ar(BrownNoise.ar, MouseX.kr(-1,1), 0.3) }.scope(2);
529 { Pan2.ar(BrownNoise.ar, SinOsc.kr(0.2), 0.3) }.scope(2);
532 ## link::Classes/LinPan2:: - linear pan a mono source (not equal power)
533 || arguments: code:: in, pan position, level ::
536 { LinPan2.ar(BrownNoise.ar, MouseX.kr(-1,1), 0.3) }.scope(2);
537 { LinPan2.ar(BrownNoise.ar, SinOsc.kr(0.2), 0.3) }.scope(2);
540 ## link::Classes/Balance2:: - balance a stereo source
541 || arguments: code:: left in, right in, pan position, level ::
543 { Balance2.ar(BrownNoise.ar, BrownNoise.ar, MouseX.kr(-1,1), 0.3) }.scope(2);
546 ## link::Classes/Pan4:: - equal power quad panner
549 { Pan4.ar(BrownNoise.ar, MouseX.kr(-1,1), MouseY.kr(1,-1), 0.3) }.scope(4);
552 ## link::Classes/PanAz:: - azimuth panner to any number of channels
553 || arguments: code:: num channels, in, pan position, level, width ::
555 { PanAz.ar(5, BrownNoise.ar, MouseX.kr(-1,1), 0.3, 2) }.scope(5);
558 { PanAz.ar(5, BrownNoise.ar, MouseX.kr(-1,1), 0.3, 3) }.scope(5);
561 ## link::Classes/XFade2:: - equal power cross fade between two inputs
562 || arguments: code:: in1, in2, crossfade, level ::
564 { XFade2.ar(BrownNoise.ar, SinOsc.ar(500), MouseX.kr(-1,1), 0.3) }.scope(1);
567 ## link::Classes/PanB2:: and link::Classes/DecodeB2:: - 2D ambisonics panner and decoder
572 var w, x, y, p, lf, rf, rr, lr;
574 p = BrownNoise.ar; // source
577 #w, x, y = PanB2.ar(p, MouseX.kr(-1,1), 0.3);
579 // B-format decode to quad. outputs in clockwise order
580 #lf, rf, rr, lr = DecodeB2.ar(4, w, x, y);
582 [lf, rf, lr, rr] // reorder to my speaker arrangement: Lf Rf Lr Rr
587 ## link::Classes/Rotate2:: - rotate a sound field of ambisonic or even stereo sound.
592 // rotation of stereo sound via mouse
594 x = Mix.fill(4, { LFSaw.ar(200 + 2.0.rand2, 0, 0.1) }); // left in
595 y = WhiteNoise.ar * LFPulse.kr(3,0,0.7,0.2); // right in
596 #x, y = Rotate2.ar(x, y, MouseX.kr(0,2));
606 ## link::Classes/FreeVerb::
611 // play with the room size
613 x = Klank.ar(`[[200, 671, 1153, 1723], nil, [1, 1, 1, 1]], Dust.ar(2, 0.1));
614 x = Pan2.ar(x, -0.2);
615 x = [x[0], DelayC.ar(x[1], 0.01, 0.01)]; // de-correlate
616 FreeVerb.ar(x, 0.75, 0.9, 0.4);
621 ## link::Classes/GVerb::
626 // play with the room size
628 x = Klank.ar(`[[200, 671, 1153, 1723], nil, [1, 1, 1, 1]], Dust.ar(2, 0.1));
629 GVerb.ar(x, 105, 5, 0.7, 0.8, 60, 0.1, 0.5, 0.4) + x;
635 section:: Delays and Buffer UGens
638 ## link::Classes/DelayN::, link::Classes/DelayL::, link::Classes/DelayC:: - simple delays
641 ## N - no interpolation
642 ## L - linear interpolation
643 ## C - cubic interpolation
645 arguments: code:: in, maximum delay time, current delay time, mul, add ::
649 // Dust randomly triggers Decay to create an exponential
650 // decay envelope for the WhiteNoise input source
652 z = Decay.ar(Dust.ar(1,0.5), 0.3, WhiteNoise.ar);
653 DelayN.ar(z, 0.1, 0.1, 1, z); // input is mixed with delay via the add input
660 z = Decay.ar(Impulse.ar(2,0,0.4), 0.3, WhiteNoise.ar);
661 DelayL.ar(z, 0.3, MouseX.kr(0,0.3), 1, z); // input is mixed with delay via the add input
666 ## link::Classes/CombN::, link::Classes/CombL::, link::Classes/CombC:: - feedback delays
667 || arguments: code:: in, maximum delay time, current delay time, echo decay time, mul, add ::
671 { CombN.ar(Decay.ar(Dust.ar(1,0.5), 0.2, WhiteNoise.ar), 0.2, 0.2, 3) }.scope(1, zoom:4);
673 // Comb used as a resonator. The resonant fundamental is equal to
674 // reciprocal of the delay time.
675 { CombN.ar(WhiteNoise.ar(0.02), 0.01, XLine.kr(0.0001, 0.01, 20), 0.2) }.scope(1);
677 { CombL.ar(WhiteNoise.ar(0.02), 0.01, XLine.kr(0.0001, 0.01, 20), 0.2) }.scope(1);
679 { CombC.ar(WhiteNoise.ar(0.02), 0.01, XLine.kr(0.0001, 0.01, 20), 0.2) }.scope(1);
681 // with negative feedback:
682 { CombN.ar(WhiteNoise.ar(0.02), 0.01, XLine.kr(0.0001, 0.01, 20), -0.2) }.scope(1);
684 { CombL.ar(WhiteNoise.ar(0.02), 0.01, XLine.kr(0.0001, 0.01, 20), -0.2) }.scope(1);
686 { CombC.ar(WhiteNoise.ar(0.02), 0.01, XLine.kr(0.0001, 0.01, 20), -0.2) }.scope(1);
688 { CombC.ar(Decay.ar(Dust.ar(1,0.1), 0.2, WhiteNoise.ar), 1/100, 1/100, 3) }.play;
689 { CombC.ar(Decay.ar(Dust.ar(1,0.1), 0.2, WhiteNoise.ar), 1/200, 1/200, 3) }.play;
690 { CombC.ar(Decay.ar(Dust.ar(1,0.1), 0.2, WhiteNoise.ar), 1/300, 1/300, 3) }.play;
691 { CombC.ar(Decay.ar(Dust.ar(1,0.1), 0.2, WhiteNoise.ar), 1/400, 1/400, 3) }.scope(1, zoom:4);
694 ## link::Classes/AllpassN::, link::Classes/AllpassL::, link::Classes/AllpassC:: - allpass delay
695 || arguments: code:: in, maximum delay time, current delay time, echo decay time, mul, add ::
700 z = Decay.ar(Dust.ar(1,0.5), 0.1, WhiteNoise.ar);
701 8.do { z = AllpassL.ar(z, 0.04, 0.04.rand, 2) };
707 ## link::Classes/PlayBuf:: - buffer playback
708 || arguments: code:: numChannels, buffer number, rate, trigger, start pos, loop ::
711 b = Buffer.read(s, Help.dir +/+ "sounds/a11wlk01.wav");
713 { SinOsc.ar(800 + (700 * PlayBuf.ar(1,b, BufRateScale.kr(b), loop:1)),0,0.3) }.scope(1);
716 { PlayBuf.ar(1,b, BufRateScale.kr(b), loop:1) }.scope(1);
719 // trigger one shot on each pulse
723 trig = Impulse.kr(2.0);
724 PlayBuf.ar(1,b,BufRateScale.kr(b),trig,0,0);
728 // trigger one shot on each pulse
732 trig = Impulse.kr(XLine.kr(0.1,100,30));
733 PlayBuf.ar(1,b,BufRateScale.kr(b),trig,5000,0);
738 // mouse control of trigger rate and startpos
742 trig = Impulse.kr(MouseY.kr(0.5,200,1));
743 PlayBuf.ar(1,b,BufRateScale.kr(b),trig,MouseX.kr(0,BufFrames.kr(b)),1)
747 // accelerating pitch
751 rate = XLine.kr(0.1,100,60);
752 PlayBuf.ar(1, b, rate, 1.0,0.0, 1.0)
756 // sine wave control of playback rate. negative rate plays backwards
760 rate = FSinOsc.kr(XLine.kr(0.2,8,30), 0, 3, 0.6);
761 PlayBuf.ar(1,b,BufRateScale.kr(b)*rate,1,0,1)
765 // zig zag around sound
769 rate = LFNoise2.kr(XLine.kr(1,20,60), 2);
770 PlayBuf.ar(1,b,BufRateScale.kr(b) * rate,1,0,1)
780 section:: Granular Synthesis.
783 ## link::Classes/TGrains:: - granulation of a buffer
784 || arguments: code:: numChannels, trigger, buffer number, rate, center pos, dur, pan, amp, interpolation ::
787 b = Buffer.read(s, Help.dir +/+ "sounds/a11wlk01.wav");
792 trate = MouseY.kr(2,200,1);
794 TGrains.ar(2, Impulse.ar(trate), b, 1, MouseX.kr(0,BufDur.kr(b)), dur, 0, 0.1, 2);
800 var trate, dur, clk, pos, pan;
801 trate = MouseY.kr(8,120,1);
803 clk = Impulse.kr(trate);
804 pos = MouseX.kr(0,BufDur.kr(b)) + TRand.kr(0, 0.01, clk);
805 pan = WhiteNoise.kr(0.6);
806 TGrains.ar(2, clk, b, 1, pos, dur, pan, 0.1);
813 var trate, dur, clk, pos, pan;
814 trate = MouseY.kr(8,120,1);
816 clk = Impulse.kr(trate);
817 pos = MouseX.kr(0,BufDur.kr(b)) + TRand.kr(0, 0.01, clk);
818 pan = WhiteNoise.kr(0.6);
819 TGrains.ar(4, clk, b, 1, pos, dur, pan, 0.1);
825 var trate, dur, clk, pos, pan;
826 trate = MouseY.kr(8,120,1);
828 clk = Dust.kr(trate);
829 pos = MouseX.kr(0,BufDur.kr(b)) + TRand.kr(0, 0.01, clk);
830 pan = WhiteNoise.kr(0.6);
831 TGrains.ar(2, clk, b, 1, pos, dur, pan, 0.1);
839 var trate, dur, clk, pos, pan;
840 trate = LinExp.kr(LFTri.kr(MouseY.kr(0.1,2,1)),-1,1,8,120);
842 clk = Impulse.ar(trate);
843 pos = MouseX.kr(0,BufDur.kr(b));
844 pan = WhiteNoise.kr(0.6);
845 TGrains.ar(2, clk, b, 1, pos, dur, pan, 0.1);
852 var trate, dur, clk, pos, pan;
854 dur = MouseY.kr(0.2,24,1) / trate;
855 clk = Impulse.kr(trate);
856 pos = MouseX.kr(0,BufDur.kr(b)) + TRand.kr(0, 0.01, clk);
857 pan = WhiteNoise.kr(0.6);
858 TGrains.ar(2, clk, b, 1, pos, dur, pan, 0.1);
864 var trate, dur, clk, pos, pan;
867 clk = Impulse.kr(trate);
868 pos = Integrator.kr(BrownNoise.kr(0.001));
869 pan = WhiteNoise.kr(0.6);
870 TGrains.ar(2, clk, b, 1, pos, dur, pan, 0.1);
876 var trate, dur, clk, pos, pan;
877 trate = MouseY.kr(1,400,1);
879 clk = Impulse.kr(trate);
880 pos = MouseX.kr(0,BufDur.kr(b));
881 pan = WhiteNoise.kr(0.8);
882 TGrains.ar(2, clk, b, 2 ** WhiteNoise.kr(2), pos, dur, pan, 0.1);
889 trate = MouseY.kr(2,120,1);
891 TGrains.ar(2, Impulse.ar(trate), b, (1.2 ** WhiteNoise.kr(3).round(1)), MouseX.kr(0,BufDur.kr(b)), dur, WhiteNoise.kr(0.6), 0.1);
899 ## link::Classes/GrainSin:: - sine grain
900 || arguments: code:: numChannels, trigger, dur, freq, pan, envbufnum ::
903 ( // using default window
905 var trigrate, winsize, trig;
906 trigrate = MouseX.kr(2, 120);
907 winsize = trigrate.reciprocal;
908 trig = Impulse.ar(trigrate);
909 GrainSin.ar(2, trig, winsize, TRand.ar(440.0, 880.0, trig), LFNoise1.kr(0.2),
914 b = Buffer.sendCollection(s, Env([0, 1, 0], [0.5, 0.5], [8, -8]).discretize, 1);
916 ( // using user supplied window
918 var trigrate, winsize, trig;
919 trigrate = MouseX.kr(2, 120);
920 winsize = trigrate.reciprocal;
921 trig = Impulse.ar(trigrate);
922 GrainSin.ar(2, trig, winsize, TRand.ar(440.0, 880.0, trig), LFNoise1.kr(0.2),
928 see also link::Classes/GrainFM::, link::Classes/GrainBuf:: and link::Classes/GrainIn::
932 subsection:: Filters for Controls
935 ## link::Classes/Decay:: - triggered exponential decay
936 || arguments: code:: in, decay time, mul, add ::
938 { WhiteNoise.ar * Decay.ar(Impulse.ar(1), 0.9, 0.2) }.scope(1, zoom:4);
939 { WhiteNoise.ar * Decay.ar(Dust.ar(3), 0.9, 0.2) }.scope(1, zoom:4);
940 { SinOsc.ar(Decay.ar(Dust.ar(4), 0.5, 1000, 400), 0, 0.2) }.scope(1, zoom:4);
943 ## link::Classes/Decay2:: - triggered exponential attack and exponential decay
944 || arguments: code:: trigger, attack time, decay time, mul, add ::
946 { WhiteNoise.ar * Decay2.ar(Impulse.ar(1), 0.2, 0.9, 0.2) }.scope(1, zoom:4);
947 { WhiteNoise.ar * Decay2.ar(Dust.ar(3), 0.2, 0.9, 0.2) }.scope(1, zoom:4);
950 ## link::Classes/Lag::
951 || arguments: code:: trigger, duration ::
953 { SinOsc.ar(Lag.ar(LFPulse.ar(2,0,0.5,800,400), MouseX.kr(0,0.5)), 0, 0.2) }.scope(1, zoom:4);
956 ## link::Classes/Integrator:: - leaky integrator
959 { SinOsc.ar(Integrator.ar(Dust2.ar(8), 0.99999, 200, 800), 0, 0.2) }.scope(1)
963 subsection:: Triggers
966 ## link::Classes/Trig::, link::Classes/Trig1:: - timed duration gate
967 || arguments: code:: trigger, duration ::
969 // amplitude determined by amplitude of trigger
970 { Trig.ar(Dust.ar(2), 0.2) * FSinOsc.ar(800, 0, 0.4) }.scope(1, zoom:4);
971 // amplitude always the same.
972 { Trig1.ar(Dust.ar(2), 0.2) * FSinOsc.ar(800, 0, 0.4) }.scope(1, zoom:4)
975 ## link::Classes/TDelay:: - delays a trigger. only delays one pending trigger at a time.
976 || arguments: code:: trigger, delay time ::
982 [(Trig1.ar(trig, 0.05) * FSinOsc.ar(600, 0, 0.2)),
983 (Trig1.ar(TDelay.ar(trig, 0.1), 0.05) * FSinOsc.ar(800, 0, 0.2))]
988 ## link::Classes/Latch:: - sample and hold
989 || arguments: code:: in, trigger ::
991 { Blip.ar(Latch.ar(WhiteNoise.ar, Impulse.ar(9)) * 400 + 500, 4, 0.2) }.play;
992 { Blip.ar(Latch.ar(SinOsc.ar(0.3), Impulse.ar(9)) * 400 + 500, 4, 0.2) }.play;
995 ## link::Classes/Gate:: - pass or hold
996 || arguments: code:: in, trigger ::
998 { Blip.ar(Gate.ar(LFNoise2.ar(40), LFPulse.ar(1)) * 400 + 500, 4, 0.2) }.scope(1, zoom:4);
1001 ## link::Classes/PulseCount:: - count triggers
1002 || arguments: code:: trigger, reset ::
1007 PulseCount.ar(Impulse.ar(10), Impulse.ar(0.4)) * 200,
1014 ## link::Classes/PulseDivider::
1015 || arguments: code:: trigger, div, start ::
1021 a = SinOsc.ar(1200, 0, Decay2.ar(p, 0.005, 0.1));
1022 b = SinOsc.ar(600, 0, Decay2.ar(PulseDivider.ar(p, MouseX.kr(1,8).round(1)), 0.005, 0.5));
1029 ## link::Classes/EnvGen:: - envelope generator
1030 || envelope is specified using an instance of the link::Classes/Env:: class.
1032 { EnvGen.kr(Env.perc, doneAction:2) * SinOsc.ar(880,0,0.2) }.play;
1033 { EnvGen.kr(Env.perc(1,0.005,1,4), doneAction:2) * SinOsc.ar(880,0,0.2) }.play;
1035 { EnvGen.kr(Env.perc, Impulse.kr(2)) * SinOsc.ar(880,0,0.2) }.play;
1036 { EnvGen.kr(Env.perc, Dust.kr(3)) * SinOsc.ar(880,0,0.2) }.play;
1038 // for sustain envelopes a gate is required
1039 z = { arg gate=1; EnvGen.kr(Env.adsr, gate, doneAction:2) * SinOsc.ar(880,0,0.2) }.play;
1043 // randomly generated envelope
1047 [0]++{1.0.rand.squared}.dup(n-1) ++ [0],
1048 {rrand(0.005,0.2)}.dup(n),
1050 EnvGen.kr(env, gate, doneAction: 2) * LFTri.ar(220,0,0.4)
1059 FFT, IFFT and the phase vocoder ugens.
1061 link::Classes/FFT:: calculates the spectrum of a sound, puts it into a buffer, and outputs a trigger each time the
1062 buffer is ready to process. The PV UGens process the spectrum when they receive the trigger.
1063 link::Classes/IFFT:: converts the spectrum back into sound.
1066 // alloc a buffer for the FFT
1067 b = Buffer.alloc(s,2048,1);
1069 c = Buffer.read(s, Help.dir +/+ "sounds/a11wlk01.wav");
1076 in = PlayBuf.ar(1,c, BufRateScale.kr(c), loop:1);
1083 // pass only magnitudes above a threshold
1086 in = PlayBuf.ar(1,c, BufRateScale.kr(c), loop:1);
1088 chain = PV_MagAbove(chain, MouseX.kr(0.1,512,1));
1094 // pass only magnitudes below a threshold
1097 in = PlayBuf.ar(1,c, BufRateScale.kr(c), loop:1);
1099 chain = PV_MagBelow(chain, MouseX.kr(0.1,512,1));
1105 // brick wall filter.
1108 in = PlayBuf.ar(1,c, BufRateScale.kr(c), loop:1);
1110 chain = PV_BrickWall(chain, MouseX.kr(-1,1));
1116 // pass random frequencies. Mouse controls how many to pass.
1117 // trigger changes the frequencies periodically
1120 in = PlayBuf.ar(1,c, BufRateScale.kr(c), loop:1);
1122 chain = PV_RandComb(chain, MouseX.kr(0,1), Impulse.kr(0.4));
1128 // rectangular comb filter
1131 in = PlayBuf.ar(1,c, BufRateScale.kr(c), loop:1);
1133 chain = PV_RectComb(chain, 8, MouseY.kr(0,1), MouseX.kr(0,1));
1139 // freeze magnitudes
1142 in = PlayBuf.ar(1,c, BufRateScale.kr(c), loop:1);
1144 chain = PV_MagFreeze(chain, LFPulse.kr(1, 0.75));
1151 section:: Techniques
1153 subsection:: Artificial Space
1154 Building a sense of space into a sound by setting up phase differences between the speakers.
1157 { var x; x = BrownNoise.ar(0.2); [x,x] }.scope(2); // correlated
1158 { {BrownNoise.ar(0.2)}.dup }.scope(2); // not correlated
1161 { var x; x = LPF.ar(BrownNoise.ar(0.2), MouseX.kr(100,10000)); [x,x] }.scope(2);
1163 { LPF.ar({BrownNoise.ar(0.2)}.dup, MouseX.kr(100,10000)) }.scope(2);
1169 x = Klank.ar(`[[200, 671, 1153, 1723], nil, [1, 1, 1, 1]], PinkNoise.ar(7e-3));
1173 { Klank.ar(`[[200, 671, 1153, 1723], nil, [1, 1, 1, 1]], PinkNoise.ar([7e-3,7e-3])) }.scope(2);
1175 // two waves mixed together coming out both speakers
1176 { var x; x = Mix.ar(VarSaw.ar([100,101], 0, 0.1, 0.2)); [x,x] }.scope(2);
1177 // two waves coming out each speaker independantly
1178 { VarSaw.ar([100,101], 0, 0.1, 0.2 * 1.414) }.scope(2); // * 1.414 to compensate for power
1180 // delays as cues to direction
1182 { var x; x = LFTri.ar(1000,0,Decay2.ar(Impulse.ar(4,0,0.2),0.004,0.2)); [x,x]}.scope(2);
1185 // inter-speaker delays
1186 { var x; x = LFTri.ar(1000,0,Decay2.ar(Impulse.ar(4,0,0.2),0.004,0.2));
1187 [DelayC.ar(x,0.01,0.01),DelayC.ar(x,0.02,MouseX.kr(0.02, 0))]
1192 // mixing two delays together
1193 // you hear a phasing sound but the sound is still flat.
1194 { var x; x = BrownNoise.ar(0.2);
1195 x = Mix.ar([DelayC.ar(x,0.01,0.01),DelayC.ar(x,0.02,MouseX.kr(0,0.02))]);
1201 // more spatial sounding. phasing causes you to perceive directionality
1202 { var x; x = BrownNoise.ar(0.2);
1203 [DelayC.ar(x,0.01,0.01),DelayC.ar(x,0.02,MouseX.kr(0.02, 0))]
1208 subsection:: Parallel Structures
1212 // mixing sine oscillators in parallel
1213 var n = 16; // number of structures to make
1214 // mix together parallel structures
1216 // this function creates an oscillator at a random frequency
1217 { FSinOsc.ar(200 + 1000.0.rand) }
1218 ) / (2*n) // scale amplitude
1224 // mixing sine oscillators in parallel
1225 var n = 16; // number of structures to make
1226 // mix together parallel structures
1228 // this function creates an oscillator at a random frequency
1229 { FSinOsc.ar(200 + 1000.0.rand + [0, 0.5]) }
1230 ) / (2*n) // scale amplitude
1236 // mixing sine oscillators in parallel
1237 var n = 16; // number of structures to make
1238 // mix together parallel structures
1242 amp = FSinOsc.kr(exprand(0.1,1),2pi.rand).max(0);
1244 FSinOsc.ar(exprand(100,1000.0), 0, amp),
1247 ) / (2*n) // scale amplitude
1255 n = 8; // number of 'voices'
1256 Mix.ar( // mix all stereo pairs down.
1257 Pan2.ar( // pan the voice to a stereo position
1258 CombL.ar( // a comb filter used as a string resonator
1259 Dust.ar( // random impulses as an excitation function
1260 // an array to cause expansion of Dust to n channels
1261 // 1 means one impulse per second on average
1265 0.01, // max delay time in seconds
1266 // array of different random lengths for each 'string'
1267 {0.004.rand+0.0003}.dup(n),
1268 4 // decay time in seconds
1270 {1.0.rand2}.dup(n) // give each voice a different pan position
1277 subsection:: Serial structures
1283 // The original sound source
1284 sig = sum({ SinOsc.ar(rrand(50,6000),0,2*Decay.ar(Dust2.ar(1),0.1)).tanh } ! 7);
1286 chain = sig; // Start with the original signal
1287 8.do {|i| // Loop 8 times. For each loop, connect the signal through something.
1290 chain = LeakDC.ar(AllpassL.ar(LPF.ar(chain*0.9,3000), 0.2, {0.19.rand+0.01}!2, 3));
1293 Limiter.ar(sig+chain); // dry + wet