-
Notifications
You must be signed in to change notification settings - Fork 5
/
07-effects.html
129 lines (111 loc) · 8.06 KB
/
07-effects.html
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
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 2022-04-16 Sat 02:16 -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Effects</title>
<meta name="generator" content="Org Mode" />
</head>
<body>
<div id="content" class="content">
<header>
<h1 class="title">Effects</h1>
</header><nav id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#org0e26519">1. Embedding Effects in Synths</a></li>
<li><a href="#org48ff6f8">2. Effects as Their Own Nodes</a></li>
</ul>
</div>
</nav>
<div id="outline-container-org0e26519" class="outline-2">
<h2 id="org0e26519"><span class="section-number-2">1.</span> Embedding Effects in Synths</h2>
<div class="outline-text-2" id="text-1">
<p>
To make our music more interesting, we may want to use effects to our sounds. For example, we could add a delay to make our sounds echo, or a reverb to make it sound like the music is being performed in a cave. Because SuperCollider is so open, we can simply add effects into the synth definition if we so choose. This is the most obvious and simple way of doing things:
</p>
<div class="org-src-container">
<pre class="src src-lisp">(defsynth echokick ((freq 440) (time 0.25) (delay 0.2) (amp 0.5) (out 0))
(<span style="font-weight: bold;">let*</span> ((env (env-gen.kr (env (list 0 1 1 0) (list 0.001 time 0.3))))
(fenv (env-gen.kr (env (list 1 0) (list time)) <span style="font-weight: bold;">:level-scale</span> freq))
(sig (sin-osc.ar fenv 0 (* env 0.2)))
(sig (+ sig (comb-c.ar sig delay delay (* delay 25))))
(sig (leak-dc.ar sig)))
(detect-silence.ar sig 1.0e-4 <span style="font-weight: bold;">:time</span> delay <span style="font-weight: bold;">:act</span> <span style="font-weight: bold;">:free</span>)
(out.ar out (pan2.ar sig 0 amp))))
</pre>
</div>
<p>
In the above synth, our source sound is simply a sine wave sweep from the <code>freq</code> to 0–the <code>env-gen</code>'s <code>level-scale</code> argument allows us to easily multiply the envelope by the frequency to get the sweep. Our <code>sin-osc</code> uses the frequency envelope as its frequency input and <code>0</code> as its phase. Its <code>mul</code> is <code>0.2</code> multiplied by our amplitude envelope <code>env</code>.
</p>
<p>
Below that, we apply the delay effect. An easy way to make a delay in SuperCollider is to use a comb filter–in this case, <code>comb-c</code>–which is basically the same thing as a normal delay. <code>comb-c</code>'s arguments are:
</p>
<ul class="org-ul">
<li>The input. In our case, <code>sig</code>, which is the sine sweep.</li>
<li>The maximum delay time. This is used by SuperCollider to allocate memory for the delay when the synth is created. For a simple use-case like this, we can just provide the delay time itself. If you were going to modulate the delay time, this would need to be set to the highest time you expect to use, because this argument cannot be modulated after the synth has been created.</li>
<li>The delay time. The actual amount of time between each repeat of the delay. This can be modulated.</li>
<li>The decay time. This is basically the amount of time that it takes for the delays to fade out to silence.</li>
<li>The standard <code>mul</code> and <code>add</code> arguments.</li>
</ul>
<p>
Our last step of processing for the signal is to send it through the <code>leak-dc</code> UGen, which removes what is known as a "DC offset" that some of SuperCollider's comb filters can occasionally produce. A DC offset is basically a very low frequency that causes the audio waveform to not be centered on zero, potentially resulting in clipping or audio filters misbehaving later in the signal path.
</p>
<p>
One such UGen that can misbehave with a DC offset is <code>detect-silence</code>, which, as the name suggests, is used to detect silence, but may not detect silence if its input has a DC offset. When silence is detected by <code>detect-silence</code>, it outputs a <code>1</code>, and when the input is not silence it outputs <code>0</code>. However, it can also be used to automatically free the synth when silence is detected, which is what we're using it for here–hence the <code>:act :free</code> keyword argument. We also set its <code>time</code> argument to our <code>delay</code>, to make sure if our delay time is too long, <code>detect-silence</code> won't free the synth before the first delay actually occurs.
</p>
<p>
Finally, we use <code>pan2</code> to turn our mono signal <code>sig</code> into a stereo signal, and send it to the synth's output with <code>out.ar</code>.
</p>
</div>
</div>
<div id="outline-container-org48ff6f8" class="outline-2">
<h2 id="org48ff6f8"><span class="section-number-2">2.</span> Effects as Their Own Nodes</h2>
<div class="outline-text-2" id="text-2">
<p>
Most of the time, it's preferred to run just one instance of an effect which multiple notes or synths would all be routed through. For example, having a separate reverb for each instance of a synth would not only cause unnecessarily high CPU usage, but it would also be more likely to result in audio clipping or other unwanted sound artifacts.
</p>
<p>
To convert the previous example to such a configuration, we can use the <code>proxy</code> macro as introduced in <a href="03-make-a-sound.html">chapter 3</a>:
</p>
<div class="org-src-container">
<pre class="src src-lisp"> (defsynth echokick ((freq 440) (time 0.25) (amp 0.5) (out 0))
(<span style="font-weight: bold;">let*</span> ((env (env-gen.kr (env (list 0 1 1 0) (list 0.001 time 0.3)) <span style="font-weight: bold;">:act</span> <span style="font-weight: bold;">:free</span>))
(fenv (env-gen.kr (env (list 1 0) (list time)) <span style="font-weight: bold;">:level-scale</span> freq))
(sig (sin-osc.ar fenv 0 (* env 0.2))))
(out.ar out (pan2.ar sig 0 amp))))
(<span style="font-weight: bold;">defparameter</span> <span style="font-weight: bold; font-style: italic;">*echo-bus*</span> (bus-audio <span style="font-weight: bold;">:chanls</span> 2))
(proxy <span style="font-weight: bold;">:echo</span>
(<span style="font-weight: bold;">let*</span> ((sig (in.ar *echo-bus* 2))
(delay 0.2)
(sig (+ sig (comb-c.ar sig delay delay (* delay 25))))
(sig (leak-dc.ar sig)))
(out.ar 0 sig)))
</pre>
</div>
<p>
Though buses have been mentioned before in this tutorial, they haven't been fully explained. In SuperCollider, and thus cl-collider, there is the concept of a "bus" which is basically a location in memory that can be written to or read to, similar to a buffer, but meant to be done continuously. A bus lets us do things like sending audio from one synth (or multiple) to another. Thus buses allow us to do things like implement effects that multiple synths can have their output processed by.
</p>
<p>
Here we define the <code>*echo-bus*</code> variable, which is a two-channel audio bus. Aside from audio buses, SuperCollider also supports control buses which are generally used for lower-frequency signals like LFOs that don't need as much processing power, similar to the <code>.ar~/</code>.kr~ UGen dichotomy.
</p>
<p>
Aside from the bus, it should also be noted that we don't use <code>detect-silence</code> anymore. We don't use it in the synth since there is no built-in delay and our volume envelope lasts the full length of the sound. And we don't use it in our proxy either since we want it to continue running even if it's silent.
</p>
<p>
With those three expressions evaluated, the synth is defined, the bus created, and the echo effect is running. To play the <code>echokick</code> through the effect, just make sure the synth's <code>out</code> parameter is set to the bus that the effect is reading from:
</p>
<div class="org-src-container">
<pre class="src src-lisp">(synth <span style="font-weight: bold;">:echokick</span> <span style="font-weight: bold;">:out</span> *echo-bus*)
</pre>
</div>
</div>
</div>
</div>
<div id="postamble" class="status">
<p class="date">Last updated: 2022-04-16 Sat 02:15</p>
</div>
</body>
</html>