supernova: fix for small audio vector sizes
[supercollider.git] / HelpSource / Tutorials / Mark_Polishook_tutorial / 17_Delays_reverbs.schelp
blob2097e55ca689860c20363c9eb604a7d385b70b0c
1 title:: 17_Delays_reverbs
2 summary:: Mark Polishook tutorial
3 categories:: Tutorials>Mark_Polishook_tutorial
4 related:: Tutorials/Mark_Polishook_tutorial/00_Introductory_tutorial
6 section::Time-based filters
8 The Delay, Comb, and Allpass family of ugens create time-based effects to give a sense of location and space.
10 ////////////////////////////////////////////////////////////////////////////////////////////////////
12 code::
13 // 2 synthdefs - the 1st to make grains and the 2nd to delay them
15 // the synthdef that makes the grains is on the left channel
16 // the synthdef that delays the grains is on the right channel
18 SynthDef("someGrains", { arg centerFreq = 777, freqDev = 200, grainFreq = 2;
19         var gate;
20         gate = Impulse.kr(grainFreq);
21         Out.ar(
22                 0,
23                 SinOsc.ar(
24                         LFNoise0.kr(4, freqDev, centerFreq),
25                         0,
26                         EnvGen.kr(Env.sine(0.1), gate, 0.1)
27                 )
28         )
29 }).add;
31 SynthDef("aDelay", { arg delay = 0.25;
32         Out.ar(
33                 1,
34                 DelayN.ar(
35                         In.ar(0, 1),
36                         delay,
37                         delay
38                 )
39         )
40 }).add;
43 ////////////////////////////////////////////////
44 // test the grains ... and then turn them off
45 // ... they're all on the left channel ... good!
46 Synth("someGrains");
47 ////////////////////////////////////////////////
49 // make 2 groups, the 1st for sources and the 2nd for effects
51 ~source = Group.head(s);
52 ~effects = Group.tail(s);
55 // place grains into the delay ... source is on the left and delayed source is on the right
57 Synth.head(~source, "someGrains");
58 Synth.head(~effects, "aDelay");
62 section::Feedback filters
64 Comb and Allpass filters are examples of ugens that feed some of their output back into their input. Allpass filters change the phase of signals passed through them. For this reason, they're useful even though don't seeem to differ much from comb filters.
66 code::
67 /////////////////////////////////////////////////////////////////////////////////////////
68 // TURN ON THE INTERNAL SERVER!!
69 // first a comb filter and then an allpass with (with the same parameters) - compare them
70 /////////////////////////////////////////////////////////////////////////////////////////
72 // comb example
75         CombN.ar(
76                 SinOsc.ar(500.rrand(1000), 0, 0.2) * Line.kr(1, 0, 0.1),
77                 0.3,
78                 0.25,
79                 6
80         )
81 }.scope;
84 // allpass example - not much difference from the comb example
87         AllpassN.ar(
88                 SinOsc.ar(500.rrand(1000), 0, 0.2) * Line.kr(1, 0, 0.1),
89                 0.3,
90                 0.25,
91                 6
92         )
93 }.scope;
97 code::
98 /////////////////////////////////////////////////////////////////////////////////////////
100 // first a comb example and then an allpass
101 // both examples have the same parameters
102 // the 2 examples have relatively short delay times ... 0.1 seconds
104 /////////////////////////////////////////////////////////////////////////////////////////
107 // comb
110         CombN.ar(
111                 SinOsc.ar(500.rrand(1000), 0, 0.2) * Line.kr(1, 0, 0.1),
112                 0.1,
113                 0.025,
114                 6
115         )
116 }.scope;
119 // allpass ... what's the difference between this example and the comb filter?
122         AllpassN.ar(
123                 SinOsc.ar(500.rrand(1000), 0, 0.2) * Line.kr(1, 0, 0.1),
124                 0.1,
125                 0.025,
126                 6
127         )
128 }.scope
132 section::Reverberation
134 The next example is by James McCartney. It comes from the link::#why_supercollider_2.0_?#01 Why SuperCollider:: document that was part of the SuperCollider2 distribution.
136 The example is more or less a Schroeder reverb - a signal passed through a parallel bank of comb filters which then pass through a series of allpass filters.
138 code::
141 var s, z, y;
142         // 10 voices of a random sine percussion sound :
143 s = Mix.ar(Array.fill(10, { Resonz.ar(Dust.ar(0.2, 50), 200 + 3000.0.rand, 0.003)}) );
144         // reverb predelay time :
145 z = DelayN.ar(s, 0.048);
146         // 7 length modulated comb delays in parallel :
147 y = Mix.ar(Array.fill(7,{ CombL.ar(z, 0.1, LFNoise1.kr(0.1.rand, 0.04, 0.05), 15) }));
148         // two parallel chains of 4 allpass delays (8 total) :
149 4.do({ y = AllpassN.ar(y, 0.050, [0.050.rand, 0.050.rand], 1) });
150         // add original sound to reverb and play it :
151 s+(0.2*y)
152 }.scope
156 section::Components
158 The following shows one way to divide the JMC example into components.
160 code::
162 SynthDef("filteredDust", {
163         Out.ar(
164                 2,
165                 Mix.arFill(10, { Resonz.ar(Dust.ar(0.2, 50), Rand(200, 3200), 0.003) })
166         )
167 }).add;
169 SynthDef("preDelay", {
170         ReplaceOut.ar(
171                 4,
172                 DelayN.ar(In.ar(2, 1), 0.048, 0.048)
173         )
174 }).add;
176 SynthDef("combs", {
177         ReplaceOut.ar(
178                 6,
179                 Mix.arFill(7, { CombL.ar(In.ar(4, 1), 0.1, LFNoise1.kr(Rand(0, 0.1), 0.04, 0.05), 15) })
180         )
181 }).add;
183 SynthDef("allpass", { arg gain = 0.2;
184         var source;
185         source = In.ar(6, 1);
186         4.do({ source = AllpassN.ar(source, 0.050, [Rand(0, 0.05), Rand(0, 0.05)], 1) });
187         ReplaceOut.ar(
188                 8,
189                 source * gain
190         )
191 }).add;
193 SynthDef("theMixer", { arg gain = 1;
194         ReplaceOut.ar(
195                 0,
196                 Mix.ar([In.ar(2, 1), In.ar(8, 2)]) * gain
197         )
198 }).add;
201 // as each line is executed, it becomes the tail node. the result is that
202 // "filteredDust" is the first node and  "theMixer" is the last node ...
203 // ... exactly what we need
205 Synth.tail(s, "filteredDust");
206 Synth.tail(s, "preDelay");
207 Synth.tail(s, "combs");
208 Synth.tail(s, "allpass");
209 Synth.tail(s, "theMixer");
213 s.queryAllNodes;
217 ////////////////////////////////////////////////////////////////////////////////////////////////////
219 Or, use groups to control the order of execution.
221 code::
223 ~source = Group.tail(s);
224 ~proc1 = Group.tail(s);
225 ~proc2 = Group.tail(s);
226 ~proc3 = Group.tail(s);
227 ~final = Group.tail(s);
230 // the nodes, below, are assigned to the groups, as ordered above,
232 Synth.head(~final, "theMixer");
233 Synth.head(~proc3, "allpass");
234 Synth.head(~proc2, "combs");
235 Synth.head(~proc1, "preDelay");
236 Synth.head(~source, "filteredDust");
240 s.queryAllNodes;
244 ////////////////////////////////////////////////////////////////////////////////////////////////////
246 For context, here, below, is the complete text of the strong::01 Why SuperCollider:: document (by James McCartney) from the SuperCollider 2 distribution.
248 section::Why SuperCollider 2.0 ?
250 SuperCollider version 2.0 is a new programming language. strong::Why invent a new language and not use an existing language?:: Computer music composition is a specification problem. Both sound synthesis and the composition of sounds are complex problems and demand a language which is highly expressive in order to deal with that complexity. Real time signal processing is a problem demanding an efficient implementation with bounded time operations.
251 There was no language combining the features I wanted and needed for doing digital music synthesis. The SuperCollider language is most like Smalltalk. Everything is an object. It has class objects, methods, dynamic typing, full closures, default arguments, variable length argument lists, multiple assignment, etc. The implementation provides fast, constant time method lookup, real time garbage collection, and stack allocation of most function contexts while maintaining full closure semantics.
252 The SuperCollider virtual machine is designed so that it can be run at interrupt level. There was no other language readily available that was high level, real time and capable of running at interrupt level.
254 SuperCollider version 1.0 was completely rewritten to make it both more expressive and more efficient. This required rethinking the implementation in light of the experience of the first version. It is my opinion that the new version has benefitted significantly from this rethink. It is not simply version 1.0 with more features.
256 strong::Why use a text based language rather than a graphical language? ::
257 There are at least two answers to this. strong::Dynamism:: : Most graphical synthesis environments use statically allocated unit generators. In SuperCollider, the user can create structures which spawn events dynamically and in a nested fashion. Patches can be built dynamically and parameterized not just by floating point numbers from a static score, but by other graphs of unit generators as well. Or you can construct patches algorithmically on the fly. This kind of fluidity is not possible in a language with statically allocated unit generators.
258 strong::Brevity:: : In SuperCollider, symmetries in a patch can be exploited by either multichannel expansion or programmatic patch building. For example, the following short program generates a patch of 49 unit generators. In a graphical program this might require a significant amount of time and space to wire up. Another advantage is that the size of the patch below can be easily expanded or contracted just by changing a few constants.
260 code::
263         // 10 voices of a random sine percussion sound :
264 s = Mix.ar(Array.fill(10, { Resonz.ar(Dust.ar(0.2, 50), 200 + 3000.0.rand, 0.003)}) );
265         // reverb predelay time :
266 z = DelayN.ar(s, 0.048);
267         // 7 length modulated comb delays in parallel :
268 y = Mix.ar(Array.fill(7,{ CombL.ar(z, 0.1, LFNoise1.kr(0.1.rand, 0.04, 0.05), 15) }));
269         // two parallel chains of 4 allpass delays (8 total) :
270 4.do({ y = AllpassN.ar(y, 0.050, [0.050.rand, 0.050.rand], 1) });
271         // add original sound to reverb and play it :
272 s+(0.2*y)
273 }.play )
276 Graphical synthesis environments are becoming a dime a dozen. It seems like a new one is announced every month. None of them have the dynamic flexibility of SuperCollider's complete programming environment. Look through the SuperCollider help files and examples and see for yourself.
278 ////////////////////////////////////////////////////////////////////////////////////////////////////
280 go to link::Tutorials/Mark_Polishook_tutorial/18_Frequency_modulation::