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 .dur:
${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
- flip(n: number, ratio: number = 50): the flip method is a temporal switch. If the value 2 is given, the function will return true for two beats and false for two beats. There are multiple ways to use it effectively. You can pass an integer or a floating point number.
- ratio: number = 50: this argument is ratio expressed in %. It determines how much of the period should be true or false. A ratio of 75 means that 75% of the period will be true. A ratio of 25 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,
)}
flip 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 flip 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,
)}
- flipbar(n: number = 1): this method works just like flip but counts in bars instead of beats. It allows you to think about even larger time cycles. You can also pair it with regular flip 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,
)};
- onbar(bars: number | number[], n: number): The second argument, n, is used to divide the time in a period of n consecutive bars. The first argument should be a bar number or a list of bar numbers to play on. For example, onbar([1, 4], 5) will return true on bar 1 and 4 but return false 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,
)}
`;
};