File: 17_Delays_reverbs.schelp

package info (click to toggle)
supercollider 1%3A3.6.6~repack-2-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 23,792 kB
  • ctags: 25,269
  • sloc: cpp: 177,129; lisp: 63,421; ansic: 11,297; python: 1,787; perl: 766; yacc: 311; sh: 286; lex: 181; ruby: 173; makefile: 168; xml: 13
file content (280 lines) | stat: -rw-r--r-- 9,447 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
title:: 17_Delays_reverbs
summary:: Mark Polishook tutorial
categories:: Tutorials>Mark_Polishook_tutorial
related:: Tutorials/Mark_Polishook_tutorial/00_Introductory_tutorial

section::Time-based filters

The Delay, Comb, and Allpass family of ugens create time-based effects to give a sense of location and space.

////////////////////////////////////////////////////////////////////////////////////////////////////

code::
// 2 synthdefs - the 1st to make grains and the 2nd to delay them

// the synthdef that makes the grains is on the left channel
// the synthdef that delays the grains is on the right channel
(
SynthDef("someGrains", { arg centerFreq = 777, freqDev = 200, grainFreq = 2;
	var gate;
	gate = Impulse.kr(grainFreq);
	Out.ar(
		0,
		SinOsc.ar(
			LFNoise0.kr(4, freqDev, centerFreq),
			0,
			EnvGen.kr(Env.sine(0.1), gate, 0.1)
		)
	)
}).add;

SynthDef("aDelay", { arg delay = 0.25;
	Out.ar(
		1,
		DelayN.ar(
			In.ar(0, 1),
			delay,
			delay
		)
	)
}).add;
)

////////////////////////////////////////////////
// test the grains ... and then turn them off
// ... they're all on the left channel ... good!
Synth("someGrains");
////////////////////////////////////////////////

// make 2 groups, the 1st for sources and the 2nd for effects
(
~source = Group.head(s);
~effects = Group.tail(s);
)

// place grains into the delay ... source is on the left and delayed source is on the right
(
Synth.head(~source, "someGrains");
Synth.head(~effects, "aDelay");
)
::

section::Feedback filters

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.

code::
/////////////////////////////////////////////////////////////////////////////////////////
// TURN ON THE INTERNAL SERVER!!
// first a comb filter and then an allpass with (with the same parameters) - compare them
/////////////////////////////////////////////////////////////////////////////////////////

// comb example
(
{
	CombN.ar(
		SinOsc.ar(500.rrand(1000), 0, 0.2) * Line.kr(1, 0, 0.1),
		0.3,
		0.25,
		6
	)
}.scope;
)

// allpass example - not much difference from the comb example
(
{
	AllpassN.ar(
		SinOsc.ar(500.rrand(1000), 0, 0.2) * Line.kr(1, 0, 0.1),
		0.3,
		0.25,
		6
	)
}.scope;
)
::

code::
/////////////////////////////////////////////////////////////////////////////////////////
//
// first a comb example and then an allpass
// both examples have the same parameters
// the 2 examples have relatively short delay times ... 0.1 seconds
//
/////////////////////////////////////////////////////////////////////////////////////////


// comb
(
{
	CombN.ar(
		SinOsc.ar(500.rrand(1000), 0, 0.2) * Line.kr(1, 0, 0.1),
		0.1,
		0.025,
		6
	)
}.scope;
)

// allpass ... what's the difference between this example and the comb filter?
(
{
	AllpassN.ar(
		SinOsc.ar(500.rrand(1000), 0, 0.2) * Line.kr(1, 0, 0.1),
		0.1,
		0.025,
		6
	)
}.scope
)
::

section::Reverberation

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.

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.

