updating examples with new features

This commit is contained in:
2023-09-18 18:55:14 +02:00
parent fa0f786252
commit 04d8d7fe6d
2 changed files with 198 additions and 189 deletions

View File

@ -13,40 +13,40 @@ Topos comes by default with a forever-increasing number of synthesis capabilitie
The <ic>sound</ic> function can take the name of a synthesizer or waveform as first argument. This has for effect to turn the sampler we all know and love into a synthesizer. <ic>sine</ic>, <ic>sawtooth</ic>,<ic>triangle</ic>, <ic>square</ic> are the names used to select classic oscillator waveforms. Note that you can also make use of filters and envelopes to shape the sound to your liking. The <ic>sound</ic> function can take the name of a synthesizer or waveform as first argument. This has for effect to turn the sampler we all know and love into a synthesizer. <ic>sine</ic>, <ic>sawtooth</ic>,<ic>triangle</ic>, <ic>square</ic> are the names used to select classic oscillator waveforms. Note that you can also make use of filters and envelopes to shape the sound to your liking.
${makeExample( ${makeExample(
"Listening to the different waveforms from the sweetest to the harshest", "Listening to the different waveforms from the sweetest to the harshest",
` `
beat(.5) && snd(['sine', 'triangle', 'sawtooth', 'square'].beat()).freq(100).out() beat(.5) && snd(['sine', 'triangle', 'sawtooth', 'square'].beat()).freq(100).out()
`, `,
true true
)} )}
Two functions are primarily used to control the frequency of the synthesizer: Two functions are primarily used to control the frequency of the synthesizer:
- <ic>freq(hz: number)</ic>: sets the frequency of the oscillator. - <ic>freq(hz: number)</ic>: sets the frequency of the oscillator.
- <ic>note(note: number)</ic>: sets the MIDI note of the oscillator (MIDI note converted to hertz). - <ic>note(note: number)</ic>: sets the MIDI note of the oscillator (MIDI note converted to hertz).
${makeExample( ${makeExample(
"Selecting a pitch or note", "Selecting a pitch or note",
` `
beat(.5) && snd('triangle').freq([100,200,400].beat(2)).out() beat(.5) && snd('triangle').freq([100,200,400].beat(2)).out()
`, `,
true true
)} )}
## Vibrato ## Vibrato
You can also add some amount of vibrato to the sound using the <ic>vib</ic> and <ic>vibmod</ic> methods. These can turn any oscillator into something more lively and/or into a sound effect when used with a high amount of modulation. You can also add some amount of vibrato to the sound using the <ic>vib</ic> and <ic>vibmod</ic> methods. These can turn any oscillator into something more lively and/or into a sound effect when used with a high amount of modulation.
${makeExample( ${makeExample(
"Different vibrato settings", "Different vibrato settings",
` `
bpm(140); bpm(140);
beat(1) :: sound('triangle') beat(1) :: sound('triangle')
.freq(400).release(0.2) .freq(400).release(0.2)
.vib([1/2, 1, 2, 4].beat()) .vib([1/2, 1, 2, 4].beat())
.vibmod([1,2,4,8].beat(2)) .vibmod([1,2,4,8].beat(2))
.out()`, .out()`,
true true
)} )}
## Controlling the amplitude ## Controlling the amplitude
@ -55,16 +55,16 @@ Controlling the amplitude and duration of the sound can be done using various te
- <ic>velocity(velocity: number)</ic>: sets the velocity of the oscillator (velocity is a multiple of gain). - <ic>velocity(velocity: number)</ic>: sets the velocity of the oscillator (velocity is a multiple of gain).
${makeExample( ${makeExample(
"Setting the gain", "Setting the gain",
`beat(0.25) :: sound('sawtooth').gain([0.0, 1/8, 1/4, 1/2, 1].beat(0.5)).out()`, `beat(0.25) :: sound('sawtooth').gain([0.0, 1/8, 1/4, 1/2, 1].beat(0.5)).out()`,
true true
)} )}
${makeExample( ${makeExample(
"Setting the velocity", "Setting the velocity",
`beat(0.25) :: sound('sawtooth').velocity([0.0, 1/8, 1/4, 1/2, 1].beat(0.5)).out()`, `beat(0.25) :: sound('sawtooth').velocity([0.0, 1/8, 1/4, 1/2, 1].beat(0.5)).out()`,
true true
)} )}
<div class="mt-4 mb-4 lg:grid lg:grid-cols-4 lg:gap-4"> <div class="mt-4 mb-4 lg:grid lg:grid-cols-4 lg:gap-4">
<img class="col-span-1 lg:ml-12 bg-gray-100 rounded-lg px-2 py-2", src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/ef/ADSR_Envelope_Graph.svg/1280px-ADSR_Envelope_Graph.svg.png" width="400" /> <img class="col-span-1 lg:ml-12 bg-gray-100 rounded-lg px-2 py-2", src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/ef/ADSR_Envelope_Graph.svg/1280px-ADSR_Envelope_Graph.svg.png" width="400" />
@ -77,31 +77,31 @@ ${makeExample(
- <ic>release(release: number)</ic> / <ic>rel(rel: number)</ic>: sets the release time of the envelope. - <ic>release(release: number)</ic> / <ic>rel(rel: number)</ic>: sets the release time of the envelope.
${makeExample( ${makeExample(
"Using decay and sustain to set the ADSR envelope", "Using decay and sustain to set the ADSR envelope",
` `
beat(0.5) :: sound('wt_piano') beat(0.5) :: sound('wt_piano')
.cutoff(1000 + usine() * 4000) .cutoff(1000 + usine() * 4000)
.freq(100).decay(.2) .freq(100).decay(.2)
.sustain([0.1,0.5].beat(4)) .sustain([0.1,0.5].beat(4))
.out()`, .out()`,
true 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. 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: 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. - <ic>adsr(attack: number, decay: number, sustain: number, release: number)</ic>: sets the ADSR envelope.
${makeExample( ${makeExample(
"Replacing the previous example with the adsr() method", "Replacing the previous example with the adsr() method",
` `
beat(0.5) :: sound('wt_piano') beat(0.5) :: sound('wt_piano')
.cutoff(1000 + usine() * 4000) .cutoff(1000 + usine() * 4000)
.freq(100) .freq(100)
.adsr(0, .2, [0.1,0.5].beat(4), 0) .adsr(0, .2, [0.1,0.5].beat(4), 0)
.out() .out()
`, `,
true true
)} )}
## Substractive synthesis using filters ## Substractive synthesis using filters
@ -113,10 +113,10 @@ The most basic synthesis technique used since the 1970s is called substractive s
- **bandpass filter**: filters the low and high frequencies around a frequency band, keeping what's in the middle. - **bandpass filter**: filters the low and high frequencies around a frequency band, keeping what's in the middle.
${makeExample( ${makeExample(
"Filtering the high frequencies of an oscillator", "Filtering the high frequencies of an oscillator",
`beat(.5) :: sound('sawtooth').cutoff(50 + usine(1/8) * 2000).out()`, `beat(.5) :: sound('sawtooth').cutoff(50 + usine(1/8) * 2000).out()`,
true true
)} )}
These filters all come with their own set of parameters. Note that we are describing the parameters of the three different filter types here. Choose the right parameters depending on the filter type you are using: These filters all come with their own set of parameters. Note that we are describing the parameters of the three different filter types here. Choose the right parameters depending on the filter type you are using:
@ -129,10 +129,10 @@ These filters all come with their own set of parameters. Note that we are descri
| resonance | lpq | resonance of the lowpass filter | | resonance | lpq | resonance of the lowpass filter |
${makeExample( ${makeExample(
"Filtering a bass", "Filtering a bass",
`beat(.5) :: sound('jvbass').lpf([250,1000,8000].beat()).out()`, `beat(.5) :: sound('jvbass').lpf([250,1000,8000].beat()).out()`,
true true
)} )}
### Highpass filter ### Highpass filter
@ -142,10 +142,10 @@ ${makeExample(
| hresonance | hpq | resonance of the highpass filter | | hresonance | hpq | resonance of the highpass filter |
${makeExample( ${makeExample(
"Filtering a noise source", "Filtering a noise source",
`beat(.5) :: sound('gtr').hpf([250,1000, 2000, 3000, 4000].beat()).end(0.5).out()`, `beat(.5) :: sound('gtr').hpf([250,1000, 2000, 3000, 4000].beat()).end(0.5).out()`,
true true
)} )}
### Bandpass filter ### Bandpass filter
@ -155,10 +155,10 @@ ${makeExample(
| bandq | bpq | resonance of the bandpass filter | | bandq | bpq | resonance of the bandpass filter |
${makeExample( ${makeExample(
"Sweeping the filter on the same guitar sample", "Sweeping the filter on the same guitar sample",
`beat(.5) :: sound('gtr').bandf(100 + usine(1/8) * 4000).end(0.5).out()`, `beat(.5) :: sound('gtr').bandf(100 + usine(1/8) * 4000).end(0.5).out()`,
true true
)} )}
## Filter order (type) ## Filter order (type)
@ -167,38 +167,38 @@ You can also use the <ic>ftype</ic> method to change the filter type (order). Th
- <ic>ftype(type: string)</ic>: sets the filter type (order), either <ic>12db</ic> or <ic>24db</ic>. - <ic>ftype(type: string)</ic>: sets the filter type (order), either <ic>12db</ic> or <ic>24db</ic>.
${makeExample( ${makeExample(
"Filtering a bass", "Filtering a bass",
`beat(.5) :: sound('jvbass').ftype(['12db', '24db'].beat(4)).lpf([250,1000,8000].beat()).out()`, `beat(.5) :: sound('jvbass').ftype(['12db', '24db'].beat(4)).lpf([250,1000,8000].beat()).out()`,
true true
)} )}
I also encourage you to study these simple examples to get more familiar with the construction of basic substractive synthesizers: I also encourage you to study these simple examples to get more familiar with the construction of basic substractive synthesizers:
${makeExample( ${makeExample(
"Simple synthesizer voice with filter", "Simple synthesizer voice with filter",
` `
beat(.5) && snd('sawtooth') beat(.5) && snd('sawtooth')
.cutoff([2000,500].pick() + usine(.5) * 4000) .cutoff([2000,500].pick() + usine(.5) * 4000)
.resonance(0.9).freq([100,150].pick()) .resonance(0.9).freq([100,150].pick())
.out() .out()
`, `,
true true
)} )}
${makeExample( ${makeExample(
"Blessed by the square wave", "Blessed by the square wave",
` `
beat(4) :: [100,101].forEach((freq) => sound('square').freq(freq).sustain(0.1).out()) beat(4) :: [100,101].forEach((freq) => sound('square').freq(freq).sustain(0.1).out())
beat(.5) :: [100,101].forEach((freq) => sound('square').freq(freq*2).sustain(0.01).out()) beat(.5) :: [100,101].forEach((freq) => sound('square').freq(freq*2).sustain(0.01).out())
beat([.5, .75, 2].beat()) :: [100,101].forEach((freq) => sound('square') beat([.5, .75, 2].beat()) :: [100,101].forEach((freq) => sound('square')
.freq(freq*4 + usquare(2) * 200).sustain(0.125).out()) .freq(freq*4 + usquare(2) * 200).sustain(0.125).out())
beat(.25) :: sound('square').freq(100*[1,2,4,8].beat()).sustain(0.1).out()`, beat(.25) :: sound('square').freq(100*[1,2,4,8].beat()).sustain(0.1).out()`,
false false
)} )}
${makeExample( ${makeExample(
"Ghost carillon (move your mouse!)", "Ghost carillon (move your mouse!)",
` `
beat(1/8)::sound('sine') beat(1/8)::sound('sine')
.velocity(rand(0.0, 1.0)) .velocity(rand(0.0, 1.0))
.delay(0.75).delayt(.5) .delay(0.75).delayt(.5)
@ -207,8 +207,8 @@ beat(1/8)::sound('sine')
.freq(mouseX()) .freq(mouseX())
.gain(0.25) .gain(0.25)
.out()`, .out()`,
false false
)} )}
## Filter envelopes ## Filter envelopes
@ -223,14 +223,16 @@ The examples we have studied so far are static. They filter the sound around a f
| lpdecay | lpd | decay of the lowpass filter | | lpdecay | lpd | decay of the lowpass filter |
| lpsustain | lps | sustain of the lowpass filter | | lpsustain | lps | sustain of the lowpass filter |
| lprelease | lpr | release of the lowpass filter | | lprelease | lpr | release of the lowpass filter |
| lpadsr | | (**takes five arguments**) set all the parameters |
${makeExample( ${makeExample(
"Filtering a sawtooth wave dynamically", "Filtering a sawtooth wave dynamically",
`beat(.5) :: sound('sawtooth').note([48,60].beat()) `beat(.5) :: sound('sawtooth').note([48,60].beat())
.cutoff(5000).lpa([0.05, 0.25, 0.5].beat(2)) .cutoff(5000).lpa([0.05, 0.25, 0.5].beat(2))
.lpenv(-8).lpq(10).out()`, .lpenv(-8).lpq(10).out()`,
true true
)} )}
### Highpass envelope ### Highpass envelope
@ -241,14 +243,16 @@ ${makeExample(
| hpdecay | hpd | decay of the highpass filter | | hpdecay | hpd | decay of the highpass filter |
| hpsustain | hps | sustain of the highpass filter | | hpsustain | hps | sustain of the highpass filter |
| hprelease | hpr | release of the highpass filter | | hprelease | hpr | release of the highpass filter |
| hpadsr | | (**takes five arguments**) set all the parameters |
${makeExample( ${makeExample(
"Let's use another filter using the same example", "Let's use another filter using the same example",
`beat(.5) :: sound('sawtooth').note([48,60].beat()) `beat(.5) :: sound('sawtooth').note([48,60].beat())
.hcutoff(1000).hpa([0.05, 0.25, 0.5].beat(2)) .hcutoff(1000).hpa([0.05, 0.25, 0.5].beat(2))
.hpenv(8).hpq(10).out()`, .hpenv(8).hpq(10).out()`,
true true
)} )}
### Bandpass envelope ### Bandpass envelope
@ -259,16 +263,18 @@ ${makeExample(
| bpdecay | bpd | decay of the bandpass filter | | bpdecay | bpd | decay of the bandpass filter |
| bpsustain | bps | sustain of the bandpass filter | | bpsustain | bps | sustain of the bandpass filter |
| bprelease | bpr | release of the bandpass filter | | bprelease | bpr | release of the bandpass filter |
| bpadsr | | (**takes five arguments**) set all the parameters |
${makeExample( ${makeExample(
"And the bandpass filter, just for fun", "And the bandpass filter, just for fun",
`beat(.5) :: sound('sawtooth').note([48,60].beat()) `beat(.5) :: sound('sawtooth').note([48,60].beat())
.bandf([500,1000,2000].beat(2)) .bandf([500,1000,2000].beat(2))
.bpa([0.25, 0.125, 0.5].beat(2) * 4) .bpa([0.25, 0.125, 0.5].beat(2) * 4)
.bpenv(-4).release(2).out() .bpenv(-4).release(2).out()
`, `,
true true
)} )}
## Wavetable synthesis ## Wavetable synthesis
@ -276,8 +282,8 @@ ${makeExample(
Topos can also do wavetable synthesis. Wavetable synthesis allows you to use any sound file as a source to build an oscillator. By default, Topos comes with more than 1000 waveforms thanks to the awesome [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) pack made by Kristoffer Ekstrand. Any sample name that contains <ic>wt_</ic> as a prefix will be interpreted by the sampler as a wavetable and thus as an oscillator. See for yourself: Topos can also do wavetable synthesis. Wavetable synthesis allows you to use any sound file as a source to build an oscillator. By default, Topos comes with more than 1000 waveforms thanks to the awesome [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) pack made by Kristoffer Ekstrand. Any sample name that contains <ic>wt_</ic> as a prefix will be interpreted by the sampler as a wavetable and thus as an oscillator. See for yourself:
${makeExample( ${makeExample(
"Acidity test", "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) .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) .cutoff(1500 + usine(1/8) * 5000).lpadsr(16, 0.2, 0.2, 0.125/2, 0)
@ -285,15 +291,15 @@ beat(.25) :: sound('wt_symetric:8').note([50,55,57,60].beat(.25) - [12,0]
beat(1) :: sound('kick').n(4).out() beat(1) :: sound('kick').n(4).out()
beat(2) :: sound('snare').out() beat(2) :: sound('snare').out()
beat(.5) :: sound('hh').out()`, beat(.5) :: sound('hh').out()`,
true true
)} )}
Let's explore the galaxy of possible waveforms. It can be hard to explore them all, there is a **lot** of them: Let's explore the galaxy of possible waveforms. It can be hard to explore them all, there is a **lot** of them:
${makeExample( ${makeExample(
"Let's explore some wavetables", "Let's explore some wavetables",
` `
// Exploring a vast galaxy of waveforms // Exploring a vast galaxy of waveforms
let collection = [ let collection = [
'wt_sinharm', 'wt_linear', 'wt_bw_sawrounded', 'wt_sinharm', 'wt_linear', 'wt_bw_sawrounded',
@ -303,8 +309,8 @@ beat(2) :: v('selec', irand(1, 100))
beat(2) :: v('swave', collection.pick()) beat(2) :: v('swave', collection.pick())
beat(0.5) :: sound(v('swave')).n(v('selec')).out() beat(0.5) :: sound(v('swave')).n(v('selec')).out()
`, `,
true true
)} )}
You can work with them just like with any other waveform. Having so many of them makes them also very useful for generating sound effects, percussive, sounds, etc... You can work with them just like with any other waveform. Having so many of them makes them also very useful for generating sound effects, percussive, sounds, etc...
@ -319,8 +325,8 @@ Another really useful technique to know about is FM synthesis, FM standing for _
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. 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( ${makeExample(
"80s nostalgia", "80s nostalgia",
` `
beat([.5, 1].beat(8)) && snd('triangle').adsr(0.02, 0.5, 0.5, 0.25) 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) .fmi(2).fmh(1.5).note([60,55, 60, 63].beat() - 12)
.pan(noise()).out() .pan(noise()).out()
@ -329,23 +335,23 @@ beat(.25) && snd('triangle').adsr(0.02, 0.1, 0.1, 0.1)
.pan(noise()).note([60,55, 60, 63].beat() + [0, 7].pick()).out() .pan(noise()).note([60,55, 60, 63].beat() + [0, 7].pick()).out()
beat(2) :: sound('cp').room(1).sz(1).out() beat(2) :: sound('cp').room(1).sz(1).out()
`, `,
true true
)} )}
${makeExample( ${makeExample(
"Giving some love to ugly inharmonic sounds", "Giving some love to ugly inharmonic sounds",
` `
beat([.5, .25].bar()) :: sound('sine').fm('2.2183:3.18293').sustain(0.05).out() 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([4].bar()) :: sound('sine').fm('5.2183:4.5').sustain(0.05).out()
beat(.5) :: sound('sine') beat(.5) :: sound('sine')
.fmh([1, 1.75].beat()) .fmh([1, 1.75].beat())
.fmi($(1) % 30).orbit(2).room(0.5).out()`, .fmi($(1) % 30).orbit(2).room(0.5).out()`,
true true
)} )}
${makeExample( ${makeExample(
"Peace and serenity through FM synthesis", "Peace and serenity through FM synthesis",
` `
beat(0.25) :: sound('sine') beat(0.25) :: sound('sine')
.note([60, 67, 70, 72, 77].beat() - [0,12].bar()) .note([60, 67, 70, 72, 77].beat() - [0,12].bar())
.attack(0.2).release(0.5).gain(0.25) .attack(0.2).release(0.5).gain(0.25)
@ -354,8 +360,8 @@ beat(0.25) :: sound('sine')
.cutoff(1500).delay(0.5).delayt(0.125) .cutoff(1500).delay(0.5).delayt(0.125)
.delayfb(0.8).fmh(Math.floor(usine(.5) * 4)) .delayfb(0.8).fmh(Math.floor(usine(.5) * 4))
.out()`, .out()`,
true 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. **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.
@ -366,8 +372,8 @@ There is also a more advanced set of parameters you can use to control the envel
- <ic>fmrelease</ic> / <ic>fmrel</ic>: release time of the modulator envelope. - <ic>fmrelease</ic> / <ic>fmrel</ic>: release time of the modulator envelope.
${makeExample( ${makeExample(
"FM Synthesis with envelope control", "FM Synthesis with envelope control",
` `
beat(.5) :: sound('sine') beat(.5) :: sound('sine')
.note([50,53,55,57].beat(.5) - 12) .note([50,53,55,57].beat(.5) - 12)
.fmi(0.5 + usine(.25) * 1.5) .fmi(0.5 + usine(.25) * 1.5)
@ -375,8 +381,8 @@ beat(.5) :: sound('sine')
.fmwave('triangle') .fmwave('triangle')
.fmsus(0).fmdec(0.2).out() .fmsus(0).fmdec(0.2).out()
`, `,
true true
)} )}
## ZzFX ## ZzFX
@ -385,15 +391,15 @@ beat(.5) :: sound('sine')
ZZfX can be triggered by picking a default ZZfX waveform in the following list: <ic>z_sine</ic>, <ic>z_triangle</ic>, <ic>z_sawtooth</ic>, <ic>z_tan</ic>, <ic>z_noise</ic>. ZZfX can be triggered by picking a default ZZfX waveform in the following list: <ic>z_sine</ic>, <ic>z_triangle</ic>, <ic>z_sawtooth</ic>, <ic>z_tan</ic>, <ic>z_noise</ic>.
${makeExample( ${makeExample(
"Picking a waveform", "Picking a waveform",
` `
beat(.5) :: sound(['z_sine', 'z_triangle', 'z_sawtooth', 'z_tan', 'z_noise'].beat()).out() beat(.5) :: sound(['z_sine', 'z_triangle', 'z_sawtooth', 'z_tan', 'z_noise'].beat()).out()
`, `,
true true
)} )}
${makeExample( ${makeExample(
"Minimalist chiptune", "Minimalist chiptune",
` `
beat(.5) :: sound('z_triangle') beat(.5) :: sound('z_triangle')
.note([60, 67, 72, 63, 65, 70].beat(.5)) .note([60, 67, 72, 63, 65, 70].beat(.5))
.zrand(0).curve([1,2,3,4].beat(1)) .zrand(0).curve([1,2,3,4].beat(1))
@ -403,8 +409,8 @@ beat(.5) :: sound('z_triangle')
.room(0.5).size(0.9) .room(0.5).size(0.9)
.pitchJumpTime(0.01).out() .pitchJumpTime(0.01).out()
`, `,
true true
)} )}
It comes with a set of parameters that can be used to tweak the sound. Don't underestimate this synth! It is very powerful for generating anything ranging from chaotic noise sources to lush pads: It comes with a set of parameters that can be used to tweak the sound. Don't underestimate this synth! It is very powerful for generating anything ranging from chaotic noise sources to lush pads:
@ -430,8 +436,8 @@ It comes with a set of parameters that can be used to tweak the sound. Don't und
|<ic>duration</ic>|| Total sound duration (overrides envelope) | |<ic>duration</ic>|| Total sound duration (overrides envelope) |
${makeExample( ${makeExample(
"Chaotic Noise source", "Chaotic Noise source",
` `
beat(.25) :: sound('z_tan') beat(.25) :: sound('z_tan')
.note(40).noise(rand(0.0, 1.0)) .note(40).noise(rand(0.0, 1.0))
.pitchJump(84).pitchJumpTime(rand(0.0, 1.0)) .pitchJump(84).pitchJumpTime(rand(0.0, 1.0))
@ -441,21 +447,21 @@ beat(.25) :: sound('z_tan')
.sustain(0).decay([0.2, 0.1].pick()) .sustain(0).decay([0.2, 0.1].pick())
.out() .out()
`, `,
true true
)} )}
${makeExample( ${makeExample(
"What is happening to me?", "What is happening to me?",
` `
beat(1) :: snd('zzfx').zzfx([ beat(1) :: snd('zzfx').zzfx([
[4.77,,25,,.15,.2,3,.21,,2.4,,,,,,,.23,.35], [4.77,,25,,.15,.2,3,.21,,2.4,,,,,,,.23,.35],
[1.12,,97,.11,.16,.01,4,.77,,,30,.17,,,-1.9,,.01,.67,.2] [1.12,,97,.11,.16,.01,4,.77,,,30,.17,,,-1.9,,.01,.67,.2]
].beat()).out() ].beat()).out()
`, `,
false false
)} )}
${makeExample( ${makeExample(
"Les voitures dans le futur", "Les voitures dans le futur",
` `
beat(1) :: sound(['z_triangle', 'z_sine'].pick()) beat(1) :: sound(['z_triangle', 'z_sine'].pick())
.note([60,63,72,75].pick()).tremolo(16) .note([60,63,72,75].pick()).tremolo(16)
.zmod([0, 1/2, 1/8].div(2).pick()) .zmod([0, 1/2, 1/8].div(2).pick())
@ -463,18 +469,18 @@ beat(1) :: sound(['z_triangle', 'z_sine'].pick())
.room(0.9).size(0.9) .room(0.9).size(0.9)
.delayt(0.75).delayfb(0.5).out() .delayt(0.75).delayfb(0.5).out()
`, `,
false false
)} )}
Note that you can also design sounds [on this website](https://killedbyapixel.github.io/ZzFX/) and copy the generated code in Topos. To do so, please use the <ic>zzfx</ic> method with the generated array: Note that you can also design sounds [on this website](https://killedbyapixel.github.io/ZzFX/) and copy the generated code in Topos. To do so, please use the <ic>zzfx</ic> method with the generated array:
${makeExample( ${makeExample(
"Designing a sound on the ZzFX website", "Designing a sound on the ZzFX website",
` `
beat(2) :: sound('zzfx').zzfx([3.62,,452,.16,.1,.21,,2.5,,,403,.05,.29,,,,.17,.34,.22,.68]).out() beat(2) :: sound('zzfx').zzfx([3.62,,452,.16,.1,.21,,2.5,,,403,.05,.29,,,,.17,.34,.22,.68]).out()
`, `,
true true
)} )}
# Speech synthesis # Speech synthesis
@ -489,35 +495,35 @@ Topos can also speak using the [Web Speech API](https://developer.mozilla.org/en
- <ic>volume(number)</ic>: speaking volume, from <ic>0.0</ic> to <ic>1.0</ic>. - <ic>volume(number)</ic>: speaking volume, from <ic>0.0</ic> to <ic>1.0</ic>.
${makeExample( ${makeExample(
"Hello world!", "Hello world!",
` `
beat(4) :: speak("Hello world!") beat(4) :: speak("Hello world!")
`, `,
true true
)} )}
${makeExample( ${makeExample(
"Let's hear people talking about Topos", "Let's hear people talking about Topos",
` `
beat(2) :: speak("Topos!","fr",irand(0,5)) beat(2) :: speak("Topos!","fr",irand(0,5))
`, `,
true true
)} )}
You can also use speech by chaining methods to a string: You can also use speech by chaining methods to a string:
${makeExample( ${makeExample(
"Foobaba is the real deal", "Foobaba is the real deal",
` `
onbeat(4) :: "Foobaba".voice(irand(0,10)).speak() onbeat(4) :: "Foobaba".voice(irand(0,10)).speak()
`, `,
true true
)} )}
${makeExample( ${makeExample(
"Building string and chaining", "Building string and chaining",
` `
const subject = ["coder","user","loser"].pick() const subject = ["coder","user","loser"].pick()
const verb = ["is", "was", "isnt"].pick() const verb = ["is", "was", "isnt"].pick()
const object = ["happy","sad","tired"].pick() const object = ["happy","sad","tired"].pick()
@ -525,12 +531,12 @@ ${makeExample(
beat(6) :: sentence.pitch(0).rate(0).voice([0,2].pick()).speak() beat(6) :: sentence.pitch(0).rate(0).voice([0,2].pick()).speak()
`, `,
true true
)} )}
${makeExample( ${makeExample(
"Live coded poetry with array and string chaining", "Live coded poetry with array and string chaining",
` `
bpm(70) bpm(70)
const croissant = [ const croissant = [
@ -545,7 +551,7 @@ ${makeExample(
.rate(rand(.4,.6)) .rate(rand(.4,.6))
.speak(); .speak();
`, `,
true true
)} )}
`; `;
}; };

View File

@ -13,24 +13,26 @@ beat(.5) :: sound('hh').out()`,
`// Entering the secret room - Bubobubobubo `// Entering the secret room - Bubobubobubo
let frequencies = [200,400,600,800,1000,2000].beat(2); let frequencies = [200,400,600,800,1000,2000].beat(2);
beat(2) :: sound('sine').freq(frequencies) beat(2) :: sound('sine').freq(frequencies)
.delay(0.5).delayt(usine(.5)).delayfb(0.8).size(0.9).room(0.9).out() .delay(0.25).delayt(usine(.25)).delayfb(0.25).size(0.9).room(0.9).out()
beat(2) :: app.hydra.osc(frequencies/100, 0.25, 0.5) beat(2) :: app.hydra.osc(frequencies/100, 0.25, 0.5)
.posterize([32,4,8,16].beat(2)).rotate(cpulse()) .posterize([32,4,8,16].beat(2)).rotate(cpulse())
.kaleid([1,2,3].beat()).out()`, .kaleid([1,2,3].beat()).out()`,
`// The real internet of things - Bubobubobubo `// The real internet of things - Bubobubobubo
beat(.5) :: sound('STA6').cut(1).vel(0.4) beat(.5) :: sound('STA6').cut(1).vel(0.4)
.orbit(2).room(0.5).size(0.5).n(irand(1,4)) .orbit(2).room(0.5).size(0.5).n(irand(1,4))
.speed([0.15, 0.30].beat()).out() .speed([0.15, 0.30].beat() * 1.5).loop([1,0]
.beat(.125)).loopEnd([1,0.5].beat(2)).out()
binrhythm(.5, 50) :: sound('shaker').out() binrhythm(.5, 50) :: sound('shaker').out()
binrhythm(.5, 52) :: sound('808bd').n(3).out() binrhythm(.5, 52) :: sound('808bd').n(3).out()
rhythm(.25, 6, 8) :: sound('808sd').out()`, rhythm(.25, 6, 8) :: sound('808sd').out()`,
`// Numerology - Bubobubobubo `// Numerology - Bubobubobubo
bpm(130);
let mel = [ let mel = [
"0.125 _ (0 3 7 0 3 5 0 3 9)+(0 2)", "0.125 (0 7 0 10 0 5)+(0 3)", "0.125 _ (0 3 7 0 3 5 0 3 9)+(0 2)", "0.125 (0 7 0 10 0 5)+(0 3)",
"0.125 (0 3 7 0 3 5 0 3 9)+(0 2)", "0.125 (0 2 4 5 9 10)+(0 2)", "0.125 (0 3 7 0 3 5 0 3 9)+(0 2)", "0.125 (0 2 4 5 9 10)+(0 2)",
].beat(4); ].beat(4);
z0(mel) z0(mel)
.scale('minor').sound('square').cutoff(800 + usine(.5) * 5000) .scale('minor').sound('wt_piano').cutoff(800 + usine(.5) * 5000)
.fmi([2, 4, 8].beat(2)).fmh(flip(2) ? 2 : 4) .fmi([2, 4, 8].beat(2)).fmh(flip(2) ? 2 : 4)
.delay(bpm() / 60 / 9).delayt(0.25).delayfb(0.5) .delay(bpm() / 60 / 9).delayt(0.25).delayfb(0.5)
.fmsus(0.3).fmrel(0.3).rel(rand(0.5,0.8)) .fmsus(0.3).fmrel(0.3).rel(rand(0.5,0.8))
@ -42,34 +44,35 @@ beat([.25, .5].beat(4)) :: flip(6) && sound('dr')
`// Harmonic Leaps and Gaps -- Bubobubobubo `// Harmonic Leaps and Gaps -- Bubobubobubo
let oscillation = quant(usine(.25) * 20, [35, 40, 38, 50, 55]); let oscillation = quant(usine(.25) * 20, [35, 40, 38, 50, 55]);
let tonal = [0, 5, 0, 0, 7].palindrome().bar() + 40 - 24; let tonal = [0, 5, 0, 0, 7].palindrome().bar() + 40 - 24;
[tonal, tonal + 7, tonal + [12,3].bar()].forEach((e) => { [tonal, tonal + 7, tonal + [12, 3, 24].bar()].forEach((e) => {
flip(2) :: beat(.5) :: sound('square').fmi(2) flip(2) :: beat(.5) :: sound('square').fmi(1.5).fmh(0.99)
.cutoff(500 + usine(1/2)).n(irand(1,10)) .cutoff(500 + usine(1/2)).n($(1) % 20).cut(.1)
.note(e + oscillation + [0, 5].beat(.5)).out() .note(e + oscillation + [0, 5].beat(.5)).out()
!flip(2) :: beat(.5) :: sound('sawtooth').fmi(2) !flip(2) :: beat(.5) :: sound('sawtooth').fmi(1.49).fmh(1.01)
.cutoff(500 + usine(1/2) * 5000).n(irand(1,10)) .cutoff(500 + usine(1/2) * 5000).n(irand(1,10)).cut(.1)
.note(e + oscillation + [0, 5].beat(.5)).out() .note(e + oscillation + [0, 5].beat(.5)).out()
}); });
oncount([2, 3.5, [5,0].pick()], 6) :: sound('snare').out() oncount([2, 4, 4.5], 6) :: sound('snare').n(4).out()
`, rhythm(.25, 6, 8) :: sound('shaker').end(0.25).out()
rhythm(.5,4,8) :: sound('bd').out()`,
`// Computer Music Classroom, Monday (8AM) -- Bubobubobubo `// Computer Music Classroom, Monday (8AM) -- Bubobubobubo
let ur = [0, 12, 7].beat(24), let ur = [0, 5, 12, 7, 5].beat(24),
fundamental = [0, 5, 10, 8, 6].repeatAll(4).bar(); fundamental = [0, 5, 10, 8, 6].repeatAll(4).bar();
beat(.25) :: sound('square') beat(.25) :: sound('triangle')
.note(ur + fundamental + 40).n(1 + $(1) % 8) .note(ur + fundamental + 40).n(1 + $(1) % 16)
.atk(0.05).sustain(0.1).release(0.1) .atk(0.05).sustain(0.1).release(0.1)
.room(0.9).size(0.9) .room(0.9).size(0.9).vib(2).vibmod(0.125)
.out() .out()
beat(.25) :: sound('sawtooth') beat(.25) :: sound('triangle')
.note(ur + fundamental + 47).n(1 + $(2) % 8) .note(ur + fundamental + 47).n(1 + $(2) % 16)
.atk(0.05).sustain(0.1).release(0.1) .atk(0.05).sustain(0.1).release(0.1)
.room(0.9).size(0.9) .room(0.9).size(0.9).vib(4).vibmod(0.125)
.out() .out()
beat(.25) :: sound(['sawtooth', 'square'].bar()) beat(.25) :: sound(['sawtooth', 'square'].bar())
.note(ur + fundamental + 40+[10,12].bar()).n(1 + $(3) % 8) .note(ur + fundamental + 40+[10,12].bar()).n(1 + $(3) % 16)
.atk(0.05).sustain(0.1).release(0.1) .atk(0.05).sustain(0.1).release(0.1).vib([0, 0, 0, 0.5])
.room(0.9).size(0.9).out() .vibmod(often() ? 1 : [16,8,4].pick())
`, .room(0.9).size(0.9).out()`,
`// Lamento for Digital Harpists -- Bubobubobubo `// Lamento for Digital Harpists -- Bubobubobubo
beat(4) :: sound('triangle') beat(4) :: sound('triangle')
.note(60).fmwave('triangle').fmi(3.95) .note(60).fmwave('triangle').fmi(3.95)
@ -92,21 +95,18 @@ beat([4, 2, 8].pick() / [2,1].bar()) :: sound('triangle')
let melody = [30,30,34,35,37].palindrome() let melody = [30,30,34,35,37].palindrome()
.beat() + [0, -12].repeatAll(2).beat(2) .beat() + [0, -12].repeatAll(2).beat(2)
if (flip(8, 75)) { if (flip(8, 75)) {
log('first section') rhythm(.5, 4, 8) :: sound('ST12').n([0,1,2].beat(0.5)).speed(0.5).gain(0.4).out()
rhythm(.5, 4, 8) :: sound('ST12').n([0,1,2].beat(0.5)).speed(0.5).out() rhythm(.5, 6, 8) :: sound('ST20').n([0,1,2].beat(0.5) + 20).gain(0.4)
rhythm(.5, 6, 8) :: sound('ST20').n([0,1,2].beat(0.5) + 20)
.speed(0.25).end(0.1).orbit(2).room(0.5).size(0.5).out() .speed(0.25).end(0.1).orbit(2).room(0.5).size(0.5).out()
beat(.5) :: sound('ST01').note(melody) beat(.5) :: sound('ST01').note(melody).gain(0.4)
.n($(1)).speed(0.5).room(0.5).size(0.5).out() .n($(1)).speed(0.5).room(0.5).size(0.5).out()
} else { } else {
log('second section')
rhythm(.5, 2, 8) :: sound('ST20') rhythm(.5, 2, 8) :: sound('ST20')
.n([0,1,2].beat(0.5)).speed(0.5) .n([0,1,2].beat(0.5)).speed(0.5).gain(0.4)
.end(0.1).out() .end(0.1).out()
beat(.5) :: sound('ST01').note(melody).n($(1)).speed(0.5).end(0.1).out() beat(.5) :: sound('ST01').note(melody).n($(1)).speed(0.5).gain(0.4).end(0.1).out()
beat(1) :: sound('ST02').note(melody).n($(1)).speed(0.5).end(0.1).out() beat(1) :: sound('ST02').note(melody).n($(1)).speed(0.5).gain(0.4).end(0.1).out()
} }`,
`,
`// Race day - Bubobubobubo `// Race day - Bubobubobubo
bpm(125); bpm(125);
beat(.5) :: sound('STB6') beat(.5) :: sound('STB6')
@ -173,6 +173,7 @@ beat(0.25) :: snd('hh').out();
beat(2) :: snd('square') beat(2) :: snd('square')
.cutoff(500).note(50-12).resonance(20).sustain(0.2).out() .cutoff(500).note(50-12).resonance(20).sustain(0.2).out()
beat(1/4)::snd(['sawtooth', 'triangle', 'square'].beat(1)) beat(1/4)::snd(['sawtooth', 'triangle', 'square'].beat(1))
.vib(2).vibmod(0.5)
.note([50, 53, 55, 50, 50, 52, 58, 50+12, 50+15].beat(4) + [0, 12, 24].beat(0.5)) .note([50, 53, 55, 50, 50, 52, 58, 50+12, 50+15].beat(4) + [0, 12, 24].beat(0.5))
.cutoff(usine(.5)*10000).resonance([10,20].beat(2)) .cutoff(usine(.5)*10000).resonance([10,20].beat(2))
.fmi($(1) % 10).fmh($(2) % 5) .fmi($(1) % 10).fmh($(2) % 5)
@ -183,17 +184,20 @@ beat(1/4)::snd(['sawtooth', 'triangle', 'square'].beat(1))
beat(4)::snd('amencutup').n($(19)).cut(1).orbit(2).pan(rand(0.0,1.0)).out()`, beat(4)::snd('amencutup').n($(19)).cut(1).orbit(2).pan(rand(0.0,1.0)).out()`,
`// Crazy arpeggios - Bubobubobubo `// Crazy arpeggios - Bubobubobubo
bpm(110) bpm(110)
beat(0.125) && sound('sawtooth') beat([0.25, 0.5].beat(4)) && sound('sawtooth')
.note([60, 62, 63, 67, 70].beat(.125) + .note([60, 62, 63, 67, 70].beat(.125) +
[-12,0,12].beat() + [0, 0, 5, 7].bar()) [-12,0,12].beat() + [0, 0, 5, 7].bar())
.sustain(0.1).fmi(0.25).fmh(2).room(0.9) .sustain(0.1).fmi(0.25).fmh(2).room(0.9)
.gain(0.75).cutoff(500 + usine(8) * [500, 1000, 2000].bar()) .gain(0.75).cutoff([500,1000].beat(2))
.delay(0.5).delayt(0.25).delayfb(0.25) .lpadsr([6,8].beat(), 1/16, .125, 0, 0)
.delay(0.5).delayt(0.25)
.delayfb(0.25)
.out(); .out();
beat(1) && snd('kick').out(); binrhythm([.25, .5].beat(), 11) :: often() :: sound('dr')
beat(2) && snd('snare').out(); .n([0, 1].beat()).gain(0.4).out()
beat(.5) && snd('hat').out(); binrhythm([.5, .25, .5, .5, .25].beat(), 122) :: often() :: sound('dr')
`, .n([9, 24].beat()).gain(0.4).out()
rhythm([.5, .25, 1].beat(2), 4, 8) :: sound('kick').gain(0.6).out()`,
`// Obscure Shenanigans - Bubobubobubo `// Obscure Shenanigans - Bubobubobubo
beat([1/4,1/8,1/16].beat(8)):: sound('sine') beat([1/4,1/8,1/16].beat(8)):: sound('sine')
.freq([100,50].beat(16) + 50 * ($(1)%10)) .freq([100,50].beat(16) + 50 * ($(1)%10))
@ -205,15 +209,14 @@ flip(3) :: beat([.25,.5].beat(.5)) :: sound('dr')
.n([8,9].pick()).gain([.8,.5,.25,.1,.0].beat(.25)).out() .n([8,9].pick()).gain([.8,.5,.25,.1,.0].beat(.25)).out()
`, `,
`// Resonance bliss - Bubobubobubo `// Resonance bliss - Bubobubobubo
beat(.25)::snd('arpy') beat([.25,.125].beat(4))::snd('arpy:4')
.note(30 + [0,3,7,10].beat()) .note(30 + [0,3,7,10, 12, 5, 7].beat()).speed(0.999).pan(0)
.cutoff(usine(.5) * 5000).resonance(10).gain(0.3) .cutoff(1000 + usine(1/8) * 400).lpadsr(8, 1/8, 1/16, 0, 0)
.end(0.8).room(0.9).size(0.9).n(0).out(); .resonance(1).gain(0.4).end(0.8).room(0.9).size(0.9).n(0).out();
beat([.25,.125].beat(2))::snd('arpy') beat([.25,.125, .5].beat(4))::snd('arpy:4')
.note(30 + [0,3,7,10].beat()) .note(30 + [0,3,7,10, 12, 5, 7].beat()).speed(1.001).pan(1)
.cutoff(usine(.5) * 5000).resonance(20).gain(0.3) .cutoff(100 + usine(1/8) * 800).lpadsr(5, 0, [1/8, 1.16].beat(), 0, 0)
.end(0.8).room(0.9).size(0.9).n(3).out(); .resonance(5).gain(0.4).end(0.8).room(0.9).size(0.9).n(3).out();
beat(.5) :: snd('arpy').note( beat(.5) :: snd('arpy').note([30, 33, 35].repeatAll(4).beat(1) - [24,12].beat(0.5))
[30, 33, 35].repeatAll(4).beat(1) - [12,0].beat(0.5)).out() .cutoff(500).lpadsr(8, 0.05, .125, 0, 0).out()`,
`,
]; ];