+
+
Time
@@ -154,11 +156,22 @@
Time & Transport
Time & Cycles
Time & Structure
-
-
Patterns
-
Audio Engine
+
+
+
+ Audio Engine
+
+
Basics
+
Amplitude
+
Sampler
+
Reverb & Delay
+
Distortion
+
+
+
+
Samples
@@ -167,6 +180,7 @@
+
Patterns
Samples
Synths
MIDI
diff --git a/src/Documentation.ts b/src/Documentation.ts
index 59dc42e..2745c13 100644
--- a/src/Documentation.ts
+++ b/src/Documentation.ts
@@ -2,7 +2,12 @@ import { type Editor } from "./main";
// Basics
import { introduction } from "./documentation/basics/welcome";
import { loading_samples } from "./documentation/samples/loading_samples";
+import { amplitude } from "./documentation/audio_engine/amplitude";
+import { distortion } from "./documentation/audio_engine/distortion";
+import { reverb } from "./documentation/audio_engine/reverb_delay";
+import { sampler } from "./documentation/audio_engine/sampler";
import { sample_banks } from "./documentation/samples/sample_banks";
+import { audio_basics } from "./documentation/audio_engine/audio_basics";
import { sample_list } from "./documentation/samples/sample_list";
import { software_interface } from "./documentation/basics/interface";
import { shortcuts } from "./documentation/basics/keyboard";
@@ -93,8 +98,13 @@ export const documentation_factory = (application: Editor) => {
functions: functions(application),
reference: reference(),
shortcuts: shortcuts(application),
+ amplitude: amplitude(application),
+ distortion: distortion(application),
+ reverb_delay: reverb(application),
+ sampler: sampler(application),
mouse: mouse(application),
oscilloscope: oscilloscope(application),
+ audio_basics: audio_basics(application),
synchronisation: synchronisation(application),
bonus: bonus(application),
sample_list: sample_list(application),
diff --git a/src/InterfaceLogic.ts b/src/InterfaceLogic.ts
index 85eda09..3b22a2b 100644
--- a/src/InterfaceLogic.ts
+++ b/src/InterfaceLogic.ts
@@ -481,6 +481,11 @@ export const installInterfaceLogic = (app: Editor) => {
[
"introduction",
+ "sampler",
+ "amplitude",
+ "audio_basics",
+ "reverb_delay",
+ "distortion",
"interface",
"interaction",
"code",
@@ -488,8 +493,7 @@ export const installInterfaceLogic = (app: Editor) => {
"linear",
"cyclic",
"longform",
- "sound",
- "samples",
+ // "sound",
"synths",
"chaining",
"patterns",
@@ -499,7 +503,6 @@ export const installInterfaceLogic = (app: Editor) => {
"lfos",
"probabilities",
"variables",
- // "reference",
"synchronisation",
"mouse",
"shortcuts",
@@ -510,6 +513,7 @@ export const installInterfaceLogic = (app: Editor) => {
"loading_samples",
].forEach((e) => {
let name = `docs_` + e;
+ console.log(name)
document.getElementById(name)!.addEventListener("click", async () => {
if (name !== "docs_samples") {
app.currentDocumentationPane = e;
diff --git a/src/classes/SoundEvent.ts b/src/classes/SoundEvent.ts
index 012fcbc..7da55dd 100644
--- a/src/classes/SoundEvent.ts
+++ b/src/classes/SoundEvent.ts
@@ -436,7 +436,8 @@ export class SoundEvent extends AudibleEvent {
}
};
- out = (): void => {
+ out = (orbit?: number | number[]): void => {
+ if (orbit) this.values["orbit"] = orbit;
const events = objectWithArraysToArrayOfObjects(this.values, [
"parsedScale",
]);
diff --git a/src/documentation/audio_engine/amplitude.ts b/src/documentation/audio_engine/amplitude.ts
new file mode 100644
index 0000000..9d3e0f5
--- /dev/null
+++ b/src/documentation/audio_engine/amplitude.ts
@@ -0,0 +1,78 @@
+import { type Editor } from "../../main";
+import { makeExampleFactory } from "../../Documentation";
+
+export const amplitude = (application: Editor): string => {
+ // @ts-ignore
+ const makeExample = makeExampleFactory(application);
+ return `# Amplitude
+
+Controlling the volume is probably the most important concept you need to know about. Let's learn the basics.
+
+## Volume / Gain
+
+| Method | Alias | Description |
+|----------|-------|------------------------------------------------------------------------------------|
+|
gain | | Volume of the synth/sample (exponential). |
+|
velocity | vel | Velocity (amplitude) from
0 to
1. Multipled with gain. |
+|
dbgain | db | Attenuation in dB from
-inf to
+10 (acts as a sound mixer fader).|
+
+${makeExample(
+ "Velocity manipulated by a counter",
+ `
+beat(.5)::snd('cp').vel($(1)%10 / 10).out()`,
+ true
+ )}
+
+## Amplitude Enveloppe
+
+**Superdough** is applying an **ADSR** envelope to every sound being played. This is a very standard and conventional amplitude envelope composed of four stages: _attack_, _decay_, _sustain_ and _release_. You will find the same parameters on most synthesizers.
+
+| Method | Alias | Description |
+|---------|-------|-----------------------------------------------|
+|
attack | atk | Attack value (time to maximum volume) |
+|
decay | dec | Decay value (time to decay to sustain level) |
+|
sustain | sus | Sustain value (gain when sound is held) |
+|
release | rel | Release value (time for the sound to die off) |
+
+Note that the **sustain** value is not a duration but an amplitude value (how loud). The other values are the time for each stage to take place. Here is a fairly complete example using the
sawtooth basic waveform.
+
+${makeExample(
+ "Simple synthesizer",
+ `
+let smooth = (sound) => {
+ return sound.cutoff(r(100,500))
+ .lpadsr(usaw(1/8) * 8, 0.05, .125, 0, 0)
+ .gain(r(0.25, 0.4)).adsr(0, r(.2,.4), r(0,0.5), 0)
+ .room(0.9).size(2).o(2).vib(r(2,8)).vibmod(0.125)
+}
+beat(.25)::smooth(sound('sawtooth')
+ .note([50,57,55,60].beat(1))).out();
+beat(.25)::smooth(sound('sawtooth')
+ .note([50,57,55,60].add(12).beat(1.5))).out();
+ `,
+ true
+ )};
+
+Sometimes, using a full ADSR envelope is a bit overkill. There are other simpler controls to manipulate the envelope like the
.ad method:
+
+
+${makeExample(
+ "Replacing .adsr by .ad",
+ `
+let smooth = (sound) => {
+ return sound.cutoff(r(100,500))
+ .lpadsr(usaw(1/8) * 8, 0.05, .125, 0, 0)
+ .gain(r(0.25, 0.4)).ad(0, .25)
+ .room(0.9).size(2).o(2).vib(r(2,8)).vibmod(0.125)
+}
+beat(.25)::smooth(sound('sawtooth')
+ .note([50,57,55,60].beat(1))).out();
+beat(.25)::smooth(sound('sawtooth')
+ .note([50,57,55,60].add(12).beat(1.5))).out();
+ `,
+ true
+ )};
+
+`}
+
+
diff --git a/src/documentation/audio_engine/audio_basics.ts b/src/documentation/audio_engine/audio_basics.ts
new file mode 100644
index 0000000..81f577e
--- /dev/null
+++ b/src/documentation/audio_engine/audio_basics.ts
@@ -0,0 +1,177 @@
+import { type Editor } from "../../main";
+import { makeExampleFactory } from "../../Documentation";
+
+export const audio_basics = (application: Editor): string => {
+ // @ts-ignore
+ const makeExample = makeExampleFactory(application);
+ return `# Audio Basics
+
+The Topos audio engine is [SuperDough](https://www.npmjs.com/package/superdough). This audio engine 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! Let's learn the basics.
+
+## Sound basics
+
+Use the
sound(name: string) function to play a sound. You can also write
snd. A sound can be:
+- an audio sample: taken from the samples currently loaded
+- a synthesizer: the name of a specific waveform
+
+Whatever you choose, the syntax stays the same. See the following example:
+
+${makeExample(
+ "Playing sounds is easy",
+ `
+beat(1) && sound('bd').out()
+beat(0.5) && sound('hh').out()
+`,
+ true
+ )}
+
+These commands, in plain english, can be translated to:
+
+> Every beat, play a kick drum.
+> Every half-beat, play a high-hat.
+
+Let's make this example a bit more complex:
+
+${makeExample(
+ "Adding some effects",
+ `
+beat(1) && sound('bd').coarse(0.25).room(0.5).orbit(2).out();
+beat(0.5) && sound('hh').delay(0.25).delaytime(0.125).out();
+`,
+ true
+ )}
+
+Now, it translates as follows:
+
+> Every beat, play a kick drum with some amount of distortion on the third audio bus.
+> 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.
+
+If you remove
beat instruction, you will end up with a deluge of kick drums and high-hats.
beat in that case, is used to filter time. It is a very useful instruction to create basic rhythms. Check out the **Time** section if you haven't read it already.
+
+## The
.out method
+
+To play a sound, you always need the
.out() method at the end of your chain. THis method tells **Topos** to send the chain to the audio engine. The
.out method can take an optional argument to send the sound to a numbered effect bus, from
0 to
n :
+
+${makeExample("Using the .out method",
+ `
+// Playing a clap on the third bus (0-indexed)
+beat(1)::sound('cp').out(2)
+`, true
+ )}
+
+Try to remove
.out. You will see that no sound is playing at all!
+
+## Sound chains
+
+- Sounds are **composed** by adding qualifiers/parameters that modify the sound or synthesizer you have picked (_e.g_
sound('...').blabla(...)..something(...).out(). Think of it as _audio chains_.
+
+${makeExample(
+ '"Composing" a complex sonic object by making a sound chain',
+ `
+beat(1) :: sound('pad').n(1)
+ .begin(rand(0, 0.4))
+ .freq([50,52].beat())
+ .size(0.9).room(0.9)
+ .velocity(0.25)
+ .pan(usine()).release(2).out()`,
+ true
+ )}
+
+## Picking a specific sound
+
+If you choose the sound
kick, you are asking for the first sample in the
kick folder. All the sample names are folders. Synthesizers are not affected by this. Here is what the
kickmight be looking like:
+
+\`\`\`shell
+.
+├── KICK9.wav
+├── kick1.wav
+├── kick10.wav
+├── kick2-1.wav
+├── kick2.wav
+├── kick3-1.wav
+├── kick3.wav
+├── kick4.wav
+├── kick5.wav
+├── kick6.wav
+├── kick7.wav
+└── kick8.wav
+\`\`\`
+
+The
.n(number) method can be used to pick a sample from the currently selected sample folder. For instance, the following script will play a random sample from the _kick_ folder:
+${makeExample(
+ "Picking a sample",
+ `
+beat(1) && sound('kick').n([1,2,3,4,5,6,7,8].pick()).out()
+`,
+ true
+ )}
+
+You can also use the
: to pick a sample number directly from the
sound function:
+
+ ${makeExample(
+ "Picking a sample using :",
+ `
+beat(1) && sound('kick:3').out()
+`,
+ true
+ )}
+
+You can use any number to pick a sound. Don't be afraid of using a number too big. If the number exceeds the number of available samples, it will simply wrap around and loop infinitely over the folder. Let's demonstrate this by using the mouse over a very large sample folder:
+
+${makeExample(
+ "Picking a sample... with the mouse!",
+ `
+// Move your mouse to change the sample being used!
+beat(.25) && sound('ST09').n(Math.floor(mouseX())).out()`,
+ true
+ )}
+
+
+The
.n method is also used for synthesizers but it behaves differently. When using a synthesizer, this method can help you determine the number of harmonics in your waveform. See the **Synthesizers** section to learn more about this.
+
+## Orbits and audio busses
+
+**Topos** is inheriting some audio bus management principles taken from the [SuperDirt](https://github.com/musikinformatik/SuperDirt) and [Superdough](https://www.npmjs.com/package/superdough) engine, a WebAudio based recreation of the same engine.
+
+Each sound that you play is associated with an audio bus, called an _orbit_. Some effects are affecting **all sounds currently playing on that bus**. These are called **global effects**, to distinguish from **local effects**:
+
+- **global effects**: _reverberation_ and _delay_.
+- **local effects**: everything else :smile:
+
+There is a special method to choose the _orbit_ that your sound is going to use:
+
+| Method | Alias | Description |
+|----------|-------|------------------------------------------------------------|
+|
orbit | o | Orbit number |
+
+You can play a sound _dry_ and another sound _wet_. Take a look at this example where the reverb is only affecting one of the sounds:
+
+${makeExample("Dry and wet", `
+
+// This sound is dry
+beat(1)::sound('hh').out()
+
+// This sound is wet (reverb)
+beat(2)::sound('cp').orbit(2).room(0.5).size(8).out()
+`, true)}
+
+## The art of chaining
+
+Learning to create complex chains is very important when using **Topos**. It can take some time to learn all the possible parameters. Don't worry, it's actually rather easy to learn.
+
+${makeExample(
+ "Complex chain",
+ `
+beat(0.25) && sound('fhh')
+ .sometimes(s=>s.speed([2, 0.5].pick()))
+ .room(0.9).size(0.9).gain(1)
+ .cutoff(usine(1/2) * 5000)
+ .out()`,
+ true
+ )}
+
+Most audio parameters can be used both for samples and synthesizers. This is quite unconventional if you are familiar with a more traditional music software.
+`}
+
+
diff --git a/src/documentation/audio_engine/distortion.ts b/src/documentation/audio_engine/distortion.ts
new file mode 100644
index 0000000..5f51d16
--- /dev/null
+++ b/src/documentation/audio_engine/distortion.ts
@@ -0,0 +1,31 @@
+import { type Editor } from "../../main";
+import { makeExampleFactory } from "../../Documentation";
+
+export const distortion = (application: Editor): string => {
+ // @ts-ignore
+ const makeExample = makeExampleFactory(application);
+ return `# Distortion
+
+## Distorsion, saturation, destruction
+
+Three additional effects that are easy enough to understand. These effects are deteriorating the signal, making it easy to get digital or gritty audio sample playback or synthesizers destroyed beyond recognition. Be careful with your signal level!
+
+| Method | Alias | Description |
+|------------|-----------|---------------------------------|
+|
coarse | | Artificial sample-rate lowering |
+|
crush | | bitcrushing.
1 is extreme, the more you go up, the less it takes effect. |
+|
shape | | Waveshaping distortion (between
0 and
1) |
+
+
+${makeExample(
+ "Crunch... crunch... crunch!",
+ `
+beat(.5)::snd('pad').coarse($(1) % 16).clip(.5).out(); // Comment me
+beat(.5)::snd('pad').crush([16, 8, 4].beat(2)).clip(.5).out()
+ `,
+ true
+ )};
+
+`}
+
+
diff --git a/src/documentation/audio_engine/reverb_delay.ts b/src/documentation/audio_engine/reverb_delay.ts
new file mode 100644
index 0000000..8d01cf4
--- /dev/null
+++ b/src/documentation/audio_engine/reverb_delay.ts
@@ -0,0 +1,52 @@
+import { type Editor } from "../../main";
+import { makeExampleFactory } from "../../Documentation";
+
+export const reverb = (application: Editor): string => {
+ // @ts-ignore
+ const makeExample = makeExampleFactory(application);
+ return `# Reverberation and delay
+
+## Reverb
+
+A good sounding reverb. This reverb unit is using a convolution that gets updated everytime you change a parameter.
+For that reason, it is often a good idea to set fixed reverb values per orbit. Do not try to pattern the reverb too much.
+
+| Method | Alias | Description |
+|------------|-------|---------------------------------|
+|
room | rm | Reverb level (between
0 and
1 |
+|
size | sz | Reverb room size of the reverb, between
0 and
n |
+|
roomfade | | Reverb fade time, in seconds |
+|
roomlp | | Reverb lowpass starting frequency (in hertz) |
+|
roomdim | | Reverb lowpass frequency at -60db (in hertz) |
+
+${makeExample(
+ "Clapping in the cavern",
+ `
+beat(2)::snd('cp').room(0.5).size(4).out()
+ `,
+ true
+ )};
+
+
+## Delay
+
+A good sounding delay unit that can go into feedback territory. Use it without moderation.
+
+| Method | Alias | Description |
+|------------|-----------|---------------------------------|
+|
delay | | Delay _wet/dry_ (between
0 and
1) |
+|
delaytime | delayt | Delay time (in milliseconds) |
+|
delayfeedback | delayfb | Delay feedback (between
0 and
1) |
+
+${makeExample(
+ "Who doesn't like delay?",
+ `
+beat(2)::snd('cp').delay(0.5).delaytime(0.75).delayfb(0.8).out()
+beat(4)::snd('snare').out()
+beat(1)::snd('kick').out()
+ `,
+ true
+ )};
+
+
+`}
diff --git a/src/documentation/audio_engine/sampler.ts b/src/documentation/audio_engine/sampler.ts
new file mode 100644
index 0000000..ce35223
--- /dev/null
+++ b/src/documentation/audio_engine/sampler.ts
@@ -0,0 +1,130 @@
+import { type Editor } from "../../main";
+import { makeExampleFactory } from "../../Documentation";
+
+export const sampler = (application: Editor): string => {
+ // @ts-ignore
+ const makeExample = makeExampleFactory(application);
+ return `# Sampler
+
+The sampler is a rather complex beast. There is a lot you can do by manipulating samples, from simple playback to complex granulation, wavetable synthesis and concrete music.
+
+
+| Method | Alias | Description |
+|---------|--------|--------------------------------------------------------|
+|
n | | Select a sample in the current folder (from
0 to infinity) |
+|
begin | | Beginning of the sample playback (between
0 and
1) |
+|
end | | End of the sample (between
0 and
1) |
+|
loopBegin | | Beginning of the loop section (between
0 and
1) |
+|
loopEnd | | End of the loop section (between
0 and
1) |
+|
loop | | Whether to loop or not the audio sample |
+|
stretch | | Stretches the audio playback rate of a sample over
n beats |
+|
speed | | Playback speed (
2 = twice as fast) |
+|
cut | | Set with
0 or
1. 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 |
+|
pan | | Stereo position of the audio playback (
0 = left,
1 = right)|
+
+Let's apply some of these methods naïvely. We will then break everything using simpler examples.
+
+${makeExample(
+ "Complex sampling duties",
+ `
+// Using some of the modifiers described above :)
+beat(.5)::snd('pad').begin(0.2)
+ .speed([1, 0.9, 0.8].beat(4))
+ .n(2).pan(usine(.5))
+ .end(rand(0.3,0.8))
+ .room(0.8).size(0.5)
+ .clip(1).out()
+ `,
+ true
+ )};
+
+
+## Playback speed / pitching samples
+
+Let's play with the
speed parameter to control the pitch of sample playback:
+
+${makeExample("Controlling the playback speed", `
+beat(0.5)::sound('notes')
+ .speed([1,2,3,4].palindrome().beat(0.5)).out()
+`, true)}
+
+It also works by using negative values. It reverses the playback:
+
+${makeExample("Playing samples backwards", `
+beat(0.5)::sound('notes')
+ .speed(-[1,2,3,4].palindrome().beat(0.5)).out()
+`, true)}
+
+Of course you can play melodies using samples:
+
+
+${makeExample("Playing melodies using samples", `
+beat(0.5)::sound('notes')
+ .room(0.5).size(4)
+ .note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.5)).out()
+`, true)}
+
+## Panning
+
+To pan samples, use the
.pan method with a number between
0 and
1.
+
+
+${makeExample("Playing melodies using samples", `
+beat(0.25)::sound('notes')
+ .room(0.5).size(4).pan(r(0, 1))
+ .note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
+`, true)}
+
+
+## Looping over a sample
+
+Using
loop (
1 for looping),
loopBegin and
loopEnd (between
0 and
1), you can loop over the length of a sample. It can be super effective to create granular effects.
+
+
+${makeExample("Granulation using loop", `
+beat(0.25)::sound('fikea').loop(1)
+ .lpf(ir(2000, 5000))
+ .loopBegin(0).loopEnd(r(0, 1))
+ .room(0.5).size(4).pan(r(0, 1))
+ .note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
+`, true)}
+
+## Stretching a sample
+
+The
stretch parameter can help you to stretch long samples like amen breaks:
+
+${makeExample(
+ "Playing an amen break",
+ `
+// Note that stretch has the same value as beat
+beat(4) :: sound('amen1').n(11).stretch(4).out()
+beat(1) :: sound('kick').shape(0.35).out()`,
+ true,
+ )};
+
+## Cutting samples
+
+Sometimes, you will find it necessary to cut a sample. It can be because the sample is too long but also because it takes too much CPU to play too many samples at the same time.
+
+Know about the
begin and
end parameters. They are not related to the sampler itself, but to the length of the event you are playing. Let's cut the granular example:
+
+${makeExample("Cutting a sample using end", `
+beat(0.25)::sound('notes')
+ .end(usine(1/2)/0.5)
+ .room(0.5).size(4).pan(r(0, 1))
+ .note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
+`, true)}
+
+You can also use
clip to cut the sample everytime a new sample comes in:
+
+
+${makeExample("Cutting a sample using end", `
+beat(0.125)::sound('notes')
+ .cut(1)
+ .room(0.5).size(4).pan(r(0, 1))
+ .note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.125)
+ + [-12,12].beat()).out()
+`, true)}
+
+`}
diff --git a/src/documentation/engine.ts b/src/documentation/engine.ts
index e9900c6..3d1bb55 100644
--- a/src/documentation/engine.ts
+++ b/src/documentation/engine.ts
@@ -4,179 +4,8 @@ import { makeExampleFactory } from "../Documentation";
export const sound = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
return `
-# Audio engine
-
-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...
sound(name: string) (you can also write
snd 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",
- `
-beat(1) && sound('bd').out()
-beat(0.5) && sound('hh').out()
-`,
- true
- )}
-
-In plain english, this translates to:
-
-> Every beat, play a kick drum.
-> Every half-beat, play a high-hat.
-
-Let's make it slightly more complex:
-${makeExample(
- "Adding some effects",
- `
-beat(1) && sound('bd').coarse(0.25).room(0.5).orbit(2).out();
-beat(0.5) && sound('hh').delay(0.25).delaytime(0.125).out();
-`,
- true
- )}
-
-Now, it reads as follow:
-
-> 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 and explain what is going on:
-- If you remove
beat instruction, you will end up with a deluge of kick drums and high-hats.
beat 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
.out() method that gives the instruction to send a message to the audio engine.
-- Sounds are **composed** by adding qualifiers/parameters that will modify the sound or synthesizer being played (_e.g_
sound('...').blabla(...)..something(...).out(). Think of it as _audio chains_.
-
-${makeExample(
- '"Composing" a complex sonic object by making a sound chain',
- `
-beat(1) :: sound('pad').n(1)
- .begin(rand(0, 0.4))
- .freq([50,52].beat())
- .size(0.9).room(0.9)
- .velocity(0.25)
- .pan(usine()).release(2).out()`,
- true
- )}
-
-## Audio Sample Folders / Sample Files
-
-When you type
kick in the
sound('kick').out() expression, you are referring to a sample folder containing multiple audio samples. If you look at the sample folder, it would look something like this:
-
-\`\`\`shell
-.
-├── KICK9.wav
-├── kick1.wav
-├── kick10.wav
-├── kick2-1.wav
-├── kick2.wav
-├── kick3-1.wav
-├── kick3.wav
-├── kick4.wav
-├── kick5.wav
-├── kick6.wav
-├── kick7.wav
-└── kick8.wav
-\`\`\`
-
-The
.n(number) method can be used to pick a sample from the currently selected sample folder. For instance, the following script will play a random sample from the _kick_ folder:
-${makeExample(
- "Picking a sample",
- `
-beat(1) && sound('kick').n([1,2,3,4,5,6,7,8].pick()).out()
-`,
- true
- )}
-
-Don't worry about the number. If it gets too big, it will be automatically wrapped to the number of samples in the folder. You can type any number, it will always fall on a sample. Let's use our mouse to select a sample number in a folder:
-
-${makeExample(
- "Picking a sample... with your mouse!",
- `
-// Move your mouse to change the sample being used!
-beat(.25) && sound('numbers').n(Math.floor(mouseX())).out()`,
- true
- )}
-
-**Note:** the
sound function can also be used to play synthesizers (see the **Synthesizers** page). In that case, the
.n(n: number) becomes totally useless!
-
-## Learning about sound modifiers
-
-As we said earlier, the
sound('sample_name') function can be chained to _specify_ a sound more. For instance, you can add a filter and some effects to your high-hat:
-${makeExample(
- "Let's make something more complex",
- `
-beat(0.25) && sound('jvbass')
- .sometimes(s=>s.speed([2, 0.5].pick()))
- .room(0.9).size(0.9).gain(1)
- .cutoff(usine(1/2) * 5000)
- .out()`,
- true
- )}
-
-There are many possible arguments that you can add to your sounds. Learning them can take a long time but it will open up a lot of possibilities. Let's try to make it through all of them. They can all be used both with synthesizers and audio samples, which is kind of unconventional with normal / standard electronic music softwares.
-
-## Orbits and audio busses
-
-Topos is inheriting some audio bus management principles taken from the [SuperDirt](https://github.com/musikinformatik/SuperDirt) and [Superdough](https://www.npmjs.com/package/superdough) engine, a WebAudio based recreation of the same engine. Each sound that you play is associated with an audio bus, called an _orbit_. Some effects are affecting **all sounds currently playing on that bus**. These are called **global effects**, to distinguish from **local effects**:
-
-- **global effects**: _reverberation_ and _delay_.
-- **local effects**: everything else :smile:
-
-There is a special method to choose the _orbit_ that your sound is going to use:
-
-| Method | Alias | Description |
-|----------|-------|------------------------------------------------------------|
-|
orbit | o | Orbit number |
-
-
-## Amplitude
-
-Simple controls over the amplitude (volume) of a given sound.
-
-| Method | Alias | Description |
-|----------|-------|------------------------------------------------------------------------------------|
-|
gain | | Volume of the synth/sample (exponential) |
-|
velocity | vel | Velocity (amplitude) from
0 to
1. Multipled with gain |
-|
dbgain | db | Attenuation in dB from
-inf to
+10 (acts as a sound mixer fader) |
-
-${makeExample(
- "Velocity manipulated by a counter",
- `
-beat(.5)::snd('cp').vel($(1)%10 / 10).out()`,
- true
- )}
-
-## Amplitude Enveloppe
-
-**Superdough** is applying an **ADSR** envelope to every sound being played. This is a very standard and conventional amplitude envelope composed of four stages: _attack_, _decay_, _sustain_ and _release_. You will find the same parameters on most synthesizers.
-
-| Method | Alias | Description |
-|---------|-------|-----------------------------------------------|
-|
attack | atk | Attack value (time to maximum volume) |
-|
decay | dec | Decay value (time to decay to sustain level) |
-|
sustain | sus | Sustain value (gain when sound is held) |
-|
release | rel | Release value (time for the sound to die off) |
-
-Note that the **sustain** value is not a duration but an amplitude value (how loud). The other values are the time for each stage to take place. Here is a fairly complete example using the
sawtooth basic waveform.
-
-${makeExample(
- "Simple synthesizer",
- `
-let smooth = (sound) => {
- return sound.cutoff(r(100,500))
- .lpadsr(usaw(1/8) * 8, 0.05, .125, 0, 0)
- .gain(r(0.25, 0.4)).adsr(0, r(.2,.4), r(0,0.5), 0)
- .room(0.9).size(2).o(2).vib(r(2,8)).vibmod(0.125)
-}
-beat(.25)::smooth(sound('sawtooth').note([50,57,55,60].beat(1))).out();
-beat(.25)::smooth(sound('sawtooth').note([50,57,55,60].add(12).beat(1.5))).out();
- `,
- true
- )};
-
## Sample Controls
There are some basic controls over the playback of each sample. This allows you to get into more serious sampling if you take the time to really work with your audio materials.
@@ -244,47 +73,6 @@ beat(.5) && snd('sawtooth')
true
)};
-## Reverb
-
-A good sounding reverb. This reverb unit is using a convolution that gets updated everytime you change a parameter.
-For that reason, it is often a good idea to set fixed reverb values per orbit. Do not try to pattern the reverb too much.
-
-| Method | Alias | Description |
-|------------|-------|---------------------------------|
-|
room | rm | Reverb level (between
0 and
1 |
-|
size | sz | Reverb room size of the reverb, between
0 and
n |
-|
roomfade | | Reverb fade time, in seconds |
-|
roomlp | | Reverb lowpass starting frequency (in hertz) |
-|
roomdim | | Reverb lowpass frequency at -60db (in hertz) |
-
-${makeExample(
- "Clapping in the cavern",
- `
-beat(2)::snd('cp').room(0.5).size(4).out()
- `,
- true
- )};
-
-
-## Delay
-
-A good sounding delay unit that can go into feedback territory. Use it without moderation.
-
-| Method | Alias | Description |
-|------------|-----------|---------------------------------|
-|
delay | | Delay _wet/dry_ (between
0 and
1) |
-|
delaytime | delayt | Delay time (in milliseconds) |
-|
delayfeedback | delayfb | Delay feedback (between
0 and
1) |
-
-${makeExample(
- "Who doesn't like delay?",
- `
-beat(2)::snd('cp').delay(0.5).delaytime(0.75).delayfb(0.8).out()
-beat(4)::snd('snare').out()
-beat(1)::snd('kick').out()
- `,
- true
- )};
## Compression
@@ -299,24 +87,5 @@ This effect is leveraging the basic WebAudio compressor. More information can be
|
compRelease | cmpr | In seconds, time to increase the gain by 10db |
-## Distorsion, saturation, destruction
-
-Three additional effects that are easy enough to understand. These effects are deteriorating the signal, making it easy to get digital or gritty audio sample playback or synthesizers destroyed beyond recognition. Be careful with your signal level!
-
-| Method | Alias | Description |
-|------------|-----------|---------------------------------|
-|
coarse | | Artificial sample-rate lowering |
-|
crush | | bitcrushing.
1 is extreme, the more you go up, the less it takes effect. |
-|
shape | | Waveshaping distortion (between
0 and
1) |
-
-
-${makeExample(
- "Crunch... crunch... crunch!",
- `
-beat(.5)::snd('pad').coarse($(1) % 16).clip(.5).out(); // Comment me
-beat(.5)::snd('pad').crush([16, 8, 4].beat(2)).clip(.5).out()
- `,
- true
- )};
`;
};