mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-25 04:28:30 +00:00
commit
d18116fcbe
@ -23,7 +23,7 @@ const generic_params = [
|
|||||||
* @name s
|
* @name s
|
||||||
* @param {string | Pattern} sound The sound / pattern of sounds to pick
|
* @param {string | Pattern} sound The sound / pattern of sounds to pick
|
||||||
* @example
|
* @example
|
||||||
* s("bd hh").out()
|
* s("bd hh")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
['s', 's', 'sound'],
|
['s', 's', 'sound'],
|
||||||
@ -67,7 +67,7 @@ const generic_params = [
|
|||||||
* @name gain
|
* @name gain
|
||||||
* @param {number | Pattern} amount gain.
|
* @param {number | Pattern} amount gain.
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").gain(".4!2 1 .4!2 1 .4 1").out()
|
* s("hh*8").gain(".4!2 1 .4!2 1 .4 1")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
@ -129,7 +129,7 @@ const generic_params = [
|
|||||||
* @name bandf
|
* @name bandf
|
||||||
* @param {number | Pattern} frequency center frequency
|
* @param {number | Pattern} frequency center frequency
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd,hh*3").bandf("<1000 2000 4000 8000>").out()
|
* s("bd sd,hh*3").bandf("<1000 2000 4000 8000>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
['f', 'bandf', 'A pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'],
|
['f', 'bandf', 'A pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'],
|
||||||
@ -140,7 +140,7 @@ const generic_params = [
|
|||||||
* @name bandq
|
* @name bandq
|
||||||
* @param {number | Pattern} q q factor
|
* @param {number | Pattern} q q factor
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd").bandf(500).bandq("<0 1 2 3>").out()
|
* s("bd sd").bandf(500).bandq("<0 1 2 3>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'],
|
['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'],
|
||||||
@ -152,7 +152,7 @@ const generic_params = [
|
|||||||
* @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample
|
* @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample
|
||||||
* @example
|
* @example
|
||||||
* samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/')
|
* samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/')
|
||||||
* s("rave").begin("<0 .25 .5 .75>").out()
|
* s("rave").begin("<0 .25 .5 .75>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
@ -167,7 +167,7 @@ const generic_params = [
|
|||||||
* @name end
|
* @name end
|
||||||
* @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc..
|
* @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc..
|
||||||
* @example
|
* @example
|
||||||
* s("bd*2,ho*4").end("<.1 .2 .5 1>").out()
|
* s("bd*2,oh*4").end("<.1 .2 .5 1>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
@ -205,7 +205,7 @@ const generic_params = [
|
|||||||
* @name crush
|
* @name crush
|
||||||
* @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction).
|
* @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction).
|
||||||
* @example
|
* @example
|
||||||
* s("<bd sd>,hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>").out()
|
* s("<bd sd>,hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
@ -214,12 +214,12 @@ const generic_params = [
|
|||||||
'bit crushing, a pattern of numbers from 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction).',
|
'bit crushing, a pattern of numbers from 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction).',
|
||||||
],
|
],
|
||||||
/**
|
/**
|
||||||
* fake-resampling for lowering the sample rate
|
* fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers
|
||||||
*
|
*
|
||||||
* @name coarse
|
* @name coarse
|
||||||
* @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on.
|
* @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on.
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd,hh*4").coarse("<1 4 8 16 32>").out()
|
* s("bd sd,hh*4").coarse("<1 4 8 16 32>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
@ -256,7 +256,7 @@ const generic_params = [
|
|||||||
* @name cutoff
|
* @name cutoff
|
||||||
* @param {number | Pattern} frequency audible between 0 and 20000
|
* @param {number | Pattern} frequency audible between 0 and 20000
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd,hh*3").cutoff("<4000 2000 1000 500 200 100>").out()
|
* s("bd sd,hh*3").cutoff("<4000 2000 1000 500 200 100>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
// TODO: add lpf synonym
|
// TODO: add lpf synonym
|
||||||
@ -267,7 +267,7 @@ const generic_params = [
|
|||||||
* @name hcutoff
|
* @name hcutoff
|
||||||
* @param {number | Pattern} frequency audible between 0 and 20000
|
* @param {number | Pattern} frequency audible between 0 and 20000
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd,hh*4").hcutoff("<4000 2000 1000 500 200 100>").out()
|
* s("bd sd,hh*4").hcutoff("<4000 2000 1000 500 200 100>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
// TODO: add hpf synonym
|
// TODO: add hpf synonym
|
||||||
@ -280,9 +280,9 @@ const generic_params = [
|
|||||||
* Applies the resonance of the high-pass filter.
|
* Applies the resonance of the high-pass filter.
|
||||||
*
|
*
|
||||||
* @name hresonance
|
* @name hresonance
|
||||||
* @param {number | Pattern} q resonance factor between 0 and 1
|
* @param {number | Pattern} q resonance factor between 0 and 50
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd,hh*4").hcutoff(2000).hresonance("<0 10 20 30>").out()
|
* s("bd sd,hh*4").hcutoff(2000).hresonance("<0 10 20 30>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
@ -295,9 +295,9 @@ const generic_params = [
|
|||||||
* Applies the cutoff frequency of the low-pass filter.
|
* Applies the cutoff frequency of the low-pass filter.
|
||||||
*
|
*
|
||||||
* @name resonance
|
* @name resonance
|
||||||
* @param {number | Pattern} q resonance factor between 0 and 1
|
* @param {number | Pattern} q resonance factor between 0 and 50
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd,hh*4").cutoff(2000).resonance("<0 10 20 30>").out()
|
* s("bd sd,hh*4").cutoff(2000).resonance("<0 10 20 30>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
['f', 'resonance', 'a pattern of numbers from 0 to 1. Specifies the resonance of the low-pass filter.'],
|
['f', 'resonance', 'a pattern of numbers from 0 to 1. Specifies the resonance of the low-pass filter.'],
|
||||||
@ -371,7 +371,7 @@ const generic_params = [
|
|||||||
* @name fadeTime
|
* @name fadeTime
|
||||||
* @param {number | Pattern} time between 0 and 1
|
* @param {number | Pattern} time between 0 and 1
|
||||||
* @example
|
* @example
|
||||||
* s("ho*4").end(.1).fadeTime("<0 .2 .4 .8>").osc()
|
* s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
@ -496,7 +496,7 @@ const generic_params = [
|
|||||||
* @name pan
|
* @name pan
|
||||||
* @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel)
|
* @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel)
|
||||||
* @example
|
* @example
|
||||||
* s("[bd hh]*2").pan("<.5 1 .5 0>").out()
|
* s("[bd hh]*2").pan("<.5 1 .5 0>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
@ -599,7 +599,7 @@ const generic_params = [
|
|||||||
* @name shape
|
* @name shape
|
||||||
* @param {number | Pattern} distortion between 0 and 1
|
* @param {number | Pattern} distortion between 0 and 1
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd,hh*4").shape("<0 .2 .4 .6 .8>").out()
|
* s("bd sd,hh*4").shape("<0 .2 .4 .6 .8>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
@ -665,7 +665,7 @@ const generic_params = [
|
|||||||
* @param {string | Pattern} vowel You can use a e i o u.
|
* @param {string | Pattern} vowel You can use a e i o u.
|
||||||
* @example
|
* @example
|
||||||
* note("c2 <eb2 <g2 g1>>").s('sawtooth')
|
* note("c2 <eb2 <g2 g1>>").s('sawtooth')
|
||||||
* .vowel("<a e i <o u>>").out()
|
* .vowel("<a e i <o u>>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
|
|||||||
@ -32,52 +32,52 @@ const euclid = (pulses, steps, rotation = 0) => {
|
|||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* // The Cuban tresillo pattern.
|
* // The Cuban tresillo pattern.
|
||||||
* n("c3").euclid(3,8).out()
|
* note("c3").euclid(3,8)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @example // A thirteenth century Persian rhythm called Khafif-e-ramal.
|
* @example // A thirteenth century Persian rhythm called Khafif-e-ramal.
|
||||||
* n("c3").euclid(2,5)
|
* note("c3").euclid(2,5)
|
||||||
* @example // The archetypal pattern of the Cumbia from Colombia, as well as a Calypso rhythm from Trinidad.
|
* @example // The archetypal pattern of the Cumbia from Colombia, as well as a Calypso rhythm from Trinidad.
|
||||||
* "c3".euclid(3,4)
|
* note("c3").euclid(3,4)
|
||||||
* @example // Another thirteenth century Persian rhythm by the name of Khafif-e-ramal, as well as a Rumanian folk-dance rhythm.
|
* @example // Another thirteenth century Persian rhythm by the name of Khafif-e-ramal, as well as a Rumanian folk-dance rhythm.
|
||||||
* "c3".euclid(3,5,2)
|
* note("c3").euclid(3,5,2)
|
||||||
* @example // A Ruchenitza rhythm used in a Bulgarian folk-dance.
|
* @example // A Ruchenitza rhythm used in a Bulgarian folk-dance.
|
||||||
* "c3".euclid(3,7)
|
* note("c3").euclid(3,7)
|
||||||
* @example // The Cuban tresillo pattern.
|
* @example // The Cuban tresillo pattern.
|
||||||
* "c3".euclid(3,8)
|
* note("c3").euclid(3,8)
|
||||||
* @example // Another Ruchenitza Bulgarian folk-dance rhythm.
|
* @example // Another Ruchenitza Bulgarian folk-dance rhythm.
|
||||||
* "c3".euclid(4,7)
|
* note("c3").euclid(4,7)
|
||||||
* @example // The Aksak rhythm of Turkey.
|
* @example // The Aksak rhythm of Turkey.
|
||||||
* "c3".euclid(4,9)
|
* note("c3").euclid(4,9)
|
||||||
* @example // The metric pattern used by Frank Zappa in his piece titled Outside Now.
|
* @example // The metric pattern used by Frank Zappa in his piece titled Outside Now.
|
||||||
* "c3".euclid(4,11)
|
* note("c3").euclid(4,11)
|
||||||
* @example // Yields the York-Samai pattern, a popular Arab rhythm.
|
* @example // Yields the York-Samai pattern, a popular Arab rhythm.
|
||||||
* "c3".euclid(5,6)
|
* note("c3").euclid(5,6)
|
||||||
* @example // The Nawakhat pattern, another popular Arab rhythm.
|
* @example // The Nawakhat pattern, another popular Arab rhythm.
|
||||||
* "c3".euclid(5,7)
|
* note("c3").euclid(5,7)
|
||||||
* @example // The Cuban cinquillo pattern.
|
* @example // The Cuban cinquillo pattern.
|
||||||
* "c3".euclid(5,8)
|
* note("c3").euclid(5,8)
|
||||||
* @example // A popular Arab rhythm called Agsag-Samai.
|
* @example // A popular Arab rhythm called Agsag-Samai.
|
||||||
* "c3".euclid(5,9)
|
* note("c3").euclid(5,9)
|
||||||
* @example // The metric pattern used by Moussorgsky in Pictures at an Exhibition.
|
* @example // The metric pattern used by Moussorgsky in Pictures at an Exhibition.
|
||||||
* "c3".euclid(5,11)
|
* note("c3").euclid(5,11)
|
||||||
* @example // The Venda clapping pattern of a South African children’s song.
|
* @example // The Venda clapping pattern of a South African children’s song.
|
||||||
* "c3".euclid(5,12)
|
* note("c3").euclid(5,12)
|
||||||
* @example // The Bossa-Nova rhythm necklace of Brazil.
|
* @example // The Bossa-Nova rhythm necklace of Brazil.
|
||||||
* "c3".euclid(5,16)
|
* note("c3").euclid(5,16)
|
||||||
* @example // A typical rhythm played on the Bendir (frame drum).
|
* @example // A typical rhythm played on the Bendir (frame drum).
|
||||||
* "c3".euclid(7,8)
|
* note("c3").euclid(7,8)
|
||||||
* @example // A common West African bell pattern.
|
* @example // A common West African bell pattern.
|
||||||
* "c3".euclid(7,12)
|
* note("c3").euclid(7,12)
|
||||||
* @example // A Samba rhythm necklace from Brazil.
|
* @example // A Samba rhythm necklace from Brazil.
|
||||||
* "c3".euclid(7,16,14)
|
* note("c3").euclid(7,16,14)
|
||||||
* @example // A rhythm necklace used in the Central African Republic.
|
* @example // A rhythm necklace used in the Central African Republic.
|
||||||
* "c3".euclid(9,16)
|
* note("c3").euclid(9,16)
|
||||||
* @example // A rhythm necklace of the Aka Pygmies of Central Africa.
|
* @example // A rhythm necklace of the Aka Pygmies of Central Africa.
|
||||||
* "c3".euclid(11,24,14)
|
* note("c3").euclid(11,24,14)
|
||||||
* @example // Another rhythm necklace of the Aka Pygmies of the upper Sangha.
|
* @example // Another rhythm necklace of the Aka Pygmies of the upper Sangha.
|
||||||
* "c3".euclid(13,24,5)
|
* note("c3").euclid(13,24,5)
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.euclid = function (pulses, steps, rotation = 0) {
|
Pattern.prototype.euclid = function (pulses, steps, rotation = 0) {
|
||||||
return this.struct(euclid(pulses, steps, rotation));
|
return this.struct(euclid(pulses, steps, rotation));
|
||||||
@ -88,7 +88,7 @@ Pattern.prototype.euclid = function (pulses, steps, rotation = 0) {
|
|||||||
* @name euclidLegato
|
* @name euclidLegato
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @example
|
* @example
|
||||||
* n("g2").decay(.1).sustain(.3).euclidLegato(3,8).out()
|
* n("g2").decay(.1).sustain(.3).euclidLegato(3,8)
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.euclidLegato = function (pulses, steps, rotation = 0) {
|
Pattern.prototype.euclidLegato = function (pulses, steps, rotation = 0) {
|
||||||
const bin_pat = euclid(pulses, steps, rotation);
|
const bin_pat = euclid(pulses, steps, rotation);
|
||||||
|
|||||||
@ -472,7 +472,7 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "0.5 1.5 2.5".round().scale('C major')
|
* "0.5 1.5 2.5".round().scale('C major').note()
|
||||||
*/
|
*/
|
||||||
round() {
|
round() {
|
||||||
return this._asNumber().fmap((v) => Math.round(v));
|
return this._asNumber().fmap((v) => Math.round(v));
|
||||||
@ -524,7 +524,7 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd,hh*4").cutoff(sine.range(500,2000).slow(4)).out()
|
* s("bd sd,hh*4").cutoff(sine.range(500,2000).slow(4))
|
||||||
*/
|
*/
|
||||||
_range(min, max) {
|
_range(min, max) {
|
||||||
return this.mul(max - min).add(min);
|
return this.mul(max - min).add(min);
|
||||||
@ -704,7 +704,7 @@ export class Pattern {
|
|||||||
* @name apply
|
* @name apply
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @example
|
* @example
|
||||||
* "<c3 eb3 g3>".scale('C minor').apply(scaleTranspose("0,2,4"))
|
* "<c3 eb3 g3>".scale('C minor').apply(scaleTranspose("0,2,4")).note()
|
||||||
*/
|
*/
|
||||||
_apply(func) {
|
_apply(func) {
|
||||||
return func(this);
|
return func(this);
|
||||||
@ -718,7 +718,7 @@ export class Pattern {
|
|||||||
* @example
|
* @example
|
||||||
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
|
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
|
||||||
* .layer(x=>x.add("0,2"))
|
* .layer(x=>x.add("0,2"))
|
||||||
* .scale('C minor').note().out()
|
* .scale('C minor').note()
|
||||||
*/
|
*/
|
||||||
layer(...funcs) {
|
layer(...funcs) {
|
||||||
return stack(...funcs.map((func) => func(this)));
|
return stack(...funcs.map((func) => func(this)));
|
||||||
@ -756,11 +756,13 @@ export class Pattern {
|
|||||||
const cycle = begin.sam();
|
const cycle = begin.sam();
|
||||||
const beginPos = begin.sub(cycle).div(factor).min(1);
|
const beginPos = begin.sub(cycle).div(factor).min(1);
|
||||||
const endPos = end.sub(cycle).div(factor).min(1);
|
const endPos = end.sub(cycle).div(factor).min(1);
|
||||||
const newPart = new TimeSpan(cycle.add(beginPos), cycle.add(endPos))
|
const newPart = new TimeSpan(cycle.add(beginPos), cycle.add(endPos));
|
||||||
const newWhole = !hap.whole ? undefined : new TimeSpan(
|
const newWhole = !hap.whole
|
||||||
newPart.begin.sub(begin.sub(hap.whole.begin).div(factor)),
|
? undefined
|
||||||
newPart.end.add(hap.whole.end.sub(end).div(factor))
|
: new TimeSpan(
|
||||||
);
|
newPart.begin.sub(begin.sub(hap.whole.begin).div(factor)),
|
||||||
|
newPart.end.add(hap.whole.end.sub(end).div(factor)),
|
||||||
|
);
|
||||||
return new Hap(newWhole, newPart, hap.value, hap.context);
|
return new Hap(newWhole, newPart, hap.value, hap.context);
|
||||||
};
|
};
|
||||||
return this.withQuerySpan(qf)._withHap(ef)._splitQueries();
|
return this.withQuerySpan(qf)._withHap(ef)._splitQueries();
|
||||||
@ -796,7 +798,7 @@ export class Pattern {
|
|||||||
* @param {number | Pattern} factor speed up factor
|
* @param {number | Pattern} factor speed up factor
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("<bd sd> hh").fast(2).out() // s("[<bd sd> hh]*2").out()
|
* s("<bd sd> hh").fast(2) // s("[<bd sd> hh]*2")
|
||||||
*/
|
*/
|
||||||
_fast(factor) {
|
_fast(factor) {
|
||||||
const fastQuery = this.withQueryTime((t) => t.mul(factor));
|
const fastQuery = this.withQueryTime((t) => t.mul(factor));
|
||||||
@ -811,7 +813,7 @@ export class Pattern {
|
|||||||
* @param {number | Pattern} factor slow down factor
|
* @param {number | Pattern} factor slow down factor
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("<bd sd> hh").slow(2).out() // s("[<bd sd> hh]/2").out()
|
* s("<bd sd> hh").slow(2) // s("[<bd sd> hh]/2")
|
||||||
*/
|
*/
|
||||||
_slow(factor) {
|
_slow(factor) {
|
||||||
return this._fast(Fraction(1).div(factor));
|
return this._fast(Fraction(1).div(factor));
|
||||||
@ -841,7 +843,7 @@ export class Pattern {
|
|||||||
* .chop(4)
|
* .chop(4)
|
||||||
* .rev() // reverse order of chops
|
* .rev() // reverse order of chops
|
||||||
* .loopAt(4,1) // fit sample into 4 cycles
|
* .loopAt(4,1) // fit sample into 4 cycles
|
||||||
* .out()
|
*
|
||||||
*/
|
*/
|
||||||
_chop(n) {
|
_chop(n) {
|
||||||
const slices = Array.from({ length: n }, (x, i) => i);
|
const slices = Array.from({ length: n }, (x, i) => i);
|
||||||
@ -872,7 +874,7 @@ export class Pattern {
|
|||||||
* @param {number | Pattern} cycles number of cycles to nudge left
|
* @param {number | Pattern} cycles number of cycles to nudge left
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "bd ~".stack("hh ~".early(.1)).s().out()
|
* "bd ~".stack("hh ~".early(.1)).s()
|
||||||
*/
|
*/
|
||||||
_early(offset) {
|
_early(offset) {
|
||||||
offset = Fraction(offset);
|
offset = Fraction(offset);
|
||||||
@ -887,7 +889,7 @@ export class Pattern {
|
|||||||
* @param {number | Pattern} cycles number of cycles to nudge right
|
* @param {number | Pattern} cycles number of cycles to nudge right
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "bd ~".stack("hh ~".late(.1)).s().out()
|
* "bd ~".stack("hh ~".late(.1)).s()
|
||||||
*/
|
*/
|
||||||
_late(offset) {
|
_late(offset) {
|
||||||
offset = Fraction(offset);
|
offset = Fraction(offset);
|
||||||
@ -923,9 +925,9 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "c3,eb3,g3"
|
* note("c3,eb3,g3")
|
||||||
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
|
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
|
||||||
* .slow(4).note().out()
|
* .slow(4)
|
||||||
*/
|
*/
|
||||||
// struct(...binary_pats) {
|
// struct(...binary_pats) {
|
||||||
// // Re structure the pattern according to a binary pattern (false values are dropped)
|
// // Re structure the pattern according to a binary pattern (false values are dropped)
|
||||||
@ -982,7 +984,7 @@ export class Pattern {
|
|||||||
* @param {function} func
|
* @param {function} func
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "c3 eb3 g3".when("<0 1>/2", x=>x.sub(5))
|
* "c3 eb3 g3".when("<0 1>/2", x=>x.sub(5)).note()
|
||||||
*/
|
*/
|
||||||
when(binary_pat, func) {
|
when(binary_pat, func) {
|
||||||
//binary_pat = sequence(binary_pat)
|
//binary_pat = sequence(binary_pat)
|
||||||
@ -1001,7 +1003,7 @@ export class Pattern {
|
|||||||
* @param {function} func function to apply
|
* @param {function} func function to apply
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "c3 eb3 g3".off(1/8, x=>x.add(7))
|
* "c3 eb3 g3".off(1/8, x=>x.add(7)).note()
|
||||||
*/
|
*/
|
||||||
off(time_pat, func) {
|
off(time_pat, func) {
|
||||||
return stack(this, func(this.late(time_pat)));
|
return stack(this, func(this.late(time_pat)));
|
||||||
@ -1015,7 +1017,7 @@ export class Pattern {
|
|||||||
* @param {function} func function to apply
|
* @param {function} func function to apply
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* note("c3 d3 e3 g3").every(4, x=>x.rev()).out()
|
* note("c3 d3 e3 g3").every(4, x=>x.rev())
|
||||||
*/
|
*/
|
||||||
every(n, func) {
|
every(n, func) {
|
||||||
const pat = this;
|
const pat = this;
|
||||||
@ -1032,7 +1034,7 @@ export class Pattern {
|
|||||||
* @param {function} func function to apply
|
* @param {function} func function to apply
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* note("c3 d3 e3 g3").every(4, x=>x.rev()).out()
|
* note("c3 d3 e3 g3").every(4, x=>x.rev())
|
||||||
*/
|
*/
|
||||||
every(n, func) {
|
every(n, func) {
|
||||||
const pat = this;
|
const pat = this;
|
||||||
@ -1049,7 +1051,7 @@ export class Pattern {
|
|||||||
* @param {function} func function to apply
|
* @param {function} func function to apply
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* note("c3 d3 e3 g3").every(4, x=>x.rev()).out()
|
* note("c3 d3 e3 g3").every(4, x=>x.rev())
|
||||||
*/
|
*/
|
||||||
each(n, func) {
|
each(n, func) {
|
||||||
const pat = this;
|
const pat = this;
|
||||||
@ -1075,7 +1077,7 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "c3 d3 e3 g3".rev()
|
* note("c3 d3 e3 g3").rev()
|
||||||
*/
|
*/
|
||||||
rev() {
|
rev() {
|
||||||
const pat = this;
|
const pat = this;
|
||||||
@ -1126,7 +1128,7 @@ export class Pattern {
|
|||||||
* @example
|
* @example
|
||||||
* s("hh*2").stack(
|
* s("hh*2").stack(
|
||||||
* n("c2(3,8)")
|
* n("c2(3,8)")
|
||||||
* ).out()
|
* )
|
||||||
*/
|
*/
|
||||||
stack(...pats) {
|
stack(...pats) {
|
||||||
return stack(this, ...pats);
|
return stack(this, ...pats);
|
||||||
@ -1143,7 +1145,7 @@ export class Pattern {
|
|||||||
* @example
|
* @example
|
||||||
* s("hh*2").seq(
|
* s("hh*2").seq(
|
||||||
* n("c2(3,8)")
|
* n("c2(3,8)")
|
||||||
* ).out()
|
* )
|
||||||
*/
|
*/
|
||||||
seq(...pats) {
|
seq(...pats) {
|
||||||
return sequence(this, ...pats);
|
return sequence(this, ...pats);
|
||||||
@ -1156,7 +1158,7 @@ export class Pattern {
|
|||||||
* @example
|
* @example
|
||||||
* s("hh*2").cat(
|
* s("hh*2").cat(
|
||||||
* n("c2(3,8)")
|
* n("c2(3,8)")
|
||||||
* ).out()
|
* )
|
||||||
*/
|
*/
|
||||||
cat(...pats) {
|
cat(...pats) {
|
||||||
return cat(this, ...pats);
|
return cat(this, ...pats);
|
||||||
@ -1178,7 +1180,7 @@ export class Pattern {
|
|||||||
* @example
|
* @example
|
||||||
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
|
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
|
||||||
* .superimpose(x=>x.add(2))
|
* .superimpose(x=>x.add(2))
|
||||||
* .scale('C minor').note().out()
|
* .scale('C minor').note()
|
||||||
*/
|
*/
|
||||||
superimpose(...funcs) {
|
superimpose(...funcs) {
|
||||||
return this.stack(...funcs.map((func) => func(this)));
|
return this.stack(...funcs.map((func) => func(this)));
|
||||||
@ -1203,7 +1205,7 @@ export class Pattern {
|
|||||||
* @example
|
* @example
|
||||||
* "<0 [2 4]>"
|
* "<0 [2 4]>"
|
||||||
* .echoWith(4, 1/8, (p,n) => p.add(n*2))
|
* .echoWith(4, 1/8, (p,n) => p.add(n*2))
|
||||||
* .scale('C minor').note().legato(.2).out()
|
* .scale('C minor').note().legato(.2)
|
||||||
*/
|
*/
|
||||||
_echoWith(times, time, func) {
|
_echoWith(times, time, func) {
|
||||||
return stack(...listRange(0, times - 1).map((i) => func(this.late(Fraction(time).mul(i)), i)));
|
return stack(...listRange(0, times - 1).map((i) => func(this.late(Fraction(time).mul(i)), i)));
|
||||||
@ -1218,7 +1220,7 @@ export class Pattern {
|
|||||||
* @param {number} time cycle offset between iterations
|
* @param {number} time cycle offset between iterations
|
||||||
* @param {number} feedback velocity multiplicator for each iteration
|
* @param {number} feedback velocity multiplicator for each iteration
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd").echo(3, 1/6, .8).out()
|
* s("bd sd").echo(3, 1/6, .8)
|
||||||
*/
|
*/
|
||||||
_echo(times, time, feedback) {
|
_echo(times, time, feedback) {
|
||||||
return this._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
|
return this._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
|
||||||
@ -1230,7 +1232,7 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* note("0 1 2 3".scale('A minor')).iter(4).out()
|
* note("0 1 2 3".scale('A minor')).iter(4)
|
||||||
*/
|
*/
|
||||||
iter(times, back = false) {
|
iter(times, back = false) {
|
||||||
return slowcat(...listRange(0, times - 1).map((i) => (back ? this.late(i / times) : this.early(i / times))));
|
return slowcat(...listRange(0, times - 1).map((i) => (back ? this.late(i / times) : this.early(i / times))));
|
||||||
@ -1242,7 +1244,7 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* note("0 1 2 3".scale('A minor')).iterBack(4).out()
|
* note("0 1 2 3".scale('A minor')).iterBack(4)
|
||||||
*/
|
*/
|
||||||
iterBack(times) {
|
iterBack(times) {
|
||||||
return this.iter(times, true);
|
return this.iter(times, true);
|
||||||
@ -1254,7 +1256,7 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "0 1 2 3".chunk(4, x=>x.add(7)).scale('A minor').note().out()
|
* "0 1 2 3".chunk(4, x=>x.add(7)).scale('A minor').note()
|
||||||
*/
|
*/
|
||||||
_chunk(n, func, back = false) {
|
_chunk(n, func, back = false) {
|
||||||
const binary = Array(n - 1).fill(false);
|
const binary = Array(n - 1).fill(false);
|
||||||
@ -1269,7 +1271,7 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "0 1 2 3".chunkBack(4, x=>x.add(7)).scale('A minor').note().out()
|
* "0 1 2 3".chunkBack(4, x=>x.add(7)).scale('A minor').note()
|
||||||
*/
|
*/
|
||||||
_chunkBack(n, func) {
|
_chunkBack(n, func) {
|
||||||
return this._chunk(n, func, true);
|
return this._chunk(n, func, true);
|
||||||
@ -1295,7 +1297,7 @@ export class Pattern {
|
|||||||
* @name legato
|
* @name legato
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @example
|
* @example
|
||||||
* n("c3 eb3 g3 c4").legato("<.25 .5 1 2>").out()
|
* note("c3 eb3 g3 c4").legato("<.25 .5 1 2>")
|
||||||
*/
|
*/
|
||||||
_legato(value) {
|
_legato(value) {
|
||||||
return this.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(span.end.sub(span.begin).mul(value))));
|
return this.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(span.end.sub(span.begin).mul(value))));
|
||||||
@ -1308,7 +1310,7 @@ export class Pattern {
|
|||||||
* @example
|
* @example
|
||||||
* s("hh*8")
|
* s("hh*8")
|
||||||
* .gain(".4!2 1 .4!2 1 .4 1")
|
* .gain(".4!2 1 .4!2 1 .4 1")
|
||||||
* .velocity(".4 1").out()
|
* .velocity(".4 1")
|
||||||
*/
|
*/
|
||||||
_velocity(velocity) {
|
_velocity(velocity) {
|
||||||
return this._withContext((context) => ({ ...context, velocity: (context.velocity || 1) * velocity }));
|
return this._withContext((context) => ({ ...context, velocity: (context.velocity || 1) * velocity }));
|
||||||
@ -1321,7 +1323,7 @@ export class Pattern {
|
|||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' })
|
* samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' })
|
||||||
* s("rhodes").loopAt(4,1).out()
|
* s("rhodes").loopAt(4,1)
|
||||||
*/
|
*/
|
||||||
_loopAt(factor, cps = 1) {
|
_loopAt(factor, cps = 1) {
|
||||||
return this.speed((1 / factor) * cps)
|
return this.speed((1 / factor) * cps)
|
||||||
@ -1385,14 +1387,14 @@ function _composeOp(a, b, func) {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @example
|
* @example
|
||||||
* // Here, the triad 0, 2, 4 is shifted by different amounts
|
* // Here, the triad 0, 2, 4 is shifted by different amounts
|
||||||
* "0 2 4".add("<0 3 4 0>").scale('C major')
|
* "0 2 4".add("<0 3 4 0>").scale('C major').note()
|
||||||
* // Without add, the equivalent would be:
|
* // Without add, the equivalent would be:
|
||||||
* // "<[0 2 4] [3 5 7] [4 6 8] [0 2 4]>".scale('C major')
|
* // "<[0 2 4] [3 5 7] [4 6 8] [0 2 4]>".scale('C major').note()
|
||||||
* @example
|
* @example
|
||||||
* // You can also use add with notes:
|
* // You can also use add with notes:
|
||||||
* "c3 e3 g3".add("<0 5 7 0>")
|
* "c3 e3 g3".add("<0 5 7 0>").note()
|
||||||
* // Behind the scenes, the notes are converted to midi numbers:
|
* // Behind the scenes, the notes are converted to midi numbers:
|
||||||
* // "48 52 55".add("<0 5 7 0>")
|
* // "48 52 55".add("<0 5 7 0>").note()
|
||||||
*/
|
*/
|
||||||
add: [(a, b) => a + b, numOrString], // support string concatenation
|
add: [(a, b) => a + b, numOrString], // support string concatenation
|
||||||
/**
|
/**
|
||||||
@ -1401,7 +1403,7 @@ function _composeOp(a, b, func) {
|
|||||||
* @name sub
|
* @name sub
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @example
|
* @example
|
||||||
* "0 2 4".sub("<0 1 2 3>").scale('C4 minor')
|
* "0 2 4".sub("<0 1 2 3>").scale('C4 minor').note()
|
||||||
* // See add for more information.
|
* // See add for more information.
|
||||||
*/
|
*/
|
||||||
sub: [(a, b) => a - b, num],
|
sub: [(a, b) => a - b, num],
|
||||||
@ -1411,7 +1413,7 @@ function _composeOp(a, b, func) {
|
|||||||
* @name mul
|
* @name mul
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @example
|
* @example
|
||||||
* "1 1.5 [1.66, <2 2.33>]".mul(150).freq().out()
|
* "1 1.5 [1.66, <2 2.33>]".mul(150).freq()
|
||||||
*/
|
*/
|
||||||
mul: [(a, b) => a * b, num],
|
mul: [(a, b) => a * b, num],
|
||||||
/**
|
/**
|
||||||
@ -1577,7 +1579,7 @@ export function reify(thing) {
|
|||||||
*
|
*
|
||||||
* @return {Pattern}
|
* @return {Pattern}
|
||||||
* @example
|
* @example
|
||||||
* stack(g3, b3, [e4, d4]) // "g3,b3,[e4,d4]"
|
* stack(g3, b3, [e4, d4]).note() // "g3,b3,[e4,d4]".note()
|
||||||
*/
|
*/
|
||||||
export function stack(...pats) {
|
export function stack(...pats) {
|
||||||
// Array test here is to avoid infinite recursions..
|
// Array test here is to avoid infinite recursions..
|
||||||
@ -1650,7 +1652,7 @@ export function fastcat(...pats) {
|
|||||||
* @param {...any} items - The items to concatenate
|
* @param {...any} items - The items to concatenate
|
||||||
* @return {Pattern}
|
* @return {Pattern}
|
||||||
* @example
|
* @example
|
||||||
* cat(e5, b4, [d5, c5]) // "<e5 b4 [d5 c5]>"
|
* cat(e5, b4, [d5, c5]).note() // "<e5 b4 [d5 c5]>".note()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export function cat(...pats) {
|
export function cat(...pats) {
|
||||||
@ -1660,7 +1662,7 @@ export function cat(...pats) {
|
|||||||
/** Like {@link seq}, but each step has a length, relative to the whole.
|
/** Like {@link seq}, but each step has a length, relative to the whole.
|
||||||
* @return {Pattern}
|
* @return {Pattern}
|
||||||
* @example
|
* @example
|
||||||
* timeCat([3,e3],[1, g3]) // "e3@3 g3"
|
* timeCat([3,e3],[1, g3]).note() // "e3@3 g3".note()
|
||||||
*/
|
*/
|
||||||
export function timeCat(...timepats) {
|
export function timeCat(...timepats) {
|
||||||
const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0));
|
const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0));
|
||||||
@ -1681,7 +1683,7 @@ export function sequence(...pats) {
|
|||||||
|
|
||||||
/** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence
|
/** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence
|
||||||
* @example
|
* @example
|
||||||
* seq(e5, b4, [d5, c5]) // "e5 b4 [d5 c5]"
|
* seq(e5, b4, [d5, c5]).note() // "e5 b4 [d5 c5]".note()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export function seq(...pats) {
|
export function seq(...pats) {
|
||||||
|
|||||||
@ -27,9 +27,9 @@ export const isaw2 = isaw._toBipolar();
|
|||||||
*
|
*
|
||||||
* @return {Pattern}
|
* @return {Pattern}
|
||||||
* @example
|
* @example
|
||||||
* "c3 [eb3,g3] g2 [g3,bb3]".legato(saw.slow(4))
|
* "c3 [eb3,g3] g2 [g3,bb3]".legato(saw.slow(4)).note()
|
||||||
* @example
|
* @example
|
||||||
* saw.range(0,8).segment(8).scale('C major').slow(4)
|
* saw.range(0,8).segment(8).scale('C major').slow(4).note()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const saw = signal((t) => t % 1);
|
export const saw = signal((t) => t % 1);
|
||||||
@ -42,7 +42,7 @@ export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));
|
|||||||
*
|
*
|
||||||
* @return {Pattern}
|
* @return {Pattern}
|
||||||
* @example
|
* @example
|
||||||
* sine.segment(16).range(0,15).slow(2).scale('C minor')
|
* sine.segment(16).range(0,15).slow(2).scale('C minor').note()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const sine = sine2._fromBipolar();
|
export const sine = sine2._fromBipolar();
|
||||||
@ -52,7 +52,7 @@ export const sine = sine2._fromBipolar();
|
|||||||
*
|
*
|
||||||
* @return {Pattern}
|
* @return {Pattern}
|
||||||
* @example
|
* @example
|
||||||
* stack(sine,cosine).segment(16).range(0,15).slow(2).scale('C minor')
|
* stack(sine,cosine).segment(16).range(0,15).slow(2).scale('C minor').note()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const cosine = sine._early(Fraction(1).div(4));
|
export const cosine = sine._early(Fraction(1).div(4));
|
||||||
@ -63,7 +63,7 @@ export const cosine2 = sine2._early(Fraction(1).div(4));
|
|||||||
*
|
*
|
||||||
* @return {Pattern}
|
* @return {Pattern}
|
||||||
* @example
|
* @example
|
||||||
* square.segment(2).range(0,7).scale('C minor')
|
* square.segment(2).range(0,7).scale('C minor').note()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const square = signal((t) => Math.floor((t * 2) % 2));
|
export const square = signal((t) => Math.floor((t * 2) % 2));
|
||||||
@ -74,7 +74,7 @@ export const square2 = square._toBipolar();
|
|||||||
*
|
*
|
||||||
* @return {Pattern}
|
* @return {Pattern}
|
||||||
* @example
|
* @example
|
||||||
* tri.segment(8).range(0,7).scale('C minor')
|
* tri.segment(8).range(0,7).scale('C minor').note()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const tri = fastcat(isaw, saw);
|
export const tri = fastcat(isaw, saw);
|
||||||
@ -120,7 +120,7 @@ const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n);
|
|||||||
* @name rand
|
* @name rand
|
||||||
* @example
|
* @example
|
||||||
* // randomly change the cutoff
|
* // randomly change the cutoff
|
||||||
* s("bd sd,hh*4").cutoff(rand.range(500,2000)).out()
|
* s("bd sd,hh*4").cutoff(rand.range(500,2000))
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const rand = signal(timeToRand);
|
export const rand = signal(timeToRand);
|
||||||
@ -142,7 +142,7 @@ export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i));
|
|||||||
* @param {number} n max value (exclusive)
|
* @param {number} n max value (exclusive)
|
||||||
* @example
|
* @example
|
||||||
* // randomly select scale notes from 0 - 7 (= C to C)
|
* // randomly select scale notes from 0 - 7 (= C to C)
|
||||||
* irand(8).struct("x(3,8)").scale('C minor').note().out()
|
* irand(8).struct("x(3,8)").scale('C minor').note()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin();
|
export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin();
|
||||||
@ -208,9 +208,9 @@ Pattern.prototype.choose2 = function (...xs) {
|
|||||||
* Picks one of the elements at random each cycle.
|
* Picks one of the elements at random each cycle.
|
||||||
* @returns {Pattern}
|
* @returns {Pattern}
|
||||||
* @example
|
* @example
|
||||||
* chooseCycles("bd", "hh", "sd").s().fast(4).out()
|
* chooseCycles("bd", "hh", "sd").s().fast(4)
|
||||||
* @example
|
* @example
|
||||||
* "bd | hh | sd".s().fast(4).out()
|
* "bd | hh | sd".s().fast(4)
|
||||||
*/
|
*/
|
||||||
export const chooseCycles = (...xs) => chooseInWith(rand.segment(1), xs);
|
export const chooseCycles = (...xs) => chooseInWith(rand.segment(1), xs);
|
||||||
|
|
||||||
@ -252,7 +252,7 @@ export const perlinWith = (pat) => {
|
|||||||
* @name perlin
|
* @name perlin
|
||||||
* @example
|
* @example
|
||||||
* // randomly change the cutoff
|
* // randomly change the cutoff
|
||||||
* s("bd sd,hh*4").cutoff(perlin.range(500,2000)).out()
|
* s("bd sd,hh*4").cutoff(perlin.range(500,2000))
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const perlin = perlinWith(time);
|
export const perlin = perlinWith(time);
|
||||||
@ -271,9 +271,9 @@ Pattern.prototype._degradeByWith = function (withPat, x) {
|
|||||||
* @param {number} amount - a number between 0 and 1
|
* @param {number} amount - a number between 0 and 1
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").degradeBy(0.2).out()
|
* s("hh*8").degradeBy(0.2)
|
||||||
* @example
|
* @example
|
||||||
* s("[hh?0.2]*8").out()
|
* s("[hh?0.2]*8")
|
||||||
*/
|
*/
|
||||||
Pattern.prototype._degradeBy = function (x) {
|
Pattern.prototype._degradeBy = function (x) {
|
||||||
return this._degradeByWith(rand, x);
|
return this._degradeByWith(rand, x);
|
||||||
@ -287,9 +287,9 @@ Pattern.prototype._degradeBy = function (x) {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").degrade().out()
|
* s("hh*8").degrade()
|
||||||
* @example
|
* @example
|
||||||
* s("[hh?]*8").out()
|
* s("[hh?]*8")
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.degrade = function () {
|
Pattern.prototype.degrade = function () {
|
||||||
return this._degradeBy(0.5);
|
return this._degradeBy(0.5);
|
||||||
@ -306,7 +306,7 @@ Pattern.prototype.degrade = function () {
|
|||||||
* @param {number} amount - a number between 0 and 1
|
* @param {number} amount - a number between 0 and 1
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").undegradeBy(0.2).out()
|
* s("hh*8").undegradeBy(0.2)
|
||||||
*/
|
*/
|
||||||
Pattern.prototype._undegradeBy = function (x) {
|
Pattern.prototype._undegradeBy = function (x) {
|
||||||
return this._degradeByWith(
|
return this._degradeByWith(
|
||||||
@ -340,7 +340,7 @@ Pattern.prototype._sometimesBy = function (x, func) {
|
|||||||
* @param {function} function - the transformation to apply
|
* @param {function} function - the transformation to apply
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh(3,8)").sometimesBy(.4, x=>x.speed("0.5")).out()
|
* s("hh(3,8)").sometimesBy(.4, x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.sometimesBy = function (patx, func) {
|
Pattern.prototype.sometimesBy = function (patx, func) {
|
||||||
const pat = this;
|
const pat = this;
|
||||||
@ -370,7 +370,7 @@ Pattern.prototype.sometimesByPre = function (patx, func) {
|
|||||||
* @param {function} function - the transformation to apply
|
* @param {function} function - the transformation to apply
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*4").sometimes(x=>x.speed("0.5")).out()
|
* s("hh*4").sometimes(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.sometimes = function (func) {
|
Pattern.prototype.sometimes = function (func) {
|
||||||
return this._sometimesBy(0.5, func);
|
return this._sometimesBy(0.5, func);
|
||||||
@ -398,7 +398,7 @@ Pattern.prototype._someCyclesBy = function (x, func) {
|
|||||||
* @param {function} function - the transformation to apply
|
* @param {function} function - the transformation to apply
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh(3,8)").someCyclesBy(.3, x=>x.speed("0.5")).out()
|
* s("hh(3,8)").someCyclesBy(.3, x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.someCyclesBy = function (patx, func) {
|
Pattern.prototype.someCyclesBy = function (patx, func) {
|
||||||
const pat = this;
|
const pat = this;
|
||||||
@ -415,7 +415,7 @@ Pattern.prototype.someCyclesBy = function (patx, func) {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh(3,8)").someCycles(x=>x.speed("0.5")).out()
|
* s("hh(3,8)").someCycles(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.someCycles = function (func) {
|
Pattern.prototype.someCycles = function (func) {
|
||||||
return this._someCyclesBy(0.5, func);
|
return this._someCyclesBy(0.5, func);
|
||||||
@ -429,7 +429,7 @@ Pattern.prototype.someCycles = function (func) {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").often(x=>x.speed("0.5")).out()
|
* s("hh*8").often(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.often = function (func) {
|
Pattern.prototype.often = function (func) {
|
||||||
return this.sometimesBy(0.75, func);
|
return this.sometimesBy(0.75, func);
|
||||||
@ -443,7 +443,7 @@ Pattern.prototype.often = function (func) {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").rarely(x=>x.speed("0.5")).out()
|
* s("hh*8").rarely(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.rarely = function (func) {
|
Pattern.prototype.rarely = function (func) {
|
||||||
return this.sometimesBy(0.25, func);
|
return this.sometimesBy(0.25, func);
|
||||||
@ -457,7 +457,7 @@ Pattern.prototype.rarely = function (func) {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").almostNever(x=>x.speed("0.5")).out()
|
* s("hh*8").almostNever(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.almostNever = function (func) {
|
Pattern.prototype.almostNever = function (func) {
|
||||||
return this.sometimesBy(0.1, func);
|
return this.sometimesBy(0.1, func);
|
||||||
@ -471,7 +471,7 @@ Pattern.prototype.almostNever = function (func) {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").almostAlways(x=>x.speed("0.5")).out()
|
* s("hh*8").almostAlways(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.almostAlways = function (func) {
|
Pattern.prototype.almostAlways = function (func) {
|
||||||
return this.sometimesBy(0.9, func);
|
return this.sometimesBy(0.9, func);
|
||||||
@ -485,7 +485,7 @@ Pattern.prototype.almostAlways = function (func) {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").never(x=>x.speed("0.5")).out()
|
* s("hh*8").never(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.never = function (func) {
|
Pattern.prototype.never = function (func) {
|
||||||
return this;
|
return this;
|
||||||
@ -499,7 +499,7 @@ Pattern.prototype.never = function (func) {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8").always(x=>x.speed("0.5")).out()
|
* s("hh*8").always(x=>x.speed("0.5"))
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.always = function (func) {
|
Pattern.prototype.always = function (func) {
|
||||||
return func(this);
|
return func(this);
|
||||||
|
|||||||
@ -18,7 +18,6 @@ Either install with `npm i @strudel.cycles/embed` or just use a cdn to import th
|
|||||||
.legato(sine.range(0.3, 2).slow(28))
|
.legato(sine.range(0.3, 2).slow(28))
|
||||||
.wave("sawtooth square".fast(2))
|
.wave("sawtooth square".fast(2))
|
||||||
.filter('lowpass', cosine.range(500,4000).slow(16))
|
.filter('lowpass', cosine.range(500,4000).slow(16))
|
||||||
.out()
|
|
||||||
.pianoroll({minMidi:20,maxMidi:120,background:'#202124'})
|
.pianoroll({minMidi:20,maxMidi:120,background:'#202124'})
|
||||||
-->
|
-->
|
||||||
</strudel-repl>
|
</strudel-repl>
|
||||||
|
|||||||
@ -10,7 +10,6 @@
|
|||||||
.legato(sine.range(0.3, 2).slow(28))
|
.legato(sine.range(0.3, 2).slow(28))
|
||||||
.wave("sawtooth square".fast(2))
|
.wave("sawtooth square".fast(2))
|
||||||
.filter('lowpass', cosine.range(500,4000).slow(16))
|
.filter('lowpass', cosine.range(500,4000).slow(16))
|
||||||
.out()
|
|
||||||
.pianoroll({minMidi:20,maxMidi:120,background:'#202124'})
|
.pianoroll({minMidi:20,maxMidi:120,background:'#202124'})
|
||||||
-->
|
-->
|
||||||
</strudel-repl>
|
</strudel-repl>
|
||||||
|
|||||||
4
packages/react/dist/index.cjs.js
vendored
4
packages/react/dist/index.cjs.js
vendored
File diff suppressed because one or more lines are too long
24
packages/react/dist/index.es.js
vendored
24
packages/react/dist/index.es.js
vendored
@ -7,9 +7,9 @@ import { tags } from '@lezer/highlight';
|
|||||||
import { createTheme } from '@uiw/codemirror-themes';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
import { useInView } from 'react-hook-inview';
|
import { useInView } from 'react-hook-inview';
|
||||||
import { evaluate } from '@strudel.cycles/eval';
|
import { evaluate } from '@strudel.cycles/eval';
|
||||||
import { getPlayableNoteValue } from '@strudel.cycles/core/util.mjs';
|
|
||||||
import { Tone } from '@strudel.cycles/tone';
|
import { Tone } from '@strudel.cycles/tone';
|
||||||
import { TimeSpan, State } from '@strudel.cycles/core';
|
import { TimeSpan, State } from '@strudel.cycles/core';
|
||||||
|
import { webaudioOutputTrigger } from '@strudel.cycles/webaudio';
|
||||||
import { WebMidi, enableWebMidi } from '@strudel.cycles/midi';
|
import { WebMidi, enableWebMidi } from '@strudel.cycles/midi';
|
||||||
|
|
||||||
var strudelTheme = createTheme({
|
var strudelTheme = createTheme({
|
||||||
@ -251,7 +251,7 @@ let s4 = () => {
|
|||||||
};
|
};
|
||||||
const generateHash = (code) => encodeURIComponent(btoa(code));
|
const generateHash = (code) => encodeURIComponent(btoa(code));
|
||||||
|
|
||||||
function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawProp }) {
|
function useRepl({ tune, autolink = true, onEvent, onDraw: onDrawProp }) {
|
||||||
const id = useMemo(() => s4(), []);
|
const id = useMemo(() => s4(), []);
|
||||||
const [code, setCode] = useState(tune);
|
const [code, setCode] = useState(tune);
|
||||||
const [activeCode, setActiveCode] = useState();
|
const [activeCode, setActiveCode] = useState();
|
||||||
@ -282,26 +282,15 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawP
|
|||||||
if (event.context.logs?.length) {
|
if (event.context.logs?.length) {
|
||||||
event.context.logs.forEach(pushLog);
|
event.context.logs.forEach(pushLog);
|
||||||
}
|
}
|
||||||
const { onTrigger, velocity } = event.context;
|
const { onTrigger = webaudioOutputTrigger } = event.context;
|
||||||
if (!onTrigger) {
|
onTrigger(time, event, currentTime, 1 /* cps */);
|
||||||
if (defaultSynth) {
|
|
||||||
const note = getPlayableNoteValue(event);
|
|
||||||
defaultSynth.triggerAttackRelease(note, event.duration.valueOf(), time, velocity);
|
|
||||||
} else {
|
|
||||||
throw new Error('no defaultSynth passed to useRepl.');
|
|
||||||
}
|
|
||||||
/* console.warn('no instrument chosen', event);
|
|
||||||
throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */
|
|
||||||
} else {
|
|
||||||
onTrigger(time, event, currentTime, 1 /* cps */);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
err.message = 'unplayable event: ' + err?.message;
|
err.message = 'unplayable event: ' + err?.message;
|
||||||
pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event
|
pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onEvent, pushLog, defaultSynth],
|
[onEvent, pushLog],
|
||||||
),
|
),
|
||||||
onQuery: useCallback(
|
onQuery: useCallback(
|
||||||
(state) => {
|
(state) => {
|
||||||
@ -484,10 +473,9 @@ function Icon({ type }) {
|
|||||||
}[type]);
|
}[type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function MiniRepl({ tune, defaultSynth, hideOutsideView = false, theme, init, onEvent, enableKeyboard }) {
|
function MiniRepl({ tune, hideOutsideView = false, init, onEvent, enableKeyboard }) {
|
||||||
const { code, setCode, pattern, activeCode, activateCode, evaluateOnly, error, cycle, dirty, togglePlay, stop } = useRepl({
|
const { code, setCode, pattern, activeCode, activateCode, evaluateOnly, error, cycle, dirty, togglePlay, stop } = useRepl({
|
||||||
tune,
|
tune,
|
||||||
defaultSynth,
|
|
||||||
autolink: false,
|
autolink: false,
|
||||||
onEvent
|
onEvent
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MiniRepl } from './components/MiniRepl';
|
import { MiniRepl } from './components/MiniRepl';
|
||||||
import 'tailwindcss/tailwind.css';
|
import 'tailwindcss/tailwind.css';
|
||||||
import { Tone, getDefaultSynth } from '@strudel.cycles/tone';
|
|
||||||
import { evalScope } from '@strudel.cycles/eval';
|
import { evalScope } from '@strudel.cycles/eval';
|
||||||
|
|
||||||
const defaultSynth = getDefaultSynth();
|
|
||||||
|
|
||||||
evalScope(
|
evalScope(
|
||||||
Tone,
|
|
||||||
import('@strudel.cycles/core'),
|
import('@strudel.cycles/core'),
|
||||||
import('@strudel.cycles/tone'),
|
import('@strudel.cycles/tone'),
|
||||||
import('@strudel.cycles/tonal'),
|
import('@strudel.cycles/tonal'),
|
||||||
@ -20,7 +16,7 @@ evalScope(
|
|||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<MiniRepl tune={`"c3"`} defaultSynth={defaultSynth} />
|
<MiniRepl tune={`"c3"`} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,11 +9,10 @@ import './style.css';
|
|||||||
import styles from './MiniRepl.module.css';
|
import styles from './MiniRepl.module.css';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
|
|
||||||
export function MiniRepl({ tune, defaultSynth, hideOutsideView = false, theme, init, onEvent, enableKeyboard }) {
|
export function MiniRepl({ tune, hideOutsideView = false, init, onEvent, enableKeyboard }) {
|
||||||
const { code, setCode, pattern, activeCode, activateCode, evaluateOnly, error, cycle, dirty, togglePlay, stop } =
|
const { code, setCode, pattern, activeCode, activateCode, evaluateOnly, error, cycle, dirty, togglePlay, stop } =
|
||||||
useRepl({
|
useRepl({
|
||||||
tune,
|
tune,
|
||||||
defaultSynth,
|
|
||||||
autolink: false,
|
autolink: false,
|
||||||
onEvent,
|
onEvent,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,9 +6,9 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
|
|
||||||
import { useCallback, useState, useMemo } from 'react';
|
import { useCallback, useState, useMemo } from 'react';
|
||||||
import { evaluate } from '@strudel.cycles/eval';
|
import { evaluate } from '@strudel.cycles/eval';
|
||||||
import { getPlayableNoteValue } from '@strudel.cycles/core/util.mjs';
|
|
||||||
import useCycle from './useCycle.mjs';
|
import useCycle from './useCycle.mjs';
|
||||||
import usePostMessage from './usePostMessage.mjs';
|
import usePostMessage from './usePostMessage.mjs';
|
||||||
|
import { webaudioOutputTrigger } from '@strudel.cycles/webaudio';
|
||||||
|
|
||||||
let s4 = () => {
|
let s4 = () => {
|
||||||
return Math.floor((1 + Math.random()) * 0x10000)
|
return Math.floor((1 + Math.random()) * 0x10000)
|
||||||
@ -17,7 +17,7 @@ let s4 = () => {
|
|||||||
};
|
};
|
||||||
const generateHash = (code) => encodeURIComponent(btoa(code));
|
const generateHash = (code) => encodeURIComponent(btoa(code));
|
||||||
|
|
||||||
function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawProp }) {
|
function useRepl({ tune, autolink = true, onEvent, onDraw: onDrawProp }) {
|
||||||
const id = useMemo(() => s4(), []);
|
const id = useMemo(() => s4(), []);
|
||||||
const [code, setCode] = useState(tune);
|
const [code, setCode] = useState(tune);
|
||||||
const [activeCode, setActiveCode] = useState();
|
const [activeCode, setActiveCode] = useState();
|
||||||
@ -48,26 +48,15 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawP
|
|||||||
if (event.context.logs?.length) {
|
if (event.context.logs?.length) {
|
||||||
event.context.logs.forEach(pushLog);
|
event.context.logs.forEach(pushLog);
|
||||||
}
|
}
|
||||||
const { onTrigger, velocity } = event.context;
|
const { onTrigger = webaudioOutputTrigger } = event.context;
|
||||||
if (!onTrigger) {
|
onTrigger(time, event, currentTime, 1 /* cps */);
|
||||||
if (defaultSynth) {
|
|
||||||
const note = getPlayableNoteValue(event);
|
|
||||||
defaultSynth.triggerAttackRelease(note, event.duration.valueOf(), time, velocity);
|
|
||||||
} else {
|
|
||||||
throw new Error('no defaultSynth passed to useRepl.');
|
|
||||||
}
|
|
||||||
/* console.warn('no instrument chosen', event);
|
|
||||||
throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */
|
|
||||||
} else {
|
|
||||||
onTrigger(time, event, currentTime, 1 /* cps */);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
err.message = 'unplayable event: ' + err?.message;
|
err.message = 'unplayable event: ' + err?.message;
|
||||||
pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event
|
pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onEvent, pushLog, defaultSynth],
|
[onEvent, pushLog],
|
||||||
),
|
),
|
||||||
onQuery: useCallback(
|
onQuery: useCallback(
|
||||||
(state) => {
|
(state) => {
|
||||||
|
|||||||
@ -69,9 +69,9 @@ function scaleOffset(scale, offset, note) {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @name transpose
|
* @name transpose
|
||||||
* @example
|
* @example
|
||||||
* "c2 c3".fast(2).transpose("<0 -2 5 3>".slow(2)).transpose(0)
|
* "c2 c3".fast(2).transpose("<0 -2 5 3>".slow(2)).note()
|
||||||
* @example
|
* @example
|
||||||
* "c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).transpose(0)
|
* "c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype._transpose = function (intervalOrSemitones) {
|
Pattern.prototype._transpose = function (intervalOrSemitones) {
|
||||||
@ -107,6 +107,7 @@ Pattern.prototype._transpose = function (intervalOrSemitones) {
|
|||||||
* "-8 [2,4,6]"
|
* "-8 [2,4,6]"
|
||||||
* .scale('C4 bebop major')
|
* .scale('C4 bebop major')
|
||||||
* .scaleTranspose("<0 -1 -2 -3 -4 -5 -6 -4>")
|
* .scaleTranspose("<0 -1 -2 -3 -4 -5 -6 -4>")
|
||||||
|
* .note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
|
Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
|
||||||
@ -134,9 +135,10 @@ Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
|
|||||||
* @name scale
|
* @name scale
|
||||||
* @param {string} scale Name of scale
|
* @param {string} scale Name of scale
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "0 2 4 6 4 2"
|
* "0 2 4 6 4 2"
|
||||||
* .scale(seq('C2 major', 'C2 minor').slow(2))
|
* .scale(seq('C2 major', 'C2 minor').slow(2))
|
||||||
|
* .note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype._scale = function (scale /* : string */) {
|
Pattern.prototype._scale = function (scale /* : string */) {
|
||||||
|
|||||||
@ -40,7 +40,7 @@ Pattern.prototype.fmapNested = function (func) {
|
|||||||
* @param {range} range note range for possible voicings (optional, defaults to `['F3', 'A4']`)
|
* @param {range} range note range for possible voicings (optional, defaults to `['F3', 'A4']`)
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>")
|
* stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>").note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype.voicings = function (range) {
|
Pattern.prototype.voicings = function (range) {
|
||||||
|
|||||||
@ -15,13 +15,13 @@ npm i @strudel.cycles/webaudio --save
|
|||||||
import { Scheduler, getAudioContext } from '@strudel.cycles/webaudio';
|
import { Scheduler, getAudioContext } from '@strudel.cycles/webaudio';
|
||||||
|
|
||||||
const scheduler = new Scheduler({
|
const scheduler = new Scheduler({
|
||||||
audioContext: getAudioContext(),
|
audioContext: getAudioContext(),
|
||||||
interval: 0.1,
|
interval: 0.1,
|
||||||
onEvent: (e) => e.context?.createAudioNode?.(e),
|
onEvent: (e) => e.context?.createAudioNode?.(e),
|
||||||
});
|
});
|
||||||
const pattern = sequence([55, 99], 110).osc('sawtooth').out()
|
const pattern = sequence([55, 99], 110).osc('sawtooth');
|
||||||
scheduler.setPattern(pattern);
|
scheduler.setPattern(pattern);
|
||||||
scheduler.start()
|
scheduler.start();
|
||||||
//scheduler.stop()
|
//scheduler.stop()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -98,7 +98,7 @@ export const loadGithubSamples = async (path, nameFn) => {
|
|||||||
* bd: '808bd/BD0000.WAV',
|
* bd: '808bd/BD0000.WAV',
|
||||||
* sd: '808sd/SD0010.WAV'
|
* sd: '808sd/SD0010.WAV'
|
||||||
* }, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
|
* }, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
|
||||||
* s("[bd ~]*2, [~ hh]*2, ~ sd").out()
|
* s("[bd ~]*2, [~ hh]*2, ~ sd")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
|
|
||||||
// import { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core';
|
// import { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core';
|
||||||
import * as strudel from '@strudel.cycles/core';
|
import * as strudel from '@strudel.cycles/core';
|
||||||
import { fromMidi, toMidi } from '@strudel.cycles/core';
|
import { fromMidi, isNote, toMidi } from '@strudel.cycles/core';
|
||||||
import './feedbackdelay.mjs';
|
import './feedbackdelay.mjs';
|
||||||
import './reverb.mjs';
|
import './reverb.mjs';
|
||||||
import { loadBuffer, reverseBuffer } from './sampler.mjs';
|
import { loadBuffer, reverseBuffer } from './sampler.mjs';
|
||||||
@ -115,7 +115,11 @@ const getSampleBufferSource = async (s, n, note, speed) => {
|
|||||||
}
|
}
|
||||||
const bank = samples?.[s];
|
const bank = samples?.[s];
|
||||||
if (!bank) {
|
if (!bank) {
|
||||||
throw new Error(`sample not found: "${s}", try one of ${Object.keys(samples).join(', ')}`);
|
throw new Error(
|
||||||
|
`sample not found: "${s}", try one of ${Object.keys(samples)
|
||||||
|
.map((s) => `"${s}"`)
|
||||||
|
.join(', ')}.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof bank !== 'object') {
|
if (typeof bank !== 'object') {
|
||||||
throw new Error('wrong format for sample bank:', s);
|
throw new Error('wrong format for sample bank:', s);
|
||||||
@ -234,6 +238,10 @@ function effectSend(input, effect, wet) {
|
|||||||
export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
||||||
try {
|
try {
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
|
/* if (isNote(hap.value)) {
|
||||||
|
// supports primitive hap values that look like notes
|
||||||
|
hap.value = { note: hap.value };
|
||||||
|
} */
|
||||||
if (typeof hap.value !== 'object') {
|
if (typeof hap.value !== 'object') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`hap.value ${hap.value} is not supported by webaudio output. Hint: append .note() or .s() to the end`,
|
`hap.value ${hap.value} is not supported by webaudio output. Hint: append .note() or .s() to the end`,
|
||||||
@ -249,7 +257,7 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
|||||||
clip = 0, // if 1, samples will be cut off when the hap ends
|
clip = 0, // if 1, samples will be cut off when the hap ends
|
||||||
n = 0,
|
n = 0,
|
||||||
note,
|
note,
|
||||||
gain = 1,
|
gain = 0.8,
|
||||||
cutoff,
|
cutoff,
|
||||||
resonance = 1,
|
resonance = 1,
|
||||||
hcutoff,
|
hcutoff,
|
||||||
@ -260,10 +268,6 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
|||||||
crush,
|
crush,
|
||||||
shape,
|
shape,
|
||||||
pan,
|
pan,
|
||||||
attack = 0.001,
|
|
||||||
decay = 0.001,
|
|
||||||
sustain = 1,
|
|
||||||
release = 0.001,
|
|
||||||
speed = 1, // sample playback speed
|
speed = 1, // sample playback speed
|
||||||
begin = 0,
|
begin = 0,
|
||||||
end = 1,
|
end = 1,
|
||||||
@ -291,6 +295,8 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
|||||||
[note, n] = splitSN(note, n);
|
[note, n] = splitSN(note, n);
|
||||||
}
|
}
|
||||||
if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) {
|
if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) {
|
||||||
|
// destructure adsr here, because the default should be different for synths and samples
|
||||||
|
const { attack = 0.001, decay = 0.05, sustain = 0.6, release = 0.01 } = hap.value;
|
||||||
// with synths, n and note are the same thing
|
// with synths, n and note are the same thing
|
||||||
n = note || n || 36;
|
n = note || n || 36;
|
||||||
if (typeof n === 'string') {
|
if (typeof n === 'string') {
|
||||||
@ -310,6 +316,8 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
|||||||
const adsr = getADSR(attack, decay, sustain, release, 1, t, t + hapDuration);
|
const adsr = getADSR(attack, decay, sustain, release, 1, t, t + hapDuration);
|
||||||
chain.push(adsr);
|
chain.push(adsr);
|
||||||
} else {
|
} else {
|
||||||
|
// destructure adsr here, because the default should be different for synths and samples
|
||||||
|
const { attack = 0.001, decay = 0.001, sustain = 1, release = 0.001 } = hap.value;
|
||||||
// load sample
|
// load sample
|
||||||
if (speed === 0) {
|
if (speed === 0) {
|
||||||
// no playback
|
// no playback
|
||||||
@ -422,7 +430,9 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const webaudioOutputTrigger = (t, hap, ct, cps) => webaudioOutput(hap, t - ct, hap.duration / cps);
|
||||||
|
|
||||||
Pattern.prototype.out = function () {
|
Pattern.prototype.out = function () {
|
||||||
// TODO: refactor (t, hap, ct, cps) to (hap, deadline, duration) ?
|
// TODO: refactor (t, hap, ct, cps) to (hap, deadline, duration) ?
|
||||||
return this.onTrigger((t, hap, ct, cps) => webaudioOutput(hap, t - ct, hap.duration / cps));
|
return this.onTrigger(webaudioOutputTrigger);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,14 +7,13 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
import controls from '@strudel.cycles/core/controls.mjs';
|
import controls from '@strudel.cycles/core/controls.mjs';
|
||||||
import { evalScope, evaluate } from '@strudel.cycles/eval';
|
import { evalScope, evaluate } from '@strudel.cycles/eval';
|
||||||
import { CodeMirror, cx, flash, useHighlighting, useRepl, useWebMidi } from '@strudel.cycles/react';
|
import { CodeMirror, cx, flash, useHighlighting, useRepl, useWebMidi } from '@strudel.cycles/react';
|
||||||
import { getDefaultSynth, cleanupDraw, cleanupUi, Tone } from '@strudel.cycles/tone';
|
import { cleanupDraw, cleanupUi, Tone } from '@strudel.cycles/tone';
|
||||||
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import logo from './logo.svg';
|
import logo from './logo.svg';
|
||||||
import * as tunes from './tunes.mjs';
|
import * as tunes from './tunes.mjs';
|
||||||
import { prebake } from './prebake.mjs';
|
import { prebake } from './prebake.mjs';
|
||||||
import * as WebDirt from 'WebDirt';
|
import * as WebDirt from 'WebDirt';
|
||||||
import { loadWebDirt } from '@strudel.cycles/webdirt';
|
|
||||||
import { resetLoadedSamples, getAudioContext } from '@strudel.cycles/webaudio';
|
import { resetLoadedSamples, getAudioContext } from '@strudel.cycles/webaudio';
|
||||||
import { createClient } from '@supabase/supabase-js';
|
import { createClient } from '@supabase/supabase-js';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
@ -37,16 +36,10 @@ evalScope(
|
|||||||
import('@strudel.cycles/xen'),
|
import('@strudel.cycles/xen'),
|
||||||
import('@strudel.cycles/webaudio'),
|
import('@strudel.cycles/webaudio'),
|
||||||
import('@strudel.cycles/osc'),
|
import('@strudel.cycles/osc'),
|
||||||
import('@strudel.cycles/webdirt'),
|
|
||||||
import('@strudel.cycles/serial'),
|
import('@strudel.cycles/serial'),
|
||||||
import('@strudel.cycles/soundfonts'),
|
import('@strudel.cycles/soundfonts'),
|
||||||
);
|
);
|
||||||
|
|
||||||
loadWebDirt({
|
|
||||||
sampleMapUrl: 'EmuSP12.json',
|
|
||||||
sampleFolder: 'EmuSP12',
|
|
||||||
});
|
|
||||||
|
|
||||||
prebake();
|
prebake();
|
||||||
|
|
||||||
async function initCode() {
|
async function initCode() {
|
||||||
@ -87,7 +80,6 @@ function getRandomTune() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const randomTune = getRandomTune();
|
const randomTune = getRandomTune();
|
||||||
const defaultSynth = getDefaultSynth();
|
|
||||||
const isEmbedded = window.location !== window.parent.location;
|
const isEmbedded = window.location !== window.parent.location;
|
||||||
function App() {
|
function App() {
|
||||||
// const [editor, setEditor] = useState();
|
// const [editor, setEditor] = useState();
|
||||||
@ -112,7 +104,6 @@ function App() {
|
|||||||
hideConsole,
|
hideConsole,
|
||||||
} = useRepl({
|
} = useRepl({
|
||||||
tune: '// LOADING...',
|
tune: '// LOADING...',
|
||||||
defaultSynth,
|
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initCode().then((decoded) => setCode(decoded || randomTune));
|
initCode().then((decoded) => setCode(decoded || randomTune));
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Pattern, toMidi } from '@strudel.cycles/core';
|
import { Pattern, toMidi } from '@strudel.cycles/core';
|
||||||
import { samples } from '@strudel.cycles/webaudio';
|
import { samples } from '@strudel.cycles/webaudio';
|
||||||
|
|
||||||
export async function prebake(isMock = false) {
|
export async function prebake({ isMock = false, baseDir = '.' } = {}) {
|
||||||
samples(
|
samples(
|
||||||
{
|
{
|
||||||
piano: {
|
piano: {
|
||||||
@ -39,12 +39,12 @@ export async function prebake(isMock = false) {
|
|||||||
},
|
},
|
||||||
// https://archive.org/details/SalamanderGrandPianoV3
|
// https://archive.org/details/SalamanderGrandPianoV3
|
||||||
// License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm
|
// License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm
|
||||||
'./piano/',
|
`${baseDir}/piano/`,
|
||||||
);
|
);
|
||||||
if (!isMock) {
|
if (!isMock) {
|
||||||
await fetch('EmuSP12.json')
|
await fetch('EmuSP12.json')
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((json) => samples(json, './EmuSP12/'));
|
.then((json) => samples(json, `${baseDir}/EmuSP12/`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ const panwidth = (pan, width) => pan * width + (1 - width) / 2;
|
|||||||
Pattern.prototype.piano = function () {
|
Pattern.prototype.piano = function () {
|
||||||
return this.clip(1)
|
return this.clip(1)
|
||||||
.s('piano')
|
.s('piano')
|
||||||
.release(.1)
|
.release(0.1)
|
||||||
.fmap((value) => {
|
.fmap((value) => {
|
||||||
const midi = typeof value.note === 'string' ? toMidi(value.note) : value.note;
|
const midi = typeof value.note === 'string' ? toMidi(value.note) : value.note;
|
||||||
// pan by pitch
|
// pan by pitch
|
||||||
|
|||||||
@ -123,7 +123,7 @@ const uiHelpersMocked = {
|
|||||||
backgroundImage: id,
|
backgroundImage: id,
|
||||||
};
|
};
|
||||||
|
|
||||||
prebake(true);
|
prebake({ isMock: true });
|
||||||
|
|
||||||
// TODO: refactor to evalScope
|
// TODO: refactor to evalScope
|
||||||
evalScope(
|
evalScope(
|
||||||
|
|||||||
@ -6,11 +6,8 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
|
|
||||||
import { Tone } from '@strudel.cycles/tone';
|
import { Tone } from '@strudel.cycles/tone';
|
||||||
import { State, TimeSpan } from '@strudel.cycles/core';
|
import { State, TimeSpan } from '@strudel.cycles/core';
|
||||||
import { getPlayableNoteValue } from '@strudel.cycles/core/util.mjs';
|
|
||||||
import { evaluate } from '@strudel.cycles/eval';
|
import { evaluate } from '@strudel.cycles/eval';
|
||||||
import { getDefaultSynth } from '@strudel.cycles/tone';
|
import { webaudioOutputTrigger } from '@strudel.cycles/webaudio';
|
||||||
|
|
||||||
const defaultSynth = getDefaultSynth();
|
|
||||||
|
|
||||||
// this is a test to play back events with as less runtime code as possible..
|
// this is a test to play back events with as less runtime code as possible..
|
||||||
// the code asks for the number of seconds to prequery
|
// the code asks for the number of seconds to prequery
|
||||||
@ -47,17 +44,8 @@ async function playStatic(code) {
|
|||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
Tone.getTransport().schedule((time) => {
|
Tone.getTransport().schedule((time) => {
|
||||||
try {
|
try {
|
||||||
const { onTrigger, velocity } = event.context;
|
const { onTrigger = webaudioOutputTrigger } = event.context;
|
||||||
if (!onTrigger) {
|
onTrigger(time, event);
|
||||||
if (defaultSynth) {
|
|
||||||
const note = getPlayableNoteValue(event);
|
|
||||||
defaultSynth.triggerAttackRelease(note, event.duration.valueOf(), time, velocity);
|
|
||||||
} else {
|
|
||||||
throw new Error('no defaultSynth passed to useRepl.');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
onTrigger(time, event);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
err.message = 'unplayable event: ' + err?.message;
|
err.message = 'unplayable event: ' + err?.message;
|
||||||
|
|||||||
@ -155,7 +155,7 @@ export const zeldasRescue = `stack(
|
|||||||
.gain(.1)
|
.gain(.1)
|
||||||
.s('triangle')
|
.s('triangle')
|
||||||
.room(1)
|
.room(1)
|
||||||
.out()`;
|
`;
|
||||||
|
|
||||||
export const caverave = `const keys = x => x.s('sawtooth').cutoff(1200).gain(.5).attack(0).decay(.16).sustain(.3).release(.1);
|
export const caverave = `const keys = x => x.s('sawtooth').cutoff(1200).gain(.5).attack(0).decay(.16).sustain(.3).release(.1);
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ const synths = stack(
|
|||||||
stack(
|
stack(
|
||||||
drums.fast(2),
|
drums.fast(2),
|
||||||
synths
|
synths
|
||||||
).slow(2).out()`;
|
).slow(2)`;
|
||||||
|
|
||||||
export const sampleDrums = `samples({
|
export const sampleDrums = `samples({
|
||||||
bd: 'bd/BT0A0D0.wav',
|
bd: 'bd/BT0A0D0.wav',
|
||||||
@ -198,7 +198,7 @@ stack(
|
|||||||
"<bd!3 bd(3,4,2)>",
|
"<bd!3 bd(3,4,2)>",
|
||||||
"hh*4",
|
"hh*4",
|
||||||
"~ <sn!3 sn(3,4,1)>"
|
"~ <sn!3 sn(3,4,1)>"
|
||||||
).s().out()
|
).s()
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
@ -290,7 +290,7 @@ export const barryHarris = `backgroundImage(
|
|||||||
.scale('C bebop major')
|
.scale('C bebop major')
|
||||||
.transpose("<0 1 2 1>/8")
|
.transpose("<0 1 2 1>/8")
|
||||||
.slow(2)
|
.slow(2)
|
||||||
.note().piano().out()
|
.note().piano()
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const blippyRhodes = `samples({
|
export const blippyRhodes = `samples({
|
||||||
@ -331,7 +331,7 @@ stack(
|
|||||||
.slow(2).superimpose(x=>x.add(.02))
|
.slow(2).superimpose(x=>x.add(.02))
|
||||||
.note().gain(.3)
|
.note().gain(.3)
|
||||||
.s('sawtooth').cutoff(600),
|
.s('sawtooth').cutoff(600),
|
||||||
).fast(3/2).out()`;
|
).fast(3/2)`;
|
||||||
|
|
||||||
export const wavyKalimba = `samples({
|
export const wavyKalimba = `samples({
|
||||||
'kalimba': { c5:'https://freesound.org/data/previews/536/536549_11935698-lq.mp3' }
|
'kalimba': { c5:'https://freesound.org/data/previews/536/536549_11935698-lq.mp3' }
|
||||||
@ -359,7 +359,7 @@ stack(
|
|||||||
.clip(1)
|
.clip(1)
|
||||||
.s('kalimba')
|
.s('kalimba')
|
||||||
.delay(.2)
|
.delay(.2)
|
||||||
.out()`;
|
`;
|
||||||
|
|
||||||
// TODO: rework tune to use freq
|
// TODO: rework tune to use freq
|
||||||
/*
|
/*
|
||||||
@ -401,7 +401,7 @@ stack(
|
|||||||
.scaleTranspose("0 4 0 6".early(".125 .5")).layer(scaleTranspose("0,<2 [4,6] [5,7]>/4"))
|
.scaleTranspose("0 4 0 6".early(".125 .5")).layer(scaleTranspose("0,<2 [4,6] [5,7]>/4"))
|
||||||
).slow(2)
|
).slow(2)
|
||||||
.velocity(sine.struct("x*8").add(3/5).mul(2/5).fast(8))
|
.velocity(sine.struct("x*8").add(3/5).mul(2/5).fast(8))
|
||||||
.note().piano().out()`;
|
.note().piano()`;
|
||||||
|
|
||||||
// iter, echo, echoWith
|
// iter, echo, echoWith
|
||||||
export const undergroundPlumber = `backgroundImage('https://images.nintendolife.com/news/2016/08/video_exploring_the_funky_inspiration_for_the_super_mario_bros_underground_theme/large.jpg',{ className:'darken' })
|
export const undergroundPlumber = `backgroundImage('https://images.nintendolife.com/news/2016/08/video_exploring_the_funky_inspiration_for_the_super_mario_bros_underground_theme/large.jpg',{ className:'darken' })
|
||||||
@ -425,7 +425,7 @@ stack(
|
|||||||
.echoWith(4, 1/8, (x,n)=>x.transpose(n*12).velocity(Math.pow(.4,n)))
|
.echoWith(4, 1/8, (x,n)=>x.transpose(n*12).velocity(Math.pow(.4,n)))
|
||||||
.legato(.1)
|
.legato(.1)
|
||||||
.layer(h).note()
|
.layer(h).note()
|
||||||
).out()
|
)
|
||||||
.fast(2/3)
|
.fast(2/3)
|
||||||
.pianoroll({})`;
|
.pianoroll({})`;
|
||||||
|
|
||||||
@ -444,7 +444,7 @@ stack(
|
|||||||
).transpose(-1).note().piano(),
|
).transpose(-1).note().piano(),
|
||||||
s("mad").slow(2)
|
s("mad").slow(2)
|
||||||
).cpm(78).slow(4)
|
).cpm(78).slow(4)
|
||||||
.out()
|
|
||||||
.pianoroll()
|
.pianoroll()
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -463,7 +463,7 @@ stack(
|
|||||||
.scale(scale)
|
.scale(scale)
|
||||||
.scaleTranspose("<0>".slow(4))
|
.scaleTranspose("<0>".slow(4))
|
||||||
.transpose(5)
|
.transpose(5)
|
||||||
.note().piano().out()
|
.note().piano()
|
||||||
.velocity(.8)
|
.velocity(.8)
|
||||||
.slow(2)
|
.slow(2)
|
||||||
.pianoroll({maxMidi:100,minMidi:20})`;
|
.pianoroll({maxMidi:100,minMidi:20})`;
|
||||||
@ -475,7 +475,7 @@ export const echoPiano = `"<0 2 [4 6](3,4,1) 3*2>"
|
|||||||
.off(1/2, x=>x.scaleTranspose(6).color('steelblue'))
|
.off(1/2, x=>x.scaleTranspose(6).color('steelblue'))
|
||||||
.legato(.5)
|
.legato(.5)
|
||||||
.echo(4, 1/8, .5)
|
.echo(4, 1/8, .5)
|
||||||
.note().piano().out()
|
.note().piano()
|
||||||
.pianoroll()`;
|
.pianoroll()`;
|
||||||
|
|
||||||
export const sml1 = `
|
export const sml1 = `
|
||||||
@ -511,7 +511,7 @@ stack(
|
|||||||
f3!2 e3!2 ab3!2 ~!2
|
f3!2 e3!2 ab3!2 ~!2
|
||||||
>\`
|
>\`
|
||||||
.legato(.5)
|
.legato(.5)
|
||||||
).fast(2) // .note().piano().out()`;
|
).fast(2) // .note().piano()`;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// TODO: does not work on linux (at least for me..)
|
// TODO: does not work on linux (at least for me..)
|
||||||
@ -554,7 +554,7 @@ stack(
|
|||||||
"<D2 A2 G2 F2>".euclidLegato(6,8,1).note().s('bass').clip(1).gain(.8)
|
"<D2 A2 G2 F2>".euclidLegato(6,8,1).note().s('bass').clip(1).gain(.8)
|
||||||
)
|
)
|
||||||
.slow(6)
|
.slow(6)
|
||||||
.out()
|
|
||||||
.pianoroll({minMidi:20,maxMidi:120,background:'transparent'})
|
.pianoroll({minMidi:20,maxMidi:120,background:'transparent'})
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -570,7 +570,7 @@ export const waa2 = `n(
|
|||||||
.cutoff(cosine.range(500,4000).slow(16))
|
.cutoff(cosine.range(500,4000).slow(16))
|
||||||
.gain(.5)
|
.gain(.5)
|
||||||
.room(.5)
|
.room(.5)
|
||||||
.out()`;
|
`;
|
||||||
|
|
||||||
export const hyperpop = `const lfo = cosine.slow(15);
|
export const hyperpop = `const lfo = cosine.slow(15);
|
||||||
const lfo2 = sine.slow(16);
|
const lfo2 = sine.slow(16);
|
||||||
@ -618,7 +618,7 @@ stack(
|
|||||||
"~ sn",
|
"~ sn",
|
||||||
"[~ hh3]*2"
|
"[~ hh3]*2"
|
||||||
).s().fast(2).gain(.7)
|
).s().fast(2).gain(.7)
|
||||||
).slow(2).out()
|
).slow(2)
|
||||||
// strudel disable-highlighting`;
|
// strudel disable-highlighting`;
|
||||||
|
|
||||||
export const festivalOfFingers3 = `"[-7*3],0,2,6,[8 7]"
|
export const festivalOfFingers3 = `"[-7*3],0,2,6,[8 7]"
|
||||||
@ -634,7 +634,7 @@ export const festivalOfFingers3 = `"[-7*3],0,2,6,[8 7]"
|
|||||||
.scale(cat('D dorian','G mixolydian','C dorian','F mixolydian'))
|
.scale(cat('D dorian','G mixolydian','C dorian','F mixolydian'))
|
||||||
.legato(1)
|
.legato(1)
|
||||||
.slow(2)
|
.slow(2)
|
||||||
.note().piano().out()
|
.note().piano()
|
||||||
//.pianoroll({maxMidi:160})`;
|
//.pianoroll({maxMidi:160})`;
|
||||||
|
|
||||||
export const bossa = `
|
export const bossa = `
|
||||||
@ -642,7 +642,7 @@ const scales = sequence('C minor', ['D locrian', 'G phrygian'], 'Bb2 minor', ['C
|
|||||||
stack(
|
stack(
|
||||||
"<Cm7 [Dm7b5 G7b9] Bbm7 [Cm7b5 F7b9]>".fast(2).struct("x ~ x@3 x ~ x ~ ~ ~ x ~ x@3".late(1/8)).early(1/8).slow(2).voicings(),
|
"<Cm7 [Dm7b5 G7b9] Bbm7 [Cm7b5 F7b9]>".fast(2).struct("x ~ x@3 x ~ x ~ ~ ~ x ~ x@3".late(1/8)).early(1/8).slow(2).voicings(),
|
||||||
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
|
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
|
||||||
).note().piano().out().slow(2)`;
|
).note().piano().slow(2)`;
|
||||||
/*
|
/*
|
||||||
export const customTrigger = `stack(
|
export const customTrigger = `stack(
|
||||||
freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)").layer(x=>x.mul("1.006,.995"))),
|
freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)").layer(x=>x.mul("1.006,.995"))),
|
||||||
@ -701,7 +701,7 @@ stack(
|
|||||||
.echoWith(4,.125,(x,n)=>x.gain(.15*1/(n+1))) // echo notes
|
.echoWith(4,.125,(x,n)=>x.gain(.15*1/(n+1))) // echo notes
|
||||||
//.hush()
|
//.hush()
|
||||||
)
|
)
|
||||||
.out()
|
|
||||||
.slow(3/2)`;
|
.slow(3/2)`;
|
||||||
|
|
||||||
export const swimmingWithSoundfonts = `stack(
|
export const swimmingWithSoundfonts = `stack(
|
||||||
@ -763,7 +763,7 @@ export const swimmingWithSoundfonts = `stack(
|
|||||||
"[G2 C2 F2 F2]"
|
"[G2 C2 F2 F2]"
|
||||||
).s('Acoustic Bass: Bass')
|
).s('Acoustic Bass: Bass')
|
||||||
).slow(51)
|
).slow(51)
|
||||||
.out()`;
|
`;
|
||||||
|
|
||||||
export const outroMusic = `samples({
|
export const outroMusic = `samples({
|
||||||
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
|
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
|
||||||
@ -790,7 +790,7 @@ export const outroMusic = `samples({
|
|||||||
.n(3).color('gray')
|
.n(3).color('gray')
|
||||||
).slow(3/2)
|
).slow(3/2)
|
||||||
//.pianoroll({autorange:1,vertical:1,fold:0})
|
//.pianoroll({autorange:1,vertical:1,fold:0})
|
||||||
.out()`;
|
`;
|
||||||
|
|
||||||
export const bassFuge = `samples({ flbass: ['00_c2_finger_long_neck.wav','01_c2_finger_short_neck.wav','02_c2_finger_long_bridge.wav','03_c2_finger_short_bridge.wav','04_c2_pick_long.wav','05_c2_pick_short.wav','06_c2_palm_mute.wav'] },
|
export const bassFuge = `samples({ flbass: ['00_c2_finger_long_neck.wav','01_c2_finger_short_neck.wav','02_c2_finger_long_bridge.wav','03_c2_finger_short_bridge.wav','04_c2_pick_long.wav','05_c2_pick_short.wav','06_c2_palm_mute.wav'] },
|
||||||
'github:cleary/samples-flbass/main/')
|
'github:cleary/samples-flbass/main/')
|
||||||
@ -814,7 +814,7 @@ x=>x.add(7).color('steelblue')
|
|||||||
//.hcutoff(400)
|
//.hcutoff(400)
|
||||||
.clip(1)
|
.clip(1)
|
||||||
.stack(s("bd:1*2,~ sd:0,[~ hh:0]*2"))
|
.stack(s("bd:1*2,~ sd:0,[~ hh:0]*2"))
|
||||||
.out()
|
|
||||||
.pianoroll({vertical:1})`;
|
.pianoroll({vertical:1})`;
|
||||||
|
|
||||||
export const bossaRandom = `const chords = "<Am7 Am7 Dm7 E7>"
|
export const bossaRandom = `const chords = "<Am7 Am7 Dm7 E7>"
|
||||||
@ -826,7 +826,7 @@ stack(
|
|||||||
x? ~ ~ x@3 ~ x |
|
x? ~ ~ x@3 ~ x |
|
||||||
x? ~ ~ x ~ x@3\`),
|
x? ~ ~ x ~ x@3\`),
|
||||||
roots.struct("x [~ x?0.2] x [~ x?] | x!4 | x@2 ~ ~ ~ x x x").transpose("0 7")
|
roots.struct("x [~ x?0.2] x [~ x?] | x!4 | x@2 ~ ~ ~ x x x").transpose("0 7")
|
||||||
).slow(2).pianoroll().note().piano().out()`;
|
).slow(2).pianoroll().note().piano()`;
|
||||||
|
|
||||||
export const chop = `samples({ p: 'https://cdn.freesound.org/previews/648/648433_11943129-lq.mp3' })
|
export const chop = `samples({ p: 'https://cdn.freesound.org/previews/648/648433_11943129-lq.mp3' })
|
||||||
|
|
||||||
@ -837,14 +837,14 @@ s("p")
|
|||||||
.shape(.4)
|
.shape(.4)
|
||||||
.decay(.1)
|
.decay(.1)
|
||||||
.sustain(.6)
|
.sustain(.6)
|
||||||
.out()`;
|
`;
|
||||||
|
|
||||||
export const delay = `stack(
|
export const delay = `stack(
|
||||||
s("bd <sd cp>")
|
s("bd <sd cp>")
|
||||||
.delay("<0 .5>")
|
.delay("<0 .5>")
|
||||||
.delaytime(".16 | .33")
|
.delaytime(".16 | .33")
|
||||||
.delayfeedback(".6 | .8")
|
.delayfeedback(".6 | .8")
|
||||||
).sometimes(x=>x.speed("-1")).out()`;
|
).sometimes(x=>x.speed("-1"))`;
|
||||||
|
|
||||||
export const orbit = `stack(
|
export const orbit = `stack(
|
||||||
s("bd <sd cp>")
|
s("bd <sd cp>")
|
||||||
@ -856,4 +856,4 @@ export const orbit = `stack(
|
|||||||
.delaytime(.08)
|
.delaytime(.08)
|
||||||
.delayfeedback(.7)
|
.delayfeedback(.7)
|
||||||
.orbit(2)
|
.orbit(2)
|
||||||
).sometimes(x=>x.speed("-1")).out()`;
|
).sometimes(x=>x.speed("-1"))`;
|
||||||
|
|||||||
@ -1,24 +1,14 @@
|
|||||||
import { Tone } from '@strudel.cycles/tone';
|
|
||||||
import { evalScope } from '@strudel.cycles/eval';
|
import { evalScope } from '@strudel.cycles/eval';
|
||||||
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
|
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
|
||||||
import controls from '@strudel.cycles/core/controls.mjs';
|
import controls from '@strudel.cycles/core/controls.mjs';
|
||||||
import { loadWebDirt } from '@strudel.cycles/webdirt';
|
|
||||||
import { samples } from '@strudel.cycles/webaudio';
|
import { samples } from '@strudel.cycles/webaudio';
|
||||||
|
import { prebake } from '../repl/src/prebake.mjs';
|
||||||
export const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destination).set({
|
|
||||||
oscillator: { type: 'triangle' },
|
|
||||||
envelope: {
|
|
||||||
release: 0.01,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
fetch('https://strudel.tidalcycles.org/EmuSP12.json')
|
fetch('https://strudel.tidalcycles.org/EmuSP12.json')
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((json) => samples(json, 'https://strudel.tidalcycles.org/EmuSP12/'));
|
.then((json) => samples(json, 'https://strudel.tidalcycles.org/EmuSP12/'));
|
||||||
|
|
||||||
|
|
||||||
evalScope(
|
evalScope(
|
||||||
Tone,
|
|
||||||
controls,
|
controls,
|
||||||
import('@strudel.cycles/core'),
|
import('@strudel.cycles/core'),
|
||||||
import('@strudel.cycles/tone'),
|
import('@strudel.cycles/tone'),
|
||||||
@ -28,14 +18,10 @@ evalScope(
|
|||||||
import('@strudel.cycles/xen'),
|
import('@strudel.cycles/xen'),
|
||||||
import('@strudel.cycles/webaudio'),
|
import('@strudel.cycles/webaudio'),
|
||||||
import('@strudel.cycles/osc'),
|
import('@strudel.cycles/osc'),
|
||||||
import('@strudel.cycles/webdirt'),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
loadWebDirt({
|
prebake();
|
||||||
sampleMapUrl: '../EmuSP12.json',
|
|
||||||
sampleFolder: '../EmuSP12',
|
|
||||||
});
|
|
||||||
|
|
||||||
export function MiniRepl({ tune }) {
|
export function MiniRepl({ tune }) {
|
||||||
return <_MiniRepl tune={tune} defaultSynth={defaultSynth} hideOutsideView={true} />;
|
return <_MiniRepl tune={tune} hideOutsideView={true} />;
|
||||||
}
|
}
|
||||||
|
|||||||
152
tutorial/old.mdx
Normal file
152
tutorial/old.mdx
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
# Old APIs
|
||||||
|
|
||||||
|
These APIs are outdated and might break in the future.
|
||||||
|
|
||||||
|
## Webdirt API (deprecated)
|
||||||
|
|
||||||
|
You can use the powerful sampling engine [Webdirt](https://github.com/dktr0/WebDirt) with Strudel.
|
||||||
|
|
||||||
|
{{ 'Pattern.webdirt' | jsdoc }}
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## Tone API (deprecated)
|
||||||
|
|
||||||
|
The Tone API uses Tone.js instruments ands effects to create sounds.
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`stack(
|
||||||
|
"[c5 c5 bb4 c5] [~ g4 ~ g4] [c5 f5 e5 c5] ~"
|
||||||
|
.tone(synth(adsr(0,.1,0,0)).chain(out())),
|
||||||
|
"[c2 c3]*8"
|
||||||
|
.tone(synth({
|
||||||
|
...osc('sawtooth'),
|
||||||
|
...adsr(0,.1,0.4,0)
|
||||||
|
}).chain(lowpass(300), out()))
|
||||||
|
).slow(4)`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
### tone(instrument)
|
||||||
|
|
||||||
|
To change the instrument of a pattern, you can pass any [Tone.js Source](https://tonejs.github.io/docs/14.7.77/index.html) to .tone:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
|
.tone(new FMSynth().toDestination())`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
While this works, it is a little bit verbose. To simplify things, all Tone Synths have a shortcut:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const amsynth = (options) => new AMSynth(options);
|
||||||
|
const duosynth = (options) => new DuoSynth(options);
|
||||||
|
const fmsynth = (options) => new FMSynth(options);
|
||||||
|
const membrane = (options) => new MembraneSynth(options);
|
||||||
|
const metal = (options) => new MetalSynth(options);
|
||||||
|
const monosynth = (options) => new MonoSynth(options);
|
||||||
|
const noise = (options) => new NoiseSynth(options);
|
||||||
|
const pluck = (options) => new PluckSynth(options);
|
||||||
|
const polysynth = (options) => new PolySynth(options);
|
||||||
|
const synth = (options) => new Synth(options);
|
||||||
|
const sampler = (options, baseUrl?) => new Sampler(options); // promisified, see below
|
||||||
|
const players = (options, baseUrl?) => new Sampler(options); // promisified, see below
|
||||||
|
```
|
||||||
|
|
||||||
|
### sampler
|
||||||
|
|
||||||
|
With sampler, you can create tonal instruments from samples:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`sampler({
|
||||||
|
C5: 'https://freesound.org/data/previews/536/536549_11935698-lq.mp3'
|
||||||
|
}).then(kalimba =>
|
||||||
|
saw.struct("x*8").mul(16).round()
|
||||||
|
.legato(4).scale('D dorian').slow(2)
|
||||||
|
.tone(kalimba.toDestination())
|
||||||
|
)`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
The sampler function promisifies [Tone.js Sampler](https://tonejs.github.io/docs/14.7.77/Sampler).
|
||||||
|
|
||||||
|
Note that this function currently only works with this promise notation, but in the future,
|
||||||
|
it will be possible to use async instruments in a synchronous fashion.
|
||||||
|
|
||||||
|
### players
|
||||||
|
|
||||||
|
With players, you can create sound banks:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`players({
|
||||||
|
bd: 'samples/tidal/bd/BT0A0D0.wav',
|
||||||
|
sn: 'samples/tidal/sn/ST0T0S3.wav',
|
||||||
|
hh: 'samples/tidal/hh/000_hh3closedhh.wav'
|
||||||
|
}, 'https://loophole-letters.vercel.app/')
|
||||||
|
.then(drums=>
|
||||||
|
"bd hh sn hh".tone(drums.toDestination())
|
||||||
|
)
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
The sampler function promisifies [Tone.js Players](https://tonejs.github.io/docs/14.7.77/Players).
|
||||||
|
|
||||||
|
Note that this function currently only works with this promise notation, but in the future,
|
||||||
|
it will be possible to use async instruments in a synchronous fashion.
|
||||||
|
|
||||||
|
### out
|
||||||
|
|
||||||
|
Shortcut for Tone.Destination. Intended to be used with Tone's .chain:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
|
.tone(membrane().chain(out()))`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
This alone is not really useful, so read on..
|
||||||
|
|
||||||
|
### vol(volume)
|
||||||
|
|
||||||
|
Helper that returns a Gain Node with the given volume. Intended to be used with Tone's .chain:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
|
.tone(noise().chain(vol(0.5), out()))`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
### osc(type)
|
||||||
|
|
||||||
|
Helper to set the waveform of a synth, monosynth or polysynth:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
|
.tone(synth(osc('sawtooth4')).chain(out()))`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
The base types are `sine`, `square`, `sawtooth`, `triangle`. You can also append a number between 1 and 32 to reduce the harmonic partials.
|
||||||
|
|
||||||
|
### lowpass(cutoff)
|
||||||
|
|
||||||
|
Helper that returns a Filter Node of type lowpass with the given cutoff. Intended to be used with Tone's .chain:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
|
.tone(synth(osc('sawtooth')).chain(lowpass(800), out()))`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
### highpass(cutoff)
|
||||||
|
|
||||||
|
Helper that returns a Filter Node of type highpass with the given cutoff. Intended to be used with Tone's .chain:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
|
.tone(synth(osc('sawtooth')).chain(highpass(2000), out()))`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
### adsr
|
||||||
|
|
||||||
|
Helper to set the envelope of a Tone.js instrument. Intended to be used with Tone's .set:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
|
.tone(synth(adsr(0,.1,0,0)).chain(out()))`}
|
||||||
|
/>
|
||||||
@ -17,182 +17,6 @@ The best place to actually make music with Strudel is the [Strudel REPL](https:/
|
|||||||
|
|
||||||
To get a taste of what Strudel can do, check out this track:
|
To get a taste of what Strudel can do, check out this track:
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out());
|
|
||||||
const kick = new MembraneSynth().chain(vol(.8), out());
|
|
||||||
const snare = new NoiseSynth().chain(vol(.8), out());
|
|
||||||
const hihat = new MetalSynth().set(adsr(0, .08, 0, .1)).chain(vol(.3).connect(delay),out());
|
|
||||||
const bass = new Synth().set({ ...osc('sawtooth'), ...adsr(0, .1, .4) }).chain(lowpass(900), vol(.5), out());
|
|
||||||
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out());
|
|
||||||
const drums = stack(
|
|
||||||
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
|
||||||
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
|
||||||
"[~ c4]*2".tone(hihat)
|
|
||||||
);
|
|
||||||
const thru = (x) => x.transpose("<0 1>/8").transpose(-1);
|
|
||||||
const synths = stack(
|
|
||||||
"<eb4 d4 c4 b3>/2".scale(timeCat([3,'C minor'],[1,'C melodic minor']).slow(8)).struct("[~ x]\*2")
|
|
||||||
.layer(
|
|
||||||
scaleTranspose(0).early(0),
|
|
||||||
scaleTranspose(2).early(1/8),
|
|
||||||
scaleTranspose(7).early(1/4),
|
|
||||||
scaleTranspose(8).early(3/8)
|
|
||||||
).layer(thru).tone(keys).bypass("<1 0>/16"),
|
|
||||||
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).layer(thru).tone(bass),
|
|
||||||
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.1 ~]".fast(2)).voicings().layer(thru).every(2, early(1/8)).tone(keys).bypass("<0@7 1>/8".early(1/4))
|
|
||||||
)
|
|
||||||
stack(
|
|
||||||
drums.fast(2),
|
|
||||||
synths
|
|
||||||
).slow(2);
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
[Open this track in the REPL](https://strudel.tidalcycles.org/#KCkgPT4gewogIGNvbnN0IGRlbGF5ID0gbmV3IEZlZWRiYWNrRGVsYXkoMS84LCAuNCkuY2hhaW4odm9sKDAuNSksIG91dCk7CiAgY29uc3Qga2ljayA9IG5ldyBNZW1icmFuZVN5bnRoKCkuY2hhaW4odm9sKC44KSwgb3V0KTsKICBjb25zdCBzbmFyZSA9IG5ldyBOb2lzZVN5bnRoKCkuY2hhaW4odm9sKC44KSwgb3V0KTsKICBjb25zdCBoaWhhdCA9IG5ldyBNZXRhbFN5bnRoKCkuc2V0KGFkc3IoMCwgLjA4LCAwLCAuMSkpLmNoYWluKHZvbCguMykuY29ubmVjdChkZWxheSksb3V0KTsKICBjb25zdCBiYXNzID0gbmV3IFN5bnRoKCkuc2V0KHsgLi4ub3NjKCdzYXd0b290aCcpLCAuLi5hZHNyKDAsIC4xLCAuNCkgfSkuY2hhaW4obG93cGFzcyg5MDApLCB2b2woLjUpLCBvdXQpOwogIGNvbnN0IGtleXMgPSBuZXcgUG9seVN5bnRoKCkuc2V0KHsgLi4ub3NjKCdzYXd0b290aCcpLCAuLi5hZHNyKDAsIC41LCAuMiwgLjcpIH0pLmNoYWluKGxvd3Bhc3MoMTIwMCksIHZvbCguNSksIG91dCk7CiAgCiAgY29uc3QgZHJ1bXMgPSBzdGFjaygKICAgICdjMSoyJy5tLnRvbmUoa2ljaykuYnlwYXNzKCc8MEA3IDE%2BLzgnLm0pLAogICAgJ34gPHghNyBbeEAzIHhdPicubS50b25lKHNuYXJlKS5ieXBhc3MoJzwwQDcgMT4vNCcubSksCiAgICAnW34gYzRdKjInLm0udG9uZShoaWhhdCkKICApOwogIAogIGNvbnN0IHRocnUgPSAoeCkgPT4geC50cmFuc3Bvc2UoJzwwIDE%2BLzgnLm0pLnRyYW5zcG9zZSgtMSk7CiAgY29uc3Qgc3ludGhzID0gc3RhY2soCiAgICAnPGViNCBkNCBjNCBiMz4vMicubS5zY2FsZSh0aW1lQ2F0KFszLCdDIG1pbm9yJ10sWzEsJ0MgbWVsb2RpYyBtaW5vciddKS5zbG93KDgpKS5ncm9vdmUoJ1t%2BIHhdKjInLm0pCiAgICAuZWRpdCgKICAgICAgc2NhbGVUcmFuc3Bvc2UoMCkuZWFybHkoMCksCiAgICAgIHNjYWxlVHJhbnNwb3NlKDIpLmVhcmx5KDEvOCksCiAgICAgIHNjYWxlVHJhbnNwb3NlKDcpLmVhcmx5KDEvNCksCiAgICAgIHNjYWxlVHJhbnNwb3NlKDgpLmVhcmx5KDMvOCkKICAgICkuZWRpdCh0aHJ1KS50b25lKGtleXMpLmJ5cGFzcygnPDEgMD4vMTYnLm0pLAogICAgJzxDMiBCYjEgQWIxIFtHMSBbRzIgRzFdXT4vMicubS5ncm9vdmUoJ1t4IFt%2BIHhdIDxbfiBbfiB4XV0hMyBbeCB4XT5AMl0vMicubS5mYXN0KDIpKS5lZGl0KHRocnUpLnRvbmUoYmFzcyksCiAgICAnPENtNyBCYjcgRm03IEc3YjEzPi8yJy5tLmdyb292ZSgnfiBbeEAwLjEgfl0nLm0uZmFzdCgyKSkudm9pY2luZ3MoKS5lZGl0KHRocnUpLmV2ZXJ5KDIsIGVhcmx5KDEvOCkpLnRvbmUoa2V5cykuYnlwYXNzKCc8MEA3IDE%2BLzgnLm0uZWFybHkoMS80KSkKICApCiAgcmV0dXJuIHN0YWNrKAogICAgZHJ1bXMuZmFzdCgyKSwgCiAgICBzeW50aHMKICApLnNsb3coMik7Cn0%3D)
|
|
||||||
|
|
||||||
## Disclaimer
|
|
||||||
|
|
||||||
- This project is still in its experimental state. In the future, parts of it might change significantly.
|
|
||||||
- This tutorial is far from complete.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
# Mini Notation
|
|
||||||
|
|
||||||
Similar to Tidal Cycles, Strudel has an embedded mini language that is designed to write rhythmic patterns in a short manner.
|
|
||||||
Before diving deeper into the details, here is a flavor of how the mini language looks like:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`\`[
|
|
||||||
[
|
|
||||||
[e5 [b4 c5] d5 [c5 b4]]
|
|
||||||
[a4 [a4 c5] e5 [d5 c5]]
|
|
||||||
[b4 [~ c5] d5 e5]
|
|
||||||
[c5 a4 a4 ~]
|
|
||||||
[[~ d5] [~ f5] a5 [g5 f5]]
|
|
||||||
[e5 [~ c5] e5 [d5 c5]]
|
|
||||||
[b4 [b4 c5] d5 e5]
|
|
||||||
[c5 a4 a4 ~]
|
|
||||||
],[
|
|
||||||
[[e2 e3]*4]
|
|
||||||
[[a2 a3]*4]
|
|
||||||
[[g#2 g#3]*2 [e2 e3]*2]
|
|
||||||
[a2 a3 a2 a3 a2 a3 b1 c2]
|
|
||||||
[[d2 d3]*4]
|
|
||||||
[[c2 c3]*4]
|
|
||||||
[[b1 b2]*2 [e2 e3]*2]
|
|
||||||
[[a1 a2]*4]
|
|
||||||
]
|
|
||||||
]/16\``}
|
|
||||||
/>
|
|
||||||
|
|
||||||
The snippet above is enclosed in backticks (`), which allows you to write multi-line strings.
|
|
||||||
You can also use double quotes (") for single line mini notation.
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
Notes are notated with the note letter, followed by the octave number. You can notate flats with `b` and sharps with `#`.
|
|
||||||
|
|
||||||
<MiniRepl tune={`"e5"`} />
|
|
||||||
|
|
||||||
Here, the same note is played over and over again, once a second. This one second is the default length of one so called "cycle".
|
|
||||||
|
|
||||||
By the way, you can edit the contents of the player, and press "update" to hear your change!
|
|
||||||
You can also press "play" on the next player without needing to stop the last one.
|
|
||||||
|
|
||||||
## Sequences
|
|
||||||
|
|
||||||
We can play more notes by separating them with spaces:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"e5 b4 d5 c5"`} />
|
|
||||||
|
|
||||||
Here, those four notes are squashed into one cycle, so each note is a quarter second long.
|
|
||||||
|
|
||||||
## Division
|
|
||||||
|
|
||||||
We can slow the sequence down by enclosing it in brackets and dividing it by a number:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"[e5 b4 d5 c5]/2"`} />
|
|
||||||
|
|
||||||
The division by two means that the sequence will be played over the course of two cycles.
|
|
||||||
You can also use decimal numbers for any tempo you like.
|
|
||||||
|
|
||||||
## Angle Brackets
|
|
||||||
|
|
||||||
Using angle brackets, we can define the sequence length based on the number of children:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"<e5 b4 d5 c5>"`} />
|
|
||||||
|
|
||||||
The above snippet is the same as:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"[e5 b4 d5 c5]/4"`} />
|
|
||||||
|
|
||||||
The advantage of the angle brackets, is that we can add more children without needing to change the number at the end.
|
|
||||||
|
|
||||||
## Multiplication
|
|
||||||
|
|
||||||
Contrary to division, a sequence can be sped up by multiplying it by a number:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"[e5 b4 d5 c5]*2"`} />
|
|
||||||
|
|
||||||
The multiplication by 2 here means that the sequence will play twice a cycle.
|
|
||||||
|
|
||||||
## Bracket Nesting
|
|
||||||
|
|
||||||
To create more interesting rhythms, you can nest sequences with brackets, like this:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"e5 [b4 c5] d5 [c5 b4]"`} />
|
|
||||||
|
|
||||||
## Rests
|
|
||||||
|
|
||||||
The "~" represents a rest:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"[b4 [~ c5] d5 e5]"`} />
|
|
||||||
|
|
||||||
## Parallel
|
|
||||||
|
|
||||||
Using commas, we can play chords:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"g3,b3,e4"`} />
|
|
||||||
|
|
||||||
To play multiple chords in a sequence, we have to wrap them in brackets:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>"`} />
|
|
||||||
|
|
||||||
## Elongation
|
|
||||||
|
|
||||||
With the "@" symbol, we can specify temporal "weight" of a sequence child:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>"`} />
|
|
||||||
|
|
||||||
Here, the first chord has a weight of 2, making it twice the length of the other chords. The default weight is 1.
|
|
||||||
|
|
||||||
## Replication
|
|
||||||
|
|
||||||
Using "!" we can repeat without speeding up:
|
|
||||||
|
|
||||||
<MiniRepl tune={`"<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>"`} />
|
|
||||||
|
|
||||||
In essence, the `x!n` is like a shortcut for `[x*n]@n`.
|
|
||||||
|
|
||||||
## Euclidian
|
|
||||||
|
|
||||||
Using round brackets, we can create rhythmical sub-divisions based on three parameters: beats, segments and offset.
|
|
||||||
The first parameter controls how may beats will be played.
|
|
||||||
The second parameter controls the total amount of segments the beats will be distributed over.
|
|
||||||
The third (optional) parameter controls the starting position for distributing the beats.
|
|
||||||
One popular Euclidian rhythm (going by various names, such as "Pop Clave") is "(3,8,1)" or simply "(3,8)",
|
|
||||||
resulting in a rhythmical structure of "x ~ ~ x ~ ~ x ~" (3 beats over 8 segments, starting on position 1).
|
|
||||||
|
|
||||||
<MiniRepl tune={`"e5(2,8) b4(3,8) d5(2,8) c5(3,8)".slow(4)`} />
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
# Web Audio Output
|
|
||||||
|
|
||||||
The default way to output sound is by using the Web Audio Output.
|
|
||||||
Here is a little beat to show some of the possibilities:
|
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`samples({
|
tune={`samples({
|
||||||
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
|
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
|
||||||
@ -220,23 +44,239 @@ s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
|
|||||||
.cutoff(500) // fixed cutoff
|
.cutoff(500) // fixed cutoff
|
||||||
.attack(1) // slowly fade in
|
.attack(1) // slowly fade in
|
||||||
)
|
)
|
||||||
.slow(3/2)
|
.slow(3/2)`}
|
||||||
.out()`}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
- This project is still in its experimental state. In the future, parts of it might change significantly.
|
||||||
|
- This tutorial is far from complete.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
# Playing Pitches
|
||||||
|
|
||||||
|
Pitches are an essential building block for music. In Strudel, there are 3 different options to express a pitch:
|
||||||
|
|
||||||
|
- `note`: letter notation
|
||||||
|
- `n`: number notation
|
||||||
|
- `freq`: frequency notation
|
||||||
|
|
||||||
|
## note
|
||||||
|
|
||||||
|
Notes are notated with the note letter, followed by the octave number. You can notate flats with `b` and sharps with `#`.
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("a3 c#4 e4 a4")`} />
|
||||||
|
|
||||||
|
By the way, you can edit the contents of the player, and press "update" to hear your change!
|
||||||
|
You can also press "play" on the next player without needing to stop the last one.
|
||||||
|
|
||||||
|
## n
|
||||||
|
|
||||||
|
If you don't like notes, you can also use numbers with `n` instead:
|
||||||
|
|
||||||
|
<MiniRepl tune={`n("57 61 64 69")`} />
|
||||||
|
|
||||||
|
These numbers are interpreted as so called midi numbers, where adjacent whole numbers are 1 semitone apart.
|
||||||
|
You could also write decimal numbers to get microtonal pitches:
|
||||||
|
|
||||||
|
<MiniRepl tune={`n("74.5 75 75.5 76")`} />
|
||||||
|
|
||||||
|
## freq
|
||||||
|
|
||||||
|
To get maximum freedom, you can also use `freq` to directly control the frequency:
|
||||||
|
|
||||||
|
<MiniRepl tune={`freq("220 275 330 440")`} />
|
||||||
|
|
||||||
|
In this example, we play A3 (220Hz), C#4 natural (275Hz), E4 (330Hz) and A4 (440Hz).
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
# Playing Sounds
|
||||||
|
|
||||||
|
Instead of pitches, we can also play sounds with `s`:
|
||||||
|
|
||||||
|
<MiniRepl tune={`s("bd hh sd hh")`} />
|
||||||
|
|
||||||
|
Similarly, we can also use `s` to change the sound of our pitches:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("a3 c#4 e4 a4").s("sawtooth")`} />
|
||||||
|
|
||||||
|
Try changing the sound to `square`, `triangle` or `sine`!
|
||||||
|
|
||||||
|
We will go into the defails of sounds and synths [later](http://localhost:3000/tutorial/#web-audio-output).
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
# Syntax
|
||||||
|
|
||||||
|
So far, we've seen the following syntax:
|
||||||
|
|
||||||
|
```
|
||||||
|
xxx("foo").yyy("bar")
|
||||||
|
```
|
||||||
|
|
||||||
|
Generally, `xxx` and `yyy` are called functions, while `foo` and `bar` are called function arguments.
|
||||||
|
So far, we've used the functions to declare which aspect of the sound we want to control, and their arguments for the actual data.
|
||||||
|
The `yyy` function is called a chained function, because it is appended with a dot.
|
||||||
|
|
||||||
|
Strudel makes heavy use of chained functions. Here is a more extreme example:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`note("a3 c#4 e4 a4")
|
||||||
|
.s("sawtooth")
|
||||||
|
.cutoff(500)
|
||||||
|
//.delay(0.5)
|
||||||
|
.room(0.5)`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
The `//` is a line comment, resulting in the `delay` function being ignored.
|
||||||
|
It is a handy way to quickly turn stuff on and off. Try uncommenting this line by deleting `//`!
|
||||||
|
|
||||||
|
The good news is, that this covers 99% of the JavaScript syntax needed for Strudel!
|
||||||
|
|
||||||
|
Let's now look at the way we can express rhythms..
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
# Mini Notation
|
||||||
|
|
||||||
|
Similar to Tidal Cycles, Strudel has an embedded mini language that is designed to write rhythmic patterns in a short manner.
|
||||||
|
Before diving deeper into the details, here is a flavor of how the mini language looks like:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`note(\`[
|
||||||
|
[
|
||||||
|
[e5 [b4 c5] d5 [c5 b4]]
|
||||||
|
[a4 [a4 c5] e5 [d5 c5]]
|
||||||
|
[b4 [~ c5] d5 e5]
|
||||||
|
[c5 a4 a4 ~]
|
||||||
|
[[~ d5] [~ f5] a5 [g5 f5]]
|
||||||
|
[e5 [~ c5] e5 [d5 c5]]
|
||||||
|
[b4 [b4 c5] d5 e5]
|
||||||
|
[c5 a4 a4 ~]
|
||||||
|
],[
|
||||||
|
[[e2 e3]*4]
|
||||||
|
[[a2 a3]*4]
|
||||||
|
[[g#2 g#3]*2 [e2 e3]*2]
|
||||||
|
[a2 a3 a2 a3 a2 a3 b1 c2]
|
||||||
|
[[d2 d3]*4]
|
||||||
|
[[c2 c3]*4]
|
||||||
|
[[b1 b2]*2 [e2 e3]*2]
|
||||||
|
[[a1 a2]*4]
|
||||||
|
]
|
||||||
|
]/16\`)`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
The snippet above is enclosed in backticks (`), which allows you to write multi-line strings.
|
||||||
|
You can also use double quotes (") for single line mini notation.
|
||||||
|
|
||||||
|
## Sequences
|
||||||
|
|
||||||
|
We can play more notes by separating them with spaces:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("e5 b4 d5 c5")`} />
|
||||||
|
|
||||||
|
Here, those four notes are squashed into one cycle, so each note is a quarter second long.
|
||||||
|
Try adding or removing notes and notice how the tempo changes!
|
||||||
|
|
||||||
|
## Division
|
||||||
|
|
||||||
|
We can slow the sequence down by enclosing it in brackets and dividing it by a number:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("[e5 b4 d5 c5]/2")`} />
|
||||||
|
|
||||||
|
The division by two means that the sequence will be played over the course of two cycles.
|
||||||
|
You can also use decimal numbers for any tempo you like.
|
||||||
|
|
||||||
|
## Angle Brackets
|
||||||
|
|
||||||
|
Using angle brackets, we can define the sequence length based on the number of children:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("<e5 b4 d5 c5>")`} />
|
||||||
|
|
||||||
|
The above snippet is the same as:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("[e5 b4 d5 c5]/4")`} />
|
||||||
|
|
||||||
|
The advantage of the angle brackets, is that we can add more children without needing to change the number at the end.
|
||||||
|
|
||||||
|
## Multiplication
|
||||||
|
|
||||||
|
Contrary to division, a sequence can be sped up by multiplying it by a number:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("[e5 b4 d5 c5]*2")`} />
|
||||||
|
|
||||||
|
The multiplication by 2 here means that the sequence will play twice a cycle.
|
||||||
|
|
||||||
|
## Bracket Nesting
|
||||||
|
|
||||||
|
To create more interesting rhythms, you can nest sequences with brackets, like this:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("e5 [b4 c5] d5 [c5 b4]")`} />
|
||||||
|
|
||||||
|
## Rests
|
||||||
|
|
||||||
|
The "~" represents a rest:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("[b4 [~ c5] d5 e5]")`} />
|
||||||
|
|
||||||
|
## Parallel
|
||||||
|
|
||||||
|
Using commas, we can play chords:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("g3,b3,e4")`} />
|
||||||
|
|
||||||
|
To play multiple chords in a sequence, we have to wrap them in brackets:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>")`} />
|
||||||
|
|
||||||
|
## Elongation
|
||||||
|
|
||||||
|
With the "@" symbol, we can specify temporal "weight" of a sequence child:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>")`} />
|
||||||
|
|
||||||
|
Here, the first chord has a weight of 2, making it twice the length of the other chords. The default weight is 1.
|
||||||
|
|
||||||
|
## Replication
|
||||||
|
|
||||||
|
Using "!" we can repeat without speeding up:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>")`} />
|
||||||
|
|
||||||
|
In essence, the `x!n` is like a shortcut for `[x*n]@n`.
|
||||||
|
|
||||||
|
## Euclidian
|
||||||
|
|
||||||
|
Using round brackets, we can create rhythmical sub-divisions based on three parameters: beats, segments and offset.
|
||||||
|
The first parameter controls how may beats will be played.
|
||||||
|
The second parameter controls the total amount of segments the beats will be distributed over.
|
||||||
|
The third (optional) parameter controls the starting position for distributing the beats.
|
||||||
|
One popular Euclidian rhythm (going by various names, such as "Pop Clave") is "(3,8,1)" or simply "(3,8)",
|
||||||
|
resulting in a rhythmical structure of "x ~ ~ x ~ ~ x ~" (3 beats over 8 segments, starting on position 1).
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("e5(2,8) b4(3,8) d5(2,8) c5(3,8)").slow(4)`} />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
# Synths, Samples & Effects
|
||||||
|
|
||||||
|
Let's take a closer look at how we can control synths, sounds and effects.
|
||||||
|
|
||||||
## Synths
|
## Synths
|
||||||
|
|
||||||
So far, all the mini notation examples all used the same sound, which is kind of boring.
|
So far, all the mini notation examples all used the same sound, which is kind of boring.
|
||||||
We can change the sound, using the `s` function:
|
We can change the sound, using the `s` function:
|
||||||
|
|
||||||
<MiniRepl tune={`note("c2 <eb2 <g2 g1>>").s('sawtooth').out()`} />
|
<MiniRepl tune={`note("c2 <eb2 <g2 g1>>").s('sawtooth')`} />
|
||||||
|
|
||||||
Here, we are wrapping our notes inside `note` and set the sound using `s`, connected by a dot.
|
Here, we are wrapping our notes inside `note` and set the sound using `s`, connected by a dot.
|
||||||
|
|
||||||
Those functions are only 2 of many ways to alter the properties, or _params_ of a sound.
|
Those functions are only 2 of many ways to alter the properties, or _params_ of a sound.
|
||||||
The power of patterns allows us to sequence any _param_ independently:
|
The power of patterns allows us to sequence any _param_ independently:
|
||||||
|
|
||||||
<MiniRepl tune={`note("c2 <eb2 <g2 g1>>").s("<sawtooth square triangle>").out()`} />
|
<MiniRepl tune={`note("c2 <eb2 <g2 g1>>").s("<sawtooth square triangle>")`} />
|
||||||
|
|
||||||
Now we not only pattern the notes, but the sound as well!
|
Now we not only pattern the notes, but the sound as well!
|
||||||
`sawtooth` `square` and `triangle` are the basic waveforms available in `s`.
|
`sawtooth` `square` and `triangle` are the basic waveforms available in `s`.
|
||||||
@ -247,14 +287,14 @@ You can control the envelope of a synth using the `attack`, `decay`, `sustain` a
|
|||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`note("c2 <eb2 <g2 g1>>").s('sawtooth')
|
tune={`note("c2 <eb2 <g2 g1>>").s('sawtooth')
|
||||||
.attack(.1).decay(.1).sustain(.2).release(.1).out()`}
|
.attack(.1).decay(.1).sustain(.2).release(.1)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## Samples
|
## Samples
|
||||||
|
|
||||||
Besides Synths, `s` can also play back samples:
|
Besides Synths, `s` can also play back samples:
|
||||||
|
|
||||||
<MiniRepl tune={`s("bd sd,hh*8,misc/2").out()`} />
|
<MiniRepl tune={`s("bd sd,hh*8,misc/2")`} />
|
||||||
|
|
||||||
To know which sounds are available, open the [default sample map](https://strudel.tidalcycles.org/EmuSP12.json)
|
To know which sounds are available, open the [default sample map](https://strudel.tidalcycles.org/EmuSP12.json)
|
||||||
|
|
||||||
@ -268,7 +308,7 @@ You can load your own sample map like this:
|
|||||||
sd: 'sd/rytm-01-classic.wav',
|
sd: 'sd/rytm-01-classic.wav',
|
||||||
hh: 'hh27/000_hh27closedhh.wav',
|
hh: 'hh27/000_hh27closedhh.wav',
|
||||||
}, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
|
}, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
|
||||||
s("bd sd,hh*8").out()`}
|
s("bd sd,hh*8")`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
The `samples` function takes an object that maps sound names to audio file paths.
|
The `samples` function takes an object that maps sound names to audio file paths.
|
||||||
@ -282,7 +322,7 @@ Because github is a popular choice to dump samples, there is a shortcut for that
|
|||||||
sd: 'sd/rytm-01-classic.wav',
|
sd: 'sd/rytm-01-classic.wav',
|
||||||
hh: 'hh27/000_hh27closedhh.wav',
|
hh: 'hh27/000_hh27closedhh.wav',
|
||||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||||
s("bd sd,hh*8").out()`}
|
s("bd sd,hh*8")`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
The format is `github:user/repo/branch/`.
|
The format is `github:user/repo/branch/`.
|
||||||
@ -297,7 +337,7 @@ It is also possible, to declare multiple files for one sound, using the array no
|
|||||||
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
||||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||||
s("<bd:0 bd:1>,~ <sd:0 sd:1>,[hh:0 hh:1]*2").out()`}
|
s("<bd:0 bd:1>,~ <sd:0 sd:1>,[hh:0 hh:1]*2")`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
The `:0` `:1` etc. are the indices of the array.
|
The `:0` `:1` etc. are the indices of the array.
|
||||||
@ -309,7 +349,7 @@ The sample number can also be set using `n`:
|
|||||||
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
||||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||||
s("bd,~ sd,hh*4").n("<0 1>").out()`}
|
s("bd,~ sd,hh*4").n("<0 1>")`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
### Pitched Sounds
|
### Pitched Sounds
|
||||||
@ -320,7 +360,7 @@ For pitched sounds, you can use `note`, just like with synths:
|
|||||||
tune={`samples({
|
tune={`samples({
|
||||||
"gtr": 'gtr/0001_cleanC.wav',
|
"gtr": 'gtr/0001_cleanC.wav',
|
||||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||||
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').gain(.5).out()`}
|
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').gain(.5)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
Here, the guitar samples will overlap, because they always play till the end.
|
Here, the guitar samples will overlap, because they always play till the end.
|
||||||
@ -331,7 +371,7 @@ If we want them to behave more like a synth, we can add `clip(1)`:
|
|||||||
"gtr": 'gtr/0001_cleanC.wav',
|
"gtr": 'gtr/0001_cleanC.wav',
|
||||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||||
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').clip(1)
|
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').clip(1)
|
||||||
.gain(.5).out()`}
|
.gain(.5)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
### Base Pitch
|
### Base Pitch
|
||||||
@ -344,7 +384,7 @@ If we have 2 samples with different base pitches, we can make them in tune by sp
|
|||||||
"moog": { 'g3': 'moog/005_Mighty%20Moog%20G3.wav' },
|
"moog": { 'g3': 'moog/005_Mighty%20Moog%20G3.wav' },
|
||||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||||
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s("gtr,moog").clip(1)
|
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s("gtr,moog").clip(1)
|
||||||
.gain(.5).out()`}
|
.gain(.5)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
If a sample has no pitch set, `c3` is the default.
|
If a sample has no pitch set, `c3` is the default.
|
||||||
@ -360,7 +400,7 @@ We can also declare different samples for different regions of the keyboard:
|
|||||||
}}, 'github:tidalcycles/Dirt-Samples/master/');
|
}}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||||
note("g2!2 <bb2 c3>!2, <c4@3 [<eb4 bb3> g4 f4]>")
|
note("g2!2 <bb2 c3>!2, <c4@3 [<eb4 bb3> g4 f4]>")
|
||||||
.s('moog').clip(1)
|
.s('moog').clip(1)
|
||||||
.gain(.5).out()`}
|
.gain(.5)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
The sampler will always pick the closest matching sample for the current note!
|
The sampler will always pick the closest matching sample for the current note!
|
||||||
@ -407,33 +447,71 @@ Wether you're using a synth or a sample, you can apply these effects:
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
# Core API
|
# JavaScript API
|
||||||
|
|
||||||
While the mini notation is powerful on its own, there is much more to discover.
|
While the mini notation is powerful on its own, there is much more to discover.
|
||||||
Internally, the mini notation will expand to use the actual functional JavaScript API.
|
Internally, the mini notation will expand to use the actual functional JavaScript API.
|
||||||
|
|
||||||
|
For example, this Pattern in Mini Notation:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note("c3 eb3 g3")`} />
|
||||||
|
|
||||||
|
is equivalent to this Pattern without Mini Notation:
|
||||||
|
|
||||||
|
<MiniRepl tune={`note(seq(c3, eb3, g3))`} />
|
||||||
|
|
||||||
|
Similarly, there is an equivalent function for every aspect of the mini notation.
|
||||||
|
|
||||||
|
Which representation to use is a matter of context. As a rule of thumb, you can think of the JavaScript API
|
||||||
|
to fit better for the larger context, while mini notation is more practical for individiual rhythms.
|
||||||
|
|
||||||
|
## Limits of Mini Notation
|
||||||
|
|
||||||
|
While the Mini Notation is a powerful way to write rhythms shortly, it also has its limits. Take this example:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
tune={`stack(
|
||||||
|
note("c2 eb2(3,8)").s('sawtooth').cutoff(800),
|
||||||
|
s("bd,~ sd,hh*4")
|
||||||
|
)`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
Here, we are using mini notation for the individual rhythms, while using the function `stack` to mix them.
|
||||||
|
While stack is also available as `,` in mini notation, we cannot use it here, because we have different types of sounds.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
Notes are automatically available as variables:
|
Notes are automatically available as variables:
|
||||||
|
|
||||||
<MiniRepl tune={`seq(d4, fs4, a4)`} />
|
<MiniRepl tune={`note(seq(d4, fs4, a4)) // note("d4 f#4 a4")`} />
|
||||||
|
|
||||||
An important difference to the mini notation:
|
An important difference to the mini notation:
|
||||||
For sharp notes, the letter "s" is used instead of "#", because JavaScript does not support "#" in a variable name.
|
For sharp notes, the letter "s" is used instead of "#", because JavaScript does not support "#" in a variable name.
|
||||||
|
|
||||||
The above is the same as:
|
The above is the same as:
|
||||||
|
|
||||||
<MiniRepl tune={`seq('d4', 'f#4', 'a4')`} />
|
<MiniRepl tune={`note(seq('d4', 'f#4', 'a4'))`} />
|
||||||
|
|
||||||
Using strings, you can also use "#".
|
Using strings, you can also use "#".
|
||||||
|
|
||||||
|
## Alternative Syntax
|
||||||
|
|
||||||
|
In the above example, we are nesting a function inside a function, which makes reading the parens a little more difficult.
|
||||||
|
To avoid getting to many nested parens, there is an alternative syntax to add a type to a pattern:
|
||||||
|
|
||||||
|
<MiniRepl tune={`seq(d4, fs4, a4).note()`} />
|
||||||
|
|
||||||
|
You can use this with any function that declares a type (like `n`, `s`, `note`, `freq` etc), just make sure to leave the parens empty!
|
||||||
|
|
||||||
## Pattern Factories
|
## Pattern Factories
|
||||||
|
|
||||||
The following functions will return a pattern.
|
The following functions will return a pattern.
|
||||||
|
|
||||||
|
<!--
|
||||||
{{ 'pure' | jsdoc }}
|
{{ 'pure' | jsdoc }}
|
||||||
|
|
||||||
Most of the time, you won't need that function as input values of pattern creating functions are purified by default.
|
Most of the time, you won't need that function as input values of pattern creating functions are purified by default.
|
||||||
|
-->
|
||||||
|
|
||||||
{{ 'cat' | jsdoc }}
|
{{ 'cat' | jsdoc }}
|
||||||
|
|
||||||
@ -477,7 +555,7 @@ You can freely mix JS patterns, mini patterns and values! For example, this patt
|
|||||||
stack(a3,c3,e4),
|
stack(a3,c3,e4),
|
||||||
stack(b3,d3,fs4),
|
stack(b3,d3,fs4),
|
||||||
stack(b3,e4,g4)
|
stack(b3,e4,g4)
|
||||||
)`}
|
).note()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
...is equivalent to:
|
...is equivalent to:
|
||||||
@ -488,12 +566,12 @@ You can freely mix JS patterns, mini patterns and values! For example, this patt
|
|||||||
"a3,c3,e4",
|
"a3,c3,e4",
|
||||||
"b3,d3,f#4",
|
"b3,d3,f#4",
|
||||||
"b3,e4,g4"
|
"b3,e4,g4"
|
||||||
)`}
|
).note()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
... as well as:
|
... as well as:
|
||||||
|
|
||||||
<MiniRepl tune={`"<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>"`} />
|
<MiniRepl tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>")`} />
|
||||||
|
|
||||||
While mini notation is almost always shorter, it only has a handful of modifiers: \* / ! @.
|
While mini notation is almost always shorter, it only has a handful of modifiers: \* / ! @.
|
||||||
When using JS patterns, there is a lot more you can do.
|
When using JS patterns, there is a lot more you can do.
|
||||||
@ -640,13 +718,13 @@ The Tonal API, uses [tonaljs](https://github.com/tonaljs/tonal) to provide helpe
|
|||||||
|
|
||||||
Transposes all notes to the given number of semitones:
|
Transposes all notes to the given number of semitones:
|
||||||
|
|
||||||
<MiniRepl tune={`"c2 c3".fast(2).transpose("<0 -2 5 3>".slow(2)).transpose(0)`} />
|
<MiniRepl tune={`"c2 c3".fast(2).transpose("<0 -2 5 3>".slow(2)).note()`} />
|
||||||
|
|
||||||
This method gets really exciting when we use it with a pattern as above.
|
This method gets really exciting when we use it with a pattern as above.
|
||||||
|
|
||||||
Instead of numbers, scientific interval notation can be used as well:
|
Instead of numbers, scientific interval notation can be used as well:
|
||||||
|
|
||||||
<MiniRepl tune={`"c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).transpose(1)`} />
|
<MiniRepl tune={`"c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).note()`} />
|
||||||
|
|
||||||
### scale(name)
|
### scale(name)
|
||||||
|
|
||||||
@ -654,7 +732,8 @@ Turns numbers into notes in the scale (zero indexed). Also sets scale for other
|
|||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"0 2 4 6 4 2"
|
tune={`"0 2 4 6 4 2"
|
||||||
.scale(seq('C2 major', 'C2 minor').slow(2))`}
|
.scale(seq('C2 major', 'C2 minor').slow(2))
|
||||||
|
.note()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
Note that the scale root is octaved here. You can also omit the octave, then index zero will default to octave 3.
|
Note that the scale root is octaved here. You can also omit the octave, then index zero will default to octave 3.
|
||||||
@ -668,14 +747,15 @@ Transposes notes inside the scale by the number of steps:
|
|||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"-8 [2,4,6]"
|
tune={`"-8 [2,4,6]"
|
||||||
.scale('C4 bebop major')
|
.scale('C4 bebop major')
|
||||||
.scaleTranspose("<0 -1 -2 -3 -4 -5 -6 -4>")`}
|
.scaleTranspose("<0 -1 -2 -3 -4 -5 -6 -4>")
|
||||||
|
.note()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
### voicings(range?)
|
### voicings(range?)
|
||||||
|
|
||||||
Turns chord symbols into voicings, using the smoothest voice leading possible:
|
Turns chord symbols into voicings, using the smoothest voice leading possible:
|
||||||
|
|
||||||
<MiniRepl tune={`stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>")`} />
|
<MiniRepl tune={`stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>").note()`} />
|
||||||
|
|
||||||
<!-- TODO: use voicing collection as first param + patternify. -->
|
<!-- TODO: use voicing collection as first param + patternify. -->
|
||||||
|
|
||||||
@ -683,7 +763,7 @@ Turns chord symbols into voicings, using the smoothest voice leading possible:
|
|||||||
|
|
||||||
Turns chord symbols into root notes of chords in given octave.
|
Turns chord symbols into root notes of chords in given octave.
|
||||||
|
|
||||||
<MiniRepl tune={`"<C^7 A7b13 Dm7 G7>".rootNotes(3)`} />
|
<MiniRepl tune={`"<C^7 A7b13 Dm7 G7>".rootNotes(3).note()`} />
|
||||||
|
|
||||||
Together with layer, struct and voicings, this can be used to create a basic backing track:
|
Together with layer, struct and voicings, this can be used to create a basic backing track:
|
||||||
|
|
||||||
@ -691,7 +771,7 @@ Together with layer, struct and voicings, this can be used to create a basic bac
|
|||||||
tune={`"<C^7 A7b13 Dm7 G7>".layer(
|
tune={`"<C^7 A7b13 Dm7 G7>".layer(
|
||||||
x => x.voicings(['d3','g4']).struct("~ x"),
|
x => x.voicings(['d3','g4']).struct("~ x"),
|
||||||
x => x.rootNotes(2).tone(synth(osc('sawtooth4')).chain(out()))
|
x => x.rootNotes(2).tone(synth(osc('sawtooth4')).chain(out()))
|
||||||
)`}
|
).note()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- TODO: use range instead of octave. -->
|
<!-- TODO: use range instead of octave. -->
|
||||||
@ -755,152 +835,3 @@ The following functions can be used with superdirt:
|
|||||||
`s n note freq channel orbit cutoff resonance hcutoff hresonance bandf bandq djf vowel cut begin end loop fadeTime speed unitA gain amp accelerate crush coarse delay lock leslie lrate lsize pan panspan pansplay room size dry shape squiz waveloss attack decay octave detune tremolodepth`
|
`s n note freq channel orbit cutoff resonance hcutoff hresonance bandf bandq djf vowel cut begin end loop fadeTime speed unitA gain amp accelerate crush coarse delay lock leslie lrate lsize pan panspan pansplay room size dry shape squiz waveloss attack decay octave detune tremolodepth`
|
||||||
|
|
||||||
Please refer to [Tidal Docs](https://tidalcycles.org/) for more info.
|
Please refer to [Tidal Docs](https://tidalcycles.org/) for more info.
|
||||||
|
|
||||||
# Webdirt API (deprecated)
|
|
||||||
|
|
||||||
You can use the powerful sampling engine [Webdirt](https://github.com/dktr0/WebDirt) with Strudel.
|
|
||||||
|
|
||||||
{{ 'Pattern.webdirt' | jsdoc }}
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
|
|
||||||
# Tone API (deprecated)
|
|
||||||
|
|
||||||
The Tone API uses Tone.js instruments ands effects to create sounds.
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`stack(
|
|
||||||
"[c5 c5 bb4 c5] [~ g4 ~ g4] [c5 f5 e5 c5] ~"
|
|
||||||
.tone(synth(adsr(0,.1,0,0)).chain(out())),
|
|
||||||
"[c2 c3]*8"
|
|
||||||
.tone(synth({
|
|
||||||
...osc('sawtooth'),
|
|
||||||
...adsr(0,.1,0.4,0)
|
|
||||||
}).chain(lowpass(300), out()))
|
|
||||||
).slow(4)`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
### tone(instrument)
|
|
||||||
|
|
||||||
To change the instrument of a pattern, you can pass any [Tone.js Source](https://tonejs.github.io/docs/14.7.77/index.html) to .tone:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
|
||||||
.tone(new FMSynth().toDestination())`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
While this works, it is a little bit verbose. To simplify things, all Tone Synths have a shortcut:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const amsynth = (options) => new AMSynth(options);
|
|
||||||
const duosynth = (options) => new DuoSynth(options);
|
|
||||||
const fmsynth = (options) => new FMSynth(options);
|
|
||||||
const membrane = (options) => new MembraneSynth(options);
|
|
||||||
const metal = (options) => new MetalSynth(options);
|
|
||||||
const monosynth = (options) => new MonoSynth(options);
|
|
||||||
const noise = (options) => new NoiseSynth(options);
|
|
||||||
const pluck = (options) => new PluckSynth(options);
|
|
||||||
const polysynth = (options) => new PolySynth(options);
|
|
||||||
const synth = (options) => new Synth(options);
|
|
||||||
const sampler = (options, baseUrl?) => new Sampler(options); // promisified, see below
|
|
||||||
const players = (options, baseUrl?) => new Sampler(options); // promisified, see below
|
|
||||||
```
|
|
||||||
|
|
||||||
### sampler
|
|
||||||
|
|
||||||
With sampler, you can create tonal instruments from samples:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`sampler({
|
|
||||||
C5: 'https://freesound.org/data/previews/536/536549_11935698-lq.mp3'
|
|
||||||
}).then(kalimba =>
|
|
||||||
saw.struct("x*8").mul(16).round()
|
|
||||||
.legato(4).scale('D dorian').slow(2)
|
|
||||||
.tone(kalimba.toDestination())
|
|
||||||
)`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
The sampler function promisifies [Tone.js Sampler](https://tonejs.github.io/docs/14.7.77/Sampler).
|
|
||||||
|
|
||||||
Note that this function currently only works with this promise notation, but in the future,
|
|
||||||
it will be possible to use async instruments in a synchronous fashion.
|
|
||||||
|
|
||||||
### players
|
|
||||||
|
|
||||||
With players, you can create sound banks:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`players({
|
|
||||||
bd: 'samples/tidal/bd/BT0A0D0.wav',
|
|
||||||
sn: 'samples/tidal/sn/ST0T0S3.wav',
|
|
||||||
hh: 'samples/tidal/hh/000_hh3closedhh.wav'
|
|
||||||
}, 'https://loophole-letters.vercel.app/')
|
|
||||||
.then(drums=>
|
|
||||||
"bd hh sn hh".tone(drums.toDestination())
|
|
||||||
)
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
The sampler function promisifies [Tone.js Players](https://tonejs.github.io/docs/14.7.77/Players).
|
|
||||||
|
|
||||||
Note that this function currently only works with this promise notation, but in the future,
|
|
||||||
it will be possible to use async instruments in a synchronous fashion.
|
|
||||||
|
|
||||||
### out
|
|
||||||
|
|
||||||
Shortcut for Tone.Destination. Intended to be used with Tone's .chain:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
|
||||||
.tone(membrane().chain(out()))`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
This alone is not really useful, so read on..
|
|
||||||
|
|
||||||
### vol(volume)
|
|
||||||
|
|
||||||
Helper that returns a Gain Node with the given volume. Intended to be used with Tone's .chain:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
|
||||||
.tone(noise().chain(vol(0.5), out()))`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
### osc(type)
|
|
||||||
|
|
||||||
Helper to set the waveform of a synth, monosynth or polysynth:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
|
||||||
.tone(synth(osc('sawtooth4')).chain(out()))`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
The base types are `sine`, `square`, `sawtooth`, `triangle`. You can also append a number between 1 and 32 to reduce the harmonic partials.
|
|
||||||
|
|
||||||
### lowpass(cutoff)
|
|
||||||
|
|
||||||
Helper that returns a Filter Node of type lowpass with the given cutoff. Intended to be used with Tone's .chain:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
|
||||||
.tone(synth(osc('sawtooth')).chain(lowpass(800), out()))`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
### highpass(cutoff)
|
|
||||||
|
|
||||||
Helper that returns a Filter Node of type highpass with the given cutoff. Intended to be used with Tone's .chain:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
|
||||||
.tone(synth(osc('sawtooth')).chain(highpass(2000), out()))`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
### adsr
|
|
||||||
|
|
||||||
Helper to set the envelope of a Tone.js instrument. Intended to be used with Tone's .set:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
|
||||||
.tone(synth(adsr(0,.1,0,0)).chain(out()))`}
|
|
||||||
/>
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user