jevstrudel.git / website / src / pages / learn / effects.mdx
effects.mdx479 lines · 12.0 KB · raw
1---
2title: Audio effects
3layout: ../../layouts/MainLayout.astro
4---
5
6import { MiniRepl } from '../../docs/MiniRepl';
7import { JsDoc } from '../../docs/JsDoc';
8
9# Audio Effects
10
11Whether you're using a synth or a sample, you can apply any of the following built-in audio effects.
12As you might suspect, the effects can be chained together, and they accept a pattern string as their argument.
13
14# Signal chain
15
16<img src="/img/strudel-signal-flow.png"></img>
17
18The signal chain in Strudel is as follows:
19
20- An sound-generating event is triggered by a pattern
21  - This has a start time and a duration, which is usually
22    controlled by the note length and ADSR parameters
23  - If we exceed the max polyphony, old sounds begin to die off
24  - Muted sounds (one whose `s` value is `-`, `~`, or `_`) are skipped
25- A sound is produced (through, say, a sample or an oscillator)
26  - This is where detune-based effects (like `detune`, `penv`, etc. occur)
27- The following will occur _in order_ and only if they've been called in the pattern. Note that all of these are
28  single use effects, meaning that multiple occurrences of them in a pattern will simply override the values
29  (e.g. you can't do `s("bd").lpf(100).distort(2).lpf(800)` to lowpass, distort, and then lowpass
30  again)
31  - Phase vocoder (`stretch`)
32  - Gain is applied (`gain`)
33    - This is where the main (volume) ADSR happens
34  - A lowpass filter (`lpf`)
35  - A highpass filter (`hpf`)
36  - A bandpass filter (`bandpass`)
37  - A vowel filter (`vowel`)
38  - Sample rate reduction (`coarse`)
39  - Bit crushing (`crush`)
40  - Waveshape distortion (`shape`)
41  - Normal distortion (`distort`)
42  - Tremolo (`tremolo`)
43  - Compressor (`compressor`)
44  - Panning (`pan`)
45  - Phaser (`phaser`)
46  - Postgain (`post`)
47- The sound is then split into multiple destinations
48  - Dry output (amount controlled by `dry` parameter)
49  - The sends
50    - Analyzers
51      - These are used for tooling like `scope` and `spectrum` and their setup usually happens behind the scenes
52    - Delay (amount controlled by `delay` parameter)
53    - Reverb (amount controlled by `room` parameter)
54- The dry output, delay, and reverb are joined into what is called the "orbit" of the pattern (see more in the section below)
55  - The `duck` effect affects the volume of all signals in the orbit
56  - The orbit is then sent to the mixer
57
58## Orbits
59
60Orbits are the way in which outputs are handled in Strudel. They also prescribe which delay and reverb to associate with the dry signal.
61By default, all orbits are mixed down to channels `1` and `2` in stereo, however with the "Multi Channel Orbits" setting
62(under Settings at the right) you can use them as individual 2 channel stereo outs (orbit `i` will be mapped to
63to channels `2i` and `2i + 1`). You can then use routers like Blackhole 16 to retrieve and record all of the channels in a DAW for later processing.
64
65The default orbit is `1` and it is set with `orbit`. You may send a sound to multiple orbits via mininotation
66
67<MiniRepl client:visible tune={`s("white").orbit("2,3,4").gain(0.2)`} />
68
69but please be careful as this will create three copies of the sound behind the scenes, meaning that if they are mixed
70down to a single output, they will triple the volume. We've reduced the gain here to save your ears.
71
72⚠️ There is only one delay and reverb per orbit, so please be aware that if you attempt to change the parameters on two
73patterns pointing to the same orbit, it can lead to unpredictable results. Compare, for example, this pretty pluck
74with a large reverb:
75
76<MiniRepl
77  client:visible
78  tune={`
79$: s("triangle*4").decay(0.5).n(irand(12)).scale('C minor')
80  .room(1).roomsize(10)`}
81/>
82
83versus the same pluck with a muted kick drum coming in and overwriting the `roomsize` value:
84
85<MiniRepl
86  client:visible
87  tune={`
88$: s("triangle*4").decay(0.5).n(irand(12)).scale('C minor')
89  .room(1).roomsize(10)
90
91$: s("bd\*4").room(0.01).roomsize(0.01).postgain(0)`}
92/>
93
94This is due to them sharing the same orbit: the default of `1`. It can be corrected simply by updating the orbits to be
95distinct:
96
97<MiniRepl
98  client:visible
99  tune={`
100$: s("triangle*4").decay(0.5).n(irand(12)).scale('C minor')
101  .room(1).roomsize(10).orbit(2)
102
103$: s("bd\*4").room(0.01).roomsize(0.01).postgain(0)`}
104/>
105
106## Continuous changes
107
108As all of the above is triggered by a _sound occurring_, it is often the case that parameters may not be
109modified continuously in time. For example,
110
111<MiniRepl
112  client:visible
113  tune={`
114s("supersaw").lpf(tri.range(100, 5000).slow(2))`}
115/>
116
117Will not produce a continually LFO'd low-pass filter due to the `tri` only being sampled every time the note hits
118(in this case the default of once per cycle). You can fake it by introducing more sound-generating events, e.g.:
119
120<MiniRepl
121  client:visible
122  tune={`
123s("supersaw").seg(16).lpf(tri.range(100, 5000).slow(2))`}
124/>
125
126Some parameters _do_ induce continuous variations in time, though:
127
128- The ADSR curve (governed by `attack`, `sustain`, `decay`, `release`)
129- The pitch envelope curve (governed by `penv` and its associated ADSR)
130- The FM curve (`fmenv`)
131- The filter envelopes (`lpenv`, `hpenv`, `bpenv`)
132- Tremolo (`tremolo`)
133- Phaser (`phaser`)
134- Vibrato (`vib`)
135- Ducking (`duckorbit`)
136
137# Filters
138
139Filters are an essential building block of [subtractive synthesis](https://en.wikipedia.org/wiki/Subtractive_synthesis).
140Strudel comes with 3 types of filters:
141
142- low-pass filter: low frequencies may _pass_, high frequencies are cut off
143- high-pass filter: high frequencies may _pass_, low frequencies are cut off
144- band-pass filters: only a frequency band may _pass_, low and high frequencies around are cut off
145
146Each filter has 2 parameters:
147
148- cutoff: the frequency at which the filter starts to work. e.g. a low-pass filter with a cutoff of 1000Hz allows frequencies below 1000Hz to pass.
149- q-value: Controls the resonance of the filter. Higher values sound more aggressive. Also see [Q-Factor](https://en.wikipedia.org/wiki/Q_factor)
150
151## lpf
152
153<JsDoc client:idle name="lpf" h={0} />
154
155## lpq
156
157<JsDoc client:idle name="lpq" h={0} />
158
159## hpf
160
161<JsDoc client:idle name="hpf" h={0} />
162
163## hpq
164
165<JsDoc client:idle name="hpq" h={0} />
166
167## bpf
168
169<JsDoc client:idle name="bpf" h={0} />
170
171## bpq
172
173<JsDoc client:idle name="bpq" h={0} />
174
175## ftype
176
177<JsDoc client:idle name="ftype" h={0} />
178
179## vowel
180
181<JsDoc client:idle name="vowel" h={0} />
182
183# Amplitude Modulation
184
185Amplitude modulation changes the amplitude (gain) periodically over time.
186
187## am
188
189<JsDoc client:idle name="am" h={0} />
190
191## tremolosync
192
193<JsDoc client:idle name="tremolosync" h={0} />
194
195## tremolodepth
196
197<JsDoc client:idle name="tremolodepth" h={0} />
198
199## tremoloskew
200
201<JsDoc client:idle name="tremoloskew" h={0} />
202
203## tremolophase
204
205<JsDoc client:idle name="tremolophase" h={0} />
206
207## tremoloshape
208
209<JsDoc client:idle name="tremoloshape" h={0} />
210
211# Amplitude Envelope
212
213The amplitude [envelope](<https://en.wikipedia.org/wiki/Envelope_(music)>) controls the dynamic contour of a sound.
214Strudel uses ADSR envelopes, which are probably the most common way to describe an envelope:
215
216![ADSR](https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/ADSR_parameter.svg/1920px-ADSR_parameter.svg.png)
217
218[image link](https://commons.wikimedia.org/wiki/File:ADSR_parameter.svg)
219
220## attack
221
222<JsDoc client:idle name="attack" h={0} />
223
224## decay
225
226<JsDoc client:idle name="decay" h={0} />
227
228## sustain
229
230<JsDoc client:idle name="sustain" h={0} />
231
232## release
233
234<JsDoc client:idle name="release" h={0} />
235
236## adsr
237
238<JsDoc client:idle name="adsr" h={0} />
239
240# Filter Envelope
241
242Each filter can receive an additional filter envelope controlling the cutoff value dynamically. It uses an ADSR envelope similar to the one used for amplitude. There is an additional parameter to control the depth of the filter modulation: `lpenv`|`hpenv`|`bpenv`. This allows you to play subtle or huge filter modulations just the same by only increasing or decreasing the depth.
243
244<MiniRepl
245  client:idle
246  tune={`note("[c eb g <f bb>](3,8,<0 1>)".sub(12))
247  .s("<sawtooth>/64")
248  .lpf(sine.range(300,2000).slow(16))
249  .lpa(0.005)
250  .lpd(perlin.range(.02,.2))
251  .lps(perlin.range(0,.5).slow(3))
252  .lpq(sine.range(2,10).slow(32))
253  .release(.5)
254  .lpenv(perlin.range(1,8).slow(2))
255  .ftype('24db')
256  .room(1)
257  .juxBy(.5,rev)
258  .sometimes(add(note(12)))
259  .stack(s("bd*2").bank('RolandTR909'))
260  .gain(.5).fast(2)`}
261/>
262
263There is one filter envelope for each filter type and thus one set of envelope filter parameters preceded either by `lp`, `hp` or `bp`:
264
265- `lpattack`, `lpdecay`, `lpsustain`, `lprelease`, `lpenv`: filter envelope for the lowpass filter.
266  - alternatively: `lpa`, `lpd`, `lps`, `lpr` and `lpe`.
267- `hpattack`, `hpdecay`, `hpsustain`, `hprelease`, `hpenv`: filter envelope for the highpass filter.
268  - alternatively: `hpa`, `hpd`, `hps`, `hpr` and `hpe`.
269- `bpattack`, `bpdecay`, `bpsustain`, `bprelease`, `bpenv`: filter envelope for the bandpass filter.
270  - alternatively: `bpa`, `bpd`, `bps`, `bpr` and `bpe`.
271
272## lpattack
273
274<JsDoc client:idle name="lpattack" h={0} />
275
276## lpdecay
277
278<JsDoc client:idle name="lpdecay" h={0} />
279
280## lpsustain
281
282<JsDoc client:idle name="lpsustain" h={0} />
283
284## lprelease
285
286<JsDoc client:idle name="lprelease" h={0} />
287
288## lpenv
289
290<JsDoc client:idle name="lpenv" h={0} />
291
292# Pitch Envelope
293
294You can also control the pitch with envelopes!
295Pitch envelopes can breathe life into static sounds:
296
297<MiniRepl
298  client:idle
299  tune={`n("<-4,0 5 2 1>*<2!3 4>")
300  .scale("<C F>/8:pentatonic")
301  .s("gm_electric_guitar_jazz")
302  .penv("<.5 0 7 -2>*2").vib("4:.1")
303  .phaser(2).delay(.25).room(.3)
304  .size(4).fast(1.5)`}
305/>
306
307You also create some lovely chiptune-style sounds:
308
309<MiniRepl
310  client:idle
311  tune={`n(run("<4 8>/16")).jux(rev)
312.chord("<C^7 <Db^7 Fm7>>")
313.dict('ireal')
314.voicing().add(note("<0 1>/8"))
315.dec(.1).room(.2)
316.segment("<4 [2 8]>")
317.penv("<0 <2 -2>>").patt(.02).fast(2)`}
318/>
319
320Let's break down all pitch envelope controls:
321
322## pattack
323
324<JsDoc client:idle name="pattack" h={0} />
325
326## pdecay
327
328<JsDoc client:idle name="pdecay" h={0} />
329
330## prelease
331
332<JsDoc client:idle name="prelease" h={0} />
333
334## penv
335
336<JsDoc client:idle name="penv" h={0} />
337
338## pcurve
339
340<JsDoc client:idle name="pcurve" h={0} />
341
342## panchor
343
344<JsDoc client:idle name="panchor" h={0} />
345
346# Dynamics
347
348## gain
349
350<JsDoc client:idle name="gain" h={0} />
351
352## velocity
353
354<JsDoc client:idle name="velocity" h={0} />
355
356## compressor
357
358<JsDoc client:idle name="compressor" h={0} />
359
360## postgain
361
362<JsDoc client:idle name="postgain" h={0} />
363
364## xfade
365
366<JsDoc client:idle name="xfade" h={0} />
367
368# Panning
369
370## jux
371
372<JsDoc client:idle name="jux" h={0} />
373
374## juxBy
375
376<JsDoc client:idle name="juxBy" h={0} />
377
378## pan
379
380<JsDoc client:idle name="pan" h={0} />
381
382# Waveshaping
383
384## coarse
385
386<JsDoc client:idle name="coarse" h={0} />
387
388## crush
389
390<JsDoc client:idle name="crush" h={0} />
391
392## distort
393
394<JsDoc client:idle name="distort" h={0} />
395
396# Global Effects
397
398## Local vs Global Effects
399
400While the above listed "local" effects will always create a separate effects chain for each event,
401global effects use the same chain for all events of the same orbit:
402
403## orbit
404
405<JsDoc client:idle name="orbit" h={0} />
406
407## Delay
408
409### delay
410
411<JsDoc client:idle name="delay" h={0} />
412
413### delaytime
414
415<JsDoc client:idle name="delaytime" h={0} />
416
417### delayfeedback
418
419<JsDoc client:idle name="delayfeedback" h={0} />
420
421## Reverb
422
423### room
424
425<JsDoc client:idle name="room" h={0} />
426
427### roomsize
428
429<JsDoc client:idle name="roomsize" h={0} />
430
431### roomfade
432
433<JsDoc client:idle name="roomfade" h={0} />
434
435### roomlp
436
437<JsDoc client:idle name="roomlp" h={0} />
438
439### roomdim
440
441<JsDoc client:idle name="roomdim" h={0} />
442
443### iresponse
444
445<JsDoc client:idle name="iresponse" h={0} />
446
447## Phaser
448
449### phaser
450
451<JsDoc client:idle name="phaser" h={0} />
452
453### phaserdepth
454
455<JsDoc client:idle name="phaserdepth" h={0} />
456
457### phasercenter
458
459<JsDoc client:idle name="phasercenter" h={0} />
460
461### phasersweep
462
463<JsDoc client:idle name="phasersweep" h={0} />
464
465## Duck
466
467### duckorbit
468
469<JsDoc client:idle name="duckorbit" h={0} />
470
471### duckattack
472
473<JsDoc client:idle name="duckattack" h={0} />
474
475### duckdepth
476
477<JsDoc client:idle name="duckdepth" h={0} />
478
479Next, we'll look at input / output via [MIDI, OSC and other methods](/learn/input-output).