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