Files
topos/src/documentation/long_forms.ts

163 lines
5.4 KiB
TypeScript

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,
)}
`;
};