First batch of code cleaning
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const amplitude = (application: Editor): string => {
|
||||
// @ts-ignore
|
||||
@ -17,11 +17,11 @@ Controlling the volume is probably the most important concept you need to know a
|
||||
| <ic>dbgain</ic> | db | Attenuation in dB from <ic>-inf</ic> to <ic>+10</ic> (acts as a sound mixer fader).|
|
||||
|
||||
${makeExample(
|
||||
"Velocity manipulated by a counter",
|
||||
`
|
||||
"Velocity manipulated by a counter",
|
||||
`
|
||||
beat(.5)::snd('cp').vel($(1)%10 / 10).out()`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Amplitude Enveloppe
|
||||
|
||||
@ -38,8 +38,8 @@ beat(.5)::snd('cp').vel($(1)%10 / 10).out()`,
|
||||
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 <ic>sawtooth</ic> basic waveform.
|
||||
|
||||
${makeExample(
|
||||
"Simple synthesizer",
|
||||
`
|
||||
"Simple synthesizer",
|
||||
`
|
||||
register("smooth", x => x.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)
|
||||
@ -51,15 +51,15 @@ beat(.25)::sound('sawtooth')
|
||||
.note([50,57,55,60].add(12).beat(1.5))
|
||||
.smooth().out();
|
||||
`,
|
||||
true,
|
||||
)};
|
||||
true,
|
||||
)};
|
||||
|
||||
Sometimes, using a full ADSR envelope is a bit overkill. There are other simpler controls to manipulate the envelope like the <ic>.ad</ic> method:
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Replacing .adsr by .ad",
|
||||
`
|
||||
"Replacing .adsr by .ad",
|
||||
`
|
||||
register("smooth", x => x.cutoff(r(100,500))
|
||||
.lpadsr(usaw(1/8) * 8, 0.05, .125, 0, 0)
|
||||
.gain(r(0.25, 0.4)).ad(0, 0.25)
|
||||
@ -71,8 +71,8 @@ beat(.25)::sound('sawtooth')
|
||||
.note([50,57,55,60].add(12).beat(1.5))
|
||||
.smooth().out();
|
||||
`,
|
||||
true,
|
||||
)};
|
||||
true,
|
||||
)};
|
||||
|
||||
`;
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const audio_basics = (application: Editor): string => {
|
||||
// @ts-ignore
|
||||
@ -17,13 +17,13 @@ Use the <ic>sound(name: string)</ic> function to play a sound. You can also writ
|
||||
Whatever you choose, the syntax stays the same. See the following example:
|
||||
|
||||
${makeExample(
|
||||
"Playing sounds is easy",
|
||||
`
|
||||
"Playing sounds is easy",
|
||||
`
|
||||
beat(1) && sound('bd').out()
|
||||
beat(0.5) && sound('hh').out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
These commands, in plain english, can be translated to:
|
||||
|
||||
@ -33,13 +33,13 @@ These commands, in plain english, can be translated to:
|
||||
Let's make this example a bit more complex:
|
||||
|
||||
${makeExample(
|
||||
"Adding some effects",
|
||||
`
|
||||
"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,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
Now, it translates as follows:
|
||||
|
||||
@ -54,13 +54,13 @@ If you remove <ic>beat</ic> instruction, you will end up with a deluge of kick d
|
||||
To play a sound, you always need the <ic>.out()</ic> method at the end of your chain. THis method tells **Topos** to send the chain to the audio engine. The <ic>.out</ic> method can take an optional argument to send the sound to a numbered effect bus, from <ic>0</ic> to <ic>n</ic> :
|
||||
|
||||
${makeExample(
|
||||
"Using the .out method",
|
||||
`
|
||||
"Using the .out method",
|
||||
`
|
||||
// Playing a clap on the third bus (0-indexed)
|
||||
beat(1)::sound('cp').out(2)
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
Try to remove <ic>.out</ic>. You will see that no sound is playing at all!
|
||||
|
||||
@ -69,16 +69,16 @@ Try to remove <ic>.out</ic>. You will see that no sound is playing at all!
|
||||
- Sounds are **composed** by adding qualifiers/parameters that modify the sound or synthesizer you have picked (_e.g_ <ic>sound('...').blabla(...)..something(...).out()</ic>. Think of it as _audio chains_.
|
||||
|
||||
${makeExample(
|
||||
"Complex sonic object",
|
||||
`
|
||||
"Complex sonic object",
|
||||
`
|
||||
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,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Picking a specific sound
|
||||
|
||||
@ -102,12 +102,12 @@ If you choose the sound <ic>kick</ic>, you are asking for the first sample in th
|
||||
|
||||
The <ic>.n(number)</ic> 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",
|
||||
`
|
||||
"Picking a sample",
|
||||
`
|
||||
beat(1) && sound('kick').n([1,2,3,4,5,6,7,8].pick()).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
You can also use the <ic>:</ic> to pick a sample number directly from the <ic>sound</ic> function:
|
||||
|
||||
@ -122,12 +122,12 @@ beat(1) && sound('kick:3').out()
|
||||
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!",
|
||||
`
|
||||
"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,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
The <ic>.n</ic> 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.
|
||||
@ -150,8 +150,8 @@ There is a special method to choose the _orbit_ that your sound is going to use:
|
||||
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",
|
||||
`
|
||||
"Dry and wet",
|
||||
`
|
||||
|
||||
// This sound is dry
|
||||
beat(1)::sound('hh').out()
|
||||
@ -159,23 +159,23 @@ beat(1)::sound('hh').out()
|
||||
// This sound is wet (reverb)
|
||||
beat(2)::sound('cp').orbit(2).room(0.5).size(8).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
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",
|
||||
`
|
||||
"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,
|
||||
)}
|
||||
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.
|
||||
`;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const distortion = (application: Editor): string => {
|
||||
// @ts-ignore
|
||||
@ -18,13 +18,13 @@ Three additional effects that are easy enough to understand. These effects are d
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Crunch... crunch... crunch!",
|
||||
`
|
||||
"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,
|
||||
)};
|
||||
true,
|
||||
)};
|
||||
|
||||
`;
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const effects = (application: Editor): string => {
|
||||
// @ts-ignore
|
||||
@ -24,12 +24,12 @@ For that reason, it is often a good idea to set fixed reverb values per orbit. D
|
||||
| <ic>roomdim</ic> | | Reverb lowpass frequency at -60db (in hertz) |
|
||||
|
||||
${makeExample(
|
||||
"Clapping in the cavern",
|
||||
`
|
||||
"Clapping in the cavern",
|
||||
`
|
||||
beat(2)::snd('cp').room(0.5).size(4).out()
|
||||
`,
|
||||
true,
|
||||
)};
|
||||
true,
|
||||
)};
|
||||
|
||||
## Delay
|
||||
|
||||
@ -42,13 +42,13 @@ A good sounding delay unit that can go into feedback territory. Use it without m
|
||||
| <ic>delayfeedback</ic> | delayfb | Delay feedback (between <ic>0</ic> and <ic>1</ic>) |
|
||||
|
||||
${makeExample(
|
||||
"Who doesn't like delay?",
|
||||
`
|
||||
"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,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Phaser
|
||||
|
||||
@ -60,16 +60,16 @@ beat(1)::snd('kick').out()`,
|
||||
| <ic>phaserCenter</ic> | <ic>phascenter</ic> | Phaser center frequency (default to 1000) |
|
||||
|
||||
${makeExample(
|
||||
"Super cool phaser lick",
|
||||
`
|
||||
"Super cool phaser lick",
|
||||
`
|
||||
rhythm(.5, 7, 8)::sound('wt_stereo')
|
||||
.phaser(0.75).phaserSweep(3000)
|
||||
.phaserCenter(1500).phaserDepth(1)
|
||||
.note([0, 1, 2, 3, 4, 5, 6].scale('pentatonic', 50).beat(0.25))
|
||||
.room(0.5).size(4).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Distorsion, saturation, destruction
|
||||
|
||||
@ -83,29 +83,29 @@ Three additional effects that are easy enough to understand. These effects are d
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Crunch... crunch... crunch!",
|
||||
`
|
||||
"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,
|
||||
)};
|
||||
true,
|
||||
)};
|
||||
|
||||
## 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.
|
||||
|
||||
${makeExample(
|
||||
"Different vibrato settings",
|
||||
`
|
||||
"Different vibrato settings",
|
||||
`
|
||||
tempo(140);
|
||||
beat(1) :: sound('triangle')
|
||||
.freq(400).release(0.2)
|
||||
.vib([1/2, 1, 2, 4].beat())
|
||||
.vibmod([1,2,4,8].beat(2))
|
||||
.out()`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Compression
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const filters = (application: Editor): string => {
|
||||
const makeExample = makeExampleFactory(application);
|
||||
@ -13,10 +13,10 @@ Filters can be applied to both synthesizers and samples. They are used to shape
|
||||
- **bandpass filter**: filters the low and high frequencies around a frequency band, keeping what's in the middle.
|
||||
|
||||
${makeExample(
|
||||
"Filtering the high frequencies of an oscillator",
|
||||
`beat(.5) :: sound('sawtooth').cutoff(50 + usine(1/8) * 2000).out()`,
|
||||
true,
|
||||
)}
|
||||
"Filtering the high frequencies of an oscillator",
|
||||
`beat(.5) :: sound('sawtooth').cutoff(50 + usine(1/8) * 2000).out()`,
|
||||
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:
|
||||
|
||||
@ -29,10 +29,10 @@ These filters all come with their own set of parameters. Note that we are descri
|
||||
| <ic>resonance</ic> | <ic>lpq</ic> | resonance of the lowpass filter (0-1) |
|
||||
|
||||
${makeExample(
|
||||
"Filtering a bass",
|
||||
`beat(.5) :: sound('jvbass').lpf([250,1000,8000].beat()).out()`,
|
||||
true,
|
||||
)}
|
||||
"Filtering a bass",
|
||||
`beat(.5) :: sound('jvbass').lpf([250,1000,8000].beat()).out()`,
|
||||
true,
|
||||
)}
|
||||
|
||||
### Highpass filter
|
||||
|
||||
@ -42,10 +42,10 @@ ${makeExample(
|
||||
| <ic>hresonance</ic> | <ic>hpq</ic> | resonance of the highpass filter (0-1) |
|
||||
|
||||
${makeExample(
|
||||
"Filtering a noise source",
|
||||
`beat(.5) :: sound('gtr').hpf([250,1000, 2000, 3000, 4000].beat()).end(0.5).out()`,
|
||||
true,
|
||||
)}
|
||||
"Filtering a noise source",
|
||||
`beat(.5) :: sound('gtr').hpf([250,1000, 2000, 3000, 4000].beat()).end(0.5).out()`,
|
||||
true,
|
||||
)}
|
||||
|
||||
### Bandpass filter
|
||||
|
||||
@ -55,10 +55,10 @@ ${makeExample(
|
||||
| <ic>bandq</ic> | <ic>bpq</ic> | resonance of the bandpass filter (0-1) |
|
||||
|
||||
${makeExample(
|
||||
"Sweeping the filter on the same guitar sample",
|
||||
`beat(.5) :: sound('gtr').bandf(100 + usine(1/8) * 4000).end(0.5).out()`,
|
||||
true,
|
||||
)}
|
||||
"Sweeping the filter on the same guitar sample",
|
||||
`beat(.5) :: sound('gtr').bandf(100 + usine(1/8) * 4000).end(0.5).out()`,
|
||||
true,
|
||||
)}
|
||||
|
||||
Alternatively, <ic>lpf</ic>, <ic>hpf</ic> and <ic>bpf</ic> can take a second argument, the **resonance**.
|
||||
|
||||
@ -69,10 +69,10 @@ 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>.
|
||||
|
||||
${makeExample(
|
||||
"Filtering a bass",
|
||||
`beat(.5) :: sound('jvbass').ftype(['12db', '24db'].beat(4)).lpf([250,1000,8000].beat()).out()`,
|
||||
true,
|
||||
)}
|
||||
"Filtering a bass",
|
||||
`beat(.5) :: sound('jvbass').ftype(['12db', '24db'].beat(4)).lpf([250,1000,8000].beat()).out()`,
|
||||
true,
|
||||
)}
|
||||
|
||||
## Filter envelopes
|
||||
|
||||
@ -91,12 +91,12 @@ The examples we have studied so far are static. They filter the sound around a f
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Filtering a sawtooth wave dynamically",
|
||||
`beat(.5) :: sound('sawtooth').note([48,60].beat())
|
||||
"Filtering a sawtooth wave dynamically",
|
||||
`beat(.5) :: sound('sawtooth').note([48,60].beat())
|
||||
.cutoff(5000).lpa([0.05, 0.25, 0.5].beat(2))
|
||||
.lpenv(-8).lpq(10).out()`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
### Highpass envelope
|
||||
|
||||
@ -111,12 +111,12 @@ ${makeExample(
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Let's use another filter using the same example",
|
||||
`beat(.5) :: sound('sawtooth').note([48,60].beat())
|
||||
"Let's use another filter using the same example",
|
||||
`beat(.5) :: sound('sawtooth').note([48,60].beat())
|
||||
.hcutoff(1000).hpa([0.05, 0.25, 0.5].beat(2))
|
||||
.hpenv(8).hpq(10).out()`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
### Bandpass envelope
|
||||
|
||||
@ -131,14 +131,14 @@ ${makeExample(
|
||||
|
||||
|
||||
${makeExample(
|
||||
"And the bandpass filter, just for fun",
|
||||
`beat(.5) :: sound('sawtooth').note([48,60].beat())
|
||||
"And the bandpass filter, just for fun",
|
||||
`beat(.5) :: sound('sawtooth').note([48,60].beat())
|
||||
.bandf([500,1000,2000].beat(2))
|
||||
.bpa([0.25, 0.125, 0.5].beat(2) * 4)
|
||||
.bpenv(-4).release(2).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
`;
|
||||
|
||||
6
src/documentation/learning/audio_engine/index.ts
Normal file
6
src/documentation/learning/audio_engine/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { amplitude } from './amplitude';
|
||||
export { effects } from './effects';
|
||||
export { sampler } from './sampler';
|
||||
export { synths } from './synths';
|
||||
export { filters } from './filters';
|
||||
export { audio_basics } from './audio_basics';
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const sampler = (application: Editor): string => {
|
||||
// @ts-ignore
|
||||
@ -28,8 +28,8 @@ The sampler is a rather complex beast. There is a lot you can do by manipulating
|
||||
Let's apply some of these methods naïvely. We will then break everything using simpler examples.
|
||||
|
||||
${makeExample(
|
||||
"Complex sampling duties",
|
||||
`
|
||||
"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))
|
||||
@ -38,8 +38,8 @@ beat(.5)::snd('pad').begin(0.2)
|
||||
.room(0.8).size(0.5)
|
||||
.clip(1).out()
|
||||
`,
|
||||
true,
|
||||
)};
|
||||
true,
|
||||
)};
|
||||
|
||||
|
||||
## Playback speed / pitching samples
|
||||
@ -47,37 +47,37 @@ beat(.5)::snd('pad').begin(0.2)
|
||||
Let's play with the <ic>speed</ic> parameter to control the pitch of sample playback:
|
||||
|
||||
${makeExample(
|
||||
"Controlling the playback speed",
|
||||
`
|
||||
"Controlling the playback speed",
|
||||
`
|
||||
beat(0.5)::sound('notes')
|
||||
.speed([1,2,3,4].palindrome().beat(0.5)).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
It also works by using negative values. It reverses the playback:
|
||||
|
||||
${makeExample(
|
||||
"Playing samples backwards",
|
||||
`
|
||||
"Playing samples backwards",
|
||||
`
|
||||
beat(0.5)::sound('notes')
|
||||
.speed(-[1,2,3,4].palindrome().beat(0.5)).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
Of course you can play melodies using samples:
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Playing melodies using samples",
|
||||
`
|
||||
"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,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Panning
|
||||
|
||||
@ -85,14 +85,14 @@ To pan samples, use the <ic>.pan</ic> method with a number between <ic>0</ic> an
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Playing melodies using samples",
|
||||
`
|
||||
"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,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
## Looping over a sample
|
||||
@ -101,29 +101,29 @@ Using <ic>loop</ic> (<ic>1</ic> for looping), <ic>loopBegin</ic> and <ic>loopEnd
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Granulation using loop",
|
||||
`
|
||||
"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,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Stretching a sample
|
||||
|
||||
The <ic>stretch</ic> parameter can help you to stretch long samples like amen breaks:
|
||||
|
||||
${makeExample(
|
||||
"Playing an amen break",
|
||||
`
|
||||
"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,
|
||||
)};
|
||||
true,
|
||||
)};
|
||||
|
||||
## Cutting samples
|
||||
|
||||
@ -132,43 +132,43 @@ Sometimes, you will find it necessary to cut a sample. It can be because the sam
|
||||
Know about the <ic>begin</ic> and <ic>end</ic> 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",
|
||||
`
|
||||
"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,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
You can also use <ic>clip</ic> to cut the sample everytime a new sample comes in:
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Cutting a sample using end",
|
||||
`
|
||||
"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,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Adding vibrato to samples
|
||||
|
||||
You can add vibrato to any sample using <ic>vib</ic> and <ic>vibmod</ic>:
|
||||
|
||||
${makeExample(
|
||||
"Adding vibrato to a sample",
|
||||
`
|
||||
"Adding vibrato to a sample",
|
||||
`
|
||||
|
||||
beat(1)::sound('fhang').vib([1, 2, 4].bar()).vibmod([0.5, 2].beat()).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
`;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const synths = (application: Editor): string => {
|
||||
const makeExample = makeExampleFactory(application);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../main";
|
||||
import { makeExampleFactory, key_shortcut } from "../../Documentation";
|
||||
import { makeExampleFactory, key_shortcut } from "../Documentation";
|
||||
|
||||
export const midi = (application: Editor): string => {
|
||||
const makeExample = makeExampleFactory(application);
|
||||
@ -22,22 +22,22 @@ Your web browser is capable of sending and receiving MIDI information through th
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Listing MIDI outputs",
|
||||
`
|
||||
"Listing MIDI outputs",
|
||||
`
|
||||
midi_outputs()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>midi_output(output_name: string)</ic>: enter your desired output to connect to it.
|
||||
|
||||
${makeExample(
|
||||
"Changing MIDI output",
|
||||
`
|
||||
"Changing MIDI output",
|
||||
`
|
||||
midi_output("MIDI Rocket-Trumpet")
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
That's it! You are now ready to play with MIDI.
|
||||
|
||||
@ -48,69 +48,69 @@ The most basic MIDI event is the note. MIDI notes traditionally take three param
|
||||
- <ic>midi(note: number|object)</ic>: send a MIDI Note. This function is quite bizarre. It can be written and used in many different ways. You can pass form one up to three arguments in different forms.
|
||||
|
||||
${makeExample(
|
||||
"MIDI note using one parameter: note",
|
||||
`
|
||||
"MIDI note using one parameter: note",
|
||||
`
|
||||
// Configure your MIDI first!
|
||||
// => midi_output("MIDI Bus 1")
|
||||
rhythm(.5, 5, 8) :: midi(50).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"MIDI note using three parameters: note, velocity, channel",
|
||||
`
|
||||
"MIDI note using three parameters: note, velocity, channel",
|
||||
`
|
||||
// MIDI Note 50, Velocity 50 + LFO, Channel 0
|
||||
rhythm(.5, 5, 8) :: midi(50, 50 + usine(.5) * 20, 0).out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
false,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"MIDI note by passing an object",
|
||||
`
|
||||
"MIDI note by passing an object",
|
||||
`
|
||||
// MIDI Note 50, Velocity 50 + LFO, Channel 0
|
||||
rhythm(.5, 5, 8) :: midi({note: 50, velocity: 50 + usine(.5) * 20, channel: 0}).out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
false,
|
||||
)}
|
||||
|
||||
We can now have some fun and starting playing a small piano piece:
|
||||
|
||||
${makeExample(
|
||||
"Playing some piano",
|
||||
`
|
||||
"Playing some piano",
|
||||
`
|
||||
tempo(80) // Setting a default BPM
|
||||
beat(.5) && midi(36 + [0,12].beat()).sustain(0.02).out()
|
||||
beat(.25) && midi([64, 76].pick()).sustain(0.05).out()
|
||||
beat(.75) && midi([64, 67, 69].beat()).sustain(0.05).out()
|
||||
beat(.25) && midi([64, 67, 69].beat() + 24).sustain(0.05).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Control and Program Changes
|
||||
|
||||
- <ic>control_change({control: number, value: number, channel: number})</ic>: send a MIDI Control Change. This function takes a single object argument to specify the control message (_e.g._ <ic>control_change({control: 1, value: 127, channel: 1})</ic>).
|
||||
|
||||
${makeExample(
|
||||
"Imagine that I am tweaking an hardware synthesizer!",
|
||||
`
|
||||
"Imagine that I am tweaking an hardware synthesizer!",
|
||||
`
|
||||
control_change({control: [24,25].pick(), value: irand(1,120), channel: 1})
|
||||
control_change({control: [30,35].pick(), value: irand(1,120) / 2, channel: 1})
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>program_change(program: number, channel: number)</ic>: send a MIDI Program Change. This function takes two arguments to specify the program and the channel (_e.g._ <ic>program_change(1, 1)</ic>).
|
||||
|
||||
${makeExample(
|
||||
"Crashing old synthesizers: a hobby",
|
||||
`
|
||||
"Crashing old synthesizers: a hobby",
|
||||
`
|
||||
program_change([1,2,3,4,5,6,7,8].pick(), 1)
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
## System Exclusive Messages
|
||||
@ -119,44 +119,44 @@ program_change([1,2,3,4,5,6,7,8].pick(), 1)
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Nobody can say that we don't support Sysex messages!",
|
||||
`
|
||||
"Nobody can say that we don't support Sysex messages!",
|
||||
`
|
||||
sysex(0x90, 0x40, 0x7f)
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Clock
|
||||
|
||||
- <ic>midi_clock()</ic>: send a MIDI Clock message. This function is used to synchronize Topos with other MIDI devices or DAWs.
|
||||
|
||||
${makeExample(
|
||||
"Tic, tac, tic, tac...",
|
||||
`
|
||||
"Tic, tac, tic, tac...",
|
||||
`
|
||||
beat(.25) && midi_clock() // Sending clock to MIDI device from the global buffer
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Using midi with ziffers
|
||||
|
||||
Ziffers offers some shorthands for defining channels within the patterns. See Ziffers for more information.
|
||||
|
||||
${makeExample(
|
||||
"Using midi with ziffers",
|
||||
`
|
||||
"Using midi with ziffers",
|
||||
`
|
||||
z1('0 2 e 5 2 q 4 2').midi().port(2).channel(4).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Setting the channel within the pattern",
|
||||
`
|
||||
"Setting the channel within the pattern",
|
||||
`
|
||||
z1('(0 2 e 5 2):0 (4 2):1').midi().out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
`;
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../main";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
import { makeExampleFactory } from "../Documentation";
|
||||
|
||||
export const osc = (application: Editor): string => {
|
||||
// @ts-ignore
|
||||
@ -24,52 +24,52 @@ Send an **OSC** message to the server from another application or device at the
|
||||
You can access the last 1000 messages using the <ic>getOsc()</ic> function without any argument. This is raw data, you will need to parse it yourself:
|
||||
|
||||
${makeExample(
|
||||
"Reading the last OSC messages",
|
||||
`
|
||||
"Reading the last OSC messages",
|
||||
`
|
||||
beat(1)::getOsc()
|
||||
// 0 : {data: Array(2), address: '/lala'}
|
||||
// 1 : {data: Array(2), address: '/lala'}
|
||||
// 2 : {data: Array(2), address: '/lala'}`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
### Filtered messages
|
||||
|
||||
The <ic>getOsc()</ic> can receive an address filter as an argument. This will return only the messages that match the filter:
|
||||
|
||||
${makeExample(
|
||||
"Reading the last OSC messages (filtered)",
|
||||
`
|
||||
"Reading the last OSC messages (filtered)",
|
||||
`
|
||||
beat(1)::getOsc("/lala")
|
||||
// 0 : (2) [89, 'bob']
|
||||
// 1 : (2) [84, 'bob']
|
||||
// 2 : (2) [82, 'bob']
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Output
|
||||
|
||||
Once the server is loaded, you are ready to send an **OSC** message:
|
||||
|
||||
${makeExample(
|
||||
"Sending a simple OSC message",
|
||||
`
|
||||
"Sending a simple OSC message",
|
||||
`
|
||||
beat(1)::sound('cp').speed(2).vel(0.5).osc()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
This is a simple **OSC** message that will inherit all the properties of the sound. You can also send customized OSC messages using the <ic>osc()</ic> function:
|
||||
|
||||
${makeExample(
|
||||
"Sending a customized OSC message",
|
||||
`
|
||||
"Sending a customized OSC message",
|
||||
`
|
||||
// osc(address, port, ...message)
|
||||
osc('/my/osc/address', 5000, 1, 2, 3)
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
`;
|
||||
};
|
||||
|
||||
3
src/documentation/learning/samples/index.ts
Normal file
3
src/documentation/learning/samples/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { loading_samples } from './loading_samples';
|
||||
export { sample_banks } from './sample_banks';
|
||||
export { sample_list } from './sample_list';
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const loading_samples = (application: Editor): string => {
|
||||
// @ts-ignore
|
||||
@ -12,36 +12,36 @@ Topos is exposing the <ic>samples</ic> function that you can use to load your ow
|
||||
Samples are loaded on-the-fly from the web. Topos is a web application living in the browser. It is running in a sandboxed environment. Thus, it cannot have access to the files stored on your local system. Loading samples requires building a _map_ of the audio files, where a name is associated to a specific file:
|
||||
|
||||
${makeExample(
|
||||
"Loading samples from a map",
|
||||
`samples({
|
||||
"Loading samples from a map",
|
||||
`samples({
|
||||
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav'],
|
||||
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
This example is loading two samples from each folder declared in the original repository (in the <ic>strudel.json</ic> file). You can then play with them using the syntax you are already used to:
|
||||
|
||||
${makeExample(
|
||||
"Playing with the loaded samples",
|
||||
`rhythm(.5, 5, 8)::sound('bd').n(ir(1,2)).end(1).out()
|
||||
"Playing with the loaded samples",
|
||||
`rhythm(.5, 5, 8)::sound('bd').n(ir(1,2)).end(1).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
Internally, Topos is loading samples using a different technique where sample maps are directly taken from the previously mentioned <ic>strudel.json</ic> file that lives in each repository:
|
||||
|
||||
${makeExample(
|
||||
"This is how Topos is loading its own samples",
|
||||
`
|
||||
"This is how Topos is loading its own samples",
|
||||
`
|
||||
// Visit the concerned repos and search for 'strudel.json'
|
||||
samples("github:tidalcycles/Dirt-Samples/master");
|
||||
samples("github:Bubobubobubobubo/Dough-Samples/main");
|
||||
samples("github:Bubobubobubobubo/Dough-Amiga/main");
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
To learn more about the audio sample loading mechanism, please refer to [this page](https://strudel.tidalcycles.org/learn/samples) written by Felix Roos who has implemented the sample loading mechanism. The API is absolutely identic in Topos!
|
||||
|
||||
@ -50,16 +50,16 @@ To learn more about the audio sample loading mechanism, please refer to [this pa
|
||||
You can load samples coming from [Freesound](https://freesound.org/) using the [Shabda](https://shabda.ndre.gr/) API. To do so, study the following example:
|
||||
|
||||
${makeExample(
|
||||
"Loading samples from shabda",
|
||||
`
|
||||
"Loading samples from shabda",
|
||||
`
|
||||
// Prepend the sample you want with 'shabda:'
|
||||
samples("shabda:ocean")
|
||||
|
||||
// Use the sound without 'shabda:'
|
||||
beat(1)::sound('ocean').clip(1).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
You can also use the <ic>.n</ic> attribute like usual to load a different sample.
|
||||
`;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const sample_banks = (application: Editor): string => {
|
||||
// @ts-ignore
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const samples_to_markdown = (
|
||||
application: Editor,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const cyclical_time = (app: Editor): string => {
|
||||
// @ts-ignore
|
||||
@ -16,17 +16,17 @@ Time as a cycle. A cycle can be quite long (a few bars) or very short (a few pul
|
||||
- <ic>offset</ic>: offset (in beats) to apply. An offset of <ic>0.5</ic> will return true against the beat.
|
||||
|
||||
${makeExample(
|
||||
"Using different mod values",
|
||||
`
|
||||
"Using different mod values",
|
||||
`
|
||||
// This code is alternating between different mod values
|
||||
beat([1,1/2,1/4,1/8].beat(2)) :: sound('hat').n(0).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Some sort of ringtone",
|
||||
`
|
||||
"Some sort of ringtone",
|
||||
`
|
||||
// Blip generator :)
|
||||
let blip = (freq) => {
|
||||
return sound('wt_piano')
|
||||
@ -41,16 +41,16 @@ beat(1/3) :: blip(400).pan(r(0,1)).out();
|
||||
flip(3) :: beat(1/6) :: blip(800).pan(r(0,1)).out();
|
||||
beat([1,0.75].beat(2)) :: blip([50, 100].beat(2)).pan(r(0,1)).out();
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
false,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Beat can match multiple values",
|
||||
`
|
||||
"Beat can match multiple values",
|
||||
`
|
||||
beat([.5, 1.25])::sound('hat').out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
false,
|
||||
)}
|
||||
|
||||
- <ic>pulse(n: number | number[] = 1, offset: number = 1)</ic>: return true every _n_ pulses. A pulse is the tiniest possible rhythmic value.
|
||||
- <ic>number</ic>: if <ic>number = 1</ic>, the function will return <ic>true</ic> every pulse. Lists can be used too.
|
||||
@ -58,21 +58,21 @@ beat([.5, 1.25])::sound('hat').out()
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Intriguing rhythms",
|
||||
`
|
||||
"Intriguing rhythms",
|
||||
`
|
||||
pulse([24, 16])::sound('hat').ad(0, .02).out()
|
||||
pulse([48, [36,24].dur(4, 1)])::sound('fhardkick').ad(0, .1).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
${makeExample(
|
||||
"pulse is the OG rhythmic function in Topos",
|
||||
`
|
||||
"pulse is the OG rhythmic function in Topos",
|
||||
`
|
||||
pulse([48, 24, 16].beat(4)) :: sound('linnhats').out()
|
||||
beat(1)::snd(['bd', '808oh'].beat(1)).out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
false,
|
||||
)}
|
||||
|
||||
|
||||
- <ic>bar(n: number | number[] = 1, offset: number = 1)</ic>: return true every _n_ bars.
|
||||
@ -80,37 +80,37 @@ beat(1)::snd(['bd', '808oh'].beat(1)).out()
|
||||
- <ic>offset</ic>: offset (in bars) to apply.
|
||||
|
||||
${makeExample(
|
||||
"Four beats per bar: proof",
|
||||
`
|
||||
"Four beats per bar: proof",
|
||||
`
|
||||
bar(1)::sound('kick').out()
|
||||
beat(1)::sound('hat').speed(2).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Offsetting beat and bar",
|
||||
`
|
||||
"Offsetting beat and bar",
|
||||
`
|
||||
bar(1)::sound('kick').out()
|
||||
beat(1)::sound('hat').speed(2).out()
|
||||
beat(1, 0.5)::sound('hat').speed(4).out()
|
||||
bar(1, 0.5)::sound('sn').out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
false,
|
||||
)}
|
||||
|
||||
- <ic>onbeat(...n: number[])</ic>: The <ic>onbeat</ic> function allows you to lock on to a specific beat from the clock to execute code. It can accept multiple arguments. It's usage is very straightforward and not hard to understand. You can pass either integers or floating point numbers. By default, topos is using a <ic>4/4</ic> bar meaning that you can target any of these beats (or in-between) with this function.
|
||||
|
||||
${makeExample(
|
||||
"Some simple yet detailed rhythms",
|
||||
`
|
||||
"Some simple yet detailed rhythms",
|
||||
`
|
||||
onbeat(1,2,3,4)::snd('kick').out() // Bassdrum on each beat
|
||||
onbeat(2,4)::snd('snare').n([8,4].beat(4)).out() // Snare on acccentuated beats
|
||||
onbeat(1.5,2.5,3.5, 3.75)::snd('hat').gain(r(0.9,1.1)).out() // Cool high-hats
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## XOX Style sequencers
|
||||
|
||||
@ -119,32 +119,32 @@ onbeat(1.5,2.5,3.5, 3.75)::snd('hat').gain(r(0.9,1.1)).out() // Cool high-hats
|
||||
- <ic>duration: number</ic>: an optional duration (in beats) like <ic>1</ic> or </ic>4</ic>. It can be patterned.
|
||||
|
||||
${makeExample(
|
||||
"Sequence built using a classic XOX sequencer style",
|
||||
`
|
||||
"Sequence built using a classic XOX sequencer style",
|
||||
`
|
||||
seq('xoxo')::sound('fhardkick').out()
|
||||
seq('ooxo')::sound('fsoftsnare').out()
|
||||
seq('xoxo', 0.25)::sound('fhh').out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Another sequence using more complex parameters",
|
||||
`
|
||||
"Another sequence using more complex parameters",
|
||||
`
|
||||
seq('xoxooxxoo', [0.5, 0.25].dur(2, 1))::sound('fhardkick').out()
|
||||
seq('ooxo', [1, 2].bar())::sound('fsoftsnare').speed(0.5).out()
|
||||
seq(['xoxoxoxx', 'xxoo'].bar())::sound('fhh').out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>fullseq(expr: string, duration: number = 0.5): boolean</ic> : a variant. Will return <ic>true</ic> or <ic>false</ic> for a whole period, depending on the symbol. Useful for long structure patterns.
|
||||
- <ic>expr: string</ic>: any string composed of <ic>x</ic> or <ic>o</ic> like so: <ic>"xooxoxxoxoo"</ic>.
|
||||
- <ic>duration: number</ic>: an optional duration (in beats) like <ic>1</ic> or </ic>4</ic>. It can be patterned.
|
||||
|
||||
${makeExample(
|
||||
"Long structured patterns",
|
||||
`
|
||||
"Long structured patterns",
|
||||
`
|
||||
function simplePat() {
|
||||
log('Simple pattern playing!')
|
||||
seq('xoxooxxoo', [0.5, 0.25].dur(2, 1))::sound('fhardkick').out()
|
||||
@ -159,8 +159,8 @@ function complexPat() {
|
||||
}
|
||||
fullseq('xooxooxx', 4) ? simplePat() : complexPat()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
|
||||
@ -171,8 +171,8 @@ We included a bunch of popular rhythm generators in Topos such as the euclidian
|
||||
|
||||
- <ic>rhythm(divisor: number, pulses: number, length: number, rotate: number): boolean</ic>: generates <ic>true</ic> or <ic>false</ic> values from an euclidian rhythm sequence. This is another version of <ic>euclid</ic> that does not take an iterator.
|
||||
${makeExample(
|
||||
"rhythm is a beginner friendly rhythmic function!",
|
||||
`
|
||||
"rhythm is a beginner friendly rhythmic function!",
|
||||
`
|
||||
rhythm(.5, 4, 8)::sound('sine')
|
||||
.fmi(2)
|
||||
.room(0.5).size(8)
|
||||
@ -181,38 +181,38 @@ rhythm(.5, 7, 8)::sound('sine')
|
||||
.freq(125).ad(0, .2).out()
|
||||
rhythm(.5, 3, 8)::sound('sine').freq(500).ad(0, .5).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
- <ic>oneuclid(pulses: number, length: number, rotate: number): boolean</ic>: generates <ic>true</ic> or <ic>false</ic> values from an euclidian rhythm sequence. This is another version of <ic>euclid</ic> that does not take an iterator.
|
||||
${makeExample(
|
||||
"Using oneuclid to create a rhythm without iterators",
|
||||
`
|
||||
"Using oneuclid to create a rhythm without iterators",
|
||||
`
|
||||
// Change speed using bpm
|
||||
bpm(250)
|
||||
oneuclid(5, 9) :: snd('kick').out()
|
||||
oneuclid(7,16) :: snd('east').end(0.5).n(irand(3,5)).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>bin(iterator: number, n: number): boolean</ic>: a binary rhythm generator. It transforms the given number into its binary representation (_e.g_ <ic>34</ic> becomes <ic>100010</ic>). It then returns a boolean value based on the iterator in order to generate a rhythm.
|
||||
- <ic>binrhythm(divisor: number, n: number): boolean: boolean</ic>: iterator-less version of the binary rhythm generator.
|
||||
|
||||
${makeExample(
|
||||
"Change the integers for a surprise rhythm!",
|
||||
`
|
||||
"Change the integers for a surprise rhythm!",
|
||||
`
|
||||
bpm(135);
|
||||
beat(.5) && bin($(1), 12) && snd('kick').n([4,9].beat(1.5)).out()
|
||||
beat(.5) && bin($(2), 34) && snd('snare').n([3,5].beat(1)).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"binrhythm for fast cool binary rhythms!",
|
||||
`
|
||||
"binrhythm for fast cool binary rhythms!",
|
||||
`
|
||||
let a = 0;
|
||||
a = beat(4) ? irand(1,20) : a;
|
||||
binrhythm(.5, 6) && snd(['kick', 'snare'].beat(0.5)).n(11).out()
|
||||
@ -221,34 +221,34 @@ binrhythm([.5, .25].beat(1), 30) && snd('wt_granular').n(a)
|
||||
.adsr(0, r(.1, .4), 0, 0).freq([50, 60, 72].beat(4))
|
||||
.room(1).size(1).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Submarine jungle music",
|
||||
`
|
||||
"Submarine jungle music",
|
||||
`
|
||||
bpm(145);
|
||||
beat(.5) && bin($(1), 911) && snd('ST69').n([2,3,4].beat())
|
||||
.delay(0.125).delayt(0.25).end(0.25).speed(1/3)
|
||||
.room(1).size(1).out()
|
||||
beat(.5) && sound('amencutup').n(irand(2,7)).shape(0.3).out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
false,
|
||||
)}
|
||||
|
||||
If you don't find it spicy enough, you can add some more probabilities to your rhythms by taking advantage of the probability functions. See the functions documentation page to learn more about them.
|
||||
|
||||
${makeExample(
|
||||
"Probablistic drums in one line!",
|
||||
`
|
||||
"Probablistic drums in one line!",
|
||||
`
|
||||
prob(60)::beat(.5) && euclid($(1), 5, 8) && snd('kick').out()
|
||||
prob(60)::beat(.5) && euclid($(2), 3, 8) && snd('mash')
|
||||
.n([1,2,3].beat(1))
|
||||
.pan(usine(1/4)).out()
|
||||
prob(80)::beat(.5) && sound(['hh', 'hat'].pick()).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
import pulses from "./pulses.svg";
|
||||
|
||||
export const linear_time = (app: Editor): string => {
|
||||
@ -22,12 +22,12 @@ export const linear_time = (app: Editor): string => {
|
||||
There is a tiny widget at the bottom right of the screen showing you the current BPM and the status of the transport. You can turn it on or off in the settings menu.
|
||||
|
||||
${makeExample(
|
||||
"Printing the transport",
|
||||
`
|
||||
"Printing the transport",
|
||||
`
|
||||
log(\`\$\{cbar()}\, \$\{cbeat()\}, \$\{cpulse()\}\`)
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
### BPM and PPQN
|
||||
|
||||
@ -64,8 +64,8 @@ These values are **extremely useful** to craft more complex syntax or to write m
|
||||
You can use time primitives as conditionals. The following example will play a pattern A for 2 bars and a pattern B for 2 bars:
|
||||
|
||||
${makeExample(
|
||||
"Manual mode: using time primitives!",
|
||||
`
|
||||
"Manual mode: using time primitives!",
|
||||
`
|
||||
// Manual time condition
|
||||
if((cbar() % 4) > 1) {
|
||||
beat(2) && sound('kick').out()
|
||||
@ -83,8 +83,8 @@ if((cbar() % 4) > 1) {
|
||||
// This is always playing no matter what happens
|
||||
beat([.5, .5, 1, .25].beat(0.5)) :: sound('shaker').out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Time Warping
|
||||
|
||||
@ -94,8 +94,8 @@ Time generally flows from the past to the future. However, you can manipulate it
|
||||
|
||||
|
||||
${makeExample(
|
||||
"Time is now super elastic!",
|
||||
`
|
||||
"Time is now super elastic!",
|
||||
`
|
||||
// Obscure Shenanigans - Bubobubobubo
|
||||
beat([1/4,1/8,1/16].beat(8)):: sound('sine')
|
||||
.freq([100,50].beat(16) + 50 * ($(1)%10))
|
||||
@ -108,14 +108,14 @@ flip(3) :: beat([.25,.5].beat(.5)) :: sound('dr')
|
||||
// Jumping back and forth in time
|
||||
beat(.25) :: warp([12, 48, 24, 1, 120, 30].pick())
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>beat_warp(beat: number)</ic>: this function jumps to the _n_ beat of the clock. The first beat is <ic>1</ic>.
|
||||
|
||||
${makeExample(
|
||||
"Jumping back and forth with beats",
|
||||
`
|
||||
"Jumping back and forth with beats",
|
||||
`
|
||||
// Resonance bliss - Bubobubobubo
|
||||
beat(.25)::snd('arpy')
|
||||
.note(30 + [0,3,7,10].beat())
|
||||
@ -130,40 +130,40 @@ beat(.5) :: snd('arpy').note(
|
||||
// Comment me to stop warping!
|
||||
beat(1) :: beat_warp([2,4,5,10,11].pick())
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
## Transport-based rhythm generators
|
||||
|
||||
- <ic>onbeat(...n: number[])</ic>: The <ic>onbeat</ic> function allows you to lock on to a specific beat from the clock to execute code. It can accept multiple arguments. It's usage is very straightforward and not hard to understand. You can pass either integers or floating point numbers. By default, topos is using a <ic>4/4</ic> bar meaning that you can target any of these beats (or in-between) with this function.
|
||||
|
||||
${makeExample(
|
||||
"Some simple yet detailed rhythms",
|
||||
`
|
||||
"Some simple yet detailed rhythms",
|
||||
`
|
||||
onbeat(1,2,3,4)::snd('kick').out() // Bassdrum on each beat
|
||||
onbeat(2,4)::snd('snare').n([8,4].beat(4)).out() // Snare on acccentuated beats
|
||||
onbeat(1.5,2.5,3.5, 3.75)::snd('hat').gain(r(0.9,1.1)).out() // Cool high-hats
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Let's do something more complex",
|
||||
`
|
||||
"Let's do something more complex",
|
||||
`
|
||||
onbeat(0.5, 2, 3, 3.75)::snd('kick').n(2).out()
|
||||
onbeat(2, [1.5, 3, 4].pick(), 4)::snd('snare').n(8).out()
|
||||
beat([.25, 1/8].beat(1.5))::snd('hat').n(2)
|
||||
.gain(rand(0.4, 0.7)).end(0.05)
|
||||
.pan(usine()).out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
false,
|
||||
)}
|
||||
|
||||
- <ic>oncount(beats: number[], meter: number)</ic>: This function is similar to <ic>onbeat</ic> but it allows you to specify a custom number of beats as the last argument.
|
||||
|
||||
${makeExample(
|
||||
"Using oncount to create more variation in the rhythm",
|
||||
`
|
||||
"Using oncount to create more variation in the rhythm",
|
||||
`
|
||||
z1('1/16 (0 2 3 4)+(0 2 4 6)').scale('pentatonic').sound('sawtooth')
|
||||
.cutoff([400,500,1000,2000].beat(1))
|
||||
.lpadsr(2, 0, .2, 0, 0)
|
||||
@ -171,20 +171,20 @@ z1('1/16 (0 2 3 4)+(0 2 4 6)').scale('pentatonic').sound('sawtooth')
|
||||
onbeat(1,1.5,2,3,4) :: sound('bd').gain(2.0).out()
|
||||
oncount([1,3,5.5,7,7.5,8],8) :: sound('hh').gain(irand(1.0,4.0)).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Using oncount to create rhythms with a custom meter",
|
||||
`
|
||||
"Using oncount to create rhythms with a custom meter",
|
||||
`
|
||||
bpm(200)
|
||||
oncount([1, 5, 9, 13],16) :: sound('808bd').n(4).shape(0.5).gain(1.0).out()
|
||||
oncount([5, 6, 13],16) :: sound('shaker').room(0.25).gain(0.9).out()
|
||||
oncount([2, 3, 3.5, 6, 7, 10, 15],16) :: sound('hh').n(8).gain(0.8).out()
|
||||
oncount([1, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16],16) :: sound('hh').out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const long_forms = (app: Editor): string => {
|
||||
// @ts-ignore
|
||||
@ -14,23 +14,23 @@ Now you know how to play some basic rhythms but in any case, you are stuck in a
|
||||
- **Use the nine local scripts as containers** for sections of your composition. When you start playing with **Topos**, it's easy to forget that there are multiple scripts you can play with. Each script can store a different section or part from your composition. Here is a simple example:
|
||||
|
||||
${makeExample(
|
||||
"Eight bars per section",
|
||||
`
|
||||
"Eight bars per section",
|
||||
`
|
||||
// Playing each script for 8 bars in succession
|
||||
script([1,2,3,4].bar(8))
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
You can also give a specific duration to each section using <ic>.dur</ic>:
|
||||
|
||||
${makeExample(
|
||||
"N beats per section",
|
||||
`
|
||||
"N beats per section",
|
||||
`
|
||||
script([1,2,3,4].dur(8, 2, 16, 4))
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
- **Use universes as well**. Transitions between universes are _seamless_, instantaneous. Just switch to different content if you ever hit the limitations of the current _universe_.
|
||||
|
||||
@ -40,42 +40,42 @@ script([1,2,3,4].dur(8, 2, 16, 4))
|
||||
- <ic>ratio: number = 50</ic>: this argument is ratio expressed in %. It determines how much of the period should be true or false. A ratio of <ic>75</ic> means that 75% of the period will be true. A ratio of <ic>25</ic> means that 25% of the period will be true.
|
||||
|
||||
${makeExample(
|
||||
"Two beats of silence, two beats of playing",
|
||||
`
|
||||
"Two beats of silence, two beats of playing",
|
||||
`
|
||||
flip(4) :: beat(1) :: snd('kick').out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Clapping on the edge",
|
||||
`
|
||||
"Clapping on the edge",
|
||||
`
|
||||
flip(2.5, 10) :: beat(.25) :: snd('cp').out()
|
||||
flip(2.5, 75) :: beat(.25) :: snd('click')
|
||||
.speed(2).end(0.2).out()
|
||||
flip(2.5) :: beat(.5) :: snd('bd').out()
|
||||
beat(.25) :: sound('hat').end(0.1).cutoff(1200).pan(usine(1/4)).out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
false,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Good old true and false",
|
||||
`
|
||||
"Good old true and false",
|
||||
`
|
||||
if (flip(4, 75)) {
|
||||
beat(1) :: snd('kick').out()
|
||||
} else {
|
||||
beat(.5) :: snd('snare').out()
|
||||
}
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
<ic>flip</ic> is extremely powerful and is used internally for a lot of other Topos functions. You can also use it to think about **longer durations** spanning over multiple bars. Here is a silly composition that is using <ic>flip</ic> to generate a 4 bars long pattern.
|
||||
|
||||
${makeExample(
|
||||
"Clunky algorithmic rap music",
|
||||
`
|
||||
"Clunky algorithmic rap music",
|
||||
`
|
||||
// Rap God VS Lil Wild -- Adel Faure
|
||||
if (flip(8)) {
|
||||
// Playing this part for two bars
|
||||
@ -93,24 +93,24 @@ if (flip(8)) {
|
||||
beat(.5)::snd('diphone').end(0.5).n([1,2,3,4].pick()).out()
|
||||
}
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
You can use it everywhere to spice things up, including as a method parameter picker:
|
||||
|
||||
${makeExample(
|
||||
"flip is great for parameter variation",
|
||||
`
|
||||
"flip is great for parameter variation",
|
||||
`
|
||||
beat(.5)::snd(flip(2) ? 'kick' : 'hat').out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>flipbar(n: number = 1)</ic>: this method works just like <ic>flip</ic> but counts in bars instead of beats. It allows you to think about even larger time cycles. You can also pair it with regular <ic>flip</ic> for writing complex and long-spanning algorithmic beats.
|
||||
|
||||
${makeExample(
|
||||
"Thinking music over bars",
|
||||
`
|
||||
"Thinking music over bars",
|
||||
`
|
||||
let roomy = (n) => n.room(1).size(1).cutoff(500 + usaw(1/8) * 5000);
|
||||
function a() {
|
||||
beat(1) && roomy(sound('kick')).out()
|
||||
@ -122,24 +122,24 @@ function b() {
|
||||
flipbar(2) && a()
|
||||
flipbar(3) && b()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
${makeExample(
|
||||
"Alternating over four bars",
|
||||
`
|
||||
"Alternating over four bars",
|
||||
`
|
||||
flipbar(2)
|
||||
? beat(.5) && snd(['kick', 'hh'].beat(1)).out()
|
||||
: beat(.5) && snd(['east', 'east:2'].beat(1)).out()
|
||||
`,
|
||||
false,
|
||||
)};
|
||||
false,
|
||||
)};
|
||||
|
||||
|
||||
- <ic>onbar(bars: number | number[], n: number)</ic>: The second argument, <ic>n</ic>, is used to divide the time in a period of <ic>n</ic> consecutive bars. The first argument should be a bar number or a list of bar numbers to play on. For example, <ic>onbar([1, 4], 5)</ic> will return <ic>true</ic> on bar <ic>1</ic> and <ic>4</ic> but return <ic>false</ic> the rest of the time. You can easily divide time that way.
|
||||
|
||||
${makeExample(
|
||||
"Using onbar for filler drums",
|
||||
`
|
||||
"Using onbar for filler drums",
|
||||
`
|
||||
tempo(150);
|
||||
// Only play on the third and fourth bar of the cycle.
|
||||
onbar([3,4], 4)::beat(.25)::snd('hh').out();
|
||||
@ -155,8 +155,8 @@ if (onbar([1,2], 4)) {
|
||||
rhythm(.5, 1, 7) :: snd('jvbass').n(2).out();
|
||||
rhythm(.5, 2, 7) :: snd('snare').n(3).out();
|
||||
}`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
`;
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
import { type Editor } from "../../../main";
|
||||
import times from "./times.svg";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user