mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-25 15:06:05 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 50f04e8cc8 | |||
| 5e3218a67b |
+23
-74
@@ -114,7 +114,7 @@ export const { n } = registerControl('n');
|
|||||||
*
|
*
|
||||||
* - a letter (a-g or A-G)
|
* - a letter (a-g or A-G)
|
||||||
* - optional accidentals (b or #)
|
* - optional accidentals (b or #)
|
||||||
* - optional (possibly negative) octave number (0-9). Defaults to 3
|
* - optional octave number (0-9). Defaults to 3
|
||||||
*
|
*
|
||||||
* Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5`
|
* Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5`
|
||||||
*
|
*
|
||||||
@@ -127,8 +127,6 @@ export const { n } = registerControl('n');
|
|||||||
* note("c4 a4 f4 e4")
|
* note("c4 a4 f4 e4")
|
||||||
* @example
|
* @example
|
||||||
* note("60 69 65 64")
|
* note("60 69 65 64")
|
||||||
* @example
|
|
||||||
* note("fbb1 a#0 cbbb-1 e##-2").sound("saw")
|
|
||||||
*/
|
*/
|
||||||
export const { note } = registerControl(['note', 'n']);
|
export const { note } = registerControl(['note', 'n']);
|
||||||
|
|
||||||
@@ -144,8 +142,8 @@ export const { note } = registerControl(['note', 'n']);
|
|||||||
*/
|
*/
|
||||||
export const { accelerate } = registerControl('accelerate');
|
export const { accelerate } = registerControl('accelerate');
|
||||||
/**
|
/**
|
||||||
* Sets the velocity from 0 to 1. Is multiplied together with gain.
|
|
||||||
*
|
*
|
||||||
|
* Sets the velocity from 0 to 1. Is multiplied together with gain.
|
||||||
* @name velocity
|
* @name velocity
|
||||||
* @example
|
* @example
|
||||||
* s("hh*8")
|
* s("hh*8")
|
||||||
@@ -257,7 +255,7 @@ export const { fmenv } = registerControl('fmenv');
|
|||||||
export const { fmattack } = registerControl('fmattack');
|
export const { fmattack } = registerControl('fmattack');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waveform of the fm modulator
|
* waveform of the fm modulator
|
||||||
*
|
*
|
||||||
* @name fmwave
|
* @name fmwave
|
||||||
* @param {number | Pattern} wave waveform
|
* @param {number | Pattern} wave waveform
|
||||||
@@ -391,7 +389,7 @@ export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], '
|
|||||||
// ['bpq'],
|
// ['bpq'],
|
||||||
export const { bandq, bpq } = registerControl('bandq', 'bpq');
|
export const { bandq, bpq } = registerControl('bandq', 'bpq');
|
||||||
/**
|
/**
|
||||||
* A pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample.
|
* a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample.
|
||||||
*
|
*
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @name begin
|
* @name begin
|
||||||
@@ -452,7 +450,7 @@ export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb');
|
|||||||
*/
|
*/
|
||||||
export const { loopEnd, loope } = registerControl('loopEnd', 'loope');
|
export const { loopEnd, loope } = registerControl('loopEnd', 'loope');
|
||||||
/**
|
/**
|
||||||
* Bit crusher effect.
|
* bit crusher effect.
|
||||||
*
|
*
|
||||||
* @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).
|
||||||
@@ -463,7 +461,7 @@ export const { loopEnd, loope } = registerControl('loopEnd', 'loope');
|
|||||||
// ['clhatdecay'],
|
// ['clhatdecay'],
|
||||||
export const { crush } = registerControl('crush');
|
export const { crush } = registerControl('crush');
|
||||||
/**
|
/**
|
||||||
* Fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers
|
* 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.
|
||||||
@@ -474,7 +472,7 @@ export const { crush } = registerControl('crush');
|
|||||||
export const { coarse } = registerControl('coarse');
|
export const { coarse } = registerControl('coarse');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modulate the amplitude of a sound with a continuous waveform
|
* modulate the amplitude of a sound with a continuous waveform
|
||||||
*
|
*
|
||||||
* @name tremolo
|
* @name tremolo
|
||||||
* @synonyms trem
|
* @synonyms trem
|
||||||
@@ -486,7 +484,7 @@ export const { coarse } = registerControl('coarse');
|
|||||||
export const { tremolo } = registerControl(['tremolo', 'tremolodepth', 'tremoloskew', 'tremolophase'], 'trem');
|
export const { tremolo } = registerControl(['tremolo', 'tremolodepth', 'tremoloskew', 'tremolophase'], 'trem');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modulate the amplitude of a sound with a continuous waveform
|
* modulate the amplitude of a sound with a continuous waveform
|
||||||
*
|
*
|
||||||
* @name tremolosync
|
* @name tremolosync
|
||||||
* @synonyms tremsync
|
* @synonyms tremsync
|
||||||
@@ -501,7 +499,7 @@ export const { tremolosync } = registerControl(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Depth of amplitude modulation
|
* depth of amplitude modulation
|
||||||
*
|
*
|
||||||
* @name tremolodepth
|
* @name tremolodepth
|
||||||
* @synonyms tremdepth
|
* @synonyms tremdepth
|
||||||
@@ -512,7 +510,7 @@ export const { tremolosync } = registerControl(
|
|||||||
*/
|
*/
|
||||||
export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth');
|
export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth');
|
||||||
/**
|
/**
|
||||||
* Alter the shape of the modulation waveform
|
* alter the shape of the modulation waveform
|
||||||
*
|
*
|
||||||
* @name tremoloskew
|
* @name tremoloskew
|
||||||
* @synonyms tremskew
|
* @synonyms tremskew
|
||||||
@@ -524,7 +522,7 @@ export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth');
|
|||||||
export const { tremoloskew } = registerControl('tremoloskew', 'tremskew');
|
export const { tremoloskew } = registerControl('tremoloskew', 'tremskew');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alter the phase of the modulation waveform
|
* alter the phase of the modulation waveform
|
||||||
*
|
*
|
||||||
* @name tremolophase
|
* @name tremolophase
|
||||||
* @synonyms tremphase
|
* @synonyms tremphase
|
||||||
@@ -536,7 +534,7 @@ export const { tremoloskew } = registerControl('tremoloskew', 'tremskew');
|
|||||||
export const { tremolophase } = registerControl('tremolophase', 'tremphase');
|
export const { tremolophase } = registerControl('tremolophase', 'tremphase');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shape of amplitude modulation
|
* shape of amplitude modulation
|
||||||
*
|
*
|
||||||
* @name tremoloshape
|
* @name tremoloshape
|
||||||
* @synonyms tremshape
|
* @synonyms tremshape
|
||||||
@@ -547,7 +545,7 @@ export const { tremolophase } = registerControl('tremolophase', 'tremphase');
|
|||||||
*/
|
*/
|
||||||
export const { tremoloshape } = registerControl('tremoloshape', 'tremshape');
|
export const { tremoloshape } = registerControl('tremoloshape', 'tremshape');
|
||||||
/**
|
/**
|
||||||
* Filter overdrive for supported filter types
|
* filter overdrive for supported filter types
|
||||||
*
|
*
|
||||||
* @name drive
|
* @name drive
|
||||||
* @param {number | Pattern} amount
|
* @param {number | Pattern} amount
|
||||||
@@ -558,9 +556,7 @@ export const { tremoloshape } = registerControl('tremoloshape', 'tremshape');
|
|||||||
export const { drive } = registerControl('drive');
|
export const { drive } = registerControl('drive');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modulate the amplitude of an orbit to create a "sidechain" like effect.
|
* modulate the amplitude of an orbit to create a "sidechain" like effect
|
||||||
*
|
|
||||||
* Can be applied to multiple orbits with the ':' mininotation, e.g. `duckorbit("2:3")`
|
|
||||||
*
|
*
|
||||||
* @name duckorbit
|
* @name duckorbit
|
||||||
* @synonyms duck
|
* @synonyms duck
|
||||||
@@ -568,77 +564,30 @@ export const { drive } = registerControl('drive');
|
|||||||
* @example
|
* @example
|
||||||
* $: n(run(16)).scale("c:minor:pentatonic").s("sawtooth").delay(.7).orbit(2)
|
* $: n(run(16)).scale("c:minor:pentatonic").s("sawtooth").delay(.7).orbit(2)
|
||||||
* $: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth(1)
|
* $: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth(1)
|
||||||
* @example
|
|
||||||
* $: n(run(16)).scale("c:minor:pentatonic").s("sawtooth").delay(.7).orbit(2)
|
|
||||||
* $: s("hh*16").orbit(3)
|
|
||||||
* $: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit("2:3").duckattack(0.2).duckdepth(1)
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { duck } = registerControl('duckorbit', 'duck');
|
export const { duck } = registerControl('duckorbit', 'duck');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of ducking applied to target orbit
|
* the amount of ducking applied to target orbit
|
||||||
*
|
|
||||||
* Can vary across orbits with the ':' mininotation, e.g. `duckdepth("0.3:0.1")`.
|
|
||||||
* Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`.
|
|
||||||
*
|
*
|
||||||
* @name duckdepth
|
* @name duckdepth
|
||||||
* @param {number | Pattern} depth depth of modulation from 0 to 1
|
* @param {number | Pattern} depth depth of modulation from 0 to 1
|
||||||
* @example
|
* @example
|
||||||
* stack( n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2), s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth("<1 .9 .6 0>"))
|
* stack( n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2), s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack(0.2).duckdepth("<1 .9 .6 0>"))
|
||||||
* @example
|
|
||||||
* $: n(run(16)).scale("c:minor:pentatonic").s("sawtooth").delay(.7).orbit(2)
|
|
||||||
* $: s("hh*16").orbit(3)
|
|
||||||
* $: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit("2:3").duckattack(0.2).duckdepth("1:0.5")
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const { duckdepth } = registerControl('duckdepth');
|
export const { duckdepth } = registerControl('duckdepth');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time required for the ducked signal(s) to reach their lowest volume.
|
* the attack time of the duck effect
|
||||||
* Can be used to prevent clicking or for creative rhythmic effects.
|
|
||||||
*
|
|
||||||
* Can vary across orbits with the ':' mininotation, e.g. `duckonset("0:0.003")`.
|
|
||||||
* Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`.
|
|
||||||
*
|
|
||||||
* @name duckonset
|
|
||||||
* @synonyms duckons
|
|
||||||
*
|
|
||||||
* @param {number | Pattern} time The onset time in seconds
|
|
||||||
* @example
|
|
||||||
* // Clicks
|
|
||||||
* sound: freq("63.2388").s("sine").orbit(2).gain(4)
|
|
||||||
* duckerWithClick: s("bd*4").duckorbit(2).duckattack(0.3).duckonset(0).postgain(0)
|
|
||||||
* @example
|
|
||||||
* // No clicks
|
|
||||||
* sound: freq("63.2388").s("sine").orbit(2).gain(4)
|
|
||||||
* duckerWithoutClick: s("bd*4").duckorbit(2).duckattack(0.3).duckonset(0.01).postgain(0)
|
|
||||||
* @example
|
|
||||||
* // Rhythmic
|
|
||||||
* noise: s("pink").distort("2:1").orbit(4) // used rhythmically with 0.3 onset below
|
|
||||||
* hhat: s("hh*16").orbit(7)
|
|
||||||
* ducker: s("bd*4").bank("tr909").duckorbit("4:7").duckonset("0.3:0.003").duckattack(0.25)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const { duckonset } = registerControl('duckonset', 'duckons');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time required for the ducked signal(s) to return to their normal volume.
|
|
||||||
*
|
|
||||||
* Can vary across orbits with the ':' mininotation, e.g. `duckonset("0:0.003")`.
|
|
||||||
* Note: this requires first applying the effect to multiple orbits with e.g. `duckorbit("2:3")`.
|
|
||||||
*
|
*
|
||||||
* @name duckattack
|
* @name duckattack
|
||||||
* @synonyms duckatt
|
* @synonyms duckatt
|
||||||
*
|
* @param {number | Pattern} time
|
||||||
* @param {number | Pattern} time The attack time in seconds
|
|
||||||
* @example
|
* @example
|
||||||
* sound: n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2)
|
* stack( n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2), s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack("<0.2 0 0.4>").duckdepth(1))
|
||||||
* ducker: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit(2).duckattack("<0.2 0 0.4>").duckdepth(1)
|
|
||||||
* @example
|
|
||||||
* moreduck: n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2)
|
|
||||||
* lessduck: s("hh*16").orbit(5)
|
|
||||||
* ducker: s("bd:4!4").beat("0,4,8,11,14",16).duckorbit("2:5").duckattack("0.4:0.1")
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const { duckattack } = registerControl('duckattack', 'duckatt');
|
export const { duckattack } = registerControl('duckattack', 'duckatt');
|
||||||
@@ -683,7 +632,7 @@ export const { byteBeatStartTime, bbst } = registerControl('byteBeatStartTime',
|
|||||||
export const { channels, ch } = registerControl('channels', 'ch');
|
export const { channels, ch } = registerControl('channels', 'ch');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controls the pulsewidth of the pulse oscillator
|
* controls the pulsewidth of the pulse oscillator
|
||||||
*
|
*
|
||||||
* @name pw
|
* @name pw
|
||||||
* @param {number | Pattern} pulsewidth
|
* @param {number | Pattern} pulsewidth
|
||||||
@@ -695,7 +644,7 @@ export const { channels, ch } = registerControl('channels', 'ch');
|
|||||||
export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']);
|
export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controls the lfo rate for the pulsewidth of the pulse oscillator
|
* controls the lfo rate for the pulsewidth of the pulse oscillator
|
||||||
*
|
*
|
||||||
* @name pwrate
|
* @name pwrate
|
||||||
* @param {number | Pattern} rate
|
* @param {number | Pattern} rate
|
||||||
@@ -707,7 +656,7 @@ export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']);
|
|||||||
export const { pwrate } = registerControl('pwrate');
|
export const { pwrate } = registerControl('pwrate');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controls the lfo sweep for the pulsewidth of the pulse oscillator
|
* controls the lfo sweep for the pulsewidth of the pulse oscillator
|
||||||
*
|
*
|
||||||
* @name pwsweep
|
* @name pwsweep
|
||||||
* @param {number | Pattern} sweep
|
* @param {number | Pattern} sweep
|
||||||
@@ -776,7 +725,7 @@ export const { phasercenter, phc } = registerControl('phasercenter', 'phc');
|
|||||||
export const { phaserdepth, phd, phasdp } = registerControl('phaserdepth', 'phd', 'phasdp');
|
export const { phaserdepth, phd, phasdp } = registerControl('phaserdepth', 'phd', 'phasdp');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Choose the channel the pattern is sent to in superdirt
|
* choose the channel the pattern is sent to in superdirt
|
||||||
*
|
*
|
||||||
* @name channel
|
* @name channel
|
||||||
* @param {number | Pattern} channel channel number
|
* @param {number | Pattern} channel channel number
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ let debounce = 1000,
|
|||||||
lastTime;
|
lastTime;
|
||||||
|
|
||||||
export function errorLogger(e, origin = 'cyclist') {
|
export function errorLogger(e, origin = 'cyclist') {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
//TODO: add some kind of debug flag that enables this while in dev mode
|
||||||
console.error(e);
|
// console.error(e);
|
||||||
}
|
|
||||||
logger(`[${origin}] error: ${e.message}`);
|
logger(`[${origin}] error: ${e.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2354,20 +2354,42 @@ export const jux = register('jux', function (func, pat) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Superimpose and offset multiple times, applying the given function each time.
|
* Superimpose and offset multiple times, applying the given function each time.
|
||||||
* @name echoWith
|
* @name echoForEach
|
||||||
* @synonyms echowith, stutWith, stutwith
|
|
||||||
* @param {number} times how many times to repeat
|
* @param {number} times how many times to repeat
|
||||||
* @param {number} time cycle offset between iterations
|
* @param {number} time cycle offset between iterations
|
||||||
* @param {function} func function to apply, given the pattern and the iteration index
|
* @param {function} func function to apply, given the pattern and the iteration index
|
||||||
* @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()
|
* .n().scale("C:minor")
|
||||||
|
*/
|
||||||
|
export const { echoForEach } = register(['echoForEach'], function (times, time, func, pat) {
|
||||||
|
return stack(...listRange(0, times - 1).map((i) => func(pat.late(Fraction(time).mul(i)), i)));
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Superimpose and offset multiple times, applying the given function each time.
|
||||||
|
* @name echoWith
|
||||||
|
* @synonyms echowith, stutWith, stutwith
|
||||||
|
* @param {number} times how many times to repeat
|
||||||
|
* @param {number} time cycle offset between iterations
|
||||||
|
* @param {function} func function to apply, given the pattern and the iteration index
|
||||||
|
* @example
|
||||||
|
* "<0 [2 4]>".echoWith(4, 1/8, add(2))
|
||||||
|
* .n().scale("C:minor")
|
||||||
*/
|
*/
|
||||||
export const { echoWith, echowith, stutWith, stutwith } = register(
|
export const { echoWith, echowith, stutWith, stutwith } = register(
|
||||||
['echoWith', 'echowith', 'stutWith', 'stutwith'],
|
['echoWith', 'echowith', 'stutWith', 'stutwith'],
|
||||||
function (times, time, func, pat) {
|
function (times, time, func, pat) {
|
||||||
return stack(...listRange(0, times - 1).map((i) => func(pat.late(Fraction(time).mul(i)), i)));
|
if (times < 1) {
|
||||||
|
return silence;
|
||||||
|
}
|
||||||
|
let pats = pat;
|
||||||
|
for (let i = 1; i < times; i++) {
|
||||||
|
pat = func(pat.late(time));
|
||||||
|
pats = pats.stack(pat);
|
||||||
|
}
|
||||||
|
return pats;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -2383,7 +2405,7 @@ export const { echoWith, echowith, stutWith, stutwith } = register(
|
|||||||
* s("bd sd").echo(3, 1/6, .8)
|
* s("bd sd").echo(3, 1/6, .8)
|
||||||
*/
|
*/
|
||||||
export const echo = register('echo', function (times, time, feedback, pat) {
|
export const echo = register('echo', function (times, time, feedback, pat) {
|
||||||
return pat._echoWith(times, time, (pat, i) => pat.gain(Math.pow(feedback, i)));
|
return pat._echoForEach(times, time, (pat, i) => pat.gain(Math.pow(feedback, i)));
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2396,7 +2418,7 @@ export const echo = register('echo', function (times, time, feedback, pat) {
|
|||||||
* s("bd sd").stut(3, .8, 1/6)
|
* s("bd sd").stut(3, .8, 1/6)
|
||||||
*/
|
*/
|
||||||
export const stut = register('stut', function (times, feedback, time, pat) {
|
export const stut = register('stut', function (times, feedback, time, pat) {
|
||||||
return pat._echoWith(times, time, (pat, i) => pat.gain(Math.pow(feedback, i)));
|
return pat._echoForEach(times, time, (pat, i) => pat.gain(Math.pow(feedback, i)));
|
||||||
});
|
});
|
||||||
|
|
||||||
export const applyN = register('applyN', function (n, func, p) {
|
export const applyN = register('applyN', function (n, func, p) {
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import { logger } from './logger.mjs';
|
|||||||
|
|
||||||
// returns true if the given string is a note
|
// returns true if the given string is a note
|
||||||
export const isNoteWithOctave = (name) => /^[a-gA-G][#bs]*[0-9]$/.test(name);
|
export const isNoteWithOctave = (name) => /^[a-gA-G][#bs]*[0-9]$/.test(name);
|
||||||
export const isNote = (name) => /^[a-gA-G][#bsf]*-?[0-9]?$/.test(name);
|
export const isNote = (name) => /^[a-gA-G][#bsf]*[0-9]?$/.test(name);
|
||||||
export const tokenizeNote = (note) => {
|
export const tokenizeNote = (note) => {
|
||||||
if (typeof note !== 'string') {
|
if (typeof note !== 'string') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const [pc, acc = '', oct] = note.match(/^([a-gA-G])([#bsf]*)(-?[0-9]*)$/)?.slice(1) || [];
|
const [pc, acc = '', oct] = note.match(/^([a-gA-G])([#bsf]*)([0-9]*)$/)?.slice(1) || [];
|
||||||
if (!pc) {
|
if (!pc) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
let log = (msg) => console.log(msg);
|
let log = (msg) => console.log(msg);
|
||||||
|
|
||||||
export function errorLogger(e, origin = 'superdough') {
|
export function errorLogger(e, origin = 'cyclist') {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
//TODO: add some kind of debug flag that enables this while in dev mode
|
||||||
console.error(e);
|
// console.error(e);
|
||||||
}
|
|
||||||
logger(`[${origin}] error: ${e.message}`);
|
logger(`[${origin}] error: ${e.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -349,17 +349,18 @@ function getDelay(orbit, delaytime, delayfeedback, t) {
|
|||||||
//logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`);
|
//logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`);
|
||||||
}
|
}
|
||||||
delayfeedback = clamp(delayfeedback, 0, 0.98);
|
delayfeedback = clamp(delayfeedback, 0, 0.98);
|
||||||
let delayNode = orbits[orbit].delayNode;
|
if (!orbits[orbit].delayNode) {
|
||||||
if (delayNode === undefined) {
|
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
delayNode = ac.createFeedbackDelay(1, delaytime, delayfeedback);
|
const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback);
|
||||||
delayNode.start?.(t); // for some reason, this throws when audion extension is installed..
|
dly.start?.(t); // for some reason, this throws when audion extension is installed..
|
||||||
connectToOrbit(delayNode, orbit);
|
connectToOrbit(dly, orbit);
|
||||||
orbits[orbit].delayNode = delayNode;
|
orbits[orbit].delayNode = dly;
|
||||||
}
|
}
|
||||||
delayNode.delayTime.value !== delaytime && delayNode.delayTime.setValueAtTime(delaytime, t);
|
orbits[orbit].delayNode.delayTime.value !== delaytime &&
|
||||||
delayNode.feedback.value !== delayfeedback && delayNode.feedback.setValueAtTime(delayfeedback, t);
|
orbits[orbit].delayNode.delayTime.setValueAtTime(delaytime, t);
|
||||||
return delayNode;
|
orbits[orbit].delayNode.feedback.value !== delayfeedback &&
|
||||||
|
orbits[orbit].delayNode.feedback.setValueAtTime(delayfeedback, t);
|
||||||
|
return orbits[orbit].delayNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLfo(audioContext, begin, end, properties = {}) {
|
export function getLfo(audioContext, begin, end, properties = {}) {
|
||||||
@@ -415,60 +416,42 @@ function getFilterType(ftype) {
|
|||||||
return typeof ftype === 'number' ? filterTypes[Math.floor(_mod(ftype, filterTypes.length))] : ftype;
|
return typeof ftype === 'number' ? filterTypes[Math.floor(_mod(ftype, filterTypes.length))] : ftype;
|
||||||
}
|
}
|
||||||
|
|
||||||
// type orbit {
|
//type orbit {
|
||||||
// output: GainNode,
|
// gain: number,
|
||||||
// reverbNode: ConvolverNode
|
// reverbNode: reverbNode
|
||||||
// delayNode: FeedbackDelayNode
|
// delayNode: delayNode
|
||||||
// }
|
//}
|
||||||
let orbits = {};
|
let orbits = {};
|
||||||
function connectToOrbit(node, orbit) {
|
function connectToOrbit(node, orbit) {
|
||||||
if (orbits[orbit] == null) {
|
if (orbits[orbit] == null) {
|
||||||
errorLogger(new Error('target orbit does not exist'), 'superdough');
|
errorLogger(new Error('target orbit does not exist'), 'superdough');
|
||||||
}
|
}
|
||||||
node.connect(orbits[orbit].output);
|
node.connect(orbits[orbit].gain);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOrbit(audioContext, orbit, channels) {
|
function setOrbit(audioContext, orbit, channels) {
|
||||||
if (orbits[orbit] == null) {
|
if (orbits[orbit] == null) {
|
||||||
orbits[orbit] = {
|
orbits[orbit] = {
|
||||||
// Setup output node through which all audio filters prior to hitting
|
gain: new GainNode(audioContext, { gain: 1, channelCount: 2, channelCountMode: 'explicit' }),
|
||||||
// the destination (and thus allows for global volume automation)
|
|
||||||
output: new GainNode(audioContext, { gain: 1, channelCount: 2, channelCountMode: 'explicit' }),
|
|
||||||
};
|
};
|
||||||
connectToDestination(orbits[orbit].output, channels);
|
connectToDestination(orbits[orbit].gain, channels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function duckOrbit(audioContext, targetOrbit, t, attacktime = 0.1, duckdepth = 1) {
|
||||||
function duckOrbit(audioContext, targetOrbit, t, onsettime = 0, attacktime = 0.1, duckdepth = 1) {
|
|
||||||
const targetArr = [targetOrbit].flat();
|
const targetArr = [targetOrbit].flat();
|
||||||
const onsetArr = [onsettime].flat();
|
|
||||||
const attackArr = [attacktime].flat();
|
|
||||||
const depthArr = [duckdepth].flat();
|
|
||||||
|
|
||||||
targetArr.forEach((target, idx) => {
|
targetArr.forEach((target) => {
|
||||||
if (orbits[target] == null) {
|
if (orbits[target] == null) {
|
||||||
errorLogger(new Error(`duck target orbit ${target} does not exist`), 'superdough');
|
errorLogger(new Error(`duck target orbit ${target} does not exist`), 'superdough');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const onset = onsetArr[idx] ?? onsetArr[0];
|
|
||||||
const attack = Math.max(attackArr[idx] ?? attackArr[0], 0.002);
|
|
||||||
const depth = depthArr[idx] ?? depthArr[0];
|
|
||||||
const gainParam = orbits[target].output.gain;
|
|
||||||
webAudioTimeout(
|
webAudioTimeout(
|
||||||
audioContext,
|
audioContext,
|
||||||
() => {
|
() => {
|
||||||
const now = audioContext.currentTime;
|
orbits[target].gain.gain.cancelScheduledValues(t);
|
||||||
|
const currVal = orbits[target].gain.gain.value;
|
||||||
// cancelScheduledValues and setValueAtTime together emulate cancelAndHoldAtTime
|
orbits[target].gain.gain.linearRampToValueAtTime(clamp(1 - Math.pow(duckdepth, 0.5), 0.01, currVal), t);
|
||||||
// on browsers which lack that method
|
orbits[target].gain.gain.exponentialRampToValueAtTime(1, t + Math.max(0.002, attacktime));
|
||||||
const currVal = gainParam.value;
|
|
||||||
gainParam.cancelScheduledValues(now);
|
|
||||||
gainParam.setValueAtTime(currVal, now);
|
|
||||||
|
|
||||||
const t0 = Math.max(t, now); // guard against now > t
|
|
||||||
const duckedVal = clamp(1 - Math.sqrt(depth), 0.01, currVal);
|
|
||||||
gainParam.exponentialRampToValueAtTime(duckedVal, t0 + onset);
|
|
||||||
gainParam.exponentialRampToValueAtTime(1, t0 + onset + attack);
|
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
t - 0.01,
|
t - 0.01,
|
||||||
@@ -479,31 +462,30 @@ function duckOrbit(audioContext, targetOrbit, t, onsettime = 0, attacktime = 0.1
|
|||||||
let hasChanged = (now, before) => now !== undefined && now !== before;
|
let hasChanged = (now, before) => now !== undefined && now !== before;
|
||||||
function getReverb(orbit, duration, fade, lp, dim, ir, irspeed, irbegin) {
|
function getReverb(orbit, duration, fade, lp, dim, ir, irspeed, irbegin) {
|
||||||
// If no reverb has been created for a given orbit, create one
|
// If no reverb has been created for a given orbit, create one
|
||||||
let reverbNode = orbits[orbit].reverbNode;
|
if (!orbits[orbit].reverbNode) {
|
||||||
if (reverbNode === undefined) {
|
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
reverbNode = ac.createReverb(duration, fade, lp, dim, ir, irspeed, irbegin);
|
const reverb = ac.createReverb(duration, fade, lp, dim, ir, irspeed, irbegin);
|
||||||
connectToOrbit(reverbNode, orbit);
|
connectToOrbit(reverb, orbit);
|
||||||
orbits[orbit].reverbNode = reverbNode;
|
orbits[orbit].reverbNode = reverb;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
hasChanged(duration, reverbNode.duration) ||
|
hasChanged(duration, orbits[orbit].reverbNode.duration) ||
|
||||||
hasChanged(fade, reverbNode.fade) ||
|
hasChanged(fade, orbits[orbit].reverbNode.fade) ||
|
||||||
hasChanged(lp, reverbNode.lp) ||
|
hasChanged(lp, orbits[orbit].reverbNode.lp) ||
|
||||||
hasChanged(dim, reverbNode.dim) ||
|
hasChanged(dim, orbits[orbit].reverbNode.dim) ||
|
||||||
hasChanged(irspeed, reverbNode.irspeed) ||
|
hasChanged(irspeed, orbits[orbit].reverbNode.irspeed) ||
|
||||||
hasChanged(irbegin, reverbNode.irbegin) ||
|
hasChanged(irbegin, orbits[orbit].reverbNode.irbegin) ||
|
||||||
reverbNode.ir !== ir
|
orbits[orbit].reverbNode.ir !== ir
|
||||||
) {
|
) {
|
||||||
// only regenerate when something has changed
|
// only regenerate when something has changed
|
||||||
// avoids endless regeneration on things like
|
// avoids endless regeneration on things like
|
||||||
// stack(s("a"), s("b").rsize(8)).room(.5)
|
// stack(s("a"), s("b").rsize(8)).room(.5)
|
||||||
// this only works when args may stay undefined until here
|
// this only works when args may stay undefined until here
|
||||||
// setting default values breaks this
|
// setting default values breaks this
|
||||||
reverbNode.generate(duration, fade, lp, dim, ir, irspeed, irbegin);
|
orbits[orbit].reverbNode.generate(duration, fade, lp, dim, ir, irspeed, irbegin);
|
||||||
}
|
}
|
||||||
return reverbNode;
|
return orbits[orbit].reverbNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export let analysers = {},
|
export let analysers = {},
|
||||||
@@ -600,7 +582,6 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||||||
postgain = getDefaultValue('postgain'),
|
postgain = getDefaultValue('postgain'),
|
||||||
density = getDefaultValue('density'),
|
density = getDefaultValue('density'),
|
||||||
duckorbit,
|
duckorbit,
|
||||||
duckonset,
|
|
||||||
duckattack,
|
duckattack,
|
||||||
duckdepth,
|
duckdepth,
|
||||||
// filters
|
// filters
|
||||||
@@ -682,7 +663,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||||||
setOrbit(ac, orbit, channels, t, cycle, cps);
|
setOrbit(ac, orbit, channels, t, cycle, cps);
|
||||||
|
|
||||||
if (duckorbit != null) {
|
if (duckorbit != null) {
|
||||||
duckOrbit(ac, duckorbit, t, duckonset, duckattack, duckdepth);
|
duckOrbit(ac, duckorbit, t, duckattack, duckdepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
gain = applyGainCurve(nanFallback(gain, 1));
|
gain = applyGainCurve(nanFallback(gain, 1));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const tokenizeNote = (note) => {
|
|||||||
if (typeof note !== 'string') {
|
if (typeof note !== 'string') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const [pc, acc = '', oct] = note.match(/^([a-gA-G])([#bsf]*)(-?[0-9]*)$/)?.slice(1) || [];
|
const [pc, acc = '', oct] = note.match(/^([a-gA-G])([#bsf]*)([0-9]*)$/)?.slice(1) || [];
|
||||||
if (!pc) {
|
if (!pc) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,18 +7,15 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
// import { strict as assert } from 'assert';
|
// import { strict as assert } from 'assert';
|
||||||
|
|
||||||
import '../tonal.mjs'; // need to import this to add prototypes
|
import '../tonal.mjs'; // need to import this to add prototypes
|
||||||
import { pure, n, seq, note, noteToMidi } from '@strudel/core';
|
import { pure, n, seq, note } from '@strudel/core';
|
||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import { mini } from '../../mini/mini.mjs';
|
import { mini } from '../../mini/mini.mjs';
|
||||||
|
|
||||||
describe('tonal', () => {
|
describe('tonal', () => {
|
||||||
describe('scaleTranspose', () => {
|
it('Should run tonal functions ', () => {
|
||||||
it('transposes notes by scale degrees', () => {
|
|
||||||
expect(pure('c3').scale('C major').scaleTranspose(1).firstCycleValues).toEqual(['D3']);
|
expect(pure('c3').scale('C major').scaleTranspose(1).firstCycleValues).toEqual(['D3']);
|
||||||
});
|
});
|
||||||
});
|
it('scale with plain values', () => {
|
||||||
describe('scale', () => {
|
|
||||||
it('converts plain values', () => {
|
|
||||||
expect(
|
expect(
|
||||||
seq(0, 1, 2)
|
seq(0, 1, 2)
|
||||||
.scale('C major')
|
.scale('C major')
|
||||||
@@ -26,80 +23,34 @@ describe('tonal', () => {
|
|||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual(['C3', 'D3', 'E3']);
|
).toEqual(['C3', 'D3', 'E3']);
|
||||||
});
|
});
|
||||||
it('converts n values', () => {
|
it('scale with n values', () => {
|
||||||
expect(
|
expect(
|
||||||
n(seq(0, 1, 2))
|
n(seq(0, 1, 2))
|
||||||
.scale('C major')
|
.scale('C major')
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual(['C3', 'D3', 'E3']);
|
).toEqual(['C3', 'D3', 'E3']);
|
||||||
});
|
});
|
||||||
it('converts n values (mini notation)', () => {
|
it('scale with colon', () => {
|
||||||
expect(
|
expect(
|
||||||
n(seq(0, 1, 2))
|
n(seq(0, 1, 2))
|
||||||
.scale('C:major')
|
.scale('C:major')
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual(['C3', 'D3', 'E3']);
|
).toEqual(['C3', 'D3', 'E3']);
|
||||||
});
|
});
|
||||||
it('converts n values (no tonic)', () => {
|
it('scale without tonic', () => {
|
||||||
expect(
|
expect(
|
||||||
n(seq(0, 1, 2))
|
n(seq(0, 1, 2))
|
||||||
.scale('major')
|
.scale('major')
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual(['C3', 'D3', 'E3']);
|
).toEqual(['C3', 'D3', 'E3']);
|
||||||
});
|
});
|
||||||
it('converts n values (explicit mini notation)', () => {
|
it('scale with mininotation colon', () => {
|
||||||
expect(
|
expect(
|
||||||
n(seq(0, 1, 2))
|
n(seq(0, 1, 2))
|
||||||
.scale(mini('C:major'))
|
.scale(mini('C:major'))
|
||||||
.firstCycleValues.map((h) => h.note),
|
.firstCycleValues.map((h) => h.note),
|
||||||
).toEqual(['C3', 'D3', 'E3']);
|
).toEqual(['C3', 'D3', 'E3']);
|
||||||
});
|
});
|
||||||
it('converts decorated n values', () => {
|
|
||||||
expect(
|
|
||||||
n(seq('0b', '1#', '-2', '3##', '4bb'))
|
|
||||||
.scale('C major')
|
|
||||||
.firstCycleValues.map((h) => h.note),
|
|
||||||
).toEqual(['B2', 'Eb3', 'A2', 'G3', 'F3']);
|
|
||||||
});
|
|
||||||
it('produces silence for mixed sharps and flats', () => {
|
|
||||||
expect(
|
|
||||||
n(seq('0b#', '1#b', '2#b#'))
|
|
||||||
.scale('C major')
|
|
||||||
.firstCycleValues.map((h) => h.note),
|
|
||||||
).toEqual([]);
|
|
||||||
});
|
|
||||||
it('snaps notes (upwards) to scale', () => {
|
|
||||||
const inputNotes = ['Cb', 'Eb', 'G', 'A#', 'Bb'];
|
|
||||||
const expectedNotes = ['B2', 'E3', 'G3', 'B3', 'B3'];
|
|
||||||
|
|
||||||
expect(
|
|
||||||
note(seq(inputNotes))
|
|
||||||
.scale('C major')
|
|
||||||
.firstCycleValues.map((h) => h.note),
|
|
||||||
).toEqual(expectedNotes);
|
|
||||||
});
|
|
||||||
it('snaps notes to the correct octave', () => {
|
|
||||||
const inputNotes = ['Cb0', 'Eb4', 'G1', 'A#19', 'Bb8'];
|
|
||||||
const expectedNotes = ['B#-1', 'D#4', 'G#1', 'A#19', 'A#8'];
|
|
||||||
|
|
||||||
expect(
|
|
||||||
note(seq(inputNotes))
|
|
||||||
.scale('A# minor') // A#, B#, C#, D#, E#, F#, G#
|
|
||||||
.firstCycleValues.map((h) => h.note),
|
|
||||||
).toEqual(expectedNotes);
|
|
||||||
});
|
|
||||||
it('handles scale names provided with colons', () => {
|
|
||||||
const inputNotes = ['Cb', 'E', 'G', 'A#', 'Bb'];
|
|
||||||
const expectedNotes = ['A#2', 'D#3', 'G#3', 'A#3', 'A#3'];
|
|
||||||
|
|
||||||
expect(
|
|
||||||
note(seq(inputNotes))
|
|
||||||
.scale('F#:pentatonic') // F#, G#, A#, C#, and D#
|
|
||||||
.firstCycleValues.map((h) => h.note),
|
|
||||||
).toEqual(expectedNotes);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('transpose', () => {
|
|
||||||
it('transposes note numbers with interval numbers', () => {
|
it('transposes note numbers with interval numbers', () => {
|
||||||
expect(
|
expect(
|
||||||
note(seq(40, 40, 40))
|
note(seq(40, 40, 40))
|
||||||
@@ -132,5 +83,4 @@ describe('tonal', () => {
|
|||||||
).toEqual(['C', 'D', 'Eb']);
|
).toEqual(['C', 'D', 'Eb']);
|
||||||
expect(seq('c', 'c', 'c').transpose('1P', '2M', '3m').firstCycleValues).toEqual(['C', 'D', 'Eb']);
|
expect(seq('c', 'c', 'c').transpose('1P', '2M', '3m').firstCycleValues).toEqual(['C', 'D', 'Eb']);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
+43
-91
@@ -6,28 +6,19 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
|
|
||||||
import { Note, Interval, Scale } from '@tonaljs/tonal';
|
import { Note, Interval, Scale } from '@tonaljs/tonal';
|
||||||
import { register, _mod, silence, logger, pure, isNote } from '@strudel/core';
|
import { register, _mod, silence, logger, pure, isNote } from '@strudel/core';
|
||||||
import { stepInNamedScale, nearestNumberIndex } from './tonleiter.mjs';
|
import { stepInNamedScale } from './tonleiter.mjs';
|
||||||
import { noteToMidi } from '../core/util.mjs';
|
|
||||||
|
|
||||||
const octavesInterval = (octaves) => (octaves <= 0 ? -1 : 1) + octaves * 7 + 'P';
|
const octavesInterval = (octaves) => (octaves <= 0 ? -1 : 1) + octaves * 7 + 'P';
|
||||||
|
|
||||||
function getScale(scaleName) {
|
|
||||||
scaleName = scaleName.replaceAll(':', ' ');
|
|
||||||
const scale = Scale.get(scaleName);
|
|
||||||
const { tonic, empty } = scale;
|
|
||||||
if ((empty && isNote(scaleName)) || (empty && !tonic)) {
|
|
||||||
throw new Error(
|
|
||||||
`Scale name ${scaleName} is incomplete. Make sure to use ":" instead of spaces, example: .scale("C:major")`,
|
|
||||||
);
|
|
||||||
} else if (empty) {
|
|
||||||
throw new Error(`Invalid scale name "${scaleName}"`);
|
|
||||||
}
|
|
||||||
return scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
function scaleStep(step, scale) {
|
function scaleStep(step, scale) {
|
||||||
|
scale = scale.replaceAll(':', ' ');
|
||||||
step = Math.ceil(step);
|
step = Math.ceil(step);
|
||||||
let { intervals, tonic } = getScale(scale);
|
let { intervals, tonic, empty } = Scale.get(scale);
|
||||||
|
if ((empty && isNote(scale)) || (empty && !tonic)) {
|
||||||
|
throw new Error(`incomplete scale. Make sure to use ":" instead of spaces, example: .scale("C:major")`);
|
||||||
|
} else if (empty) {
|
||||||
|
throw new Error(`invalid scale "${scale}"`);
|
||||||
|
}
|
||||||
tonic = tonic || 'C';
|
tonic = tonic || 'C';
|
||||||
const { pc, oct = 3 } = Note.get(tonic);
|
const { pc, oct = 3 } = Note.get(tonic);
|
||||||
const octaveOffset = Math.floor(step / intervals.length);
|
const octaveOffset = Math.floor(step / intervals.length);
|
||||||
@@ -39,7 +30,8 @@ function scaleStep(step, scale) {
|
|||||||
// transpose note inside scale by offset steps
|
// transpose note inside scale by offset steps
|
||||||
// function scaleOffset(scale: string, offset: number, note: string) {
|
// function scaleOffset(scale: string, offset: number, note: string) {
|
||||||
function scaleOffset(scale, offset, note) {
|
function scaleOffset(scale, offset, note) {
|
||||||
let { notes } = getScale(scale);
|
let [tonic, scaleName] = Scale.tokenize(scale);
|
||||||
|
let { notes } = Scale.get(`${tonic} ${scaleName}`);
|
||||||
notes = notes.map((note) => Note.get(note).pc); // use only pc!
|
notes = notes.map((note) => Note.get(note).pc); // use only pc!
|
||||||
offset = Number(offset);
|
offset = Number(offset);
|
||||||
if (isNaN(offset)) {
|
if (isNaN(offset)) {
|
||||||
@@ -128,7 +120,10 @@ export const { transpose, trans } = register(['transpose', 'trans'], function tr
|
|||||||
const interval = !isNaN(Number(intervalOrSemitones))
|
const interval = !isNaN(Number(intervalOrSemitones))
|
||||||
? Interval.fromSemitones(intervalOrSemitones)
|
? Interval.fromSemitones(intervalOrSemitones)
|
||||||
: String(intervalOrSemitones);
|
: String(intervalOrSemitones);
|
||||||
const targetNote = Note.transpose(note, interval);
|
// TODO: move simplify to player to preserve enharmonics
|
||||||
|
// tone.js doesn't understand multiple sharps flats e.g. F##3 has to be turned into G3
|
||||||
|
// TODO: check if this is still relevant..
|
||||||
|
const targetNote = Note.simplify(Note.transpose(note, interval));
|
||||||
if (typeof hap.value === 'object') {
|
if (typeof hap.value === 'object') {
|
||||||
return hap.withValue(() => ({ ...hap.value, note: targetNote }));
|
return hap.withValue(() => ({ ...hap.value, note: targetNote }));
|
||||||
}
|
}
|
||||||
@@ -176,59 +171,8 @@ export const { scaleTranspose, scaleTrans, strans } = register(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Converts a step value, which is a number optionally decorated with sharps and flats,
|
|
||||||
// to a number and an `offset` number of semitones
|
|
||||||
function _convertStepToNumberAndOffset(step) {
|
|
||||||
let asNumber = Number(step);
|
|
||||||
let offset = 0;
|
|
||||||
if (isNaN(asNumber)) {
|
|
||||||
step = String(step);
|
|
||||||
// Check to see if the step matches the expected format:
|
|
||||||
// - A number (possibly negative)
|
|
||||||
// - Some number of sharps or flats (but not both)
|
|
||||||
const match = /^(-?\d+)(#+|b+)?$/.exec(step);
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
throw new Error(`invalid scale step "${step}", expected number or integer with optional # b suffixes`);
|
|
||||||
}
|
|
||||||
asNumber = Number(match[1]);
|
|
||||||
// These decorations will determine the semitone offset based on the number of
|
|
||||||
// sharps or flats
|
|
||||||
const decorations = match[2] || '';
|
|
||||||
offset = decorations[0] === '#' ? decorations.length : -decorations.length;
|
|
||||||
}
|
|
||||||
return [asNumber, offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
let scaleToMidisAndNotes = {};
|
|
||||||
// Finds the nearest scale note to `note`
|
|
||||||
function _getNearestScaleNote(scaleName, note, preferHigher = true) {
|
|
||||||
let noteMidi = typeof note === 'string' ? noteToMidi(note) : note;
|
|
||||||
if (scaleToMidisAndNotes[scaleName] === undefined) {
|
|
||||||
const { intervals, tonic } = getScale(scaleName);
|
|
||||||
const { pc } = Note.get(tonic);
|
|
||||||
const expandedIntervals = intervals.concat('8P'); // add the octave for wrapping
|
|
||||||
const sNotes = expandedIntervals.map((interval) => Note.transpose(pc + '0', interval));
|
|
||||||
const sMidi = sNotes.map(noteToMidi);
|
|
||||||
// Cache
|
|
||||||
scaleToMidisAndNotes[scaleName] = [sMidi, sNotes];
|
|
||||||
}
|
|
||||||
const [scaleMidis, scaleNotes] = scaleToMidisAndNotes[scaleName];
|
|
||||||
const rootMidi = scaleMidis[0];
|
|
||||||
const octaveDiff = Math.floor((noteMidi - rootMidi) / 12);
|
|
||||||
const alignedMidis = scaleMidis.map((m) => m + 12 * octaveDiff);
|
|
||||||
const noteIdx = nearestNumberIndex(noteMidi, alignedMidis, preferHigher);
|
|
||||||
const noteMatch = scaleNotes[noteIdx];
|
|
||||||
return Note.transpose(noteMatch, Interval.fromSemitones(12 * octaveDiff));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns numbers into notes in the scale (zero indexed) or quantizes notes to a scale.
|
* Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like {@link Pattern#scaleTranspose}.
|
||||||
*
|
|
||||||
* When describing notes via numbers, note that negative numbers can be used to wrap backwards
|
|
||||||
* in the scale as well as sharps or flats (but not both) to produce notes outside of the scale.
|
|
||||||
*
|
|
||||||
* Also sets scale for other scale operations, like {@link Pattern#scaleTranspose}.
|
|
||||||
*
|
*
|
||||||
* A scale consists of a root note (e.g. `c4`, `c`, `f#`, `bb4`) followed by semicolon (':') and then a [scale type](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts).
|
* A scale consists of a root note (e.g. `c4`, `c`, `f#`, `bb4`) followed by semicolon (':') and then a [scale type](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts).
|
||||||
*
|
*
|
||||||
@@ -247,12 +191,6 @@ function _getNearestScaleNote(scaleName, note, preferHigher = true) {
|
|||||||
* n(rand.range(0,12).segment(8))
|
* n(rand.range(0,12).segment(8))
|
||||||
* .scale("C:ritusen")
|
* .scale("C:ritusen")
|
||||||
* .s("piano")
|
* .s("piano")
|
||||||
* @example
|
|
||||||
* n("<[0,7b] [-4# -4] [-2,7##] 4 [0,7] [-4# -4b] [-2,7###] 4b>*4")
|
|
||||||
* .scale("C:<major minor>/2")
|
|
||||||
* .s("piano")
|
|
||||||
* @example
|
|
||||||
* note("C1*16").transpose(irand(36)).scale('Cb2 major').scaleTranspose(3)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const scale = register(
|
export const scale = register(
|
||||||
@@ -266,35 +204,49 @@ export const scale = register(
|
|||||||
pat
|
pat
|
||||||
.fmap((value) => {
|
.fmap((value) => {
|
||||||
const isObject = typeof value === 'object';
|
const isObject = typeof value === 'object';
|
||||||
// The case where the note has been defined via `n` or `pure`
|
let step = isObject ? value.n : value;
|
||||||
if (!isObject || (isObject && ('n' in value || 'value' in value))) {
|
if (isObject) {
|
||||||
const step = isObject ? (value.n ?? value.value) : value;
|
|
||||||
delete value.n; // remove n so it won't cause trouble
|
delete value.n; // remove n so it won't cause trouble
|
||||||
|
}
|
||||||
if (isNote(step)) {
|
if (isNote(step)) {
|
||||||
// legacy..
|
// legacy..
|
||||||
return pure(step);
|
return pure(step);
|
||||||
}
|
}
|
||||||
|
let asNumber = Number(step);
|
||||||
|
let semitones = 0;
|
||||||
|
if (isNaN(asNumber)) {
|
||||||
|
step = String(step);
|
||||||
|
if (!/^[-+]?\d+(#*|b*){1}$/.test(step)) {
|
||||||
|
logger(
|
||||||
|
`[tonal] invalid scale step "${step}", expected number or integer with optional # b suffixes`,
|
||||||
|
'error',
|
||||||
|
);
|
||||||
|
return silence;
|
||||||
|
}
|
||||||
|
const isharp = step.indexOf('#');
|
||||||
|
if (isharp >= 0) {
|
||||||
|
asNumber = Number(step.substring(0, isharp));
|
||||||
|
semitones = step.length - isharp;
|
||||||
|
} else {
|
||||||
|
const iflat = step.indexOf('b');
|
||||||
|
asNumber = Number(step.substring(0, iflat));
|
||||||
|
semitones = iflat - step.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const [number, offset] = _convertStepToNumberAndOffset(step);
|
|
||||||
let note;
|
let note;
|
||||||
if (isObject && value.anchor) {
|
if (isObject && value.anchor) {
|
||||||
note = stepInNamedScale(number, scale, value.anchor);
|
note = stepInNamedScale(asNumber, scale, value.anchor);
|
||||||
} else {
|
} else {
|
||||||
note = scaleStep(number, scale);
|
note = scaleStep(asNumber, scale);
|
||||||
}
|
}
|
||||||
if (offset != 0) note = Note.transpose(note, Interval.fromSemitones(offset));
|
if (semitones != 0) note = Note.transpose(note, Interval.fromSemitones(semitones));
|
||||||
value = pure(isObject ? { ...value, note } : note);
|
value = pure(isObject ? { ...value, note } : note);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger(`[tonal] ${err.message}`, 'error');
|
logger(`[tonal] ${err.message}`, 'error');
|
||||||
return silence;
|
value = silence;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
|
||||||
// The case where the note has been defined via `note`
|
|
||||||
else {
|
|
||||||
const note = _getNearestScaleNote(scale, value.note);
|
|
||||||
return pure(isObject ? { ...value, note } : note);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.outerJoin()
|
.outerJoin()
|
||||||
// legacy:
|
// legacy:
|
||||||
|
|||||||
@@ -101,11 +101,11 @@ export function nearestNumberIndex(target, numbers, preferHigher) {
|
|||||||
let scaleSteps = {}; // [scaleName]: semitones[]
|
let scaleSteps = {}; // [scaleName]: semitones[]
|
||||||
|
|
||||||
export function stepInNamedScale(step, scale, anchor, preferHigher) {
|
export function stepInNamedScale(step, scale, anchor, preferHigher) {
|
||||||
const [root, scaleName] = Scale.tokenize(scale);
|
let [root, scaleName] = Scale.tokenize(scale);
|
||||||
const rootMidi = x2midi(root);
|
const rootMidi = x2midi(root);
|
||||||
const rootChroma = midi2chroma(rootMidi);
|
const rootChroma = midi2chroma(rootMidi);
|
||||||
if (!scaleSteps[scaleName]) {
|
if (!scaleSteps[scaleName]) {
|
||||||
const { intervals } = Scale.get(`C ${scaleName}`);
|
let { intervals } = Scale.get(`C ${scaleName}`);
|
||||||
// cache result
|
// cache result
|
||||||
scaleSteps[scaleName] = intervals.map(step2semitones);
|
scaleSteps[scaleName] = intervals.map(step2semitones);
|
||||||
}
|
}
|
||||||
@@ -222,7 +222,6 @@ export const Note = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: support octave numbers
|
// TODO: support octave numbers
|
||||||
// Example: Note("Bb3").transpose("c3")
|
|
||||||
export function transpose(note, step) {
|
export function transpose(note, step) {
|
||||||
// example: E, 3
|
// example: E, 3
|
||||||
const stepNumber = Step.tokenize(step)[1]; // 3
|
const stepNumber = Step.tokenize(step)[1]; // 3
|
||||||
@@ -236,3 +235,5 @@ export function transpose(note, step) {
|
|||||||
const offsetAccidentals = accidentalString(Step.accidentals(step) + Note.accidentals(note) + stepIndex - indexOffset); // "we need to add a # to to the G to make it a major third from E"
|
const offsetAccidentals = accidentalString(Step.accidentals(step) + Note.accidentals(note) + stepIndex - indexOffset); // "we need to add a # to to the G to make it a major third from E"
|
||||||
return [targetNote, offsetAccidentals].join('');
|
return [targetNote, offsetAccidentals].join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Note("Bb3").transpose("c3")
|
||||||
|
|||||||
@@ -3093,50 +3093,57 @@ exports[`runs examples > example "dry" example index 0 1`] = `
|
|||||||
exports[`runs examples > example "duckattack" example index 0 1`] = `
|
exports[`runs examples > example "duckattack" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 0/1 → 1/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
|
"[ 0/1 → 1/8 | note:C3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 1/8 → 1/4 | note:D3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 1/4 → 5/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 1/4 → 5/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
|
"[ 1/4 → 3/8 | note:Eb3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 3/8 → 1/2 | note:F3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 1/2 → 9/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 1/2 → 9/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
|
"[ 1/2 → 5/8 | note:G3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 5/8 → 3/4 | note:Ab3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 11/16 → 3/4 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 11/16 → 3/4 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
|
"[ 3/4 → 7/8 | note:Bb3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 7/8 → 15/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 7/8 → 15/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
|
"[ 7/8 → 1/1 | note:C4 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 1/1 → 17/16 | s:bd n:4 duckorbit:2 duckattack:0 duckdepth:1 ]",
|
"[ 1/1 → 17/16 | s:bd n:4 duckorbit:2 duckattack:0 duckdepth:1 ]",
|
||||||
|
"[ 1/1 → 9/8 | note:C3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 9/8 → 5/4 | note:D3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 5/4 → 21/16 | s:bd n:4 duckorbit:2 duckattack:0 duckdepth:1 ]",
|
"[ 5/4 → 21/16 | s:bd n:4 duckorbit:2 duckattack:0 duckdepth:1 ]",
|
||||||
|
"[ 5/4 → 11/8 | note:Eb3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 11/8 → 3/2 | note:F3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 3/2 → 25/16 | s:bd n:4 duckorbit:2 duckattack:0 duckdepth:1 ]",
|
"[ 3/2 → 25/16 | s:bd n:4 duckorbit:2 duckattack:0 duckdepth:1 ]",
|
||||||
|
"[ 3/2 → 13/8 | note:G3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 13/8 → 7/4 | note:Ab3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 27/16 → 7/4 | s:bd n:4 duckorbit:2 duckattack:0 duckdepth:1 ]",
|
"[ 27/16 → 7/4 | s:bd n:4 duckorbit:2 duckattack:0 duckdepth:1 ]",
|
||||||
|
"[ 7/4 → 15/8 | note:Bb3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 15/8 → 31/16 | s:bd n:4 duckorbit:2 duckattack:0 duckdepth:1 ]",
|
"[ 15/8 → 31/16 | s:bd n:4 duckorbit:2 duckattack:0 duckdepth:1 ]",
|
||||||
|
"[ 15/8 → 2/1 | note:C4 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 2/1 → 33/16 | s:bd n:4 duckorbit:2 duckattack:0.4 duckdepth:1 ]",
|
"[ 2/1 → 33/16 | s:bd n:4 duckorbit:2 duckattack:0.4 duckdepth:1 ]",
|
||||||
|
"[ 2/1 → 17/8 | note:C3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 17/8 → 9/4 | note:D3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 9/4 → 37/16 | s:bd n:4 duckorbit:2 duckattack:0.4 duckdepth:1 ]",
|
"[ 9/4 → 37/16 | s:bd n:4 duckorbit:2 duckattack:0.4 duckdepth:1 ]",
|
||||||
|
"[ 9/4 → 19/8 | note:Eb3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 19/8 → 5/2 | note:F3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 5/2 → 41/16 | s:bd n:4 duckorbit:2 duckattack:0.4 duckdepth:1 ]",
|
"[ 5/2 → 41/16 | s:bd n:4 duckorbit:2 duckattack:0.4 duckdepth:1 ]",
|
||||||
|
"[ 5/2 → 21/8 | note:G3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 21/8 → 11/4 | note:Ab3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 43/16 → 11/4 | s:bd n:4 duckorbit:2 duckattack:0.4 duckdepth:1 ]",
|
"[ 43/16 → 11/4 | s:bd n:4 duckorbit:2 duckattack:0.4 duckdepth:1 ]",
|
||||||
|
"[ 11/4 → 23/8 | note:Bb3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 23/8 → 47/16 | s:bd n:4 duckorbit:2 duckattack:0.4 duckdepth:1 ]",
|
"[ 23/8 → 47/16 | s:bd n:4 duckorbit:2 duckattack:0.4 duckdepth:1 ]",
|
||||||
|
"[ 23/8 → 3/1 | note:C4 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 3/1 → 49/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 3/1 → 49/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
|
"[ 3/1 → 25/8 | note:C3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 25/8 → 13/4 | note:D3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 13/4 → 53/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 13/4 → 53/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
|
"[ 13/4 → 27/8 | note:Eb3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 27/8 → 7/2 | note:F3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 7/2 → 57/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 7/2 → 57/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
|
"[ 7/2 → 29/8 | note:G3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
|
"[ 29/8 → 15/4 | note:Ab3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 59/16 → 15/4 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 59/16 → 15/4 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
|
"[ 15/4 → 31/8 | note:Bb3 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
"[ 31/8 → 63/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 31/8 → 63/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
]
|
"[ 31/8 → 4/1 | note:C4 s:sawtooth delay:0.7 orbit:2 ]",
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "duckattack" example index 1 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 1/4 → 5/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 1/2 → 9/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 11/16 → 3/4 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 7/8 → 15/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 1/1 → 17/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 5/4 → 21/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 3/2 → 25/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 27/16 → 7/4 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 15/8 → 31/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 2/1 → 33/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 9/4 → 37/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 5/2 → 41/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 43/16 → 11/4 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 23/8 → 47/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 3/1 → 49/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 13/4 → 53/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 7/2 → 57/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 59/16 → 15/4 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
"[ 31/8 → 63/16 | s:bd n:4 duckorbit:[2 5] duckattack:[0.4 0.1] ]",
|
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -3197,94 +3204,6 @@ exports[`runs examples > example "duckdepth" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "duckdepth" example index 1 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 1/4 → 5/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 1/2 → 9/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 11/16 → 3/4 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 7/8 → 15/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 1/1 → 17/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 5/4 → 21/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 3/2 → 25/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 27/16 → 7/4 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 15/8 → 31/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 2/1 → 33/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 9/4 → 37/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 5/2 → 41/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 43/16 → 11/4 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 23/8 → 47/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 3/1 → 49/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 13/4 → 53/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 7/2 → 57/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 59/16 → 15/4 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
"[ 31/8 → 63/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:[1 0.5] ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "duckonset" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 1/4 → 1/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 1/2 → 3/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 3/4 → 1/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 1/1 → 5/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 5/4 → 3/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 3/2 → 7/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 7/4 → 2/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 2/1 → 9/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 9/4 → 5/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 5/2 → 11/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 11/4 → 3/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 3/1 → 13/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 13/4 → 7/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 7/2 → 15/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
"[ 15/4 → 4/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0 postgain:0 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "duckonset" example index 1 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 1/4 → 1/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 1/2 → 3/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 3/4 → 1/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 1/1 → 5/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 5/4 → 3/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 3/2 → 7/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 7/4 → 2/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 2/1 → 9/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 9/4 → 5/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 5/2 → 11/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 11/4 → 3/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 3/1 → 13/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 13/4 → 7/2 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 7/2 → 15/4 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
"[ 15/4 → 4/1 | s:bd duckorbit:2 duckattack:0.3 duckonset:0.01 postgain:0 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "duckonset" example index 2 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 1/4 → 1/2 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 1/2 → 3/4 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 3/4 → 1/1 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 1/1 → 5/4 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 5/4 → 3/2 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 3/2 → 7/4 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 7/4 → 2/1 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 2/1 → 9/4 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 9/4 → 5/2 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 5/2 → 11/4 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 11/4 → 3/1 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 3/1 → 13/4 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 13/4 → 7/2 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 7/2 → 15/4 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
"[ 15/4 → 4/1 | s:bd bank:tr909 duckorbit:[4 7] duckonset:[0.3 0.003] duckattack:0.25 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "duckorbit" example index 0 1`] = `
|
exports[`runs examples > example "duckorbit" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
"[ 0/1 → 1/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||||
@@ -3310,31 +3229,6 @@ exports[`runs examples > example "duckorbit" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "duckorbit" example index 1 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 1/4 → 5/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 1/2 → 9/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 11/16 → 3/4 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 7/8 → 15/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 1/1 → 17/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 5/4 → 21/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 3/2 → 25/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 27/16 → 7/4 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 15/8 → 31/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 2/1 → 33/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 9/4 → 37/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 5/2 → 41/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 43/16 → 11/4 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 23/8 → 47/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 3/1 → 49/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 13/4 → 53/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 7/2 → 57/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 59/16 → 15/4 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
"[ 31/8 → 63/16 | s:bd n:4 duckorbit:[2 3] duckattack:0.2 duckdepth:1 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "duration" example index 0 1`] = `
|
exports[`runs examples > example "duration" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | note:c s:piano duration:0.5 ]",
|
"[ 0/1 → 1/4 | note:c s:piano duration:0.5 ]",
|
||||||
@@ -3407,20 +3301,28 @@ exports[`runs examples > example "echo" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`runs examples > example "echoForEach" example index 0 1`] = `[]`;
|
||||||
|
|
||||||
exports[`runs examples > example "echoWith" example index 0 1`] = `
|
exports[`runs examples > example "echoWith" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ -3/8 ⇜ (0/1 → 1/8) | note:Bb3 ]",
|
"[ -3/8 ⇜ (0/1 → 1/8) | note:Bb3 ]",
|
||||||
"[ -1/4 ⇜ (0/1 → 1/4) | note:D4 ]",
|
"[ -1/4 ⇜ (0/1 → 1/8) ⇝ 1/4 | note:D4 ]",
|
||||||
"[ -1/8 ⇜ (0/1 → 3/8) | note:F4 ]",
|
"[ -1/8 ⇜ (0/1 → 1/8) ⇝ 3/8 | note:F4 ]",
|
||||||
"[ 0/1 → 1/1 | note:C3 ]",
|
"[ 0/1 → 1/1 | note:C3 ]",
|
||||||
|
"[ -1/4 ⇜ (1/8 → 1/4) | note:D4 ]",
|
||||||
|
"[ -1/8 ⇜ (1/8 → 1/4) ⇝ 3/8 | note:F4 ]",
|
||||||
"[ (1/8 → 1/1) ⇝ 9/8 | note:Eb3 ]",
|
"[ (1/8 → 1/1) ⇝ 9/8 | note:Eb3 ]",
|
||||||
|
"[ -1/8 ⇜ (1/4 → 3/8) | note:F4 ]",
|
||||||
"[ (1/4 → 1/1) ⇝ 5/4 | note:G3 ]",
|
"[ (1/4 → 1/1) ⇝ 5/4 | note:G3 ]",
|
||||||
"[ (3/8 → 1/1) ⇝ 11/8 | note:Bb3 ]",
|
"[ (3/8 → 1/1) ⇝ 11/8 | note:Bb3 ]",
|
||||||
"[ 1/8 ⇜ (1/1 → 9/8) | note:Eb3 ]",
|
"[ 1/8 ⇜ (1/1 → 9/8) | note:Eb3 ]",
|
||||||
"[ 1/4 ⇜ (1/1 → 5/4) | note:G3 ]",
|
"[ 1/4 ⇜ (1/1 → 9/8) ⇝ 5/4 | note:G3 ]",
|
||||||
"[ 3/8 ⇜ (1/1 → 11/8) | note:Bb3 ]",
|
"[ 3/8 ⇜ (1/1 → 9/8) ⇝ 11/8 | note:Bb3 ]",
|
||||||
"[ 1/1 → 3/2 | note:Eb3 ]",
|
"[ 1/1 → 3/2 | note:Eb3 ]",
|
||||||
|
"[ 1/4 ⇜ (9/8 → 5/4) | note:G3 ]",
|
||||||
|
"[ 3/8 ⇜ (9/8 → 5/4) ⇝ 11/8 | note:Bb3 ]",
|
||||||
"[ 9/8 → 13/8 | note:G3 ]",
|
"[ 9/8 → 13/8 | note:G3 ]",
|
||||||
|
"[ 3/8 ⇜ (5/4 → 11/8) | note:Bb3 ]",
|
||||||
"[ 5/4 → 7/4 | note:Bb3 ]",
|
"[ 5/4 → 7/4 | note:Bb3 ]",
|
||||||
"[ 11/8 → 15/8 | note:D4 ]",
|
"[ 11/8 → 15/8 | note:D4 ]",
|
||||||
"[ 3/2 → 2/1 | note:G3 ]",
|
"[ 3/2 → 2/1 | note:G3 ]",
|
||||||
@@ -3428,17 +3330,23 @@ exports[`runs examples > example "echoWith" example index 0 1`] = `
|
|||||||
"[ (7/4 → 2/1) ⇝ 9/4 | note:D4 ]",
|
"[ (7/4 → 2/1) ⇝ 9/4 | note:D4 ]",
|
||||||
"[ (15/8 → 2/1) ⇝ 19/8 | note:F4 ]",
|
"[ (15/8 → 2/1) ⇝ 19/8 | note:F4 ]",
|
||||||
"[ 13/8 ⇜ (2/1 → 17/8) | note:Bb3 ]",
|
"[ 13/8 ⇜ (2/1 → 17/8) | note:Bb3 ]",
|
||||||
"[ 7/4 ⇜ (2/1 → 9/4) | note:D4 ]",
|
"[ 7/4 ⇜ (2/1 → 17/8) ⇝ 9/4 | note:D4 ]",
|
||||||
"[ 15/8 ⇜ (2/1 → 19/8) | note:F4 ]",
|
"[ 15/8 ⇜ (2/1 → 17/8) ⇝ 19/8 | note:F4 ]",
|
||||||
"[ 2/1 → 3/1 | note:C3 ]",
|
"[ 2/1 → 3/1 | note:C3 ]",
|
||||||
|
"[ 7/4 ⇜ (17/8 → 9/4) | note:D4 ]",
|
||||||
|
"[ 15/8 ⇜ (17/8 → 9/4) ⇝ 19/8 | note:F4 ]",
|
||||||
"[ (17/8 → 3/1) ⇝ 25/8 | note:Eb3 ]",
|
"[ (17/8 → 3/1) ⇝ 25/8 | note:Eb3 ]",
|
||||||
|
"[ 15/8 ⇜ (9/4 → 19/8) | note:F4 ]",
|
||||||
"[ (9/4 → 3/1) ⇝ 13/4 | note:G3 ]",
|
"[ (9/4 → 3/1) ⇝ 13/4 | note:G3 ]",
|
||||||
"[ (19/8 → 3/1) ⇝ 27/8 | note:Bb3 ]",
|
"[ (19/8 → 3/1) ⇝ 27/8 | note:Bb3 ]",
|
||||||
"[ 17/8 ⇜ (3/1 → 25/8) | note:Eb3 ]",
|
"[ 17/8 ⇜ (3/1 → 25/8) | note:Eb3 ]",
|
||||||
"[ 9/4 ⇜ (3/1 → 13/4) | note:G3 ]",
|
"[ 9/4 ⇜ (3/1 → 25/8) ⇝ 13/4 | note:G3 ]",
|
||||||
"[ 19/8 ⇜ (3/1 → 27/8) | note:Bb3 ]",
|
"[ 19/8 ⇜ (3/1 → 25/8) ⇝ 27/8 | note:Bb3 ]",
|
||||||
"[ 3/1 → 7/2 | note:Eb3 ]",
|
"[ 3/1 → 7/2 | note:Eb3 ]",
|
||||||
|
"[ 9/4 ⇜ (25/8 → 13/4) | note:G3 ]",
|
||||||
|
"[ 19/8 ⇜ (25/8 → 13/4) ⇝ 27/8 | note:Bb3 ]",
|
||||||
"[ 25/8 → 29/8 | note:G3 ]",
|
"[ 25/8 → 29/8 | note:G3 ]",
|
||||||
|
"[ 19/8 ⇜ (13/4 → 27/8) | note:Bb3 ]",
|
||||||
"[ 13/4 → 15/4 | note:Bb3 ]",
|
"[ 13/4 → 15/4 | note:Bb3 ]",
|
||||||
"[ 27/8 → 31/8 | note:D4 ]",
|
"[ 27/8 → 31/8 | note:D4 ]",
|
||||||
"[ 7/2 → 4/1 | note:G3 ]",
|
"[ 7/2 → 4/1 | note:G3 ]",
|
||||||
@@ -3750,8 +3658,6 @@ exports[`runs examples > example "fast" example index 0 1`] = `
|
|||||||
|
|
||||||
exports[`runs examples > example "fastChunk" example index 0 1`] = `
|
exports[`runs examples > example "fastChunk" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | color:red note:0 ]",
|
|
||||||
"[ 1/4 → 1/2 | color:red note:1 ]",
|
|
||||||
"[ 1/2 → 3/4 | note:E2 ]",
|
"[ 1/2 → 3/4 | note:E2 ]",
|
||||||
"[ 3/4 → 1/1 | note:F2 ]",
|
"[ 3/4 → 1/1 | note:F2 ]",
|
||||||
"[ 1/1 → 5/4 | note:G2 ]",
|
"[ 1/1 → 5/4 | note:G2 ]",
|
||||||
@@ -3760,8 +3666,6 @@ exports[`runs examples > example "fastChunk" example index 0 1`] = `
|
|||||||
"[ 7/4 → 2/1 | note:C3 ]",
|
"[ 7/4 → 2/1 | note:C3 ]",
|
||||||
"[ 2/1 → 9/4 | note:D3 ]",
|
"[ 2/1 → 9/4 | note:D3 ]",
|
||||||
"[ 9/4 → 5/2 | note:D2 ]",
|
"[ 9/4 → 5/2 | note:D2 ]",
|
||||||
"[ 5/2 → 11/4 | color:red note:2 ]",
|
|
||||||
"[ 11/4 → 3/1 | color:red note:3 ]",
|
|
||||||
"[ 3/1 → 13/4 | note:G2 ]",
|
"[ 3/1 → 13/4 | note:G2 ]",
|
||||||
"[ 13/4 → 7/2 | note:A2 ]",
|
"[ 13/4 → 7/2 | note:A2 ]",
|
||||||
"[ 7/2 → 15/4 | note:B2 ]",
|
"[ 7/2 → 15/4 | note:B2 ]",
|
||||||
@@ -6670,27 +6574,6 @@ exports[`runs examples > example "note" example index 2 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "note" example index 3 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | note:fbb1 s:saw ]",
|
|
||||||
"[ 1/4 → 1/2 | note:a#0 s:saw ]",
|
|
||||||
"[ 1/2 → 3/4 | note:cbbb-1 s:saw ]",
|
|
||||||
"[ 3/4 → 1/1 | note:e##-2 s:saw ]",
|
|
||||||
"[ 1/1 → 5/4 | note:fbb1 s:saw ]",
|
|
||||||
"[ 5/4 → 3/2 | note:a#0 s:saw ]",
|
|
||||||
"[ 3/2 → 7/4 | note:cbbb-1 s:saw ]",
|
|
||||||
"[ 7/4 → 2/1 | note:e##-2 s:saw ]",
|
|
||||||
"[ 2/1 → 9/4 | note:fbb1 s:saw ]",
|
|
||||||
"[ 9/4 → 5/2 | note:a#0 s:saw ]",
|
|
||||||
"[ 5/2 → 11/4 | note:cbbb-1 s:saw ]",
|
|
||||||
"[ 11/4 → 3/1 | note:e##-2 s:saw ]",
|
|
||||||
"[ 3/1 → 13/4 | note:fbb1 s:saw ]",
|
|
||||||
"[ 13/4 → 7/2 | note:a#0 s:saw ]",
|
|
||||||
"[ 7/2 → 15/4 | note:cbbb-1 s:saw ]",
|
|
||||||
"[ 15/4 → 4/1 | note:e##-2 s:saw ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "nrpnn" example index 0 1`] = `
|
exports[`runs examples > example "nrpnn" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/1 | note:c4 nrpnn:[1 8] nrpv:123 midichan:1 ]",
|
"[ 0/1 → 1/1 | note:c4 nrpnn:[1 8] nrpv:123 midichan:1 ]",
|
||||||
@@ -8979,108 +8862,6 @@ exports[`runs examples > example "scale" example index 2 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "scale" example index 3 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | note:C3 s:piano ]",
|
|
||||||
"[ 0/1 → 1/4 | note:B3 s:piano ]",
|
|
||||||
"[ 1/4 → 3/8 | note:Gb2 s:piano ]",
|
|
||||||
"[ 3/8 → 1/2 | note:F2 s:piano ]",
|
|
||||||
"[ 1/2 → 3/4 | note:A2 s:piano ]",
|
|
||||||
"[ 1/2 → 3/4 | note:D4 s:piano ]",
|
|
||||||
"[ 3/4 → 1/1 | note:G3 s:piano ]",
|
|
||||||
"[ 1/1 → 5/4 | note:C3 s:piano ]",
|
|
||||||
"[ 1/1 → 5/4 | note:C4 s:piano ]",
|
|
||||||
"[ 5/4 → 11/8 | note:Gb2 s:piano ]",
|
|
||||||
"[ 11/8 → 3/2 | note:E2 s:piano ]",
|
|
||||||
"[ 3/2 → 7/4 | note:A2 s:piano ]",
|
|
||||||
"[ 3/2 → 7/4 | note:Eb4 s:piano ]",
|
|
||||||
"[ 7/4 → 2/1 | note:F#3 s:piano ]",
|
|
||||||
"[ 2/1 → 9/4 | note:C3 s:piano ]",
|
|
||||||
"[ 2/1 → 9/4 | note:B3 s:piano ]",
|
|
||||||
"[ 9/4 → 19/8 | note:Gb2 s:piano ]",
|
|
||||||
"[ 19/8 → 5/2 | note:F2 s:piano ]",
|
|
||||||
"[ 5/2 → 11/4 | note:Ab2 s:piano ]",
|
|
||||||
"[ 5/2 → 11/4 | note:D4 s:piano ]",
|
|
||||||
"[ 11/4 → 3/1 | note:G3 s:piano ]",
|
|
||||||
"[ 3/1 → 13/4 | note:C3 s:piano ]",
|
|
||||||
"[ 3/1 → 13/4 | note:C4 s:piano ]",
|
|
||||||
"[ 13/4 → 27/8 | note:Gb2 s:piano ]",
|
|
||||||
"[ 27/8 → 7/2 | note:E2 s:piano ]",
|
|
||||||
"[ 7/2 → 15/4 | note:Ab2 s:piano ]",
|
|
||||||
"[ 7/2 → 15/4 | note:Eb4 s:piano ]",
|
|
||||||
"[ 15/4 → 4/1 | note:F#3 s:piano ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "scale" example index 4 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/16 | note:Gb1 ]",
|
|
||||||
"[ 1/16 → 1/8 | note:Gb1 ]",
|
|
||||||
"[ 1/8 → 3/16 | note:Gb1 ]",
|
|
||||||
"[ 3/16 → 1/4 | note:Gb1 ]",
|
|
||||||
"[ 1/4 → 5/16 | note:Gb1 ]",
|
|
||||||
"[ 5/16 → 3/8 | note:Gb1 ]",
|
|
||||||
"[ 3/8 → 7/16 | note:Gb1 ]",
|
|
||||||
"[ 7/16 → 1/2 | note:Gb1 ]",
|
|
||||||
"[ 1/2 → 9/16 | note:Gb1 ]",
|
|
||||||
"[ 9/16 → 5/8 | note:Gb1 ]",
|
|
||||||
"[ 5/8 → 11/16 | note:Gb1 ]",
|
|
||||||
"[ 11/16 → 3/4 | note:Gb1 ]",
|
|
||||||
"[ 3/4 → 13/16 | note:Gb1 ]",
|
|
||||||
"[ 13/16 → 7/8 | note:Gb1 ]",
|
|
||||||
"[ 7/8 → 15/16 | note:Gb1 ]",
|
|
||||||
"[ 15/16 → 1/1 | note:Gb1 ]",
|
|
||||||
"[ 1/1 → 17/16 | note:Cb3 ]",
|
|
||||||
"[ 17/16 → 9/8 | note:Cb3 ]",
|
|
||||||
"[ 9/8 → 19/16 | note:Cb3 ]",
|
|
||||||
"[ 19/16 → 5/4 | note:Cb3 ]",
|
|
||||||
"[ 5/4 → 21/16 | note:Cb3 ]",
|
|
||||||
"[ 21/16 → 11/8 | note:Cb3 ]",
|
|
||||||
"[ 11/8 → 23/16 | note:Cb3 ]",
|
|
||||||
"[ 23/16 → 3/2 | note:Cb3 ]",
|
|
||||||
"[ 3/2 → 25/16 | note:Cb3 ]",
|
|
||||||
"[ 25/16 → 13/8 | note:Cb3 ]",
|
|
||||||
"[ 13/8 → 27/16 | note:Cb3 ]",
|
|
||||||
"[ 27/16 → 7/4 | note:Cb3 ]",
|
|
||||||
"[ 7/4 → 29/16 | note:Cb3 ]",
|
|
||||||
"[ 29/16 → 15/8 | note:Cb3 ]",
|
|
||||||
"[ 15/8 → 31/16 | note:Cb3 ]",
|
|
||||||
"[ 31/16 → 2/1 | note:Cb3 ]",
|
|
||||||
"[ 2/1 → 33/16 | note:Eb4 ]",
|
|
||||||
"[ 33/16 → 17/8 | note:Eb4 ]",
|
|
||||||
"[ 17/8 → 35/16 | note:Eb4 ]",
|
|
||||||
"[ 35/16 → 9/4 | note:Eb4 ]",
|
|
||||||
"[ 9/4 → 37/16 | note:Eb4 ]",
|
|
||||||
"[ 37/16 → 19/8 | note:Eb4 ]",
|
|
||||||
"[ 19/8 → 39/16 | note:Eb4 ]",
|
|
||||||
"[ 39/16 → 5/2 | note:Eb4 ]",
|
|
||||||
"[ 5/2 → 41/16 | note:Eb4 ]",
|
|
||||||
"[ 41/16 → 21/8 | note:Eb4 ]",
|
|
||||||
"[ 21/8 → 43/16 | note:Eb4 ]",
|
|
||||||
"[ 43/16 → 11/4 | note:Eb4 ]",
|
|
||||||
"[ 11/4 → 45/16 | note:Eb4 ]",
|
|
||||||
"[ 45/16 → 23/8 | note:Eb4 ]",
|
|
||||||
"[ 23/8 → 47/16 | note:Eb4 ]",
|
|
||||||
"[ 47/16 → 3/1 | note:Eb4 ]",
|
|
||||||
"[ 3/1 → 49/16 | note:Db2 ]",
|
|
||||||
"[ 49/16 → 25/8 | note:Db2 ]",
|
|
||||||
"[ 25/8 → 51/16 | note:Db2 ]",
|
|
||||||
"[ 51/16 → 13/4 | note:Db2 ]",
|
|
||||||
"[ 13/4 → 53/16 | note:Db2 ]",
|
|
||||||
"[ 53/16 → 27/8 | note:Db2 ]",
|
|
||||||
"[ 27/8 → 55/16 | note:Db2 ]",
|
|
||||||
"[ 55/16 → 7/2 | note:Db2 ]",
|
|
||||||
"[ 7/2 → 57/16 | note:Db2 ]",
|
|
||||||
"[ 57/16 → 29/8 | note:Db2 ]",
|
|
||||||
"[ 29/8 → 59/16 | note:Db2 ]",
|
|
||||||
"[ 59/16 → 15/4 | note:Db2 ]",
|
|
||||||
"[ 15/4 → 61/16 | note:Db2 ]",
|
|
||||||
"[ 61/16 → 31/8 | note:Db2 ]",
|
|
||||||
"[ 31/8 → 63/16 | note:Db2 ]",
|
|
||||||
"[ 63/16 → 4/1 | note:Db2 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "scaleTranspose" example index 0 1`] = `
|
exports[`runs examples > example "scaleTranspose" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/2 | note:C3 ]",
|
"[ 0/1 → 1/2 | note:C3 ]",
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
The FontStruction “TIC-80 wide font”
|
|
||||||
(https://fontstruct.com/fontstructions/show/1388526) by “nesbox” is licensed
|
|
||||||
under a Creative Commons CC0 Public Domain Dedication license
|
|
||||||
(http://creativecommons.org/publicdomain/zero/1.0/).
|
|
||||||
[ancestry]
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
The font file in this archive was created using Fontstruct the free, online
|
|
||||||
font-building tool.
|
|
||||||
This font was created by “nesbox”.
|
|
||||||
This font has a homepage where this archive and other versions may be found:
|
|
||||||
https://fontstruct.com/fontstructions/show/1388526
|
|
||||||
[ancestry]
|
|
||||||
Try Fontstruct at https://fontstruct.com
|
|
||||||
It’s easy and it’s fun.
|
|
||||||
|
|
||||||
Fontstruct is copyright ©2017-2025 Rob Meek
|
|
||||||
|
|
||||||
LEGAL NOTICE:
|
|
||||||
In using this font you must comply with the licensing terms described in the
|
|
||||||
file “license.txt” included with this archive.
|
|
||||||
If you redistribute the font file in this archive, it must be accompanied by all
|
|
||||||
the other files from this archive, including this one.
|
|
||||||
Binary file not shown.
@@ -74,7 +74,6 @@ const fontFamilyOptions = {
|
|||||||
FiraCode: 'FiraCode',
|
FiraCode: 'FiraCode',
|
||||||
'FiraCode-SemiBold': 'FiraCode SemiBold',
|
'FiraCode-SemiBold': 'FiraCode SemiBold',
|
||||||
teletext: 'teletext',
|
teletext: 'teletext',
|
||||||
tic80: 'tic80',
|
|
||||||
mode7: 'mode7',
|
mode7: 'mode7',
|
||||||
BigBlueTerminal: 'BigBlueTerminal',
|
BigBlueTerminal: 'BigBlueTerminal',
|
||||||
x3270: 'x3270',
|
x3270: 'x3270',
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ stack(
|
|||||||
.mul(gain(sine.struct("x*8").add(3/5).mul(2/5).fast(8)))
|
.mul(gain(sine.struct("x*8").add(3/5).mul(2/5).fast(8)))
|
||||||
.piano()`;
|
.piano()`;
|
||||||
|
|
||||||
// iter, echo, echoWith
|
// iter, echo, echoForEach
|
||||||
export const undergroundPlumber = `// "Underground plumber"
|
export const undergroundPlumber = `// "Underground plumber"
|
||||||
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
// @by Felix Roos
|
// @by Felix Roos
|
||||||
@@ -307,7 +307,7 @@ stack(
|
|||||||
.cutoff(400).decay(.12).sustain(0)
|
.cutoff(400).decay(.12).sustain(0)
|
||||||
,
|
,
|
||||||
"[g2,[c3 eb3]]".iter(4)
|
"[g2,[c3 eb3]]".iter(4)
|
||||||
.echoWith(4, 1/8, (x,n)=>x.transpose(n*12).gain(Math.pow(.4,n)))
|
.echoForEach(4, 1/8, (x,n)=>x.transpose(n*12).gain(Math.pow(.4,n)))
|
||||||
.note().layer(h)
|
.note().layer(h)
|
||||||
.clip(.1)
|
.clip(.1)
|
||||||
)
|
)
|
||||||
@@ -432,7 +432,7 @@ export const festivalOfFingers3 = `// "Festival of fingers 3"
|
|||||||
setcps(1)
|
setcps(1)
|
||||||
|
|
||||||
n("[-7*3],0,2,6,[8 7]")
|
n("[-7*3],0,2,6,[8 7]")
|
||||||
.echoWith(
|
.echoForEach(
|
||||||
4, // echo 4 times
|
4, // echo 4 times
|
||||||
1/4, // 1/4s between echos
|
1/4, // 1/4s between echos
|
||||||
(x,i)=>x
|
(x,i)=>x
|
||||||
@@ -485,7 +485,7 @@ stack(
|
|||||||
.decay(.1).sustain(0) // make notes short
|
.decay(.1).sustain(0) // make notes short
|
||||||
.s('triangle') // waveform
|
.s('triangle') // waveform
|
||||||
.degradeBy(perlin.range(0,.5)) // randomly controlled random removal :)
|
.degradeBy(perlin.range(0,.5)) // randomly controlled random removal :)
|
||||||
.echoWith(4,.125,(x,n)=>x.gain(.15*1/(n+1))) // echo notes
|
.echoForEach(4,.125,(x,n)=>x.gain(.15*1/(n+1))) // echo notes
|
||||||
//.hush()
|
//.hush()
|
||||||
)
|
)
|
||||||
.slow(3/2)`;
|
.slow(3/2)`;
|
||||||
|
|||||||
@@ -50,11 +50,6 @@
|
|||||||
src: url('/fonts/teletext/EuropeanTeletext.ttf');
|
src: url('/fonts/teletext/EuropeanTeletext.ttf');
|
||||||
size-adjust: 90%;
|
size-adjust: 90%;
|
||||||
}
|
}
|
||||||
@font-face {
|
|
||||||
font-family: 'tic80';
|
|
||||||
src: url('/fonts/tic80/tic-80-wide-font.otf');
|
|
||||||
size-adjust: 60%;
|
|
||||||
}
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'mode7';
|
font-family: 'mode7';
|
||||||
src: url('/fonts/mode7/MODE7GX3.TTF');
|
src: url('/fonts/mode7/MODE7GX3.TTF');
|
||||||
|
|||||||
Reference in New Issue
Block a user