mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-14 06:43:47 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 50f04e8cc8 | |||
| 5e3218a67b |
+24
-101
@@ -114,7 +114,7 @@ export const { n } = registerControl('n');
|
||||
*
|
||||
* - a letter (a-g or A-G)
|
||||
* - 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`
|
||||
*
|
||||
@@ -127,8 +127,6 @@ export const { n } = registerControl('n');
|
||||
* note("c4 a4 f4 e4")
|
||||
* @example
|
||||
* note("60 69 65 64")
|
||||
* @example
|
||||
* note("fbb1 a#0 cbbb-1 e##-2").sound("saw")
|
||||
*/
|
||||
export const { note } = registerControl(['note', 'n']);
|
||||
|
||||
@@ -144,8 +142,8 @@ export const { note } = registerControl(['note', 'n']);
|
||||
*/
|
||||
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
|
||||
* @example
|
||||
* s("hh*8")
|
||||
@@ -257,7 +255,7 @@ export const { fmenv } = registerControl('fmenv');
|
||||
export const { fmattack } = registerControl('fmattack');
|
||||
|
||||
/**
|
||||
* Waveform of the fm modulator
|
||||
* waveform of the fm modulator
|
||||
*
|
||||
* @name fmwave
|
||||
* @param {number | Pattern} wave waveform
|
||||
@@ -391,7 +389,7 @@ export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], '
|
||||
// ['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
|
||||
* @name begin
|
||||
@@ -413,32 +411,6 @@ export const { begin } = registerControl('begin');
|
||||
*
|
||||
*/
|
||||
export const { end } = registerControl('end');
|
||||
/**
|
||||
* the offset of the sample buffer in seconds
|
||||
*
|
||||
* @memberof Pattern
|
||||
* @name beginSeconds
|
||||
* @param {number | Pattern} seconds
|
||||
* @example
|
||||
* samples('github:switchangel/pad')
|
||||
* s("swpad!4").clip(1).beginSeconds("2 5 .2 3").fast(2)
|
||||
* @example
|
||||
* samples('github:switchangel/pad')
|
||||
* $: s("swpad!16").beginseconds(time).dec(.1)
|
||||
*
|
||||
*/
|
||||
export const { beginSeconds, beginseconds } = registerControl('beginSeconds', 'beginseconds');
|
||||
/**
|
||||
* The end of the sample buffer in seconds
|
||||
*
|
||||
* @memberof Pattern
|
||||
* @name endSeconds
|
||||
* @param {number | Pattern} seconds
|
||||
* @example
|
||||
* s("bd*2,oh*4").endSeconds("<.1 .05 .2 1>").fast(2)
|
||||
*
|
||||
*/
|
||||
export const { endSeconds, endseconds } = registerControl('endSeconds', 'endseconds');
|
||||
/**
|
||||
* Loops the sample.
|
||||
* Note that the tempo of the loop is not synced with the cycle tempo.
|
||||
@@ -478,7 +450,7 @@ export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb');
|
||||
*/
|
||||
export const { loopEnd, loope } = registerControl('loopEnd', 'loope');
|
||||
/**
|
||||
* Bit crusher effect.
|
||||
* bit crusher effect.
|
||||
*
|
||||
* @name crush
|
||||
* @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction).
|
||||
@@ -489,7 +461,7 @@ export const { loopEnd, loope } = registerControl('loopEnd', 'loope');
|
||||
// ['clhatdecay'],
|
||||
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
|
||||
* @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on.
|
||||
@@ -500,7 +472,7 @@ export const { crush } = registerControl('crush');
|
||||
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
|
||||
* @synonyms trem
|
||||
@@ -512,7 +484,7 @@ export const { coarse } = registerControl('coarse');
|
||||
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
|
||||
* @synonyms tremsync
|
||||
@@ -527,7 +499,7 @@ export const { tremolosync } = registerControl(
|
||||
);
|
||||
|
||||
/**
|
||||
* Depth of amplitude modulation
|
||||
* depth of amplitude modulation
|
||||
*
|
||||
* @name tremolodepth
|
||||
* @synonyms tremdepth
|
||||
@@ -538,7 +510,7 @@ export const { tremolosync } = registerControl(
|
||||
*/
|
||||
export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth');
|
||||
/**
|
||||
* Alter the shape of the modulation waveform
|
||||
* alter the shape of the modulation waveform
|
||||
*
|
||||
* @name tremoloskew
|
||||
* @synonyms tremskew
|
||||
@@ -550,7 +522,7 @@ export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth');
|
||||
export const { tremoloskew } = registerControl('tremoloskew', 'tremskew');
|
||||
|
||||
/**
|
||||
* Alter the phase of the modulation waveform
|
||||
* alter the phase of the modulation waveform
|
||||
*
|
||||
* @name tremolophase
|
||||
* @synonyms tremphase
|
||||
@@ -562,7 +534,7 @@ export const { tremoloskew } = registerControl('tremoloskew', 'tremskew');
|
||||
export const { tremolophase } = registerControl('tremolophase', 'tremphase');
|
||||
|
||||
/**
|
||||
* Shape of amplitude modulation
|
||||
* shape of amplitude modulation
|
||||
*
|
||||
* @name tremoloshape
|
||||
* @synonyms tremshape
|
||||
@@ -573,7 +545,7 @@ export const { tremolophase } = registerControl('tremolophase', 'tremphase');
|
||||
*/
|
||||
export const { tremoloshape } = registerControl('tremoloshape', 'tremshape');
|
||||
/**
|
||||
* Filter overdrive for supported filter types
|
||||
* filter overdrive for supported filter types
|
||||
*
|
||||
* @name drive
|
||||
* @param {number | Pattern} amount
|
||||
@@ -584,9 +556,7 @@ export const { tremoloshape } = registerControl('tremoloshape', 'tremshape');
|
||||
export const { drive } = registerControl('drive');
|
||||
|
||||
/**
|
||||
* 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")`
|
||||
* modulate the amplitude of an orbit to create a "sidechain" like effect
|
||||
*
|
||||
* @name duckorbit
|
||||
* @synonyms duck
|
||||
@@ -594,77 +564,30 @@ export const { drive } = registerControl('drive');
|
||||
* @example
|
||||
* $: 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)
|
||||
* @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');
|
||||
|
||||
/**
|
||||
* 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")`.
|
||||
* the amount of ducking applied to target orbit
|
||||
*
|
||||
* @name duckdepth
|
||||
* @param {number | Pattern} depth depth of modulation from 0 to 1
|
||||
* @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>"))
|
||||
* @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');
|
||||
|
||||
/**
|
||||
* The time required for the ducked signal(s) to reach their lowest volume.
|
||||
* 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")`.
|
||||
* the attack time of the duck effect
|
||||
*
|
||||
* @name duckattack
|
||||
* @synonyms duckatt
|
||||
*
|
||||
* @param {number | Pattern} time The attack time in seconds
|
||||
* @param {number | Pattern} time
|
||||
* @example
|
||||
* sound: n(run(8)).scale("c:minor").s("sawtooth").delay(.7).orbit(2)
|
||||
* 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")
|
||||
* 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))
|
||||
*
|
||||
*/
|
||||
export const { duckattack } = registerControl('duckattack', 'duckatt');
|
||||
@@ -709,7 +632,7 @@ export const { byteBeatStartTime, bbst } = registerControl('byteBeatStartTime',
|
||||
export const { channels, ch } = registerControl('channels', 'ch');
|
||||
|
||||
/**
|
||||
* Controls the pulsewidth of the pulse oscillator
|
||||
* controls the pulsewidth of the pulse oscillator
|
||||
*
|
||||
* @name pw
|
||||
* @param {number | Pattern} pulsewidth
|
||||
@@ -721,7 +644,7 @@ export const { channels, ch } = registerControl('channels', 'ch');
|
||||
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
|
||||
* @param {number | Pattern} rate
|
||||
@@ -733,7 +656,7 @@ export const { pw } = registerControl(['pw', 'pwrate', 'pwsweep']);
|
||||
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
|
||||
* @param {number | Pattern} sweep
|
||||
@@ -774,7 +697,7 @@ export const { phaserrate, ph, phaser } = registerControl(
|
||||
export const { phasersweep, phs } = registerControl('phasersweep', 'phs');
|
||||
|
||||
/**
|
||||
* The center frequency of the phaser in HZ. Defaults to 1000
|
||||
* The center frequency of the phaser in HZ. Defaults to 1000
|
||||
*
|
||||
* @name phasercenter
|
||||
* @synonyms phc
|
||||
@@ -802,7 +725,7 @@ export const { phasercenter, phc } = registerControl('phasercenter', 'phc');
|
||||
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
|
||||
* @param {number | Pattern} channel channel number
|
||||
|
||||
@@ -2354,20 +2354,42 @@ export const jux = register('jux', function (func, pat) {
|
||||
|
||||
/**
|
||||
* Superimpose and offset multiple times, applying the given function each time.
|
||||
* @name echoWith
|
||||
* @synonyms echowith, stutWith, stutwith
|
||||
* @name echoForEach
|
||||
* @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, (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(
|
||||
['echoWith', 'echowith', 'stutWith', 'stutwith'],
|
||||
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)
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
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) {
|
||||
|
||||
@@ -152,9 +152,7 @@ export const itri2 = fastcat(isaw2, saw2);
|
||||
*
|
||||
* @return {Pattern}
|
||||
*/
|
||||
export const time = signal((x) => {
|
||||
return x.valueOf();
|
||||
});
|
||||
export const time = signal(id);
|
||||
|
||||
/**
|
||||
* The mouse's x position value ranges from 0 to 1.
|
||||
|
||||
@@ -877,7 +877,7 @@ describe('Pattern', () => {
|
||||
.squeezeJoin()
|
||||
.queryArc(3, 4)
|
||||
.map((x) => x.value),
|
||||
).toStrictEqual([3]);
|
||||
).toStrictEqual([Fraction(3)]);
|
||||
});
|
||||
});
|
||||
describe('ply', () => {
|
||||
|
||||
@@ -8,12 +8,12 @@ import { logger } from './logger.mjs';
|
||||
|
||||
// returns true if the given string is a note
|
||||
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) => {
|
||||
if (typeof note !== 'string') {
|
||||
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) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -78,23 +78,13 @@ export const getSampleBufferSource = async (hapValue, bank, resolveUrl) => {
|
||||
const bufferSource = ac.createBufferSource();
|
||||
bufferSource.buffer = buffer;
|
||||
bufferSource.playbackRate.value = playbackRate;
|
||||
const { s, loopBegin = 0, loopEnd = 1, begin, end, beginSeconds, endSeconds } = hapValue;
|
||||
let offset = 0;
|
||||
let endTime = bufferSource.buffer.duration;
|
||||
|
||||
const { s, loopBegin = 0, loopEnd = 1, begin = 0, end = 1 } = hapValue;
|
||||
|
||||
// "The computation of the offset into the sound is performed using the sound buffer's natural sample rate,
|
||||
// rather than the current playback rate, so even if the sound is playing at twice its normal speed,
|
||||
// the midway point through a 10-second audio buffer is still 5."
|
||||
if (begin != null) {
|
||||
offset = begin * bufferSource.buffer.duration;
|
||||
} else if (beginSeconds != null) {
|
||||
offset = beginSeconds;
|
||||
}
|
||||
|
||||
if (end != null) {
|
||||
endTime = end * bufferSource.buffer.duration;
|
||||
} else if (endSeconds != null) {
|
||||
endTime = endSeconds;
|
||||
}
|
||||
const offset = begin * bufferSource.buffer.duration;
|
||||
|
||||
const loop = s.startsWith('wt_') ? 1 : hapValue.loop;
|
||||
if (loop) {
|
||||
@@ -103,7 +93,7 @@ export const getSampleBufferSource = async (hapValue, bank, resolveUrl) => {
|
||||
bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset;
|
||||
}
|
||||
const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value;
|
||||
const sliceDuration = (endTime - offset) / bufferSource.playbackRate.value;
|
||||
const sliceDuration = (end - begin) * bufferDuration;
|
||||
return { bufferSource, offset, bufferDuration, sliceDuration };
|
||||
};
|
||||
|
||||
@@ -357,7 +347,6 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
||||
out.disconnect();
|
||||
onended();
|
||||
};
|
||||
|
||||
let envEnd = holdEnd + release + 0.01;
|
||||
bufferSource.stop(envEnd);
|
||||
const stop = (endTime) => {
|
||||
|
||||
@@ -349,17 +349,18 @@ function getDelay(orbit, delaytime, delayfeedback, t) {
|
||||
//logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`);
|
||||
}
|
||||
delayfeedback = clamp(delayfeedback, 0, 0.98);
|
||||
let delayNode = orbits[orbit].delayNode;
|
||||
if (delayNode === undefined) {
|
||||
if (!orbits[orbit].delayNode) {
|
||||
const ac = getAudioContext();
|
||||
delayNode = ac.createFeedbackDelay(1, delaytime, delayfeedback);
|
||||
delayNode.start?.(t); // for some reason, this throws when audion extension is installed..
|
||||
connectToOrbit(delayNode, orbit);
|
||||
orbits[orbit].delayNode = delayNode;
|
||||
const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback);
|
||||
dly.start?.(t); // for some reason, this throws when audion extension is installed..
|
||||
connectToOrbit(dly, orbit);
|
||||
orbits[orbit].delayNode = dly;
|
||||
}
|
||||
delayNode.delayTime.value !== delaytime && delayNode.delayTime.setValueAtTime(delaytime, t);
|
||||
delayNode.feedback.value !== delayfeedback && delayNode.feedback.setValueAtTime(delayfeedback, t);
|
||||
return delayNode;
|
||||
orbits[orbit].delayNode.delayTime.value !== delaytime &&
|
||||
orbits[orbit].delayNode.delayTime.setValueAtTime(delaytime, t);
|
||||
orbits[orbit].delayNode.feedback.value !== delayfeedback &&
|
||||
orbits[orbit].delayNode.feedback.setValueAtTime(delayfeedback, t);
|
||||
return orbits[orbit].delayNode;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// type orbit {
|
||||
// output: GainNode,
|
||||
// reverbNode: ConvolverNode
|
||||
// delayNode: FeedbackDelayNode
|
||||
// }
|
||||
//type orbit {
|
||||
// gain: number,
|
||||
// reverbNode: reverbNode
|
||||
// delayNode: delayNode
|
||||
//}
|
||||
let orbits = {};
|
||||
function connectToOrbit(node, orbit) {
|
||||
if (orbits[orbit] == null) {
|
||||
errorLogger(new Error('target orbit does not exist'), 'superdough');
|
||||
}
|
||||
node.connect(orbits[orbit].output);
|
||||
node.connect(orbits[orbit].gain);
|
||||
}
|
||||
|
||||
function setOrbit(audioContext, orbit, channels) {
|
||||
if (orbits[orbit] == null) {
|
||||
orbits[orbit] = {
|
||||
// Setup output node through which all audio filters prior to hitting
|
||||
// the destination (and thus allows for global volume automation)
|
||||
output: new GainNode(audioContext, { gain: 1, channelCount: 2, channelCountMode: 'explicit' }),
|
||||
gain: new GainNode(audioContext, { gain: 1, channelCount: 2, channelCountMode: 'explicit' }),
|
||||
};
|
||||
connectToDestination(orbits[orbit].output, channels);
|
||||
connectToDestination(orbits[orbit].gain, channels);
|
||||
}
|
||||
}
|
||||
|
||||
function duckOrbit(audioContext, targetOrbit, t, onsettime = 0, attacktime = 0.1, duckdepth = 1) {
|
||||
function duckOrbit(audioContext, targetOrbit, t, attacktime = 0.1, duckdepth = 1) {
|
||||
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) {
|
||||
errorLogger(new Error(`duck target orbit ${target} does not exist`), 'superdough');
|
||||
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(
|
||||
audioContext,
|
||||
() => {
|
||||
const now = audioContext.currentTime;
|
||||
|
||||
// cancelScheduledValues and setValueAtTime together emulate cancelAndHoldAtTime
|
||||
// on browsers which lack that method
|
||||
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);
|
||||
orbits[target].gain.gain.cancelScheduledValues(t);
|
||||
const currVal = orbits[target].gain.gain.value;
|
||||
orbits[target].gain.gain.linearRampToValueAtTime(clamp(1 - Math.pow(duckdepth, 0.5), 0.01, currVal), t);
|
||||
orbits[target].gain.gain.exponentialRampToValueAtTime(1, t + Math.max(0.002, attacktime));
|
||||
},
|
||||
0,
|
||||
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;
|
||||
function getReverb(orbit, duration, fade, lp, dim, ir, irspeed, irbegin) {
|
||||
// If no reverb has been created for a given orbit, create one
|
||||
let reverbNode = orbits[orbit].reverbNode;
|
||||
if (reverbNode === undefined) {
|
||||
if (!orbits[orbit].reverbNode) {
|
||||
const ac = getAudioContext();
|
||||
reverbNode = ac.createReverb(duration, fade, lp, dim, ir, irspeed, irbegin);
|
||||
connectToOrbit(reverbNode, orbit);
|
||||
orbits[orbit].reverbNode = reverbNode;
|
||||
const reverb = ac.createReverb(duration, fade, lp, dim, ir, irspeed, irbegin);
|
||||
connectToOrbit(reverb, orbit);
|
||||
orbits[orbit].reverbNode = reverb;
|
||||
}
|
||||
|
||||
if (
|
||||
hasChanged(duration, reverbNode.duration) ||
|
||||
hasChanged(fade, reverbNode.fade) ||
|
||||
hasChanged(lp, reverbNode.lp) ||
|
||||
hasChanged(dim, reverbNode.dim) ||
|
||||
hasChanged(irspeed, reverbNode.irspeed) ||
|
||||
hasChanged(irbegin, reverbNode.irbegin) ||
|
||||
reverbNode.ir !== ir
|
||||
hasChanged(duration, orbits[orbit].reverbNode.duration) ||
|
||||
hasChanged(fade, orbits[orbit].reverbNode.fade) ||
|
||||
hasChanged(lp, orbits[orbit].reverbNode.lp) ||
|
||||
hasChanged(dim, orbits[orbit].reverbNode.dim) ||
|
||||
hasChanged(irspeed, orbits[orbit].reverbNode.irspeed) ||
|
||||
hasChanged(irbegin, orbits[orbit].reverbNode.irbegin) ||
|
||||
orbits[orbit].reverbNode.ir !== ir
|
||||
) {
|
||||
// only regenerate when something has changed
|
||||
// avoids endless regeneration on things like
|
||||
// stack(s("a"), s("b").rsize(8)).room(.5)
|
||||
// this only works when args may stay undefined until here
|
||||
// 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 = {},
|
||||
@@ -600,7 +582,6 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
postgain = getDefaultValue('postgain'),
|
||||
density = getDefaultValue('density'),
|
||||
duckorbit,
|
||||
duckonset,
|
||||
duckattack,
|
||||
duckdepth,
|
||||
// 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);
|
||||
|
||||
if (duckorbit != null) {
|
||||
duckOrbit(ac, duckorbit, t, duckonset, duckattack, duckdepth);
|
||||
duckOrbit(ac, duckorbit, t, duckattack, duckdepth);
|
||||
}
|
||||
|
||||
gain = applyGainCurve(nanFallback(gain, 1));
|
||||
|
||||
@@ -7,7 +7,7 @@ export const tokenizeNote = (note) => {
|
||||
if (typeof note !== 'string') {
|
||||
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) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -7,130 +7,80 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
// import { strict as assert } from 'assert';
|
||||
|
||||
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 { mini } from '../../mini/mini.mjs';
|
||||
|
||||
describe('tonal', () => {
|
||||
describe('scaleTranspose', () => {
|
||||
it('transposes notes by scale degrees', () => {
|
||||
expect(pure('c3').scale('C major').scaleTranspose(1).firstCycleValues).toEqual(['D3']);
|
||||
});
|
||||
it('Should run tonal functions ', () => {
|
||||
expect(pure('c3').scale('C major').scaleTranspose(1).firstCycleValues).toEqual(['D3']);
|
||||
});
|
||||
describe('scale', () => {
|
||||
it('converts plain values', () => {
|
||||
expect(
|
||||
seq(0, 1, 2)
|
||||
.scale('C major')
|
||||
.note()
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('converts n values', () => {
|
||||
expect(
|
||||
n(seq(0, 1, 2))
|
||||
.scale('C major')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('converts n values (mini notation)', () => {
|
||||
expect(
|
||||
n(seq(0, 1, 2))
|
||||
.scale('C:major')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('converts n values (no tonic)', () => {
|
||||
expect(
|
||||
n(seq(0, 1, 2))
|
||||
.scale('major')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('converts n values (explicit mini notation)', () => {
|
||||
expect(
|
||||
n(seq(0, 1, 2))
|
||||
.scale(mini('C:major'))
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).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);
|
||||
});
|
||||
it('scale with plain values', () => {
|
||||
expect(
|
||||
seq(0, 1, 2)
|
||||
.scale('C major')
|
||||
.note()
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
describe('transpose', () => {
|
||||
it('transposes note numbers with interval numbers', () => {
|
||||
expect(
|
||||
note(seq(40, 40, 40))
|
||||
.transpose(0, 1, 2)
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual([40, 41, 42]);
|
||||
expect(seq(40, 40, 40).transpose(0, 1, 2).firstCycleValues).toEqual([40, 41, 42]);
|
||||
});
|
||||
it('transposes note numbers with interval strings', () => {
|
||||
expect(
|
||||
note(seq(40, 40, 40))
|
||||
.transpose('1P', '2M', '3m')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual([40, 42, 43]);
|
||||
expect(seq(40, 40, 40).transpose('1P', '2M', '3m').firstCycleValues).toEqual([40, 42, 43]);
|
||||
});
|
||||
it('transposes note strings with interval numbers', () => {
|
||||
expect(
|
||||
note(seq('c', 'c', 'c'))
|
||||
.transpose(0, 1, 2)
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C', 'Db', 'D']);
|
||||
expect(seq('c', 'c', 'c').transpose(0, 1, 2).firstCycleValues).toEqual(['C', 'Db', 'D']);
|
||||
});
|
||||
it('transposes note strings with interval strings', () => {
|
||||
expect(
|
||||
note(seq('c', 'c', 'c'))
|
||||
.transpose('1P', '2M', '3m')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C', 'D', 'Eb']);
|
||||
expect(seq('c', 'c', 'c').transpose('1P', '2M', '3m').firstCycleValues).toEqual(['C', 'D', 'Eb']);
|
||||
});
|
||||
it('scale with n values', () => {
|
||||
expect(
|
||||
n(seq(0, 1, 2))
|
||||
.scale('C major')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('scale with colon', () => {
|
||||
expect(
|
||||
n(seq(0, 1, 2))
|
||||
.scale('C:major')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('scale without tonic', () => {
|
||||
expect(
|
||||
n(seq(0, 1, 2))
|
||||
.scale('major')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('scale with mininotation colon', () => {
|
||||
expect(
|
||||
n(seq(0, 1, 2))
|
||||
.scale(mini('C:major'))
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('transposes note numbers with interval numbers', () => {
|
||||
expect(
|
||||
note(seq(40, 40, 40))
|
||||
.transpose(0, 1, 2)
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual([40, 41, 42]);
|
||||
expect(seq(40, 40, 40).transpose(0, 1, 2).firstCycleValues).toEqual([40, 41, 42]);
|
||||
});
|
||||
it('transposes note numbers with interval strings', () => {
|
||||
expect(
|
||||
note(seq(40, 40, 40))
|
||||
.transpose('1P', '2M', '3m')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual([40, 42, 43]);
|
||||
expect(seq(40, 40, 40).transpose('1P', '2M', '3m').firstCycleValues).toEqual([40, 42, 43]);
|
||||
});
|
||||
it('transposes note strings with interval numbers', () => {
|
||||
expect(
|
||||
note(seq('c', 'c', 'c'))
|
||||
.transpose(0, 1, 2)
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C', 'Db', 'D']);
|
||||
expect(seq('c', 'c', 'c').transpose(0, 1, 2).firstCycleValues).toEqual(['C', 'Db', 'D']);
|
||||
});
|
||||
it('transposes note strings with interval strings', () => {
|
||||
expect(
|
||||
note(seq('c', 'c', 'c'))
|
||||
.transpose('1P', '2M', '3m')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C', 'D', 'Eb']);
|
||||
expect(seq('c', 'c', 'c').transpose('1P', '2M', '3m').firstCycleValues).toEqual(['C', 'D', 'Eb']);
|
||||
});
|
||||
});
|
||||
|
||||
+53
-101
@@ -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 { register, _mod, silence, logger, pure, isNote } from '@strudel/core';
|
||||
import { stepInNamedScale, nearestNumberIndex } from './tonleiter.mjs';
|
||||
import { noteToMidi } from '../core/util.mjs';
|
||||
import { stepInNamedScale } from './tonleiter.mjs';
|
||||
|
||||
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) {
|
||||
scale = scale.replaceAll(':', ' ');
|
||||
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';
|
||||
const { pc, oct = 3 } = Note.get(tonic);
|
||||
const octaveOffset = Math.floor(step / intervals.length);
|
||||
@@ -39,7 +30,8 @@ function scaleStep(step, scale) {
|
||||
// transpose note inside scale by offset steps
|
||||
// function scaleOffset(scale: string, offset: number, note: string) {
|
||||
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!
|
||||
offset = Number(offset);
|
||||
if (isNaN(offset)) {
|
||||
@@ -128,7 +120,10 @@ export const { transpose, trans } = register(['transpose', 'trans'], function tr
|
||||
const interval = !isNaN(Number(intervalOrSemitones))
|
||||
? Interval.fromSemitones(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') {
|
||||
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.
|
||||
*
|
||||
* 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}.
|
||||
* Turns numbers into notes in the scale (zero indexed). 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).
|
||||
*
|
||||
@@ -247,12 +191,6 @@ function _getNearestScaleNote(scaleName, note, preferHigher = true) {
|
||||
* n(rand.range(0,12).segment(8))
|
||||
* .scale("C:ritusen")
|
||||
* .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(
|
||||
@@ -266,35 +204,49 @@ export const scale = register(
|
||||
pat
|
||||
.fmap((value) => {
|
||||
const isObject = typeof value === 'object';
|
||||
// The case where the note has been defined via `n` or `pure`
|
||||
if (!isObject || (isObject && ('n' in value || 'value' in value))) {
|
||||
const step = isObject ? (value.n ?? value.value) : value;
|
||||
let step = isObject ? value.n : value;
|
||||
if (isObject) {
|
||||
delete value.n; // remove n so it won't cause trouble
|
||||
if (isNote(step)) {
|
||||
// legacy..
|
||||
return pure(step);
|
||||
}
|
||||
try {
|
||||
const [number, offset] = _convertStepToNumberAndOffset(step);
|
||||
let note;
|
||||
if (isObject && value.anchor) {
|
||||
note = stepInNamedScale(number, scale, value.anchor);
|
||||
} else {
|
||||
note = scaleStep(number, scale);
|
||||
}
|
||||
if (offset != 0) note = Note.transpose(note, Interval.fromSemitones(offset));
|
||||
value = pure(isObject ? { ...value, note } : note);
|
||||
} catch (err) {
|
||||
logger(`[tonal] ${err.message}`, 'error');
|
||||
}
|
||||
if (isNote(step)) {
|
||||
// legacy..
|
||||
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;
|
||||
}
|
||||
return value;
|
||||
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;
|
||||
}
|
||||
}
|
||||
// The case where the note has been defined via `note`
|
||||
else {
|
||||
const note = _getNearestScaleNote(scale, value.note);
|
||||
return pure(isObject ? { ...value, note } : note);
|
||||
try {
|
||||
let note;
|
||||
if (isObject && value.anchor) {
|
||||
note = stepInNamedScale(asNumber, scale, value.anchor);
|
||||
} else {
|
||||
note = scaleStep(asNumber, scale);
|
||||
}
|
||||
if (semitones != 0) note = Note.transpose(note, Interval.fromSemitones(semitones));
|
||||
value = pure(isObject ? { ...value, note } : note);
|
||||
} catch (err) {
|
||||
logger(`[tonal] ${err.message}`, 'error');
|
||||
value = silence;
|
||||
}
|
||||
return value;
|
||||
})
|
||||
.outerJoin()
|
||||
// legacy:
|
||||
|
||||
@@ -101,11 +101,11 @@ export function nearestNumberIndex(target, numbers, preferHigher) {
|
||||
let scaleSteps = {}; // [scaleName]: semitones[]
|
||||
|
||||
export function stepInNamedScale(step, scale, anchor, preferHigher) {
|
||||
const [root, scaleName] = Scale.tokenize(scale);
|
||||
let [root, scaleName] = Scale.tokenize(scale);
|
||||
const rootMidi = x2midi(root);
|
||||
const rootChroma = midi2chroma(rootMidi);
|
||||
if (!scaleSteps[scaleName]) {
|
||||
const { intervals } = Scale.get(`C ${scaleName}`);
|
||||
let { intervals } = Scale.get(`C ${scaleName}`);
|
||||
// cache result
|
||||
scaleSteps[scaleName] = intervals.map(step2semitones);
|
||||
}
|
||||
@@ -222,7 +222,6 @@ export const Note = {
|
||||
};
|
||||
|
||||
// TODO: support octave numbers
|
||||
// Example: Note("Bb3").transpose("c3")
|
||||
export function transpose(note, step) {
|
||||
// example: E, 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"
|
||||
return [targetNote, offsetAccidentals].join('');
|
||||
}
|
||||
|
||||
//Note("Bb3").transpose("c3")
|
||||
|
||||
@@ -1051,112 +1051,6 @@ exports[`runs examples > example "begin" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "beginSeconds" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:swpad clip:1 beginSeconds:2 ]",
|
||||
"[ 1/8 → 1/4 | s:swpad clip:1 beginSeconds:5 ]",
|
||||
"[ 1/4 → 3/8 | s:swpad clip:1 beginSeconds:0.2 ]",
|
||||
"[ 3/8 → 1/2 | s:swpad clip:1 beginSeconds:3 ]",
|
||||
"[ 1/2 → 5/8 | s:swpad clip:1 beginSeconds:2 ]",
|
||||
"[ 5/8 → 3/4 | s:swpad clip:1 beginSeconds:5 ]",
|
||||
"[ 3/4 → 7/8 | s:swpad clip:1 beginSeconds:0.2 ]",
|
||||
"[ 7/8 → 1/1 | s:swpad clip:1 beginSeconds:3 ]",
|
||||
"[ 1/1 → 9/8 | s:swpad clip:1 beginSeconds:2 ]",
|
||||
"[ 9/8 → 5/4 | s:swpad clip:1 beginSeconds:5 ]",
|
||||
"[ 5/4 → 11/8 | s:swpad clip:1 beginSeconds:0.2 ]",
|
||||
"[ 11/8 → 3/2 | s:swpad clip:1 beginSeconds:3 ]",
|
||||
"[ 3/2 → 13/8 | s:swpad clip:1 beginSeconds:2 ]",
|
||||
"[ 13/8 → 7/4 | s:swpad clip:1 beginSeconds:5 ]",
|
||||
"[ 7/4 → 15/8 | s:swpad clip:1 beginSeconds:0.2 ]",
|
||||
"[ 15/8 → 2/1 | s:swpad clip:1 beginSeconds:3 ]",
|
||||
"[ 2/1 → 17/8 | s:swpad clip:1 beginSeconds:2 ]",
|
||||
"[ 17/8 → 9/4 | s:swpad clip:1 beginSeconds:5 ]",
|
||||
"[ 9/4 → 19/8 | s:swpad clip:1 beginSeconds:0.2 ]",
|
||||
"[ 19/8 → 5/2 | s:swpad clip:1 beginSeconds:3 ]",
|
||||
"[ 5/2 → 21/8 | s:swpad clip:1 beginSeconds:2 ]",
|
||||
"[ 21/8 → 11/4 | s:swpad clip:1 beginSeconds:5 ]",
|
||||
"[ 11/4 → 23/8 | s:swpad clip:1 beginSeconds:0.2 ]",
|
||||
"[ 23/8 → 3/1 | s:swpad clip:1 beginSeconds:3 ]",
|
||||
"[ 3/1 → 25/8 | s:swpad clip:1 beginSeconds:2 ]",
|
||||
"[ 25/8 → 13/4 | s:swpad clip:1 beginSeconds:5 ]",
|
||||
"[ 13/4 → 27/8 | s:swpad clip:1 beginSeconds:0.2 ]",
|
||||
"[ 27/8 → 7/2 | s:swpad clip:1 beginSeconds:3 ]",
|
||||
"[ 7/2 → 29/8 | s:swpad clip:1 beginSeconds:2 ]",
|
||||
"[ 29/8 → 15/4 | s:swpad clip:1 beginSeconds:5 ]",
|
||||
"[ 15/4 → 31/8 | s:swpad clip:1 beginSeconds:0.2 ]",
|
||||
"[ 31/8 → 4/1 | s:swpad clip:1 beginSeconds:3 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "beginSeconds" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | s:swpad beginSeconds:0 decay:0.1 ]",
|
||||
"[ 1/16 → 1/8 | s:swpad beginSeconds:0.0625 decay:0.1 ]",
|
||||
"[ 1/8 → 3/16 | s:swpad beginSeconds:0.125 decay:0.1 ]",
|
||||
"[ 3/16 → 1/4 | s:swpad beginSeconds:0.1875 decay:0.1 ]",
|
||||
"[ 1/4 → 5/16 | s:swpad beginSeconds:0.25 decay:0.1 ]",
|
||||
"[ 5/16 → 3/8 | s:swpad beginSeconds:0.3125 decay:0.1 ]",
|
||||
"[ 3/8 → 7/16 | s:swpad beginSeconds:0.375 decay:0.1 ]",
|
||||
"[ 7/16 → 1/2 | s:swpad beginSeconds:0.4375 decay:0.1 ]",
|
||||
"[ 1/2 → 9/16 | s:swpad beginSeconds:0.5 decay:0.1 ]",
|
||||
"[ 9/16 → 5/8 | s:swpad beginSeconds:0.5625 decay:0.1 ]",
|
||||
"[ 5/8 → 11/16 | s:swpad beginSeconds:0.625 decay:0.1 ]",
|
||||
"[ 11/16 → 3/4 | s:swpad beginSeconds:0.6875 decay:0.1 ]",
|
||||
"[ 3/4 → 13/16 | s:swpad beginSeconds:0.75 decay:0.1 ]",
|
||||
"[ 13/16 → 7/8 | s:swpad beginSeconds:0.8125 decay:0.1 ]",
|
||||
"[ 7/8 → 15/16 | s:swpad beginSeconds:0.875 decay:0.1 ]",
|
||||
"[ 15/16 → 1/1 | s:swpad beginSeconds:0.9375 decay:0.1 ]",
|
||||
"[ 1/1 → 17/16 | s:swpad beginSeconds:1 decay:0.1 ]",
|
||||
"[ 17/16 → 9/8 | s:swpad beginSeconds:1.0625 decay:0.1 ]",
|
||||
"[ 9/8 → 19/16 | s:swpad beginSeconds:1.125 decay:0.1 ]",
|
||||
"[ 19/16 → 5/4 | s:swpad beginSeconds:1.1875 decay:0.1 ]",
|
||||
"[ 5/4 → 21/16 | s:swpad beginSeconds:1.25 decay:0.1 ]",
|
||||
"[ 21/16 → 11/8 | s:swpad beginSeconds:1.3125 decay:0.1 ]",
|
||||
"[ 11/8 → 23/16 | s:swpad beginSeconds:1.375 decay:0.1 ]",
|
||||
"[ 23/16 → 3/2 | s:swpad beginSeconds:1.4375 decay:0.1 ]",
|
||||
"[ 3/2 → 25/16 | s:swpad beginSeconds:1.5 decay:0.1 ]",
|
||||
"[ 25/16 → 13/8 | s:swpad beginSeconds:1.5625 decay:0.1 ]",
|
||||
"[ 13/8 → 27/16 | s:swpad beginSeconds:1.625 decay:0.1 ]",
|
||||
"[ 27/16 → 7/4 | s:swpad beginSeconds:1.6875 decay:0.1 ]",
|
||||
"[ 7/4 → 29/16 | s:swpad beginSeconds:1.75 decay:0.1 ]",
|
||||
"[ 29/16 → 15/8 | s:swpad beginSeconds:1.8125 decay:0.1 ]",
|
||||
"[ 15/8 → 31/16 | s:swpad beginSeconds:1.875 decay:0.1 ]",
|
||||
"[ 31/16 → 2/1 | s:swpad beginSeconds:1.9375 decay:0.1 ]",
|
||||
"[ 2/1 → 33/16 | s:swpad beginSeconds:2 decay:0.1 ]",
|
||||
"[ 33/16 → 17/8 | s:swpad beginSeconds:2.0625 decay:0.1 ]",
|
||||
"[ 17/8 → 35/16 | s:swpad beginSeconds:2.125 decay:0.1 ]",
|
||||
"[ 35/16 → 9/4 | s:swpad beginSeconds:2.1875 decay:0.1 ]",
|
||||
"[ 9/4 → 37/16 | s:swpad beginSeconds:2.25 decay:0.1 ]",
|
||||
"[ 37/16 → 19/8 | s:swpad beginSeconds:2.3125 decay:0.1 ]",
|
||||
"[ 19/8 → 39/16 | s:swpad beginSeconds:2.375 decay:0.1 ]",
|
||||
"[ 39/16 → 5/2 | s:swpad beginSeconds:2.4375 decay:0.1 ]",
|
||||
"[ 5/2 → 41/16 | s:swpad beginSeconds:2.5 decay:0.1 ]",
|
||||
"[ 41/16 → 21/8 | s:swpad beginSeconds:2.5625 decay:0.1 ]",
|
||||
"[ 21/8 → 43/16 | s:swpad beginSeconds:2.625 decay:0.1 ]",
|
||||
"[ 43/16 → 11/4 | s:swpad beginSeconds:2.6875 decay:0.1 ]",
|
||||
"[ 11/4 → 45/16 | s:swpad beginSeconds:2.75 decay:0.1 ]",
|
||||
"[ 45/16 → 23/8 | s:swpad beginSeconds:2.8125 decay:0.1 ]",
|
||||
"[ 23/8 → 47/16 | s:swpad beginSeconds:2.875 decay:0.1 ]",
|
||||
"[ 47/16 → 3/1 | s:swpad beginSeconds:2.9375 decay:0.1 ]",
|
||||
"[ 3/1 → 49/16 | s:swpad beginSeconds:3 decay:0.1 ]",
|
||||
"[ 49/16 → 25/8 | s:swpad beginSeconds:3.0625 decay:0.1 ]",
|
||||
"[ 25/8 → 51/16 | s:swpad beginSeconds:3.125 decay:0.1 ]",
|
||||
"[ 51/16 → 13/4 | s:swpad beginSeconds:3.1875 decay:0.1 ]",
|
||||
"[ 13/4 → 53/16 | s:swpad beginSeconds:3.25 decay:0.1 ]",
|
||||
"[ 53/16 → 27/8 | s:swpad beginSeconds:3.3125 decay:0.1 ]",
|
||||
"[ 27/8 → 55/16 | s:swpad beginSeconds:3.375 decay:0.1 ]",
|
||||
"[ 55/16 → 7/2 | s:swpad beginSeconds:3.4375 decay:0.1 ]",
|
||||
"[ 7/2 → 57/16 | s:swpad beginSeconds:3.5 decay:0.1 ]",
|
||||
"[ 57/16 → 29/8 | s:swpad beginSeconds:3.5625 decay:0.1 ]",
|
||||
"[ 29/8 → 59/16 | s:swpad beginSeconds:3.625 decay:0.1 ]",
|
||||
"[ 59/16 → 15/4 | s:swpad beginSeconds:3.6875 decay:0.1 ]",
|
||||
"[ 15/4 → 61/16 | s:swpad beginSeconds:3.75 decay:0.1 ]",
|
||||
"[ 61/16 → 31/8 | s:swpad beginSeconds:3.8125 decay:0.1 ]",
|
||||
"[ 31/8 → 63/16 | s:swpad beginSeconds:3.875 decay:0.1 ]",
|
||||
"[ 63/16 → 4/1 | s:swpad beginSeconds:3.9375 decay:0.1 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "berlin" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | note:D3 ]",
|
||||
@@ -3199,50 +3093,57 @@ exports[`runs examples > example "dry" 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/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 → 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 → 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 ]",
|
||||
"[ 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 → 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 → 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 → 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 → 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 ]",
|
||||
"[ 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 → 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 → 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 → 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 → 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 ]",
|
||||
"[ 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 → 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 → 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 → 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 → 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 ]",
|
||||
"[ 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 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
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] ]",
|
||||
"[ 31/8 → 4/1 | note:C4 s:sawtooth delay:0.7 orbit:2 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -3303,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`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | s:bd n:4 duckorbit:2 duckattack:0.2 duckdepth:1 ]",
|
||||
@@ -3416,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`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:c s:piano duration:0.5 ]",
|
||||
@@ -3513,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`] = `
|
||||
[
|
||||
"[ -3/8 ⇜ (0/1 → 1/8) | note:Bb3 ]",
|
||||
"[ -1/4 ⇜ (0/1 → 1/4) | note:D4 ]",
|
||||
"[ -1/8 ⇜ (0/1 → 3/8) | note:F4 ]",
|
||||
"[ -1/4 ⇜ (0/1 → 1/8) ⇝ 1/4 | note:D4 ]",
|
||||
"[ -1/8 ⇜ (0/1 → 1/8) ⇝ 3/8 | note:F4 ]",
|
||||
"[ 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/4 → 3/8) | note:F4 ]",
|
||||
"[ (1/4 → 1/1) ⇝ 5/4 | note:G3 ]",
|
||||
"[ (3/8 → 1/1) ⇝ 11/8 | note:Bb3 ]",
|
||||
"[ 1/8 ⇜ (1/1 → 9/8) | note:Eb3 ]",
|
||||
"[ 1/4 ⇜ (1/1 → 5/4) | note:G3 ]",
|
||||
"[ 3/8 ⇜ (1/1 → 11/8) | note:Bb3 ]",
|
||||
"[ 1/4 ⇜ (1/1 → 9/8) ⇝ 5/4 | note:G3 ]",
|
||||
"[ 3/8 ⇜ (1/1 → 9/8) ⇝ 11/8 | note:Bb3 ]",
|
||||
"[ 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 ]",
|
||||
"[ 3/8 ⇜ (5/4 → 11/8) | note:Bb3 ]",
|
||||
"[ 5/4 → 7/4 | note:Bb3 ]",
|
||||
"[ 11/8 → 15/8 | note:D4 ]",
|
||||
"[ 3/2 → 2/1 | note:G3 ]",
|
||||
@@ -3534,17 +3330,23 @@ exports[`runs examples > example "echoWith" example index 0 1`] = `
|
||||
"[ (7/4 → 2/1) ⇝ 9/4 | note:D4 ]",
|
||||
"[ (15/8 → 2/1) ⇝ 19/8 | note:F4 ]",
|
||||
"[ 13/8 ⇜ (2/1 → 17/8) | note:Bb3 ]",
|
||||
"[ 7/4 ⇜ (2/1 → 9/4) | note:D4 ]",
|
||||
"[ 15/8 ⇜ (2/1 → 19/8) | note:F4 ]",
|
||||
"[ 7/4 ⇜ (2/1 → 17/8) ⇝ 9/4 | note:D4 ]",
|
||||
"[ 15/8 ⇜ (2/1 → 17/8) ⇝ 19/8 | note:F4 ]",
|
||||
"[ 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 ]",
|
||||
"[ 15/8 ⇜ (9/4 → 19/8) | note:F4 ]",
|
||||
"[ (9/4 → 3/1) ⇝ 13/4 | note:G3 ]",
|
||||
"[ (19/8 → 3/1) ⇝ 27/8 | note:Bb3 ]",
|
||||
"[ 17/8 ⇜ (3/1 → 25/8) | note:Eb3 ]",
|
||||
"[ 9/4 ⇜ (3/1 → 13/4) | note:G3 ]",
|
||||
"[ 19/8 ⇜ (3/1 → 27/8) | note:Bb3 ]",
|
||||
"[ 9/4 ⇜ (3/1 → 25/8) ⇝ 13/4 | note:G3 ]",
|
||||
"[ 19/8 ⇜ (3/1 → 25/8) ⇝ 27/8 | note:Bb3 ]",
|
||||
"[ 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 ]",
|
||||
"[ 19/8 ⇜ (13/4 → 27/8) | note:Bb3 ]",
|
||||
"[ 13/4 → 15/4 | note:Bb3 ]",
|
||||
"[ 27/8 → 31/8 | note:D4 ]",
|
||||
"[ 7/2 → 4/1 | note:G3 ]",
|
||||
@@ -3607,59 +3409,6 @@ exports[`runs examples > example "end" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "endSeconds" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:oh endSeconds:0.1 ]",
|
||||
"[ 0/1 → 1/4 | s:bd endSeconds:0.1 ]",
|
||||
"[ 1/8 → 1/4 | s:oh endSeconds:0.1 ]",
|
||||
"[ 1/4 → 3/8 | s:oh endSeconds:0.1 ]",
|
||||
"[ 1/4 → 1/2 | s:bd endSeconds:0.1 ]",
|
||||
"[ 3/8 → 1/2 | s:oh endSeconds:0.1 ]",
|
||||
"[ 1/2 → 5/8 | s:oh endSeconds:0.05 ]",
|
||||
"[ 1/2 → 3/4 | s:bd endSeconds:0.05 ]",
|
||||
"[ 5/8 → 3/4 | s:oh endSeconds:0.05 ]",
|
||||
"[ 3/4 → 7/8 | s:oh endSeconds:0.05 ]",
|
||||
"[ 3/4 → 1/1 | s:bd endSeconds:0.05 ]",
|
||||
"[ 7/8 → 1/1 | s:oh endSeconds:0.05 ]",
|
||||
"[ 1/1 → 9/8 | s:oh endSeconds:0.2 ]",
|
||||
"[ 1/1 → 5/4 | s:bd endSeconds:0.2 ]",
|
||||
"[ 9/8 → 5/4 | s:oh endSeconds:0.2 ]",
|
||||
"[ 5/4 → 11/8 | s:oh endSeconds:0.2 ]",
|
||||
"[ 5/4 → 3/2 | s:bd endSeconds:0.2 ]",
|
||||
"[ 11/8 → 3/2 | s:oh endSeconds:0.2 ]",
|
||||
"[ 3/2 → 13/8 | s:oh endSeconds:1 ]",
|
||||
"[ 3/2 → 7/4 | s:bd endSeconds:1 ]",
|
||||
"[ 13/8 → 7/4 | s:oh endSeconds:1 ]",
|
||||
"[ 7/4 → 15/8 | s:oh endSeconds:1 ]",
|
||||
"[ 7/4 → 2/1 | s:bd endSeconds:1 ]",
|
||||
"[ 15/8 → 2/1 | s:oh endSeconds:1 ]",
|
||||
"[ 2/1 → 17/8 | s:oh endSeconds:0.1 ]",
|
||||
"[ 2/1 → 9/4 | s:bd endSeconds:0.1 ]",
|
||||
"[ 17/8 → 9/4 | s:oh endSeconds:0.1 ]",
|
||||
"[ 9/4 → 19/8 | s:oh endSeconds:0.1 ]",
|
||||
"[ 9/4 → 5/2 | s:bd endSeconds:0.1 ]",
|
||||
"[ 19/8 → 5/2 | s:oh endSeconds:0.1 ]",
|
||||
"[ 5/2 → 21/8 | s:oh endSeconds:0.05 ]",
|
||||
"[ 5/2 → 11/4 | s:bd endSeconds:0.05 ]",
|
||||
"[ 21/8 → 11/4 | s:oh endSeconds:0.05 ]",
|
||||
"[ 11/4 → 23/8 | s:oh endSeconds:0.05 ]",
|
||||
"[ 11/4 → 3/1 | s:bd endSeconds:0.05 ]",
|
||||
"[ 23/8 → 3/1 | s:oh endSeconds:0.05 ]",
|
||||
"[ 3/1 → 25/8 | s:oh endSeconds:0.2 ]",
|
||||
"[ 3/1 → 13/4 | s:bd endSeconds:0.2 ]",
|
||||
"[ 25/8 → 13/4 | s:oh endSeconds:0.2 ]",
|
||||
"[ 13/4 → 27/8 | s:oh endSeconds:0.2 ]",
|
||||
"[ 13/4 → 7/2 | s:bd endSeconds:0.2 ]",
|
||||
"[ 27/8 → 7/2 | s:oh endSeconds:0.2 ]",
|
||||
"[ 7/2 → 29/8 | s:oh endSeconds:1 ]",
|
||||
"[ 7/2 → 15/4 | s:bd endSeconds:1 ]",
|
||||
"[ 29/8 → 15/4 | s:oh endSeconds:1 ]",
|
||||
"[ 15/4 → 31/8 | s:oh endSeconds:1 ]",
|
||||
"[ 15/4 → 4/1 | s:bd endSeconds:1 ]",
|
||||
"[ 31/8 → 4/1 | s:oh endSeconds:1 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "euclid" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:c3 ]",
|
||||
@@ -3909,8 +3658,6 @@ exports[`runs examples > example "fast" 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 ]",
|
||||
"[ 3/4 → 1/1 | note:F2 ]",
|
||||
"[ 1/1 → 5/4 | note:G2 ]",
|
||||
@@ -3919,8 +3666,6 @@ exports[`runs examples > example "fastChunk" example index 0 1`] = `
|
||||
"[ 7/4 → 2/1 | note:C3 ]",
|
||||
"[ 2/1 → 9/4 | note:D3 ]",
|
||||
"[ 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 ]",
|
||||
"[ 13/4 → 7/2 | note:A2 ]",
|
||||
"[ 7/2 → 15/4 | note:B2 ]",
|
||||
@@ -6829,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`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | note:c4 nrpnn:[1 8] nrpv:123 midichan:1 ]",
|
||||
@@ -9138,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`] = `
|
||||
[
|
||||
"[ 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-SemiBold': 'FiraCode SemiBold',
|
||||
teletext: 'teletext',
|
||||
tic80: 'tic80',
|
||||
mode7: 'mode7',
|
||||
BigBlueTerminal: 'BigBlueTerminal',
|
||||
x3270: 'x3270',
|
||||
|
||||
@@ -285,7 +285,7 @@ stack(
|
||||
.mul(gain(sine.struct("x*8").add(3/5).mul(2/5).fast(8)))
|
||||
.piano()`;
|
||||
|
||||
// iter, echo, echoWith
|
||||
// iter, echo, echoForEach
|
||||
export const undergroundPlumber = `// "Underground plumber"
|
||||
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
// @by Felix Roos
|
||||
@@ -307,7 +307,7 @@ stack(
|
||||
.cutoff(400).decay(.12).sustain(0)
|
||||
,
|
||||
"[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)
|
||||
.clip(.1)
|
||||
)
|
||||
@@ -432,7 +432,7 @@ export const festivalOfFingers3 = `// "Festival of fingers 3"
|
||||
setcps(1)
|
||||
|
||||
n("[-7*3],0,2,6,[8 7]")
|
||||
.echoWith(
|
||||
.echoForEach(
|
||||
4, // echo 4 times
|
||||
1/4, // 1/4s between echos
|
||||
(x,i)=>x
|
||||
@@ -485,7 +485,7 @@ stack(
|
||||
.decay(.1).sustain(0) // make notes short
|
||||
.s('triangle') // waveform
|
||||
.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()
|
||||
)
|
||||
.slow(3/2)`;
|
||||
|
||||
@@ -50,11 +50,6 @@
|
||||
src: url('/fonts/teletext/EuropeanTeletext.ttf');
|
||||
size-adjust: 90%;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'tic80';
|
||||
src: url('/fonts/tic80/tic-80-wide-font.otf');
|
||||
size-adjust: 60%;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'mode7';
|
||||
src: url('/fonts/mode7/MODE7GX3.TTF');
|
||||
|
||||
Reference in New Issue
Block a user