1--- 2title: Synths 3layout: ../../layouts/MainLayout.astro 4--- 5 6import { MiniRepl } from '../../docs/MiniRepl'; 7import { JsDoc } from '../../docs/JsDoc'; 8 9# Synths 10 11In addition to the sampling engine, strudel comes with a synthesizer to create sounds on the fly. 12 13## Basic Waveforms 14 15The basic waveforms are `sine`, `sawtooth`, `square` and `triangle`, which can be selected via `sound` (or `s`): 16 17<MiniRepl 18 client:idle 19 tune={`note("c2 <eb2 <g2 g1>>".fast(2)) 20.sound("<sawtooth square triangle sine>") 21._scope()`} 22/> 23 24If you don't set a `sound` but a `note` the default value for `sound` is `triangle`! 25 26## Noise 27 28You can also use noise as a source by setting the waveform to: `white`, `pink` or `brown`. These are different 29flavours of noise, here written from hard to soft. 30 31<MiniRepl client:idle tune={`sound("<white pink brown>")._scope()`} /> 32 33Here's a more musical example of how to use noise for hihats: 34 35<MiniRepl 36 client:idle 37 tune={`sound("bd*2,<white pink brown>*8") 38.decay(.04).sustain(0)._scope()`} 39/> 40 41Some amount of pink noise can also be added to any oscillator by using the `noise` paremeter: 42 43<MiniRepl client:idle tune={`note("c3").noise("<0.1 0.25 0.5>")._scope()`} /> 44 45You can also use the `crackle` type to play some subtle noise crackles. You can control noise amount by using the `density` parameter: 46 47<MiniRepl client:idle tune={`s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(2))._scope()`} /> 48 49### Additive Synthesis 50 51Periodic waveforms are composed of several [harmonics](https://en.wikipedia.org/wiki/Harmonic) above a fundamental frequency, lying at integer multiples. These overtones combine to give a sound its unique timbral quality. 52 53For the basic waveforms, we offer you control over these harmonics with the `partials` and `phases` functions. 54 55#### Partials 56 57`partials` refers to the magnitude of each harmonic relative to the fundamental frequency. They can thus be used to spectrally filter these waveforms and tame some of their harshness: 58 59<MiniRepl 60 client:idle 61 tune={`note("c2 <eb2 <g2 g1>>".fast(2)) 62.sound("sawtooth") 63.partials([1, 1, "<1 0>", "<1 0>", "<1 0>", "<1 0>", "<1 0>"]) 64._scope()`} 65/> 66 67`partials` can also be used to construct _new_ waveforms not present in our basic set with the 'user' sound source: 68 69<MiniRepl 70 client:idle 71 tune={`note("c2 <eb2 <g2 g1>>".fast(2)) 72.sound("user") 73.partials([1, 0, 0.3, 0, 0.1, 0, 0, 0.3]) 74._scope()`} 75/> 76 77We may algorithmically construct lists of magnitudes with Javascript code like: 78 79<MiniRepl 80 client:idle 81 tune={`const numHarmonics = 22; 82note("c2 <eb2 <g2 g1>>".fast(2)) 83.sound("saw") 84.partials(new Array(numHarmonics).fill(1)) 85._scope()`} 86/> 87 88which acts as a spectral filter. Or: 89 90<MiniRepl 91 client:idle 92 tune={`note("c2 <eb2 <g2 g1>>").fast(2) 93.sound("user") 94.partials(new Array(50).fill(0) 95 .map((_, idx) => ((-1) ** (idx + 1)) / (idx + 1)) 96) 97._scope()`} 98/> 99 100which recovers a familiar waveform. 101 102`partials` is also compatible with pattern functions designed to produce lists, like `randL` or `binaryL`: 103 104<MiniRepl 105 client:idle 106 tune={`note("c2 <eb2 <g2 g1>>").fast(2) 107.sound("user") 108.partials(randL(10)) 109._scope()`} 110/> 111 112and with lists _of_ patterns: 113 114<MiniRepl 115 client:idle 116 tune={`note("c2 <eb2 <g2 g1>>".fast(4)) 117.sound("user") 118.partials([1, 0, "0 1", "0 1 0.3", rand]) 119._scope()`} 120/> 121 122Note that the first value in the `partials` array controls the magnitude of the fundamental harmonic rather than the DC offset, which is fixed at 0. 123 124#### Phases 125 126Earlier, we mentioned that periodic waveforms can be broken into a set of harmonics above a fundamental frequency. Each harmonic has two defining properties: its magnitude (how loud it is) and its phase, which determines where in its cycle that sine wave starts when the waveform is built. 127 128These phases too can be declared in Strudel and can give your sounds interesting depth. 129 130<MiniRepl 131 client:idle 132 tune={`s("saw").seg(16).n(irand(12)).scale("F1:minor") 133 .penv(48).panchor(0).pdec(0.05) 134 .delay(0.25).room(0.25) 135 .compressor(-20).vib(0.3) 136 .partials(randL(200)) 137 .phases(randL(200))`} 138/> 139 140## Vibrato 141 142### vib 143 144<JsDoc client:idle name="vib" h={0} /> 145 146### vibmod 147 148<JsDoc client:idle name="vibmod" h={0} /> 149 150## FM Synthesis 151 152FM Synthesis is a technique that changes the frequency of a basic waveform rapidly to alter the timbre. 153 154You can use fm with any of the above waveforms, although the below examples all use the default triangle wave. 155 156### fm 157 158<JsDoc client:idle name="fm" h={0} /> 159 160### fmh 161 162<JsDoc client:idle name="fmh" h={0} /> 163 164### fmattack 165 166<JsDoc client:idle name="fmattack" h={0} /> 167 168### fmdecay 169 170<JsDoc client:idle name="fmdecay" h={0} /> 171 172### fmsustain 173 174<JsDoc client:idle name="fmsustain" h={0} /> 175 176### fmenv 177 178<JsDoc client:idle name="fmenv" h={0} /> 179 180## Wavetable Synthesis 181 182Strudel can also use the sampler to load custom waveforms as a replacement of the default waveforms used by WebAudio for the base synth. A default set of more than 1000 wavetables is accessible by default (coming from the [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) set). You can also import/use your own. A wavetable is a one-cycle waveform, which is then repeated to create a sound at the desired frequency. It is a classic but very effective synthesis technique. 183 184Any sample preceded by the `wt_` prefix will be loaded as a wavetable. This means that the `loop` argument will be set to `1` by default. You can scan over the wavetable by using `loopBegin` and `loopEnd` as well. 185 186<MiniRepl 187 client:idle 188 tune={`samples('bubo:waveforms'); 189note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>") 190.n("<1 2 3 4 5 6 7 8 9 10>/2").room(0.5).size(0.9) 191.s('wt_flute').velocity(0.25).often(n => n.ply(2)) 192.release(0.125).decay("<0.1 0.25 0.3 0.4>").sustain(0) 193.cutoff(2000).cutoff("<1000 2000 4000>").fast(4) 194._scope() 195`} 196/> 197 198## ZZFX 199 200The "Zuper Zmall Zound Zynth" [ZZFX](https://github.com/KilledByAPixel/ZzFX) is also integrated in strudel. 201Developed by [Frank Force](https://frankforce.com/), it is a synth and FX engine originally intended to be used for size coding games. 202 203It has 20 parameters in total, here is a snippet that uses all: 204 205<MiniRepl 206 client:idle 207 tune={`note("c2 eb2 f2 g2") // also supports freq 208 .s("{z_sawtooth z_tan z_noise z_sine z_square}%4") 209 .zrand(0) // randomization 210 // zzfx envelope 211 .attack(0.001) 212 .decay(0.1) 213 .sustain(.8) 214 .release(.1) 215 // special zzfx params 216 .curve(1) // waveshape 1-3 217 .slide(0) // +/- pitch slide 218 .deltaSlide(0) // +/- pitch slide (?) 219 .noise(0) // make it dirty 220 .zmod(0) // fm speed 221 .zcrush(0) // bit crush 0 - 1 222 .zdelay(0) // simple delay 223 .pitchJump(0) // +/- pitch change after pitchJumpTime 224 .pitchJumpTime(0) // >0 time after pitchJump is applied 225 .lfo(0) // >0 resets slide + pitchJump + sets tremolo speed 226 .tremolo(0.5) // 0-1 lfo volume modulation amount 227 //.duration(.2) // overwrite strudel event duration 228 //.gain(1) // change volume 229 ._scope() // vizualise waveform (not zzfx related) 230`} 231/> 232 233Note that you can also combine zzfx with all the other audio fx (next chapter). 234 235Next up: [Audio Effects](/learn/effects)...