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:
note("c eb g c4") .clip(2).s("gm_electric_guitar_clean")
…or use scales:
n("0 2 4 7").scale("C:minor") .clip(2).s("gm_electric_guitar_clean")
…or chord symbols:
n("0 1 2 3").chord("Cm").mode("above:c3").voicing() .clip(2).s("gm_electric_guitar_clean")
…using off:
"0" .off(1/3, add(2)) .off(1/2, add(4)) .n() .scale("C:minor") .s("gm_electric_guitar_clean")
Chopping Breaks
A sample can be looped and chopped like this:
samples('github:yaxu/clean-breaks') s("amen/4").fit().chop(32)
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:
samples('github:yaxu/clean-breaks') s("amen/4").fit().chop(16).cut(1) .sometimesBy(.5, ply("2")) .sometimesBy(.25, mul(speed("-1")))
If we want to specify the order of samples, we can replace chop
with slice
:
samples('github:yaxu/clean-breaks') s("amen/4").fit() .slice(8, "<0 1 2 3 4*2 5 6 [6 7]>*2") .cut(1).rarely(ply("2"))
If we use splice
instead of slice
, the speed adjusts to the duration of the event:
samples('github:yaxu/clean-breaks') s("amen") .splice(8, "<0 1 2 3 4*2 5 6 [6 7]>*2") .cut(1).rarely(ply("2"))
Note that we don’t need fit
, because splice
will do that by itself.
Filter Envelopes
Using lpenv
, we can make the filter move:
note("g1 bb1 <c2 eb2> d2") .s("sawtooth") .lpf(400).lpenv(4) .scope()
The type of envelope depends on the methods you’re setting. Let’s set lpa
:
note("g1 bb1 <c2 eb2> d2") .s("sawtooth").lpq(8) .lpf(400).lpa(.2).lpenv(4) .scope()
Now the filter is attacking, rather than decaying as before (decay is the default). We can also do both
note("g1 bb1 <c2 eb2> d2") .s("sawtooth").lpq(8) .lpf(400).lpa(.1).lpd(.1).lpenv(4) .scope()
You can play around with lpa
| lpd
| lps
| lpd
to see what the filter envelope will do.
Layering Sounds
We can layer sounds by separating them with ”,“:
note("<g1 bb1 d2 f1>") .s("sawtooth, square") // <------ .scope()
We can control the gain of individual sounds like this:
note("<g1 bb1 d2 f1>") .s("sawtooth, square:0:.5") // <--- "name:number:gain" .scope()
For more control over each voice, we can use layer
:
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:
note("<g1 bb1 d2 f1>") .add(note("0,.1")) // <------ chorus .s("sawtooth").scope()
Try out different values, or add another voice!
Polyrhythms
Here is a simple example of a polyrhythm:
s("bd*2,hh*3")
A polyrhythm is when 2 different tempos happen at the same time.
Polymeter
This is a polymeter:
s("<bd rim, hh hh oh>*4")
A polymeter is when 2 different bar lengths play at the same tempo.
Phasing
This is a phasing:
note("<C D G A Bb D C A G D Bb A>*[6,6.1]").piano()
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:
samples('bubo:fox') n(run(8)).s("ftabla")
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
.
samples('bubo:fox') n(run(8)).s("ftabla").early(2/8)
Let’s add some randomness:
samples('bubo:fox') n(run(8)).s("ftabla").early(2/8) .sometimes(mul(speed("1.5")))
Tape Warble
We can emulate a pitch warbling effect like this:
note("<c4 bb f eb>*8") .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:
note("f ab bb c") .clip("<2 1 .5 .25>")
The value of clip is relative to the duration of each event. We can also create overlaps using release:
note("f ab bb c") .release("<2 1 .5 .25>")
This will smoothly fade out each sound for the given number of seconds. We could also make the notes shorter by using a decay envelope:
note("f ab bb c") .decay("<2 1 .5 .25>")
When using samples, we also have .end
to cut relative to the sample length:
s("oh*4").end("<1 .5 .25 .1>")
Compare that to clip:
s("oh*4").clip("<1 .5 .25 .1>")
or decay:
s("oh*4").decay("<1 .5 .25 .1>")
Wavetable Synthesis
You can loop a sample with loop
/ loopEnd
:
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:
samples('github:bubobubobubobubo/dough-waveforms') note("c eb g bb").s("wt_dbass").clip(2)
Running through different wavetables can also give interesting variations:
samples('github:bubobubobubobubo/dough-waveforms') note("c2*8").s("wt_dbass").n(run(8)).fast(2)
…adding a filter envelope + reverb:
samples('github:bubobubobubobubo/dough-waveforms') note("c2*8").s("wt_dbass").n(run(8)) .lpf(perlin.range(100,1000).slow(8)) .lpenv(-3).lpa(.1).room(.5).fast(2)