163 lines
5.4 KiB
TypeScript
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,
|
|
)}
|
|
|
|
`;
|
|
};
|