Moved doc files to same structure as in index
This commit is contained in:
143
src/documentation/patterns/chaining.ts
Normal file
143
src/documentation/patterns/chaining.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
import { type Editor } from "../../main";
|
||||
|
||||
export const chaining = (application: Editor): string => {
|
||||
const makeExample = makeExampleFactory(application);
|
||||
return `
|
||||
# Chaining
|
||||
|
||||
You might have noticed that **Topos** is using chains a lot. Chains are a very common pattern when programming, especially when you deal with objets that can be composed from many changing properties. Method chaining is used by many objects but mostly by <ic>sound()</ic> and <ic>midi()</ic>. It looks like this:
|
||||
|
||||
${makeExample(
|
||||
"Method chaining",
|
||||
`
|
||||
beat(1)::sound('bd').speed(2).lpf(500).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
Method chains become fun if you add just a little bit of complexity to them. You can start to add conditions, start to register complex chains to be re-used later on, etc.. We will not remind you how to write basic chains. The whole documentation is full of examples! Let's explore more delicate patterns!
|
||||
|
||||
## Registering a chain
|
||||
|
||||
You can use the <ic>register(...args)</ic> function to... register a chain that you would like to re-use later on.
|
||||
|
||||
${makeExample(
|
||||
"Re-creating a classic Tidal function",
|
||||
`
|
||||
// Playing with extreme panning and playback rate
|
||||
register('juxrev', n=>n.pan([0, 1]).speed([1, -1]))
|
||||
|
||||
// Using our new abstraction
|
||||
beat(1)::sound('fhh').juxrev().out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
This is an extremely powerful construct. For example, you can use it to create synthesizer presets and reuse them later on. You can also define parameters for your registered functions. For example:
|
||||
|
||||
${makeExample(
|
||||
"Re-creating a classic Tidal function",
|
||||
`
|
||||
// Registering a specific synth architecture
|
||||
register('sub', (n,x=4,y=80)=>n.ad(0, .25)
|
||||
.fmi(x).pan([0, 1])
|
||||
.delay(0.5).delayt(1/8).delayfb(1/3)
|
||||
.lpf(25+usine(1/3)*y)
|
||||
.lpad(4, 0, .25)
|
||||
)
|
||||
|
||||
// Using it with an arpeggio
|
||||
rhythm(.25, [6, 8].beat(), 12)::sound('sine')
|
||||
.note([0, 2, 4, 5].scale('minor', 50).beat(0.5))
|
||||
.sub(8).out()`,
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
## Conditional chaining
|
||||
|
||||
There are cases when you don't always want to apply one or many elements that are composing your chain. You can use conditionals to set a specific probability for the chaining to happen.
|
||||
|
||||
All functions from the sound object can be used to modify the event, for example:
|
||||
|
||||
${makeExample(
|
||||
"Modifying sound events with probabilities",
|
||||
`
|
||||
beat(.5) && sound('fhh')
|
||||
.odds(1/4, s => s.speed(irand(1,4)))
|
||||
.rarely(s => s.room(0.5).size(8).speed(0.5))
|
||||
.out()`,
|
||||
true,
|
||||
)}
|
||||
${makeExample(
|
||||
"Chance to play a random note",
|
||||
`
|
||||
rhythm(.5, 3, 8) && sound('pluck').note(38).out()
|
||||
beat(.5) && sound('pluck').note(60)
|
||||
.often(s => s.note(57))
|
||||
.sometimes(s => s.note(64).n(irand(1,4)))
|
||||
.note(62)
|
||||
.room(0.5).size(3)
|
||||
.out()`,
|
||||
false,
|
||||
)}
|
||||
|
||||
There is a growing collection of probability and chance methods you can use:
|
||||
|
||||
| Function Name | Description | Example |
|
||||
|----------------|-------------|---------|
|
||||
| <ic>evenbar</ic> | If the current bar is even | <ic>.evenbar(s => s.note(58))</ic> |
|
||||
| <ic>even</ic> | If the current beat is even | <ic>.even(s => s.note(59))</ic> |
|
||||
| <ic>odd</ic> | If the current beat is odd | <ic>.odd(s => s.note(61))</ic> |
|
||||
| <ic>odds</ic> | With a given probability | <ic>.odds(0.3, s => s.note(62))</ic> |
|
||||
| <ic>never</ic> | Never transforms the event | <ic>.never(s => s.note(63))</ic> |
|
||||
| <ic>almostNever</ic> | With a 2.5% probability. | <ic>.almostNever(s => s.note(64))</ic> |
|
||||
| <ic>rarely</ic> | With a 10% probability. | <ic>.rarely(s => s.note(65))</ic> |
|
||||
| <ic>scarcely</ic> | With a 25% probability. | <ic>.scarcely(s => s.note(66))</ic> |
|
||||
| <ic>sometimes</ic> | With a 50% probability. | <ic>.sometimes(s => s.note(67))</ic> |
|
||||
| <ic>often</ic> | With a 75% probability. | <ic>.often(s => s.note(68))</ic> |
|
||||
| <ic>frequently</ic> | With a 90% probability. | <ic>.frequently(s => s.note(69))</ic> |
|
||||
| <ic>almostAlways</ic> | With a 98.5% probability. | <ic>.almostAlways(s => s.note(70))</ic> |
|
||||
| <ic>always</ic> | Always transforms the Event. | <ic>.always(s => s.note(71))</ic> |
|
||||
|
||||
|
||||
|
||||
### MIDI Chaining
|
||||
|
||||
The conditional chaining also applies to MIDI. Values can also be incremented using <ic>+=</ic> notation.
|
||||
|
||||
${makeExample(
|
||||
"Modifying midi events with probabilities",
|
||||
`beat(.5) && midi(60).channel(1)
|
||||
.odds(1/4, n => n.channel(2))
|
||||
.often(n => n.note+=4)
|
||||
.sometimes(s => s.velocity(irand(50,100)))
|
||||
.out()`,
|
||||
true,
|
||||
)};
|
||||
|
||||
## Ziffers
|
||||
|
||||
Ziffers patterns can be chained to <ic>sound()</ic> and <ic>midi()</ic> as well. Chaining is often used as an alternative to passing values in objects as an option, which can be super cumbersome. The available chaining methods are:
|
||||
* <ic>key(key: string)</ic>: for changing key (_e.g._ <ic>"C"</ic> or <ic>"F#"</ic>)
|
||||
* <ic>scale(scale: string)</ic>: for changing the current scale (_e.g._ <ic>"rocritonic"</ic> or <ic>"pentatonic"</ic>)
|
||||
* <ic>octave(n: number)</ic>: for changing octave (_e.g._ <ic>0</ic> or <ic>2</ic>)
|
||||
|
||||
* <ic>sound()</ic>: for outputting pattern as a Sound (See **Sounds**)
|
||||
* <ic>midi()</ic> - for outputting pattern as MIDI (See **MIDI**)
|
||||
|
||||
${makeExample(
|
||||
"Ziffer player using a sound chain and probabilities!",
|
||||
`
|
||||
z1('s 0 5 7 0 3 7 0 2 7 0 1 7 0 1 6 5 4 3 2')
|
||||
.octave([0, 1].beat(2) - 1)
|
||||
.scale('pentatonic').sound('pluck')
|
||||
.odds(1/4, n => n.delay(0.5).delayt(0.25))
|
||||
.odds(1/2, n => n.speed(0.5))
|
||||
.room(0.5).size(0.5).out()
|
||||
`,
|
||||
true,
|
||||
)};
|
||||
`;
|
||||
};
|
||||
65
src/documentation/patterns/functions.ts
Normal file
65
src/documentation/patterns/functions.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { type Editor } from "../../main";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const functions = (application: Editor): string => {
|
||||
const makeExample = makeExampleFactory(application);
|
||||
return `
|
||||
# Functions
|
||||
|
||||
## Scripts
|
||||
|
||||
You can control scripts programatically. This is the core concept of Topos after all!
|
||||
|
||||
- <ic>script(...number: number[])</ic>: call one or more scripts (_e.g. <ic>script(1,2,3,4)</ic>). Once called, scripts will be evaluated once. There are nine local scripts by default. You cannot call the global script nor the initialisation script.
|
||||
- <ic>clear_script(number)</ic>: deletes the given script.
|
||||
- <ic>copy_script(from: number, to: number)</ic>: copies a local script denoted by its number to another local script. **This is a destructive operation!**
|
||||
|
||||
${makeExample(
|
||||
"Calling a script! The most important feature!",
|
||||
`
|
||||
beat(1) :: script(1)
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Calling mutliple scripts at the same time.",
|
||||
`
|
||||
beat(1) :: script(1, 3, 5)
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
|
||||
## Math functions
|
||||
|
||||
- <ic>max(...values: number[]): number</ic>: returns the maximum value of a list of numbers.
|
||||
- <ic>min(...values: number[]): number</ic>: returns the minimum value of a list of numbers.
|
||||
- <ic>mean(...values: number[]): number</ic>: returns the arithmetic mean of a list of numbers.
|
||||
- <ic>limit(value: number, min: number, max: number): number</ic>: Limits a value between a minimum and a maximum.
|
||||
|
||||
## Delay functions
|
||||
|
||||
- <ic>delay(ms: number, func: Function): void</ic>: Delays the execution of a function by a given number of milliseconds.
|
||||
|
||||
${makeExample(
|
||||
"Phased woodblocks",
|
||||
`
|
||||
// Some very low-budget version of phase music
|
||||
beat(.5) :: delay(usine(.125) * 80, () => sound('east').out())
|
||||
beat(.5) :: delay(50, () => sound('east').out())
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>delayr(ms: number, nb: number, func: Function): void</ic>: Delays the execution of a function by a given number of milliseconds, repeated a given number of times.
|
||||
|
||||
${makeExample(
|
||||
"Another woodblock texture",
|
||||
`
|
||||
beat(1) :: delayr(50, 4, () => sound('east').speed([0.5,.25].beat()).out())
|
||||
flip(2) :: beat(2) :: delayr(150, 4, () => sound('east').speed([0.5,.25].beat() * 4).out())
|
||||
`,
|
||||
true,
|
||||
)};
|
||||
`;
|
||||
};
|
||||
59
src/documentation/patterns/lfos.ts
Normal file
59
src/documentation/patterns/lfos.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { type Editor } from "../../main";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const lfos = (application: Editor): string => {
|
||||
const makeExample = makeExampleFactory(application);
|
||||
return `
|
||||
# Low Frequency Oscillators
|
||||
|
||||
Low Frequency Oscillators (_LFOs_) are an important piece in any digital audio workstation or synthesizer. Topos implements some basic waveforms you can play with to automatically modulate your paremeters.
|
||||
|
||||
- <ic>sine(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a sinusoïdal oscillation between <ic>-1</ic> and <ic>1</ic>.
|
||||
- <ic>freq</ic> : frequency in hertz.
|
||||
- <ic>times</ic> : output value multiplier.
|
||||
- <ic>offset</ic>: linear offset.
|
||||
- <ic>usine(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a sinusoïdal oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_.
|
||||
|
||||
${makeExample(
|
||||
"Modulating the speed of a sample player using a sine LFO",
|
||||
`beat(.25) && snd('cp').speed(1 + usine(0.25) * 2).out()`,
|
||||
true,
|
||||
)};
|
||||
|
||||
- <ic>triangle(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a triangle oscillation between <ic>-1</ic> and <ic>1</ic>.
|
||||
- <ic>utriangle(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a triangle oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_.
|
||||
|
||||
${makeExample(
|
||||
"Modulating the speed of a sample player using a triangle LFO",
|
||||
`beat(.25) && snd('cp').speed(1 + utriangle(0.25) * 2).out()`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>saw(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a sawtooth-like oscillation between <ic>-1</ic> and <ic>1</ic>.
|
||||
- <ic>usaw(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a sawtooth-like oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_.
|
||||
|
||||
${makeExample(
|
||||
"Modulating the speed of a sample player using a saw LFO",
|
||||
`beat(.25) && snd('cp').speed(1 + usaw(0.25) * 2).out()`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>square(freq: number = 1, times: number = 1, offset: number= 0, duty: number = .5): number</ic>: returns a square wave oscillation between <ic>-1</ic> and <ic>1</ic>. You can also control the duty cycle using the <ic>duty</ic> parameter.
|
||||
- <ic>usquare(freq: number = 1, times: number = 1, offset: number= 0, duty: number = .5): number</ic>: returns a square wave oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_. You can also control the duty cycle using the <ic>duty</ic> parameter.
|
||||
|
||||
${makeExample(
|
||||
"Modulating the speed of a sample player using a square LFO",
|
||||
`beat(.25) && snd('cp').speed(1 + usquare(0.25, 0, 0.25) * 2).out()`,
|
||||
true,
|
||||
)};
|
||||
|
||||
- <ic>noise(times: number = 1)</ic>: returns a random value between -1 and 1.
|
||||
|
||||
${makeExample(
|
||||
"Modulating the speed of a sample player using noise",
|
||||
`beat(.25) && snd('cp').speed(1 + noise() * 2).out()`,
|
||||
true,
|
||||
)};
|
||||
|
||||
`;
|
||||
};
|
||||
312
src/documentation/patterns/patterns.ts
Normal file
312
src/documentation/patterns/patterns.ts
Normal file
@@ -0,0 +1,312 @@
|
||||
import { type Editor } from "../../main";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const patterns = (application: Editor): string => {
|
||||
const makeExample = makeExampleFactory(application);
|
||||
return `
|
||||
# Array patterns
|
||||
|
||||
**Topos** is using arrays as a way to make dynamic patterns of data (rhythms, melodies, etc).
|
||||
It means that the following:
|
||||
|
||||
${makeExample(
|
||||
"Boring kick",
|
||||
`
|
||||
beat(1)::sound('kick').out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
can be turned into something more interesting like this easily:
|
||||
|
||||
${makeExample(
|
||||
"Less boring kick",
|
||||
`
|
||||
let c = [1,2].dur(3, 1)
|
||||
beat([1, 0.5, 0.25].dur(0.75, 0.25, 1) / c)::sound(['kick', 'fsoftsnare'].beat(0.75))
|
||||
.ad(0, .25).shape(usine(1/2)*0.5).speed([1, 2, 4].beat(0.5)).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
**Topos** comes with a lot of array methods to deal with musical patterns of increasing complexity. Some knowledge of patterns and how to use them will help you to break out of simple loops and repeating structures. The most basic JavaScript data structure is the [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array). Topos is extending it with custom methods to describe patterns that evolve over time. These methods can often be chained to compose more complex expressions: <ic>[1, 2, 3].repeatOdd(5).palindrome().beat()</ic>.
|
||||
|
||||
## Temporal iteration
|
||||
|
||||
- <ic>beat(division: number)</ic>: this method will return the next value in the list every _n_ pulses. By default, <ic>1</ic> equals to one beat but integer and floating point number values are supported as well. This method is extremely powerful and can be used for many different purposes. Check out the examples.
|
||||
|
||||
${makeExample(
|
||||
"Light drumming",
|
||||
`
|
||||
// Every bar, use a different rhythm
|
||||
beat([1, 0.75].beat(4)) :: sound('cp').out()
|
||||
beat([0.5, 1].beat(4)) :: sound('kick').out()
|
||||
beat(2)::snd('snare').shape(.5).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
${makeExample(
|
||||
"Using beat to create arpeggios",
|
||||
`
|
||||
// Arpeggio using pulse divisions
|
||||
beat([.5, .25].beat(0.5)) :: sound('sine')
|
||||
.lpf(100+usine(1/4)*400).lpad(2, 0, .25)
|
||||
.fmi([1,2].beat(8)).fmh([1, 2].beat(0.5))
|
||||
.note([0,2,4,5].scale('minor', 40).beat(0.25)
|
||||
+ [0, 7].bar()
|
||||
+ [12,24].beat(0.5))
|
||||
.sustain([0.25, 0.5].beat(8))
|
||||
.room(0.9).size(0.5)
|
||||
.delay(0.5).delayt([0.5,0.25].beat(16))
|
||||
.delayfb(0.5)
|
||||
.out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
${makeExample(
|
||||
"Cool ambiance",
|
||||
`
|
||||
beat(.5) :: snd(['kick', 'hat'].beat(0.5)).out()
|
||||
beat([2,4].beat(2)) :: snd('shaker').delay(.5).delayfb(.75).delayt(0.125).out()
|
||||
flip(2)::beat(1)::snd('froomy').out()
|
||||
flip(4)::beat(2)::snd('pad').n(2).shape(.5)
|
||||
.orbit(2).room(0.9).size(0.9).release(0.5).out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
|
||||
- <ic>bar(value: number = 1)</ic>: returns the next value every bar (if <ic>value = 1</ic>). Using a larger value will return the next value every <ic>n</ic> bars.
|
||||
|
||||
${makeExample(
|
||||
"A simple drumbeat in no time!",
|
||||
`
|
||||
beat(1)::sound(['kick', 'hat', 'snare', 'hat'].beat()).out()
|
||||
beat([1/4, 1/2].dur(1.5, 0.5))::sound(['jvbass', 'fikea'].bar())
|
||||
.ad(0, .25).room(0.5).size(2).resonance(0.15).lpf(
|
||||
[200,400,800,1200,2000].beat(2)
|
||||
* [1, 2].bar())
|
||||
.out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Using beat and bar in the same example",
|
||||
`
|
||||
beat(2)::snd('snare').out()
|
||||
beat([1, 0.5].beat()) :: sound(['bass3'].bar())
|
||||
.freq(100).n([12, 14].bar())
|
||||
.room(0.5).size(4).orbit(2)
|
||||
.pan(r(0, 1))
|
||||
.speed([1,2,3].beat())
|
||||
.out()
|
||||
`,
|
||||
)}
|
||||
|
||||
- <ic>dur(...list: numbers[])</ic> : keeps the same value for a duration of <ic>n</ic> beats corresponding to the <ic>nth</ic> number of the list you provide.
|
||||
|
||||
${makeExample(
|
||||
"Holding a value for n beats",
|
||||
`
|
||||
// The second note is kept for twice as long
|
||||
beat(0.5)::sound('notes').n([1,2].dur(1, 2))
|
||||
.room(0.5).size(8).delay(0.125).delayt(1/8)
|
||||
.speed(0.5).ad(0, .125).out()
|
||||
// Kick (3 beats), Snare (1bpm beat)
|
||||
beat(1)::sound(['kick', 'fsnare'].dur(3, 1))
|
||||
.n([0,3].dur(3, 1)).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
## Manipulating notes and scales
|
||||
|
||||
|
||||
- <ic>pitch()</ic>: convert a list of integers to pitch classes
|
||||
|
||||
${makeExample(
|
||||
"Converting a list of integers to pitch classes using key and scale",
|
||||
`
|
||||
beat(0.25) :: snd('sine')
|
||||
.pitch([0,1,2,3,4,6,7,8].beat(0.125))
|
||||
.key(["F4","F3"].beat(2.0))
|
||||
.scale("minor").ad(0, .25).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>scale(scale: string, base note: number)</ic>: Map each element of the list to the closest note of the slected scale. [0, 2, 3, 5 ].scale("major", 50) returns [50, 52, <ic>54</ic>, 55]. You can use western scale names like (Major, Minor, Minor pentatonic ...) or [zeitler](https://ianring.com/musictheory/scales/traditions/zeitler) scale names. Alternatively you can also use the integers as used by Ian Ring in his [study of scales](https://ianring.com/musictheory/scales/).
|
||||
|
||||
${makeExample(
|
||||
"Mapping the note array to the E3 major scale",
|
||||
`
|
||||
beat(1) :: snd('gtr')
|
||||
.note([0, 5, 2, 1, 7].scale("Major", 52).beat())
|
||||
.out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>scaleArp(scale: string, mask: number)</ic>: extrapolate a custom-masked scale from each list elements. [0].scale("major", 3) returns [0,2,4]. <ic>scaleArp</ic> supports the same scales as <ic>scale</ic>.
|
||||
|
||||
${makeExample(
|
||||
"Extrapolate a 3-elements Mixolydian scale from 2 notes",
|
||||
`
|
||||
beat(1) :: snd('gtr')
|
||||
.note([0, 5].scaleArp("mixolydian", 3).beat() + 50)
|
||||
.out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
## Iteration using the mouse
|
||||
|
||||
- <ic>mouseX()</ic> / <ic>mouseY()</ic>: divides the screen in <ic>n</ic> zones and returns the value corresponding to the mouse position on screen.</ic>
|
||||
|
||||
${makeExample(
|
||||
"Controlling an arpeggio (octave and note) with mouse",
|
||||
`
|
||||
beat(0.25)::sound('wt_piano')
|
||||
.note([0,2,3,4,5,7,8,9,11,12].scale(
|
||||
'minor', 30 + [0,12,24].mouseY()).mouseX())
|
||||
.room(0.5).size(4).lpad(-2, .2).lpf(500, 0.3)
|
||||
.ad(0, .2).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
## Simple data operations
|
||||
|
||||
- <ic>palindrome()</ic>: Concatenates a list with the same list in reverse.
|
||||
|
||||
${makeExample(
|
||||
"Palindrome filter sweep",
|
||||
`
|
||||
beat([1,.5,.25].beat()) :: snd('wt_stereo')
|
||||
.speed([1, 0.5, 0.25])
|
||||
.pan(r(0, 1)).freq([100,200,300].beat(0.25))
|
||||
.fmi([1,2,3].palindrome().beat(0.5))
|
||||
.fmh([0.5, 1].palindrome().beat())
|
||||
.lpf([500,1000,2000,4000].palindrome().beat())
|
||||
.lpad(4, 0, .25).sustain(0.125).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>random(index: number)</ic>: pick a random element in the given list.
|
||||
- <ic>rand(index: number)</ic>: shorter alias for the same method.
|
||||
|
||||
${makeExample(
|
||||
"Sipping some gasoline at the robot bar",
|
||||
`
|
||||
// rand, random and pick are doing the same thing!
|
||||
beat(1)::snd('fhardkick').shape(0.5)
|
||||
.ad(0, .1).lpf(500).db(-12).out()
|
||||
beat([.5, 1].rand() / 2) :: snd(
|
||||
['amencutup', 'synth'].random())
|
||||
.clip(1).n(irand(4,10)).room(0.5)
|
||||
.size(3).freq(200)
|
||||
.lpf([5000,3000,2000].pick())
|
||||
.end(0.5).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>pick()</ic>: pick a random element in the list.
|
||||
|
||||
${makeExample(
|
||||
"Picking values in lists",
|
||||
`
|
||||
beat(0.25)::sound(['ftabla', 'fwood'].pick())
|
||||
.speed([1,2,3,4].pick()).ad(0, .125).n(ir(1,10))
|
||||
.room(0.5).size(1).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>degrade(amount: number)</ic>: removes _n_% of the list elements. Lists can be degraded as long as one element remains. The amount of degradation is given as a percentage.
|
||||
|
||||
${makeExample(
|
||||
"Amen break suffering from data loss",
|
||||
`
|
||||
// Tweak the value to degrade this amen break even more!
|
||||
beat(.25)::snd('amencutup').n([1,2,3,4,5,6,7,8,9].degrade(20).loop($(1))).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>repeat(amount: number)</ic>: repeat every list elements _n_ times.
|
||||
- <ic>repeatEven(amount: number)</ic>: repeat every pair element of the list _n_ times.
|
||||
- <ic>repeatOdd(amount: number)</ic>: repeat every odd element of the list _n_ times.
|
||||
|
||||
${makeExample(
|
||||
"Repeating samples a given number of times",
|
||||
`
|
||||
beat(.25)::sound('amencutup').n([1,2,3,4,5,6,7,8].repeat(4).beat(.25)).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>loop(index: number)</ic>: loop takes one argument, the _index_. It allows you to iterate over a list using an iterator such as a counter. This is super useful to control how you are accessing values in a list without relying on a temporal method such as <ic>.beat()</ic> or </ic>.bar()</ic>.
|
||||
|
||||
${makeExample(
|
||||
"Don't you know how to count up to 5?",
|
||||
`
|
||||
beat(1) :: sound('numbers').n([1,2,3,4,5].loop($(3, 10, 2))).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>shuffle(): this</ic>: shuffles a list! Simple enough!
|
||||
|
||||
${makeExample(
|
||||
"Shuffling a list for extra randomness",
|
||||
`
|
||||
beat(1) :: sound('numbers').n([1,2,3,4,5].shuffle().loop($(1)).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>rotate(steps: number)</ic>: rotate a list to the right _n_ times. The last value become the first, rinse and repeat.
|
||||
|
||||
${makeExample(
|
||||
"To make things more complex... here you go",
|
||||
`
|
||||
beat(.25) :: snd('sine').fmi([1.99, 2])
|
||||
.ad(0, .125).lpf(500+r(1,400))
|
||||
.lpad(usine()*8, 0, .125)
|
||||
.fmenv(2).fmdecay(0.125).fmsustain(0)
|
||||
.delay(0.5).fmh(parseInt(usine(1/12)*3))
|
||||
.note(["C3", "E3", "G3", "Bb3", "D4"]
|
||||
.rotate([0, 1, 3, 5].beat(4)) // Rotation over notes
|
||||
.beat(.25)) // while the index changes
|
||||
.out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
## Filtering
|
||||
|
||||
- <ic>unique()</ic>: filter a list to remove repeated values.
|
||||
|
||||
${makeExample(
|
||||
"Demonstrative filtering. Final list is [100, 200]",
|
||||
`
|
||||
// Remove unique and 100 will repeat four times!
|
||||
beat(1)::snd('sine').sustain(0.1).freq([100,100,100,100,200].unique().beat()).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
## Simple math operations
|
||||
|
||||
- <ic>add()</ic>: add a given amount to every list element.
|
||||
- <ic>sub()</ic>: add a given amount to every list element.
|
||||
- <ic>mult()</ic>: add a given amount to every list element.
|
||||
- <ic>div()</ic>: add a given amount to every list element.
|
||||
|
||||
${makeExample("Simple addition", `[1, 2 ,3].add(2).beat()`, true)}
|
||||
|
||||
`;
|
||||
};
|
||||
97
src/documentation/patterns/probabilities.ts
Normal file
97
src/documentation/patterns/probabilities.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { type Editor } from "../../main";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const probabilities = (application: Editor): string => {
|
||||
const makeExample = makeExampleFactory(application);
|
||||
return `
|
||||
|
||||
# Probabilities
|
||||
|
||||
There are some simple functions to play with probabilities.
|
||||
|
||||
- <ic>rand(min: number, max:number)</ic>: returns a random number between <ic>min</ic> and <ic>max</ic>. Shorthand <ic>r()</ic>.
|
||||
- <ic>irand(min: number, max:number)</ic>: returns a random integer between <ic>min</ic> and <ic>max</ic>. Shorthands <ic>ir()</ic> or <ic>rI()</ic>.
|
||||
|
||||
${makeExample(
|
||||
"Bleep bloop, what were you expecting?",
|
||||
`
|
||||
rhythm(0.125, 10, 16) :: sound('sid').n(4).note(50 + irand(50, 62) % 8).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
- <ic>prob(p: number)</ic>: return <ic>true</ic> _p_% of time, <ic>false</ic> in other cases.
|
||||
- <ic>toss()</ic>: throwing a coin. Head (<ic>true</ic>) or tails (<ic>false</ic>).
|
||||
|
||||
|
||||
${makeExample(
|
||||
"The Teletype experience!",
|
||||
`
|
||||
prob(50) :: script(1);
|
||||
prob(60) :: script(2);
|
||||
prob(80) :: script(toss() ? script(3) : script(4))
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
- <ic>seed(val: number|string)</ic>: sets the seed of the random number generator. You can use a number or a string. The same seed will always return the same sequence of random numbers.
|
||||
|
||||
|
||||
## Chance operators
|
||||
|
||||
Chance operators returning a boolean value are also available. They are super important because they also exist for another mechanism called **chaining**. Checkout the **Chaining** page to learn how to use them in different contexts!
|
||||
|
||||
By default chance operators will be evaluated 48 times within a beat. You can change this value by providing a number of beats as an argument. Default value is 1. Change operators can also be used to randomly apply other operators.
|
||||
|
||||
- <ic>odds(n: number, beats?: number)</ic>: returns true for every n (odds) (eg. 1/4 = 0.25) in given number of beats
|
||||
- <ic>never(beats?: number)</ic>: returns false. Can be handy when switching between different probabilities
|
||||
- <ic>almostNever(beats?: number)</ic>: returns true 0.1% of the time in given number of beats
|
||||
- <ic>rarely(beats?: number)</ic>: returns true 1% of the time in given number of beats
|
||||
- <ic>scarcely(beats?: number)</ic>: returns true 10% of the time in given number of beats
|
||||
- <ic>sometimes(beats?: number)</ic>: returns true 50% of the time in given number of beats
|
||||
- <ic>often(beats?: number)</ic>: returns true 75% of the time in given number of beats
|
||||
- <ic>frequently(beats?: number)</ic>: returns true 90% of the time in given number of beats
|
||||
- <ic>almostAlways(beats?: number)</ic>: returns true 99% of the time in given number of beats
|
||||
- <ic>always(beats?: number)</ic>: returns true. Can be handy when switching between different probabilities
|
||||
|
||||
Examples:
|
||||
|
||||
${makeExample(
|
||||
"Using chance operators",
|
||||
`
|
||||
rarely() :: sound('hh').out(); // Rarely 48 times is still a lot
|
||||
rarely(4) :: sound('bd').out(); // Rarely in 4 beats is bit less
|
||||
rarely(8) :: sound('east').out(); // Rarely in 8 beats is even less
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Using chance with other operators",
|
||||
`
|
||||
frequently() :: beat(1) :: sound('kick').out();
|
||||
often() :: beat(0.5) :: sound('hh').out();
|
||||
sometimes() :: onbeat(1,3) :: sound('snare').out();
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Using chance with chaining",
|
||||
`
|
||||
beat(0.5) && sound("bd")
|
||||
.freq(100)
|
||||
.sometimes(s=>s.crush(2.5))
|
||||
.out()
|
||||
|
||||
beat(0.5) && sound('arp').freq(100)
|
||||
.sometimes(n=>n.freq(200).delay(0.5))
|
||||
.rarely(n=>n.freq(300).delay(2.5))
|
||||
.almostNever(n=>n.freq(400))
|
||||
.out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
`;
|
||||
};
|
||||
82
src/documentation/patterns/variables.ts
Normal file
82
src/documentation/patterns/variables.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { type Editor } from "../../main";
|
||||
import { makeExampleFactory } from "../../Documentation";
|
||||
|
||||
export const variables = (application: Editor): string => {
|
||||
const makeExample = makeExampleFactory(application);
|
||||
return `
|
||||
|
||||
# Variables
|
||||
|
||||
By default, each script is independant from each other. Scripts live in their own bubble and you cannot get or set variables affecting a script from any other script.
|
||||
|
||||
**However**, everybody knows that global variables are cool and should be used everywhere. Global variables are an incredibely powerful tool to radically alter a composition in a few lines of code.
|
||||
|
||||
- <ic>variable(a: number | string, b?: any)</ic>: if only one argument is provided, the value of the variable will be returned through its name, denoted by the first argument. If a second argument is used, it will be saved as a global variable under the name of the first argument.
|
||||
- <ic>delete_variable(name: string)</ic>: deletes a global variable from storage.
|
||||
- <ic>clear_variables()</ic>: clear **ALL** variables. **This is a destructive operation**!
|
||||
|
||||
**Note:** since this example is running in the documentation, we cannot take advantage of the multiple scripts paradigm. Try to send a variable from the global file to the local file n°6.
|
||||
|
||||
${makeExample(
|
||||
"Setting a global variable",
|
||||
`
|
||||
v('my_cool_variable', 2)
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Getting that variable back and printing!",
|
||||
`
|
||||
// Note that we just use one argument
|
||||
log(v('my_cool_variable'))
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
|
||||
|
||||
## Counter and iterators
|
||||
|
||||
You will often need to use iterators and/or counters to index over data structures (getting a note from a list of notes, etc...). There are functions ready to be used for this. Each script also comes with its own iterator that you can access using the <ic>i</ic> variable. **Note:** the script iteration count is **not** resetted between sessions. It will continue to increase the more you play, even if you just picked up an old project.
|
||||
|
||||
- <ic>counter(name: number | string, limit?: number, step?: number)</ic>: reads the value of the counter <ic>name</ic>. You can also call this function using the dollar symbol: <ic>$</ic>.
|
||||
- <ic>limit?</ic>: counter upper limit before wrapping up.
|
||||
- <ic>step?</ic>: incrementor. If step is <ic>2</ic>, the iterator will go: <ic>0, 2, 4, 6</ic>, etc...
|
||||
|
||||
- <ic>drunk(n?: number)</ic>: returns the value of the internal drunk walk counter. This iterator will sometimes go up, sometimes go down. It comes with companion functions that you can use to finetune its behavior.
|
||||
- <ic>drunk_max(max: number)</ic>: sets the maximum value.
|
||||
- <ic>drunk_min(min: number)</ic>: sets the minimum value.
|
||||
- <ic>drunk_wrap(wrap: boolean)</ic>: whether to wrap the drunk walk to 0 once the upper limit is reached or not.
|
||||
|
||||
**Note:** Counters also come with a secret syntax. They can be called with the **$** symbol!
|
||||
|
||||
${makeExample(
|
||||
"Iterating over a list of samples using a counter",
|
||||
`
|
||||
rhythm(.25, 6, 8) :: sound('dr').n($(1)).end(.25).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Using a more complex counter",
|
||||
`
|
||||
// Limit is 20, step is 5
|
||||
rhythm(.25, 6, 8) :: sound('dr').n($(1, 20, 5)).end(.25).out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Calling the drunk mechanism",
|
||||
`
|
||||
// Limit is 20, step is 5
|
||||
rhythm(.25, 6, 8) :: sound('dr').n(drunk()).end(.25).out()
|
||||
`,
|
||||
false,
|
||||
)}
|
||||
|
||||
|
||||
|
||||
`;
|
||||
};
|
||||
@@ -6,7 +6,7 @@ export const ziffers_scales = (application: Editor): string => {
|
||||
return `
|
||||
# Scales
|
||||
|
||||
Ziffers supports all the keys and scales. Keys can be defined by using [scientific pitch notation](https://en.wikipedia.org/wiki/Scientific_pitch_notation), for example <ic>F3</ic>. Western style (1490 scales) can be with scale names named after greek modes and extended by [William Zeitler](https://ianring.com/musictheory/scales/traditions/zeitler). You will never really run out of scales to play with using Ziffers. Here is a short list of some possible scales that you can play with:
|
||||
Ziffers supports all the keys and scales. Keys can be defined by using [scientific pitch notation](https://en.wikipedia.org/wiki/Scientific_pitch_notation), for example <ic>F3</ic>. Western style (1490 scales) can be with scale names named after greek modes and extended by <a href="https://allthescales.org/intro.php" target="_blank">William Zeitler</a>. You will never really run out of scales to play with using Ziffers. Here is a short list of some possible scales that you can play with:
|
||||
|
||||
| Scale name | Intervals |
|
||||
|------------|------------------------|
|
||||
@@ -20,7 +20,7 @@ export const ziffers_scales = (application: Editor): string => {
|
||||
| Soryllic | <ic>11122122</ic>|
|
||||
| Modimic | <ic>412122</ic> |
|
||||
| Ionalian | <ic>1312122</ic> |
|
||||
| ... | And it goes on for **1490** scales |
|
||||
| ... | And it goes on for <a href="https://ianring.com/musictheory/scales/traditions/zeitler" target="_blank">**1490** scales (See full list here)</a>. |
|
||||
|
||||
${makeExample(
|
||||
"What the hell is the Modimic scale?",
|
||||
@@ -33,7 +33,7 @@ export const ziffers_scales = (application: Editor): string => {
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
|
||||
You can also use more traditional <a href="https://ianring.com/musictheory/scales/traditions/western" target="_blank">western names</a>:
|
||||
|
||||
| Scale name | Intervals |
|
||||
|
||||
Reference in New Issue
Block a user