jevstrudel.git / website / src / pages / learn / synths.mdx
synths.mdx235 lines · 6.9 KB · raw
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)...