Temp push
This commit is contained in:
@ -6,11 +6,11 @@ export const sound = (application: Editor): string => {
|
||||
return `
|
||||
# Audio engine
|
||||
|
||||
The Topos audio engine is based on the [SuperDough](https://www.npmjs.com/package/superdough) audio backend, leveraging the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API). The engine is capable of playing multiple samples, synths and effects at once. It is a very powerful and almost limitless tool to create complex sounds and textures. A set of default sounds are already provided but you can also load your own audio samples and synths!
|
||||
The Topos audio engine is based on the [SuperDough](https://www.npmjs.com/package/superdough) audio backend that takes advantage of the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API). The engine is capable of many things such as playing samples, synths and effects all at once. It is a very powerful and almost limitless tool to create complex sounds and textures. A set of default sounds are already provided but you can also load your own audio samples if you wish!
|
||||
|
||||
## Sound basics
|
||||
|
||||
The basic function to play a sound is... <ic>sound(name: string)</ic> (you can also write <ic>snd</ic> to save some precious time). If the given sound or synthesizer exists in the database, it will be automatically queried/started and will start playing. Evaluate the following script in the global window:
|
||||
The basic function to play a sound is... <ic>sound(name: string)</ic> (you can also write <ic>snd</ic> to save some precious time). If the given sound (or synthesizer) is already declared, it will be automatically queried/started and will start playing. Evaluate the following script in the global window:
|
||||
|
||||
${makeExample(
|
||||
"Playing sounds is easy",
|
||||
@ -23,8 +23,8 @@ beat(0.5) && sound('hh').out()
|
||||
|
||||
In plain english, this translates to:
|
||||
|
||||
> Every 48 pulses, play a kick drum.
|
||||
> Every 24 pulses, play a high-hat.
|
||||
> Every beat, play a kick drum.
|
||||
> Every half-beat, play a high-hat.
|
||||
|
||||
Let's make it slightly more complex:
|
||||
|
||||
@ -39,14 +39,15 @@ beat(0.5) && sound('hh').delay(0.25).delaytime(0.125).out();
|
||||
|
||||
Now, it reads as follow:
|
||||
|
||||
> Every 48 pulses, play a kick drum with some amount of distortion.
|
||||
> Every 24 pulses, play a high-hat with 25% of the sound injected in
|
||||
> Every beat, play a kick drum with some amount of distortion.
|
||||
> Every half-beat, play a high-hat with 25% of the sound injected in
|
||||
> a delay unit, with a delay time of 0.125 seconds.
|
||||
|
||||
Let's pause for a moment to explain what we just wrote. There are many things to be said:
|
||||
- If you remove the **mod** instruction, you will end up with a deluge of kick drums and high-hats. The **mod** instruction is used to filter out pulses. It is a very useful instruction to create basic rhythms. Check out the **Time** page if you haven't read it already.
|
||||
Let's pause for a moment and explain what is going on:
|
||||
|
||||
- If you remove <ic>beat</ic> instruction, you will end up with a deluge of kick drums and high-hats. <ic>beat</ic> in that case, is used to filter time. It is a very useful instruction to create basic rhythms. Check out the **Time** page if you haven't read it already.
|
||||
- Playing a sound always ends up with the <ic>.out()</ic> method that gives the instruction to send a message to the audio engine.
|
||||
- Sounds are **composed** by adding qualifiers that will modify the sound or synthesizer being played (_e.g_ <ic>sound('...').blabla(...)..something(...).out()</ic>.
|
||||
- Sounds are **composed** by adding qualifiers/parameters that will modify the sound or synthesizer being played (_e.g_ <ic>sound('...').blabla(...)..something(...).out()</ic>. Think of it as _audio chains_.
|
||||
|
||||
${makeExample(
|
||||
'"Composing" a sound or making a sound chain',
|
||||
@ -106,11 +107,12 @@ beat(.25) && sound('numbers').n(Math.floor(mouseX())).out()`,
|
||||
|
||||
As we said earlier, the <ic>sound('sample_name')</ic> function can be chained to _specify_ a sound more. For instance, you can add a filter and some effects to your high-hat:
|
||||
${makeExample(
|
||||
"Learning through repetition",
|
||||
"Let's make something more complex",
|
||||
`
|
||||
beat(0.5) && sound('hh')
|
||||
beat(0.25) && sound('jvbass')
|
||||
.sometimes(s=>s.speed([1,5,10].pick()))
|
||||
.room(0.5)
|
||||
.gain(1)
|
||||
.cutoff(usine(2) * 5000)
|
||||
.out()`,
|
||||
true
|
||||
@ -129,7 +131,7 @@ There is a special method to choose the _orbit_ that your sound is going to use:
|
||||
|
||||
| Method | Alias | Description |
|
||||
|----------|-------|------------------------------------------------------------|
|
||||
| orbit | | Orbit number |
|
||||
| orbit | o | Orbit number |
|
||||
|
||||
|
||||
## Amplitude
|
||||
@ -183,6 +185,9 @@ There are some basic controls over the playback of each sample. This allows you
|
||||
| n | | Select a sample in the current folder (from <ic>0</ic> to infinity) |
|
||||
| begin | | Beginning of the sample playback (between <ic>0</ic> and <ic>1</ic>) |
|
||||
| end | | End of the sample (between <ic>0</ic> and <ic>1</ic>) |
|
||||
| loopBegin | | Beginning of the loop section (between <ic>0</ic> and <ic>1</ic>) |
|
||||
| loopEnd | | End of the loop section (between <ic>0</ic> and <ic>1</ic>) |
|
||||
| loop | | Whether to loop or not the audio sample |
|
||||
| speed | | Playback speed (<ic>2</ic> = twice as fast) |
|
||||
| cut | | Set with <ic>0</ic> or <ic>1</ic>. Will cut the sample as soon as another sample is played on the same bus |
|
||||
| clip | | Multiply the duration of the sample with the given number |
|
||||
|
||||
@ -38,7 +38,9 @@ You can also add some amount of vibrato to the sound using the <ic>vib</ic> and
|
||||
|
||||
${makeExample(
|
||||
"Different vibrato settings",
|
||||
`beat(1) :: sound('triangle')
|
||||
`
|
||||
bpm(140);
|
||||
beat(1) :: sound('triangle')
|
||||
.freq(400).release(0.2)
|
||||
.vib([1/2, 1, 2, 4].beat())
|
||||
.vibmod([1,2,4,8].beat(2))
|
||||
@ -77,15 +79,30 @@ ${makeExample(
|
||||
${makeExample(
|
||||
"Using decay and sustain to set the ADSR envelope",
|
||||
`
|
||||
beat(0.5) :: sound('sawtooth')
|
||||
beat(0.5) :: sound('wt_piano')
|
||||
.cutoff(1000 + usine() * 4000)
|
||||
.freq([50,100,150,300].pick())
|
||||
.decay(.1).sustain([0.25,0.5].pick())
|
||||
.freq(100).decay(.2)
|
||||
.sustain([0.1,0.5].beat(4))
|
||||
.out()`,
|
||||
true
|
||||
)}
|
||||
|
||||
This ADSR envelope design is important to know because it is used for other aspects of the synthesis engine such as the filters that we are now going to talk about.
|
||||
This ADSR envelope design is important to know because it is used for other aspects of the synthesis engine such as the filters that we are now going to talk about. But wait, I've kept the best for the end. The <ic>adsr()</ic> combines all the parameters together. It is a shortcut for setting the ADSR envelope:
|
||||
|
||||
- <ic>adsr(attack: number, decay: number, sustain: number, release: number)</ic>: sets the ADSR envelope.
|
||||
|
||||
${makeExample(
|
||||
"Replacing the previous example with the adsr() method",
|
||||
`
|
||||
beat(0.5) :: sound('wt_piano')
|
||||
.cutoff(1000 + usine() * 4000)
|
||||
.freq(100)
|
||||
.adsr(0, .2, [0.1,0.5].beat(4), 0)
|
||||
.out()
|
||||
`,
|
||||
true
|
||||
)}
|
||||
|
||||
|
||||
## Substractive synthesis using filters
|
||||
|
||||
@ -143,6 +160,18 @@ ${makeExample(
|
||||
true
|
||||
)}
|
||||
|
||||
## Filter order (type)
|
||||
|
||||
You can also use the <ic>ftype</ic> method to change the filter type (order). There are two types by default, <ic>12db</ic> for a gentle slope or <ic>24db</ic> for a really steep filtering slope. The <ic>24db</ic> type is particularly useful for substractive synthesis if you are trying to emulate some of the Moog or Prophet sounds:
|
||||
|
||||
- <ic>ftype(type: string)</ic>: sets the filter type (order), either <ic>12db</ic> or <ic>24db</ic>.
|
||||
|
||||
${makeExample(
|
||||
"Filtering a bass",
|
||||
`beat(.5) :: sound('jvbass').ftype(['12db', '24db'].beat(4)).lpf([250,1000,8000].beat()).out()`,
|
||||
true
|
||||
)}
|
||||
|
||||
I also encourage you to study these simple examples to get more familiar with the construction of basic substractive synthesizers:
|
||||
|
||||
${makeExample(
|
||||
@ -248,7 +277,8 @@ Topos can also do wavetable synthesis. Wavetable synthesis allows you to use any
|
||||
|
||||
${makeExample(
|
||||
"Acidity test",
|
||||
`beat(.25) :: sound('wt_symetric:8').note([50,55,57,60].beat(.25) - [12,0]
|
||||
`
|
||||
beat(.25) :: sound('wt_symetric:8').note([50,55,57,60].beat(.25) - [12,0]
|
||||
.pick()).ftype('12db').adsr(0.05/4, 1/16, 0.25/4, 0)
|
||||
.cutoff(1500 + usine(1/8) * 5000).lpadsr(16, 0.2, 0.2, 0.125/2, 0)
|
||||
.room(0.9).size(0.9).resonance(20).gain(0.3).out()
|
||||
@ -280,39 +310,41 @@ You can work with them just like with any other waveform. Having so many of them
|
||||
|
||||
# Frequency Modulation Synthesis (FM)
|
||||
|
||||
The same basic waveforms can take additional methods to switch to a basic two operators FM synth design (with _carrier_ and _modulator_). FM Synthesis is a complex topic but take this advice: simple ratios will yield stable and harmonic sounds, complex ratios will generate noises, percussions and gritty sounds.
|
||||
Another really useful technique to know about is FM synthesis, FM standing for _frequency modulation_. Our basic waveforms can take some additional parameters to be transformed into a two operators FM synthesizer (with _carrier_ and _modulator_). FM Synthesis is a very complex and fascinating topic. There are a lot of things you can design using this technique but keep in mind this advice: **simple ratios will yield stable and harmonic sounds, complex ratios will generate noises, percussions and gritty sounds**.
|
||||
|
||||
- <ic>fmi</ic> (_frequency modulation index_): a floating point value between <ic>1</ic> and <ic>n</ic>.
|
||||
- <ic>fmh</ic> (_frequency modulation harmonic ratio_): a floating point value between <ic>1</ic> and <ic>n</ic>.
|
||||
- <ic>fmwave</ic> (_frequency modulation waveform_): a waveform name (_sine_, _triangle_, _sawtooth_ or _pulse_).
|
||||
|
||||
There is also an additional parameter, <ic>fm</ic> that combines <ic>fmi</ic> and <ic>fmh</ic> using strings: <ic>fm('2:4')</ic>. Think of it as a static shortcut for getting some timbres more quickly.
|
||||
|
||||
${makeExample(
|
||||
"80s nostalgia",
|
||||
`
|
||||
beat(.25) && snd('sine')
|
||||
.fmi([1,2,4,8].pick())
|
||||
.fmh([1,2,4,8].beat(8))
|
||||
.freq([100,150].pick())
|
||||
.sustain(0.1)
|
||||
.out()
|
||||
beat([.5, 1].beat(8)) && snd('triangle').adsr(0.02, 0.5, 0.5, 0.25)
|
||||
.fmi(2).fmh(1.5).note([60,55, 60, 63].beat() - 12)
|
||||
.pan(noise()).out()
|
||||
beat(.25) && snd('triangle').adsr(0.02, 0.1, 0.1, 0.1)
|
||||
.fmi([2,4].beat(4)).fmh(1.5)
|
||||
.pan(noise()).note([60,55, 60, 63].beat() + [0, 7].pick()).out()
|
||||
beat(2) :: sound('cp').room(1).sz(1).out()
|
||||
`,
|
||||
true
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Giving some love to weird ratios",
|
||||
"Giving some love to ugly inharmonic sounds",
|
||||
`
|
||||
beat([.5, .25].bar()) :: sound('sine').fm('2.2183:3.18293').sustain(0.05).out()
|
||||
beat([4].bar()) :: sound('sine').fm('5.2183:4.5').sustain(0.05).out()
|
||||
beat(.5) :: sound('sine')
|
||||
.fmh([1, 1.75].beat())
|
||||
.fmi($(1) % 30).orbit(2).room(0.5).out()`,
|
||||
false
|
||||
true
|
||||
)}
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Some peace and serenity",
|
||||
"Peace and serenity through FM synthesis",
|
||||
`
|
||||
beat(0.25) :: sound('sine')
|
||||
.note([60, 67, 70, 72, 77].beat() - [0,12].bar())
|
||||
@ -322,7 +354,7 @@ beat(0.25) :: sound('sine')
|
||||
.cutoff(1500).delay(0.5).delayt(0.125)
|
||||
.delayfb(0.8).fmh(Math.floor(usine(.5) * 4))
|
||||
.out()`,
|
||||
false
|
||||
true
|
||||
)}
|
||||
|
||||
**Note:** you can also set the _modulation index_ and the _harmonic ratio_ with the <ic>fm</ic> argument. You will have to feed both as a string: <ic>fm('2:4')</ic>. If you only feed one number, only the _modulation index_ will be updated.
|
||||
@ -465,7 +497,7 @@ beat(4) :: speak("Hello world!")
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Different voices",
|
||||
"Let's hear people talking about Topos",
|
||||
`
|
||||
beat(2) :: speak("Topos!","fr",irand(0,5))
|
||||
`,
|
||||
@ -476,7 +508,7 @@ beat(2) :: speak("Topos!","fr",irand(0,5))
|
||||
You can also use speech by chaining methods to a string:
|
||||
|
||||
${makeExample(
|
||||
"Chaining string",
|
||||
"Foobaba is the real deal",
|
||||
`
|
||||
onbeat(4) :: "Foobaba".voice(irand(0,10)).speak()
|
||||
`,
|
||||
@ -501,7 +533,11 @@ ${makeExample(
|
||||
`
|
||||
bpm(70)
|
||||
|
||||
const croissant = ["Volant", "Arc-en-ciel", "Chocolat", "Dansant", "Nuage", "Tournant", "Galaxie", "Chatoyant", "Flamboyant", "Cosmique","Croissant!"];
|
||||
const croissant = [
|
||||
"Volant", "Arc-en-ciel", "Chocolat", "Dansant",
|
||||
"Nuage", "Tournant", "Galaxie", "Chatoyant",
|
||||
"Flamboyant", "Cosmique", "Croissant!"
|
||||
];
|
||||
|
||||
onbeat(4) :: croissant.bar()
|
||||
.lang("fr")
|
||||
|
||||
Reference in New Issue
Block a user