Moved doc files to same structure as in index
This commit is contained in:
256
src/documentation/learning/time/cyclical_time.ts
Normal file
256
src/documentation/learning/time/cyclical_time.ts
Normal file
@ -0,0 +1,256 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
|
||||
export const cyclical_time = (app: Editor): string => {
|
||||
// @ts-ignore
|
||||
let makeExample = makeExampleFactory(app);
|
||||
return `
|
||||
# Cyclical time
|
||||
|
||||
Time as a cycle. A cycle can be quite long (a few bars) or very short (a few pulses). Cyclical time is extremely interesting for _live coders_ since it allows you to control a process that will eventually repeat. If your time constructs are repeating, you are able to hear them again and again. Since you can react and alter the code to change the loops, you become part of a complex feedback system between the computer and yourself.
|
||||
|
||||
## Simple rhythms
|
||||
|
||||
- <ic>beat(n: number | number[] = 1, offset: number = 1)</ic>: return true every _n_ beats.
|
||||
- <ic>number</ic>: if <ic>number = 1</ic>, the function will return <ic>true</ic> every beat. Lists can be used too.
|
||||
- <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",
|
||||
`
|
||||
// This code is alternating between different mod values
|
||||
beat([1,1/2,1/4,1/8].beat(2)) :: sound('hat').n(0).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Some sort of ringtone",
|
||||
`
|
||||
// Blip generator :)
|
||||
let blip = (freq) => {
|
||||
return sound('wt_piano')
|
||||
.gain(1)
|
||||
.sustain(0.1)
|
||||
.freq(freq)
|
||||
.cutoff(1500)
|
||||
.lpadsr(4, 0, .25, 0, 0)
|
||||
};
|
||||
beat(1) :: blip(200).pan(r(0,1)).vib(0.5).vibmod(2).out();
|
||||
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,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Beat can match multiple values",
|
||||
`
|
||||
beat([.5, 1.25])::sound('hat').out()
|
||||
`,
|
||||
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.
|
||||
- <ic>offset</ic>: offset (in pulses) to apply.
|
||||
|
||||
|
||||
${makeExample(
|
||||
"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,
|
||||
)}
|
||||
${makeExample(
|
||||
"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,
|
||||
)}
|
||||
|
||||
|
||||
- <ic>bar(n: number | number[] = 1, offset: number = 1)</ic>: return true every _n_ bars.
|
||||
- <ic>number</ic>: if <ic>number = 1</ic>, the function will return <ic>true</ic> every bar. Lists can be used too.
|
||||
- <ic>offset</ic>: offset (in bars) to apply.
|
||||
|
||||
${makeExample(
|
||||
"Four beats per bar: proof",
|
||||
`
|
||||
bar(1)::sound('kick').out()
|
||||
beat(1)::sound('hat').speed(2).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
${makeExample(
|
||||
"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,
|
||||
)}
|
||||
|
||||
- <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",
|
||||
`
|
||||
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,
|
||||
)}
|
||||
|
||||
## XOX Style sequencers
|
||||
|
||||
- <ic>seq(expr: string, duration: number = 0.5): boolean</ic> : this function takes a string composed of <ic>x</ic> and <ic>o</ic> symbols like so: <ic>"xoxxoo"</ic>. It will return <ic>true</ic> (<ic>x</ic>) or <ic>false</ic> (<ic>o</ic>) after a <ic>duration</ic> amount.
|
||||
- <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(
|
||||
"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,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"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,
|
||||
)}
|
||||
|
||||
- <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",
|
||||
`
|
||||
function simplePat() {
|
||||
log('Simple pattern playing!')
|
||||
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()
|
||||
}
|
||||
function complexPat() {
|
||||
log('Complex pattern playing!')
|
||||
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()
|
||||
}
|
||||
fullseq('xooxooxx', 4) ? simplePat() : complexPat()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
|
||||
## Cyclical rhythm generators
|
||||
|
||||
We included a bunch of popular rhythm generators in Topos such as the euclidian rhythms algorithms or the one to generate rhythms based on a binary sequence. They all work using _iterators_ that you will gradually learn to use for iterating over lists. Note that they are levaraging <ic>mod(...n:number[])</ic> that you just learned about!
|
||||
|
||||
|
||||
- <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(.5, 4, 8)::sound('sine')
|
||||
.fmi(2)
|
||||
.room(0.5).size(8)
|
||||
.freq(250).ad(0, .2).out()
|
||||
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,
|
||||
)}
|
||||
|
||||
|
||||
- <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",
|
||||
`
|
||||
// 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,
|
||||
)}
|
||||
|
||||
- <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!",
|
||||
`
|
||||
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,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"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()
|
||||
binrhythm([.5, .25].beat(1), 30) && snd('wt_granular').n(a)
|
||||
.cutoff(800).lpadsr(4, 0, 0.125, 0.5, 0.25)
|
||||
.adsr(0, r(.1, .4), 0, 0).freq([50, 60, 72].beat(4))
|
||||
.room(1).size(1).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"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,
|
||||
)}
|
||||
|
||||
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!",
|
||||
`
|
||||
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,
|
||||
)}
|
||||
|
||||
|
||||
|
||||
`;
|
||||
};
|
||||
192
src/documentation/learning/time/linear_time.ts
Normal file
192
src/documentation/learning/time/linear_time.ts
Normal file
@ -0,0 +1,192 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import pulses from "./pulses.svg";
|
||||
|
||||
export const linear_time = (app: Editor): string => {
|
||||
// @ts-ignore
|
||||
let makeExample = makeExampleFactory(app);
|
||||
return `
|
||||
# Linear time
|
||||
|
||||
**Topos** time is flowing just like in your typical computer music program, with _bars_, _beats_, _pulses_ and so on. The transport can be **paused**, **resumed** and/or **stopped**. There are interface buttons to handle these tasks. The tiniest unit of time is the **pulse**. There is a finite number of **pulses** per **beat** (by default, <ic>48</ic> **PPQN**). Beats are passing at a given **BPM** (_beats per minute_). You can change the **BPM** anytime you want. You can also change the granularity of time.
|
||||
|
||||
<object type="image/svg+xml" data=${pulses} style="width: 100%; height: auto; background-color: transparent"></object>
|
||||
|
||||
### Beats, bar, pulses
|
||||
|
||||
**Topos** is using three core values to deal with time:
|
||||
- **bars**: how many bars have elapsed since the origin of time.
|
||||
- **beats**: how many beats have elapsed since the beginning of the bar.
|
||||
- **pulse**: how many pulses have elapsed since the last beat.
|
||||
|
||||
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",
|
||||
`
|
||||
log(\`\$\{cbar()}\, \$\{cbeat()\}, \$\{cpulse()\}\`)
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
### BPM and PPQN
|
||||
|
||||
The base functions to control time are:
|
||||
- <ic>bpm(number?)</ic> : get or set the current tempo.
|
||||
- <ic>tempo(number?)</ic> : alias to <ic>bpm</ic>.
|
||||
- <ic>ppqn(number?)</ic> : get or set the granularity of time
|
||||
- The function name comes from [PPQN](https://en.wikipedia.org/wiki/Pulses_per_quarter_note) (_pulses per quarter notes_).
|
||||
|
||||
### Controlling time
|
||||
|
||||
Note that it is preferable to use the keyboard shortcuts to manipulate the transport system. You can also use the <ic>play()</ic>, <ic>pause()</ic> and <ic>stop()</ic> functions. It is generally preferable to program things instead of relying on the interface!
|
||||
<br>
|
||||
## Time Primitives
|
||||
|
||||
Every script can access the current time by using the following functions:
|
||||
|
||||
- <ic>cbar(n: number)</ic>: current bar since the origin of time.
|
||||
|
||||
- <ic>cbeat(n: number)</ic>: current beat since the beginning of the bar.
|
||||
|
||||
- <ic>ebeat()</ic>: current beat since the origin of time (counting from 1).
|
||||
|
||||
- <ic>cpulse()</ic>: current bar since the origin of the beat.
|
||||
|
||||
- <ic>ppqn()</ic>: current **PPQN** (see above).
|
||||
|
||||
- <ic>bpm()</ic>: current **BPM** (see above).
|
||||
|
||||
- <ic>time()</ic>: current wall clock time, the real time of the system.
|
||||
|
||||
These values are **extremely useful** to craft more complex syntax or to write musical scores. However, it means that you have to write more to be more precise. There is a tradeoff between _live-codeability_ and dealing with time manually (verbose). Topos is offering high-level functions to deal with that issue, don't worry :)
|
||||
|
||||
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 time condition
|
||||
if((cbar() % 4) > 1) {
|
||||
beat(2) && sound('kick').out()
|
||||
rarely() && beat(.5) && sound('sd').out()
|
||||
beat([.5, .25].beat()) && sound('jvbass')
|
||||
.freq(100 * [2, 1].pick()).dec(2)
|
||||
.room(0.9).size(0.9).orbit(2).out()
|
||||
} else {
|
||||
beat(.5) && sound('hh').out()
|
||||
beat(2) && sound('cp').out()
|
||||
beat([.5, .5, .25].beat(.5)) && sound('jvbass')
|
||||
.freq(100 * [3, 1].pick()).dec(2)
|
||||
.room(0.9).size(0.9).orbit(2).out()
|
||||
}
|
||||
// This is always playing no matter what happens
|
||||
beat([.5, .5, 1, .25].beat(0.5)) :: sound('shaker').out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
## Time Warping
|
||||
|
||||
Time generally flows from the past to the future. However, you can manipulate it to jump back and forth. Think about looping a specific part of your current pattern or song or jumping all of the sudden in the future. This is entirely possible thanks to two simple functions: <ic>warp(n: number)</ic> and <ic>beat_warp(n: number)</ic>. They are both very easy to use and very powerful. Let's see how they work.
|
||||
|
||||
- <ic>warp(n: number)</ic>: this function jumps to the _n_ tick of the clock. <ic>1</ic> is the first pulsation ever and the number keeps increasing indefinitely. You are most likely currently listening to tick n°<ic>12838123</ic>.
|
||||
|
||||
|
||||
${makeExample(
|
||||
"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))
|
||||
.gain(0.5).room(0.9).size(0.9)
|
||||
.sustain(0.1).out()
|
||||
beat(1) :: sound('kick').out()
|
||||
beat(2) :: sound('dr').n(5).out()
|
||||
flip(3) :: beat([.25,.5].beat(.5)) :: sound('dr')
|
||||
.n([8,9].pick()).gain([.8,.5,.25,.1,.0].beat(.25)).out()
|
||||
// Jumping back and forth in time
|
||||
beat(.25) :: warp([12, 48, 24, 1, 120, 30].pick())
|
||||
`,
|
||||
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",
|
||||
`
|
||||
// Resonance bliss - Bubobubobubo
|
||||
beat(.25)::snd('arpy')
|
||||
.note(30 + [0,3,7,10].beat())
|
||||
.cutoff(usine(.5) * 5000).resonance(10).gain(0.3)
|
||||
.end(0.8).room(0.9).size(0.9).n(0).out();
|
||||
beat([.25,.125].beat(2))::snd('arpy')
|
||||
.note(30 + [0,3,7,10].beat())
|
||||
.cutoff(usine(.5) * 5000).resonance(20).gain(0.3)
|
||||
.end(0.8).room(0.9).size(0.9).n(3).out();
|
||||
beat(.5) :: snd('arpy').note(
|
||||
[30, 33, 35].repeatAll(4).beat(1) - [12,0].beat(0.5)).out()
|
||||
// Comment me to stop warping!
|
||||
beat(1) :: beat_warp([2,4,5,10,11].pick())
|
||||
`,
|
||||
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",
|
||||
`
|
||||
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,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"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,
|
||||
)}
|
||||
|
||||
- <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",
|
||||
`
|
||||
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)
|
||||
.delay(0.5).delayt(0.25).room(0.9).size(0.9).out()
|
||||
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,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"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,
|
||||
)}
|
||||
|
||||
|
||||
|
||||
`;
|
||||
};
|
||||
162
src/documentation/learning/time/long_forms.ts
Normal file
162
src/documentation/learning/time/long_forms.ts
Normal file
@ -0,0 +1,162 @@
|
||||
import { type Editor } from "../../../main";
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
|
||||
export const long_forms = (app: Editor): string => {
|
||||
// @ts-ignore
|
||||
let makeExample = makeExampleFactory(app);
|
||||
return `
|
||||
# Long forms
|
||||
|
||||
Now you know how to play some basic rhythms but in any case, you are stuck in a loop. It's time to learn how to compose larger/longer musical structures. The functions you are going to learn are all about mastering the flow of time on longer periods. **Read and experiment a lot with the following examples**.
|
||||
|
||||
## Using scripts and universes
|
||||
|
||||
- **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",
|
||||
`
|
||||
// Playing each script for 8 bars in succession
|
||||
script([1,2,3,4].bar(8))
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
You can also give a specific duration to each section using <ic>.dur</ic>:
|
||||
|
||||
${makeExample(
|
||||
"N beats per section",
|
||||
`
|
||||
script([1,2,3,4].dur(8, 2, 16, 4))
|
||||
`,
|
||||
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_.
|
||||
|
||||
## Long-form Functions
|
||||
|
||||
- <ic>flip(n: number, ratio: number = 50)</ic>: the <ic>flip</ic> method is a temporal switch. If the value <ic>2</ic> is given, the function will return <ic>true</ic> for two beats and <ic>false</ic> for two beats. There are multiple ways to use it effectively. You can pass an integer or a floating point number.
|
||||
- <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",
|
||||
`
|
||||
flip(4) :: beat(1) :: snd('kick').out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"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,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Good old true and false",
|
||||
`
|
||||
if (flip(4, 75)) {
|
||||
beat(1) :: snd('kick').out()
|
||||
} else {
|
||||
beat(.5) :: snd('snare').out()
|
||||
}
|
||||
`,
|
||||
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",
|
||||
`
|
||||
// Rap God VS Lil Wild -- Adel Faure
|
||||
if (flip(8)) {
|
||||
// Playing this part for two bars
|
||||
beat(1.5)::snd('kick').out()
|
||||
beat(2)::snd('snare').out()
|
||||
beat(.5)::snd('hh').out()
|
||||
} else {
|
||||
// Now adding some birds and tablas
|
||||
beat(1.5)::snd('kick').out()
|
||||
beat(2)::snd('snare').out()
|
||||
beat(.5)::snd('hh').out()
|
||||
beat(.5)::snd('tabla').speed([1,2].pick()).end(0.5).out()
|
||||
beat(2.34)::snd('birds').n(irand(1,10))
|
||||
.delay(0.5).delaytime(0.5).delayfb(0.25).out()
|
||||
beat(.5)::snd('diphone').end(0.5).n([1,2,3,4].pick()).out()
|
||||
}
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
You can use it everywhere to spice things up, including as a method parameter picker:
|
||||
|
||||
${makeExample(
|
||||
"flip is great for parameter variation",
|
||||
`
|
||||
beat(.5)::snd(flip(2) ? 'kick' : 'hat').out()
|
||||
`,
|
||||
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",
|
||||
`
|
||||
let roomy = (n) => n.room(1).size(1).cutoff(500 + usaw(1/8) * 5000);
|
||||
function a() {
|
||||
beat(1) && roomy(sound('kick')).out()
|
||||
beat(.5) && roomy(sound('hat')).out()
|
||||
}
|
||||
function b() {
|
||||
beat(1/4) && roomy(sound('shaker')).out()
|
||||
}
|
||||
flipbar(2) && a()
|
||||
flipbar(3) && b()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
${makeExample(
|
||||
"Alternating over four bars",
|
||||
`
|
||||
flipbar(2)
|
||||
? beat(.5) && snd(['kick', 'hh'].beat(1)).out()
|
||||
: beat(.5) && snd(['east', 'east:2'].beat(1)).out()
|
||||
`,
|
||||
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",
|
||||
`
|
||||
tempo(150);
|
||||
// Only play on the third and fourth bar of the cycle.
|
||||
onbar([3,4], 4)::beat(.25)::snd('hh').out();
|
||||
// Using JavaScript regular control flow
|
||||
if (onbar([1,2], 4)) {
|
||||
beat(.5) :: sometimes() :: sound('east').out()
|
||||
rhythm(.5, 3, 7) :: snd('kick').out();
|
||||
rhythm(.5, 1, 7) :: snd('jvbass').out();
|
||||
rhythm(.5, 2, 7) :: snd('snare').n(5).out();
|
||||
} else {
|
||||
beat(.5) :: rarely() :: sound('east').n($(1)).out()
|
||||
rhythm(.5, 3, 7) :: snd('kick').n(4).out();
|
||||
rhythm(.5, 1, 7) :: snd('jvbass').n(2).out();
|
||||
rhythm(.5, 2, 7) :: snd('snare').n(3).out();
|
||||
}`,
|
||||
true,
|
||||
)}
|
||||
|
||||
`;
|
||||
};
|
||||
4
src/documentation/learning/time/pulses.svg
Normal file
4
src/documentation/learning/time/pulses.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 8.4 KiB |
35
src/documentation/learning/time/time.ts
Normal file
35
src/documentation/learning/time/time.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { makeExampleFactory } from "../../../Documentation";
|
||||
import { type Editor } from "../../../main";
|
||||
import times from "./times.svg";
|
||||
|
||||
export const time = (application: Editor): string => {
|
||||
//@ts-ignore
|
||||
const makeExample = makeExampleFactory(application);
|
||||
return `
|
||||
# What is time?
|
||||
|
||||
There are two ways to think _intuitively_ about time:
|
||||
|
||||
- **linear time:** the _arrow_ of time, minutes/days/years passing. Time moving forward. In musical terms, a _piece_, _song_.
|
||||
- **cyclical time:** seasons, cycles, etc. In musical terms, repetitions, _beats_, _sections_, etc.
|
||||
|
||||
A musician's job is to interweave cyclical and linear time, repetition and continuity.
|
||||
|
||||
<object type="image/svg+xml" data=${times} style="width: 100%; height: auto; background-color: transparent"></object>
|
||||
|
||||
|
||||
# Time and programming
|
||||
|
||||
When you program on a computer, you can adopt a similar mindset to think about time, where time is sometimes cyclic, sometimes linear:
|
||||
- **linear:** _runtime_, _process time_, _wall clock time_, etc...
|
||||
- **cyclic:** _recursion_, repeating function, routine, etc.
|
||||
|
||||
# Time and Topos
|
||||
|
||||
By making music with **Topos**, you will mingle repetitive structures of different scale and deal with composition as well:
|
||||
- **linear time:** using _bars_, _beats_, _pulses_, transport (_start_/_pause_/_stop_), etc...
|
||||
- **cyclical time:** euclidean rhythms, beats, pulsed time, etc...
|
||||
|
||||
|
||||
`;
|
||||
};
|
||||
4
src/documentation/learning/time/times.svg
Normal file
4
src/documentation/learning/time/times.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Do not edit this file with editors other than draw.io -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="501px" height="151px" viewBox="-0.5 -0.5 501 151" class="ge-export-svg-dark" content="<mxfile host="app.diagrams.net" modified="2023-11-06T09:00:40.296Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" etag="C_foan-BfT9OTEfKR49b" version="22.0.8" type="device"><diagram name="Page-1" id="SVK7qbBq6eghmxk_gXBK">7Zddb5swFIZ/DZeVAAfSXK5Jt2lN1Wq5aHs1efgULBlMjVOgv35mGIhjsqKoH7vIFebF59jnfYxsO2iZVt8EzpNrToA5vksqB60c3/dcd6YejVK3yjwMWiEWlOhOg7ChL9BFanVLCRRGR8k5kzQ3xYhnGUTS0LAQvDS7PXJmjprjGCxhE2Fmq3eUyESrXrgYPnwHGifd0GGgK05x11uXUiSY8HJHQpcOWgrOZdtKqyWwxr3OmDbu64Gv/cwEZHJKQDm/pz8u8mj+MydXv182kK/omc7yjNlWV6wnK+vOAsG3GYEmieugizKhEjY5jpqvpYKutESmTL15qqnTgZBQHZyn11ev1g3wFKSoVZdyMDhwtWnJrreBFrGGGvexQ9mqoSsfd+Hp4e7hHv1aXxU1ytcgrtObcIoLil7eNB8ZVF+ahaWKhYzo5ipiuChoZJphOnfAB98FYqzBV4yxfek0AQxL+myu3DGv9Ai3nKqZ+G7/s7YR+k8N98wu+FZEoGN2l9lemtni33kkFjFIK89fbn3Nx6P0TygtlJ5/JEsPvZLonWGiE8wJEKbS7Cf0STSDE80JEKbSRPPPpRmeaE6AcOyu+dE0FxbNNc0Ai+a8TVOw0B5xKByhaJ0TbY7jO1rnzg7l2fkI5n0X3+7EaG9NyzpiVN0c/lPLzt/PMvU6XGPaFTncBtHlHw==</diagram></mxfile>" style="background-color: rgb(18, 18, 18);"><defs><style type="text/css">svg.ge-export-svg-dark > * { filter: invert(100%) hue-rotate(180deg); }
svg.ge-export-svg-dark image { filter: invert(100%) hue-rotate(180deg) }</style></defs><g><rect x="0" y="0" width="500" height="150" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><path d="M 10.5 65 L 10.5 55 L 470.5 55 L 470.5 44.5 L 489.5 60 L 470.5 75.5 L 470.5 65 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 10.5 125 L 10.5 115 L 110.5 115 L 110.5 104.5 L 129.5 120 L 110.5 135.5 L 110.5 125 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 130.5 125 L 130.5 115 L 230.5 115 L 230.5 104.5 L 249.5 120 L 230.5 135.5 L 230.5 125 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 250.5 125 L 250.5 115 L 350.5 115 L 350.5 104.5 L 369.5 120 L 350.5 135.5 L 350.5 125 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 370.5 125 L 370.5 115 L 470.5 115 L 470.5 104.5 L 489.5 120 L 470.5 135.5 L 470.5 125 Z" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><rect x="10" y="20" width="480" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 478px; height: 1px; padding-top: 30px; margin-left: 11px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">Linear time</div></div></div></foreignObject><text x="250" y="34" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Linear time</text></switch></g><rect x="10" y="80" width="480" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 478px; height: 1px; padding-top: 90px; margin-left: 11px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">Cyclical time</div></div></div></foreignObject><text x="250" y="94" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Cyclical time</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
Reference in New Issue
Block a user