Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recipes #742

Merged
merged 5 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 8 additions & 4 deletions website/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,10 @@ export const SIDEBAR: Sidebar = {
{ text: 'MIDI & OSC', link: 'learn/input-output' },
],
More: [
{ text: 'Recipes', link: 'recipes/recipes' },
{ text: 'Mini-Notation', link: 'learn/mini-notation' },
{ text: 'Coding syntax', link: 'learn/code' },
{ text: 'Offline', link: 'learn/pwa' },
{ text: 'Patterns', link: 'technical-manual/patterns' },
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' },
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
{ text: 'Music metadata', link: 'learn/metadata' },
{ text: 'CSound', link: 'learn/csound' },
],
Expand All @@ -89,7 +87,13 @@ export const SIDEBAR: Sidebar = {
{ text: 'Accumulation', link: 'learn/accumulation' },
{ text: 'Tonal Functions', link: 'learn/tonal' },
],
Understand: [{ text: 'Pitch', link: 'understand/pitch' }],
Understand: [
{ text: 'Coding syntax', link: 'learn/code' },
{ text: 'Pitch', link: 'understand/pitch' },
{ text: 'Cycles', link: 'understand/cycles' },
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' },
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
],
Development: [
{ text: 'REPL', link: 'technical-manual/repl' },
{ text: 'Sounds', link: 'technical-manual/sounds' },
Expand Down
312 changes: 312 additions & 0 deletions website/src/pages/recipes/recipes.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
---
title: Recipes
layout: ../../layouts/MainLayout.astro
---

import { MiniRepl } from '../../docs/MiniRepl';

# Recipes

This page shows possible ways to achieve common (or not so common) musical goals.
There are often many ways to do a thing and there is no right or wrong.
The fun part is that each representation will give you different impulses when improvising.

## Arpeggios

An arpeggio is when the notes of a chord are played in sequence.
We can either write the notes by hand:

<MiniRepl
client:visible
tune={`note("c eb g c4")
.clip(2).s("gm_electric_guitar_clean")`}
punchcard
/>

...or use scales:

<MiniRepl
client:visible
tune={`n("0 2 4 7").scale("C:minor")
.clip(2).s("gm_electric_guitar_clean")`}
punchcard
/>

...or chord symbols:

<MiniRepl
client:visible
tune={`n("0 1 2 3").chord("Cm").mode("above:c3").voicing()
.clip(2).s("gm_electric_guitar_clean")`}
punchcard
/>

...using off:

<MiniRepl
client:visible
tune={`"0"
.off(1/3, add(2))
.off(1/2, add(4))
.n()
.scale("C:minor")
.s("gm_electric_guitar_clean")`}
punchcard
/>

## Chopping Breaks

A sample can be looped and chopped like this:

<MiniRepl
client:visible
tune={`await samples('github:yaxu/clean-breaks/main')
s("amen/8").fit().chop(16)`}
punchcard
/>

This fits the break into 8 cycles + chops it in 16 pieces.
The chops are not audible yet, because we're not doing any manipulation.
Let's add randmized doubling + reversing:

<MiniRepl
client:visible
tune={`await samples('github:yaxu/clean-breaks/main')
s("amen/8").fit().chop(16).cut(1)
.sometimesBy(.5, ply(2))
.sometimesBy(.25, mul(speed(-1)))`}
punchcard
/>

If we want to specify the order of samples, we can replace `chop` with `slice`:

<MiniRepl
client:visible
tune={`await samples('github:yaxu/clean-breaks/main')
s("amen/8").fit()
.slice(8, "<0 1 2 3 4*2 5 6 [6 7]>")
.cut(1).rarely(ply(2))`}
punchcard
/>

If we use `splice` instead of `slice`, the speed adjusts to the duration of the event:

<MiniRepl
client:visible
tune={`await samples('github:yaxu/clean-breaks/main')
s("amen")
.splice(8, "<0 1 2 3 4*2 5 6 [6 7]>")
.cut(1).rarely(ply(2))`}
punchcard
/>

Note that we don't need `fit`, because `splice` will do that by itself.

## Filter Envelopes

A minimal filter envelope looks like this:

<MiniRepl
client:visible
tune={`note("g1 bb1 <c2 eb2> d2")
.s("sawtooth")
.lpf(400).lpa(.2).lpenv(4)
.scope()`}
/>

We can flip the envelope by setting `lpenv` negative + add some resonance `lpq`:

<MiniRepl
client:visible
tune={`note("g1 bb1 <c2 eb2> d2")
.s("sawtooth").lpq(8)
.lpf(400).lpa(.2).lpenv(-4)
.scope()`}
/>

## Layering Sounds

We can layer sounds by separating them with ",":

<MiniRepl
client:visible
tune={`note("<g1 bb1 d2 f1>")
.s("sawtooth, square") // <------
.scope()`}
/>

We can control the gain of individual sounds like this:

<MiniRepl
client:visible
tune={`note("<g1 bb1 d2 f1>")
.s("sawtooth, square:0:.5") // <--- "name:number:gain"
.scope()`}
/>

For more control over each voice, we can use `layer`:

<MiniRepl
client:visible
tune={`note("<g1 bb1 d2 f1>").layer(
x=>x.s("sawtooth").vib(4),
x=>x.s("square").add(note(12))
).scope()`}
/>

Here, we give the sawtooth a vibrato and the square is moved an octave up.
With `layer`, you can use any pattern method available on each voice, so sky is the limit..

## Oscillator Detune

We can fatten a sound by adding a detuned version to itself:

<MiniRepl
client:visible
tune={`note("<g1 bb1 d2 f1>")
.add(note("0,.1")) // <------ chorus
.s("sawtooth").scope()`}
punchcard
/>

Try out different values, or add another voice!

## Polyrhythms

Here is a simple example of a polyrhythm:

<MiniRepl client:visible tune={`s("bd*2,hh*3")`} punchcard />

A polyrhythm is when 2 different tempos happen at the same time.

## Polymeter

This is a polymeter:

<MiniRepl client:visible tune={`s("<bd rim>,<hh hh oh>").fast(2)`} punchcard />

A polymeter is when 2 different bar lengths play at the same tempo.

## Phasing

This is a phasing:

<MiniRepl client:visible tune={`note("<C D G A Bb D C A G D Bb A>*[6,6.1]").piano()`} punchcard />

Phasing happens when the same sequence plays at slightly different tempos.

## Running through samples

Using `run` with `n`, we can rush through a sample bank:

<MiniRepl
client:visible
tune={`await samples('github:Bubobubobubobubo/Dough-Fox/main')
n(run(8)).s("ftabla")`}
punchcard
/>

This works great with sample banks that contain similar sounds, like in this case different recordings of a tabla.
Often times, you'll hear the beginning of the phrase not where the pattern begins.
In this case, I hear the beginning at the third sample, which can be accounted for with `early`.

<MiniRepl
client:visible
tune={`await samples('github:Bubobubobubobubo/Dough-Fox/main')
n(run(8)).s("ftabla").early(2/8)`}
/>

Let's add some randomness:

<MiniRepl
client:visible
tune={`await samples('github:Bubobubobubobubo/Dough-Fox/main')
n(run(8)).s("ftabla").early(2/8)
.sometimes(mul(speed(1.5)))`}
/>

## Tape Warble

We can emulate a pitch warbling effect like this:

<MiniRepl
client:visible
tune={`note("c4 bb f eb")
.add(note(perlin.range(0,.5))) // <------ warble
.clip(2).s("gm_electric_guitar_clean")`}
/>

## Sound Duration

There are a number of ways to change the sound duration. Using clip:

<MiniRepl
client:visible
tune={`note("f ab bb c")
.clip("<2 1 .5 .25>/2")`}
/>

The value of clip is relative to the duration of each event.
We can also create overlaps using release:

<MiniRepl
client:visible
tune={`note("f ab bb c")
.release("<2 1 .5 .002>/2")`}
/>

This will smoothly fade out each sound for the given number of seconds.
We could also make the notes shorter with decay / sustain:

<MiniRepl
client:visible
tune={`note("f ab bb c")
.decay("<.2 .1 .02>/2").sustain(0)`}
/>

For now, there is a limitation where decay values that exceed the event duration may cause little cracks, so use higher numbers with caution..

When using samples, we also have `.end` to cut relative to the sample length:

<MiniRepl client:visible tune={`s("oh*4").end("<1 .5 .25 .1>")`} />

Compare that to clip:

<MiniRepl client:visible tune={`s("oh*4").clip("<1 .5 .25 .1>")`} />

or decay / sustain

<MiniRepl client:visible tune={`s("oh*4").decay("<.2 .12 .06 .01>").sustain(0)`} />

## Wavetable Synthesis

You can loop a sample with `loop` / `loopEnd`:

<MiniRepl client:visible tune={`note("<c eb g f>").s("bd").loop(1).loopEnd(.05).gain(.2)`} />

This allows us to play the first 5% of the bass drum as a synth!
To simplify loading wavetables, any sample that starts with `wt_` will be looped automatically:

<MiniRepl
client:visible
tune={`await samples('github:bubobubobubobubo/dough-waveforms/main')
note("c eb g bb").s("wt_dbass").clip(2)`}
/>

Running through different wavetables can also give interesting variations:

<MiniRepl
client:visible
tune={`await samples('github:bubobubobubobubo/dough-waveforms/main')
note("c2*8").s("wt_dbass").n(run(8))`}
/>

...adding a filter envelope + reverb:

<MiniRepl
client:visible
tune={`await samples('github:bubobubobubobubo/dough-waveforms/main')
note("c2*8").s("wt_dbass").n(run(8))
.lpf(perlin.range(200,2000).slow(8))
.lpenv(-3).lpa(.1).room(.5)`}
/>