code::
(
{
var s, z, y;
	// 10 voices of a random sine percussion sound :
s = Mix.ar(Array.fill(10, { Resonz.ar(Dust.ar(0.2, 50), 200 + 3000.0.rand, 0.003)}) );
	// reverb predelay time :
z = DelayN.ar(s, 0.048);
	// 7 length modulated comb delays in parallel :
y = Mix.ar(Array.fill(7,{ CombL.ar(z, 0.1, LFNoise1.kr(0.1.rand, 0.04, 0.05), 15) }));
	// two parallel chains of 4 allpass delays (8 total) :
4.do({ y = AllpassN.ar(y, 0.050, [0.050.rand, 0.050.rand], 1) });
	// add original sound to reverb and play it :
s+(0.2*y)
}.scope
)
::

section::Components

The following shows one way to divide the JMC example into components.

code::
(
SynthDef("filteredDust", {
	Out.ar(
		2,
		Mix.arFill(10, { Resonz.ar(Dust.ar(0.2, 50), Rand(200, 3200), 0.003) })
	)
}).add;

SynthDef("preDelay", {
	ReplaceOut.ar(
		4,
		DelayN.ar(In.ar(2, 1), 0.048, 0.048)
	)
}).add;

SynthDef("combs", {
	ReplaceOut.ar(
		6,
		Mix.arFill(7, { CombL.ar(In.ar(4, 1), 0.1, LFNoise1.kr(Rand(0, 0.1), 0.04, 0.05), 15) })
	)
}).add;

SynthDef("allpass", { arg gain = 0.2;
	var source;
	source = In.ar(6, 1);
	4.do({ source = AllpassN.ar(source, 0.050, [Rand(0, 0.05), Rand(0, 0.05)], 1) });
	ReplaceOut.ar(
		8,
		source * gain
	)
}).add;

SynthDef("theMixer", { arg gain = 1;
	ReplaceOut.ar(
		0,
		Mix.ar([In.ar(2, 1), In.ar(8, 2)]) * gain
	)
}).add;
)

// as each line is executed, it becomes the tail node. the result is that
// "filteredDust" is the first node and  "theMixer" is the last node ...
// ... exactly what we need
(
Synth.tail(s, "filteredDust");
Synth.tail(s, "preDelay");
Synth.tail(s, "combs");
Synth.tail(s, "allpass");
Synth.tail(s, "theMixer");
)

(
s.queryAllNodes;
)
::

////////////////////////////////////////////////////////////////////////////////////////////////////

Or, use groups to control the order of execution.

code::
(
~source = Group.tail(s);
~proc1 = Group.tail(s);
~proc2 = Group.tail(s);
~proc3 = Group.tail(s);
~final = Group.tail(s);
)

// the nodes, below, are assigned to the groups, as ordered above,
(
Synth.head(~final, "theMixer");
Synth.head(~proc3, "allpass");
Synth.head(~proc2, "combs");
Synth.head(~proc1, "preDelay");
Synth.head(~source, "filteredDust");
)

(
s.queryAllNodes;
)
::

////////////////////////////////////////////////////////////////////////////////////////////////////

For context, here, below, is the complete text of the strong::01 Why SuperCollider:: document (by James McCartney) from the SuperCollider 2 distribution.

section::Why SuperCollider 2.0?

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.
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.
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.

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.

strong::Why use a text based language rather than a graphical language? ::
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.
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.

code::
(
{
	// 10 voices of a random sine percussion sound :
s = Mix.ar(Array.fill(10, { Resonz.ar(Dust.ar(0.2, 50), 200 + 3000.0.rand, 0.003)}) );
	// reverb predelay time :
z = DelayN.ar(s, 0.048);
	// 7 length modulated comb delays in parallel :
y = Mix.ar(Array.fill(7,{ CombL.ar(z, 0.1, LFNoise1.kr(0.1.rand, 0.04, 0.05), 15) }));
	// two parallel chains of 4 allpass delays (8 total) :
4.do({ y = AllpassN.ar(y, 0.050, [0.050.rand, 0.050.rand], 1) });
	// add original sound to reverb and play it :
s+(0.2*y)
}.play )
::

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.

////////////////////////////////////////////////////////////////////////////////////////////////////

go to link::Tutorials/Mark_Polishook_tutorial/18_Frequency_modulation::