From cc4452ce58a3d2a3fd12fe37166d7d2047222d58 Mon Sep 17 00:00:00 2001 From: Aria Date: Sat, 4 Oct 2025 20:33:25 -0500 Subject: [PATCH 1/8] Working version of partials and phases --- packages/core/controls.mjs | 1 + packages/core/signal.mjs | 38 ++++++++++++++++- packages/core/test/pattern.test.mjs | 4 +- packages/superdough/synth.mjs | 52 +++++++++++++++++------- website/src/components/Header/Search.css | 4 +- website/src/pages/learn/samples.mdx | 2 +- 6 files changed, 79 insertions(+), 22 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 7e3c2a8e..c8005927 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -2040,6 +2040,7 @@ export const { real } = registerControl('real'); export const { imag } = registerControl('imag'); export const { enhance } = registerControl('enhance'); export const { partials } = registerControl('partials'); +export const { phases } = registerControl('phases'); export const { comb } = registerControl('comb'); export const { smear } = registerControl('smear'); export const { scram } = registerControl('scram'); diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 6bd6fde6..b4dc825f 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -228,7 +228,7 @@ const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n); export const run = (n) => saw.range(0, n).round().segment(n); /** - * Creates a pattern from a binary number. + * Creates a binary pattern from a number. * * @name binary * @param {number} n - input number to convert to binary @@ -242,7 +242,7 @@ export const binary = (n) => { }; /** - * Creates a pattern from a binary number, padded to n bits long. + * Creates a binary pattern from a number, padded to n bits long. * * @name binaryN * @param {number} n - input number to convert to binary @@ -258,6 +258,40 @@ export const binaryN = (n, nBits = 16) => { return reify(n).segment(nBits).brshift(bitPos).band(pure(1)); }; +/** + * Creates a binary list pattern from a number. + * + * @name binaryL + * @param {number} n - input number to convert to binary + */ +export const binaryL = (n) => { + const nBits = reify(n).log2(0).floor().add(1); + return binaryNL(n, nBits); +}; + +/** + * Creates a binary list pattern from a number, padded to n bits long. + * + * @name binaryNL + * @param {number} n - input number to convert to binary + * @param {number} nBits - pattern length, defaults to 16 + */ +export const binaryNL = (n, nBits = 16) => { + return reify(n) + .withValue((v) => (bits) => { + const bList = []; + for (let i = bits - 1; i >= 0; i--) { + bList.push((v >> i) & 1); + } + return bList; + }) + .appLeft(reify(nBits)); +}; + +export const randL = (n) => { + return signal((t) => (nVal) => timeToRands(t, nVal).map(Math.abs)).appLeft(reify(n)); +}; + export const randrun = (n) => { return signal((t) => { // Without adding 0.5, the first cycle is always 0,1,2,3,... diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 1df5c877..625f4075 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -796,7 +796,7 @@ describe('Pattern', () => { }); }); describe('apply', () => { - it('Can apply a function', () => { + (it('Can apply a function', () => { expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle()); }), it('Can apply a pattern of functions', () => { @@ -804,7 +804,7 @@ describe('Pattern', () => { expect(sequence('a', 'b').apply(fast(2), fast(3)).firstCycle()).toStrictEqual( sequence('a', 'b').fast(2, 3).firstCycle(), ); - }); + })); }); describe('layer', () => { it('Can layer up multiple functions', () => { diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 5dc5dc08..71c4a15d 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -14,9 +14,10 @@ import { noises, webAudioTimeout, } from './helpers.mjs'; +import { logger } from './logger.mjs'; import { getNoiseMix, getNoiseOscillator } from './noise.mjs'; -const waveforms = ['triangle', 'square', 'sawtooth', 'sine']; +const waveforms = ['triangle', 'square', 'sawtooth', 'sine', 'user']; const waveformAliases = [ ['tri', 'triangle'], ['sqr', 'square'], @@ -413,9 +414,13 @@ export function registerSynthSounds() { waveformAliases.forEach(([alias, actual]) => soundMap.set({ ...soundMap.get(), [alias]: soundMap.get()[actual] })); } -export function waveformN(partials, type) { - const real = new Float32Array(partials + 1); - const imag = new Float32Array(partials + 1); +const PI2 = 2 * Math.PI; +export function waveformN(partials, phases, type) { + const isList = typeof partials === 'object'; + partials = isList ? partials : new Float32Array(partials).fill(1); + const len = partials.length; + const real = new Float32Array(len + 1); + const imag = new Float32Array(len + 1); const ac = getAudioContext(); const osc = ac.createOscillator(); @@ -423,20 +428,29 @@ export function waveformN(partials, type) { sawtooth: (n) => [0, -1 / n], square: (n) => [0, n % 2 === 0 ? 0 : 1 / n], triangle: (n) => [n % 2 === 0 ? 0 : 1 / (n * n), 0], + user: (_n) => [0, 1], }; if (!terms[type]) { throw new Error(`unknown wave type ${type}`); } - real[0] = 0; // dc offset - imag[0] = 0; - let n = 1; - while (n <= partials) { - const [r, i] = terms[type](n); - real[n] = r; - imag[n] = i; - n++; + for (let n = 0; n < len; n++) { + const mag = partials[n]; + const [r, i] = terms[type](n + 1); // we skip n === 0 as this is dc offset + const phase = phases?.[n] ?? 0; + // Scale by `partials` + let R = r * mag; + let I = i * mag; + // Apply rotation by the phase + if (phase !== 0) { + const c = Math.cos(PI2 * phase); + const s = Math.sin(PI2 * phase); + R = c * R - s * I; + I = s * R + c * I; + } + real[n + 1] = R; + imag[n + 1] = I; } const wave = ac.createPeriodicWave(real, imag); @@ -446,16 +460,24 @@ export function waveformN(partials, type) { // expects one of waveforms as s export function getOscillator(s, t, value) { - let { n: partials, duration, noise = 0 } = value; + const { duration, noise = 0 } = value; + const partials = value.partials ?? value.n; let o; + if (s === 'user' && !partials) { + logger( + `[superdough] Synth 'user' was selected, but partials not specified. Defaulting to triangle. Use pat.partials to setup custom waveform`, + ); + s = 'triangle'; + } + s = s === 'user' && !partials ? 'triangle' : s; // If no partials are given, use stock waveforms - if (!partials || s === 'sine') { + if (!partials || !partials?.length || s === 'sine') { o = getAudioContext().createOscillator(); o.type = s || 'triangle'; } // generate custom waveform if partials are given else { - o = waveformN(partials, s); + o = waveformN(partials, value.phases, s); } // set frequency o.frequency.value = getFrequencyFromValue(value); diff --git a/website/src/components/Header/Search.css b/website/src/components/Header/Search.css index 456ef9f6..b14146cb 100644 --- a/website/src/components/Header/Search.css +++ b/website/src/components/Header/Search.css @@ -18,8 +18,8 @@ --docsearch-modal-background: var(--background); --docsearch-muted-color: color-mix(in srgb, var(--foreground), #fff 30%); --docsearch-key-gradient: var(--foreground); - --docsearch-key-shadow: inset 0 -2px 0 0 var(--gutterForeground), inset 0 0 1px 1px var(--foreground), - 0 1px 2px 1px var(--gutterBackground); + --docsearch-key-shadow: + inset 0 -2px 0 0 var(--gutterForeground), inset 0 0 1px 1px var(--foreground), 0 1px 2px 1px var(--gutterBackground); } .dark { --docsearch-muted-color: color-mix(in srgb, var(--foreground), #000 30%); diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index eb79cccf..201d57df 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -381,7 +381,7 @@ Sampler effects are functions that can be used to change the behaviour of sample ### scrub -{' '} + ### speed From 0fe7edc15756919b73564ba84f54695d9f2fcb67 Mon Sep 17 00:00:00 2001 From: Aria Date: Wed, 15 Oct 2025 14:53:07 -0500 Subject: [PATCH 2/8] Some examples --- packages/core/controls.mjs | 30 ++- packages/core/signal.mjs | 11 ++ test/__snapshots__/examples.test.mjs.snap | 212 ++++++++++++++++++++++ 3 files changed, 251 insertions(+), 2 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 30b3c14f..327f8df5 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -2072,8 +2072,6 @@ export const { tsdelay } = registerControl('tsdelay'); export const { real } = registerControl('real'); export const { imag } = registerControl('imag'); export const { enhance } = registerControl('enhance'); -export const { partials } = registerControl('partials'); -export const { phases } = registerControl('phases'); export const { comb } = registerControl('comb'); export const { smear } = registerControl('smear'); export const { scram } = registerControl('scram'); @@ -2375,3 +2373,31 @@ export const scrub = register( }, false, ); + +/** + * Scale the magnitude of the harmonics of one of the core synths ('sine', 'tri', 'saw', ..) + * + * Can also be used to create a new synth via `s('user').partials(...)` + * + * @name partials + * @param {number[] | Pattern} partials List of magnitudes for partials. 0th entry is the first harmonic (i.e. DC offset is skipped) + * @example + * s("user").seg(16).n(irand(8)).scale("A:major") + * .partials([1, 0, 1, 0, 0, 1]) + * @example + * s("saw").seg(8).n(irand(12)).scale("G#:minor") + * .partials(binaryL(256)) + */ +export const { partials } = registerControl('partials'); + +/** + * Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases + * + * @name phases + * @param {number[] | Pattern} phases List of phases for partials. 0th entry is the first phase (i.e. DC offset is skipped) + * @example + * s("saw").seg(8).n(irand(12)).scale("G#:minor") + * .partials(binaryL(256)) + * .phases(randL(20)) + */ +export const { phases } = registerControl('phases'); diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index b4dc825f..604d1c80 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -263,6 +263,8 @@ export const binaryN = (n, nBits = 16) => { * * @name binaryL * @param {number} n - input number to convert to binary + * s("saw").seg(8) + * .partials(binaryL(irand(4096).add(1))) */ export const binaryL = (n) => { const nBits = reify(n).log2(0).floor().add(1); @@ -288,6 +290,15 @@ export const binaryNL = (n, nBits = 16) => { .appLeft(reify(nBits)); }; +/** + * Creates a list of random numbers of the given length + * + * @name randL + * @param {number} n Number of random numbers to sample + * @example + * s("saw").seg(16).n(irand(12)).scale("F1:minor") + * .partials(randL(8)) + */ export const randL = (n) => { return signal((t) => (nVal) => timeToRands(t, nVal).map(Math.abs)).appLeft(reify(n)); }; diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 937d4279..17c25cc5 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -7078,6 +7078,112 @@ exports[`runs examples > example "panchor" example index 0 1`] = ` ] `; +exports[`runs examples > example "partials" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | s:user note:A3 partials:[1 0 1 0 0 1] ]", + "[ 1/16 → 1/8 | s:user note:G#4 partials:[1 0 1 0 0 1] ]", + "[ 1/8 → 3/16 | s:user note:F#4 partials:[1 0 1 0 0 1] ]", + "[ 3/16 → 1/4 | s:user note:B3 partials:[1 0 1 0 0 1] ]", + "[ 1/4 → 5/16 | s:user note:C#4 partials:[1 0 1 0 0 1] ]", + "[ 5/16 → 3/8 | s:user note:E4 partials:[1 0 1 0 0 1] ]", + "[ 3/8 → 7/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]", + "[ 7/16 → 1/2 | s:user note:C#4 partials:[1 0 1 0 0 1] ]", + "[ 1/2 → 9/16 | s:user note:C#4 partials:[1 0 1 0 0 1] ]", + "[ 9/16 → 5/8 | s:user note:B3 partials:[1 0 1 0 0 1] ]", + "[ 5/8 → 11/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]", + "[ 11/16 → 3/4 | s:user note:D4 partials:[1 0 1 0 0 1] ]", + "[ 3/4 → 13/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]", + "[ 13/16 → 7/8 | s:user note:G#4 partials:[1 0 1 0 0 1] ]", + "[ 7/8 → 15/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]", + "[ 15/16 → 1/1 | s:user note:F#4 partials:[1 0 1 0 0 1] ]", + "[ 1/1 → 17/16 | s:user note:E4 partials:[1 0 1 0 0 1] ]", + "[ 17/16 → 9/8 | s:user note:E4 partials:[1 0 1 0 0 1] ]", + "[ 9/8 → 19/16 | s:user note:F#4 partials:[1 0 1 0 0 1] ]", + "[ 19/16 → 5/4 | s:user note:A3 partials:[1 0 1 0 0 1] ]", + "[ 5/4 → 21/16 | s:user note:F#4 partials:[1 0 1 0 0 1] ]", + "[ 21/16 → 11/8 | s:user note:A3 partials:[1 0 1 0 0 1] ]", + "[ 11/8 → 23/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]", + "[ 23/16 → 3/2 | s:user note:E4 partials:[1 0 1 0 0 1] ]", + "[ 3/2 → 25/16 | s:user note:E4 partials:[1 0 1 0 0 1] ]", + "[ 25/16 → 13/8 | s:user note:B3 partials:[1 0 1 0 0 1] ]", + "[ 13/8 → 27/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]", + "[ 27/16 → 7/4 | s:user note:C#4 partials:[1 0 1 0 0 1] ]", + "[ 7/4 → 29/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]", + "[ 29/16 → 15/8 | s:user note:D4 partials:[1 0 1 0 0 1] ]", + "[ 15/8 → 31/16 | s:user note:E4 partials:[1 0 1 0 0 1] ]", + "[ 31/16 → 2/1 | s:user note:G#4 partials:[1 0 1 0 0 1] ]", + "[ 2/1 → 33/16 | s:user note:A4 partials:[1 0 1 0 0 1] ]", + "[ 33/16 → 17/8 | s:user note:C#4 partials:[1 0 1 0 0 1] ]", + "[ 17/8 → 35/16 | s:user note:A4 partials:[1 0 1 0 0 1] ]", + "[ 35/16 → 9/4 | s:user note:A3 partials:[1 0 1 0 0 1] ]", + "[ 9/4 → 37/16 | s:user note:C#4 partials:[1 0 1 0 0 1] ]", + "[ 37/16 → 19/8 | s:user note:F#4 partials:[1 0 1 0 0 1] ]", + "[ 19/8 → 39/16 | s:user note:G#4 partials:[1 0 1 0 0 1] ]", + "[ 39/16 → 5/2 | s:user note:B3 partials:[1 0 1 0 0 1] ]", + "[ 5/2 → 41/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]", + "[ 41/16 → 21/8 | s:user note:E4 partials:[1 0 1 0 0 1] ]", + "[ 21/8 → 43/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]", + "[ 43/16 → 11/4 | s:user note:E4 partials:[1 0 1 0 0 1] ]", + "[ 11/4 → 45/16 | s:user note:F#4 partials:[1 0 1 0 0 1] ]", + "[ 45/16 → 23/8 | s:user note:A4 partials:[1 0 1 0 0 1] ]", + "[ 23/8 → 47/16 | s:user note:A3 partials:[1 0 1 0 0 1] ]", + "[ 47/16 → 3/1 | s:user note:C#4 partials:[1 0 1 0 0 1] ]", + "[ 3/1 → 49/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]", + "[ 49/16 → 25/8 | s:user note:A3 partials:[1 0 1 0 0 1] ]", + "[ 25/8 → 51/16 | s:user note:C#4 partials:[1 0 1 0 0 1] ]", + "[ 51/16 → 13/4 | s:user note:A4 partials:[1 0 1 0 0 1] ]", + "[ 13/4 → 53/16 | s:user note:A4 partials:[1 0 1 0 0 1] ]", + "[ 53/16 → 27/8 | s:user note:C#4 partials:[1 0 1 0 0 1] ]", + "[ 27/8 → 55/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]", + "[ 55/16 → 7/2 | s:user note:G#4 partials:[1 0 1 0 0 1] ]", + "[ 7/2 → 57/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]", + "[ 57/16 → 29/8 | s:user note:G#4 partials:[1 0 1 0 0 1] ]", + "[ 29/8 → 59/16 | s:user note:A4 partials:[1 0 1 0 0 1] ]", + "[ 59/16 → 15/4 | s:user note:A4 partials:[1 0 1 0 0 1] ]", + "[ 15/4 → 61/16 | s:user note:G#4 partials:[1 0 1 0 0 1] ]", + "[ 61/16 → 31/8 | s:user note:A3 partials:[1 0 1 0 0 1] ]", + "[ 31/8 → 63/16 | s:user note:F#4 partials:[1 0 1 0 0 1] ]", + "[ 63/16 → 4/1 | s:user note:C#4 partials:[1 0 1 0 0 1] ]", +] +`; + +exports[`runs examples > example "partials" example index 1 1`] = ` +[ + "[ 0/1 → 1/8 | s:saw note:G#3 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 1/8 → 1/4 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 1/4 → 3/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 3/8 → 1/2 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 1/2 → 5/8 | s:saw note:C#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 5/8 → 3/4 | s:saw note:A#3 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 3/4 → 7/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 7/8 → 1/1 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 1/1 → 9/8 | s:saw note:F#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 9/8 → 5/4 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 5/4 → 11/8 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 11/8 → 3/2 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 3/2 → 13/8 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 13/8 → 7/4 | s:saw note:E4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 7/4 → 15/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 15/8 → 2/1 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 2/1 → 17/8 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 17/8 → 9/4 | s:saw note:C#5 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 9/4 → 19/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 19/8 → 5/2 | s:saw note:C#5 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 5/2 → 21/8 | s:saw note:E4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 21/8 → 11/4 | s:saw note:A#3 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 11/4 → 23/8 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 23/8 → 3/1 | s:saw note:G#3 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 3/1 → 25/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 25/8 → 13/4 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 13/4 → 27/8 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 27/8 → 7/2 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 7/2 → 29/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 29/8 → 15/4 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 15/4 → 31/8 | s:saw note:B4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 31/8 → 4/1 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] ]", +] +`; + exports[`runs examples > example "pattack" example index 0 1`] = ` [ "[ 0/1 → 1/2 | note:c pattack:0 ]", @@ -7331,6 +7437,43 @@ exports[`runs examples > example "phasersweep" example index 0 1`] = ` ] `; +exports[`runs examples > example "phases" example index 0 1`] = ` +[ + "[ 0/1 → 1/8 | s:saw note:G#3 partials:[1 0 0 0 0 0 0 0 0] phases:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] ]", + "[ 1/8 → 1/4 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.6852155700325966 0.17723728902637959 0.8521428182721138 0.5022399611771107 0.9780306946486235 0.3186125475913286 0.1851645242422819 0.8495976086705923 0.9832699615508318 0.8203876558691263 0.7365445382893085 0.6727468091994524 0.4794053863734007 0.35482912696897984 0.5666771996766329 0.4527092967182398 0.5699347071349621 0.7358296867460012 0.5444891396909952 0.11270620673894882] ]", + "[ 1/4 → 3/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.36975969187915325 0.18273563869297504 0.012781793251633644 0.5423613861203194 0.12467948533594608 0.7188410349190235 0.856887087225914 0.8818203993141651 0.8917219173163176 0.110306391492486 0.07148400321602821 0.8493648078292608 0.9100127145648003 0.4820226952433586 0.037094997242093086 0.9857278112322092 0.23439379036426544 0.46701666340231895 0.15832693874835968 0.6921216659247875] ]", + "[ 3/8 → 1/2 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.40139251574873924 0.15799915604293346 0.9604704882949591 0.03873417526483536 0.4157217647880316 0.27756719291210175 0.8800551909953356 0.5692523363977671 0.11026228219270706 0.7190891150385141 0.404962956905365 0.50828124769032 0.9681975357234478 0.34809120930731297 0.9345223866403103 0.9851128943264484 0.11768617853522301 0.538263589143753 0.6650313660502434 0.02540053054690361] ]", + "[ 1/2 → 5/8 | s:saw note:C#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.2604806162416935 0.3654713351279497 0.9900747090578079 0.22572625242173672 0.9328999016433954 0.5273839123547077 0.7047769222408533 0.7311522178351879 0.42673745937645435 0.7099553178995848 0.5597201306372881 0.8968746215105057 0.15695763938128948 0.3792166206985712 0.4147114269435406 0.38739512488245964 0.7585489805787802 0.3875726908445358 0.867314238101244 0.1866922564804554] ]", + "[ 5/8 → 3/4 | s:saw note:A#3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.1356358677148819 0.5403102282434702 0.07321739941835403 0.9646544624119997 0.7707359045743942 0.46888123638927937 0.9489036612212658 0.366748945787549 0.5029341224581003 0.7194344792515039 0.282692551612854 0.7303404528647661 0.7155798356980085 0.637981578707695 0.39654020965099335 0.008769871667027473 0.5857728160917759 0.3513293471187353 0.43080931156873703 0.518209295347333] ]", + "[ 3/4 → 7/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.19582648016512394 0.0024610888212919235 0.08272589556872845 0.09592138417065144 0.9678201265633106 0.3249557167291641 0.9463537875562906 0.20766610652208328 0.19126702845096588 0.4482936095446348 0.23314787074923515 0.07724925130605698 0.8329483829438686 0.39804019033908844 0.2529231123626232 0.889951054006815 0.716364661231637 0.3905949704349041 0.990752438083291 0.275751456618309] ]", + "[ 7/8 → 1/1 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.3976310808211565 0.13476407527923584 0.963376859202981 0.6579397786408663 0.5203975513577461 0.865439185872674 0.5583905670791864 0.2736878804862499 0.17021040990948677 0.3456706069409847 0.5848060417920351 0.9889458417892456 0.9307208992540836 0.7148868907243013 0.7419579364359379 0.8103639334440231 0.3331365995109081 0.19130694679915905 0.03261224366724491 0.808204049244523] ]", + "[ 1/1 → 9/8 | s:saw note:F#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.5195421651005745 0.9349692352116108 0.1718774326145649 0.9601866770535707 0.2802200373262167 0.8732441551983356 0.01810968853533268 0.42737805284559727 0.26281314715743065 0.7521175239235163 0.568607984110713 0.5046361442655325 0.03172125294804573 0.3706745784729719 0.9402780849486589 0.3995086643844843 0.35769628174602985 0.023460431024432182 0.043030962347984314 0.6526827793568373] ]", + "[ 9/8 → 5/4 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.6724895145744085 0.21410975232720375 0.5229047331959009 0.20957534946501255 0.7402758821845055 0.2681974694132805 0.4059371296316385 0.9422509074211121 0.6584139950573444 0.26589646376669407 0.16059227287769318 0.17166719026863575 0.6234225388616323 0.020276052877306938 0.4836352560669184 0.8991366866976023 0.1860280428081751 0.06238717399537563 0.4383583478629589 0.4902789145708084] ]", + "[ 5/4 → 11/8 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.7287282031029463 0.9341024849563837 0.5098182689398527 0.5608645845204592 0.6812942810356617 0.5015129633247852 0.4602120481431484 0.927369873970747 0.19313151575624943 0.5240397155284882 0.444337897002697 0.19362097792327404 0.7068767864257097 0.920799495652318 0.2648108974099159 0.9700228478759527 0.20331068895757198 0.38390261493623257 0.0038731005042791367 0.9156702514737844] ]", + "[ 11/8 → 3/2 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.18280375190079212 0.40527783520519733 0.30504362285137177 0.7622128054499626 0.842598931863904 0.053796686232089996 0.5124214049428701 0.9178454764187336 0.09979427233338356 0.8280061967670918 0.5572535842657089 0.0622002687305212 0.9205668028444052 0.7738517206162214 0.04644870012998581 0.2117986585944891 0.26436166651546955 0.26421429589390755 0.4649084359407425 0.10490566119551659] ]", + "[ 3/2 → 13/8 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.6084080748260021 0.4424846563488245 0.755185678601265 0.04328125901520252 0.04244415462017059 0.3994515985250473 0.787989066913724 0.3180440980941057 0.30072982981801033 0.24217353947460651 0.7830625418573618 0.7733921892940998 0.7720277719199657 0.4402343425899744 0.21046430803835392 0.38635879568755627 0.5671729445457458 0.9510521050542593 0.010756833478808403 0.5728995501995087] ]", + "[ 13/8 → 7/4 | s:saw note:E4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.49675471149384975 0.2965749856084585 0.09848833456635475 0.19172007404267788 0.22677889838814735 0.9841996673494577 0.11156083643436432 0.6079028844833374 0.9412728808820248 0.9376902114599943 0.3611182738095522 0.5901201106607914 0.26860327646136284 0.9782364591956139 0.18694544956088066 0.004641396924853325 0.42020696587860584 0.06209231913089752 0.4790260009467602 0.21088780276477337] ]", + "[ 7/4 → 15/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.20473789609968662 0.7480021081864834 0.30757393687963486 0.5827204789966345 0.22094514779746532 0.6370698790997267 0.16573002003133297 0.0638319905847311 0.12625465169548988 0.4565841108560562 0.46095744147896767 0.646710304543376 0.045173922553658485 0.4833248760551214 0.1326297614723444 0.5275116711854935 0.9871038906276226 0.424171743914485 0.761672617867589 0.08582597225904465] ]", + "[ 15/8 → 2/1 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.5945571791380644 0.3962489552795887 0.563431927934289 0.4742323160171509 0.4185752831399441 0.9703940469771624 0.5282467231154442 0.6896266285330057 0.6941124945878983 0.2423656489700079 0.225077323615551 0.5445358455181122 0.014737963676452637 0.2848400343209505 0.14955217391252518 0.9782383926212788 0.6327074971050024 0.23327310755848885 0.4324392694979906 0.4326172359287739] ]", + "[ 2/1 → 17/8 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] phases:[0.9595271199941635 0.5427457969635725 0.45864437520504 0.8057199101895094 0.18388565629720688 0.17221237532794476 0.8838487900793552 0.4976818822324276 0.7543560564517975 0.34416236728429794 0.8638174068182707 0.5697487238794565 0.005243342369794846 0.6361143626272678 0.6039986703544855 0.38708674535155296 0.5463927835226059 0.5717255529016256 0.15141930803656578 0.22688637301325798] ]", + "[ 17/8 → 9/4 | s:saw note:C#5 partials:[1 0 0 0 0 0 0 0 0] phases:[0.9033902939409018 0.22919894196093082 0.8388008493930101 0.9108077362179756 0.32246968522667885 0.16122018359601498 0.31932324543595314 0.18524732999503613 0.19110161997377872 0.5346284378319979 0.38531880639493465 0.7401201985776424 0.07450124807655811 0.18417961709201336 0.5768439751118422 0.7161206863820553 0.5836263168603182 0.9591280855238438 0.7728104889392853 0.0006709620356559753] ]", + "[ 9/4 → 19/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.34548251144587994 0.30419893004000187 0.10205957666039467 0.9906709585338831 0.3156223688274622 0.3745057098567486 0.7668170686811209 0.9379972033202648 0.689277408644557 0.8690325897186995 0.36728161945939064 0.9668608456850052 0.7341883443295956 0.32575040124356747 0.8095720671117306 0.3182998225092888 0.38158727809786797 0.5534052699804306 0.5973556581884623 0.752589326351881] ]", + "[ 19/8 → 5/2 | s:saw note:C#5 partials:[1 0 0 0 0 0 0 0 0] phases:[0.8365066405385733 0.5369812995195389 0.2263860274106264 0.21759207546710968 0.2172116208821535 0.09454222768545151 0.8075542915612459 0.9017798062413931 0.46291174553334713 0.01372767798602581 0.3509599883109331 0.4871185813099146 0.3525365497916937 0.6883120182901621 0.19577431678771973 0.287217665463686 0.025726469233632088 0.11798650585114956 0.8464976660907269 0.981348654255271] ]", + "[ 5/2 → 21/8 | s:saw note:E4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.45861607417464256 0.6807645913213491 0.07009582966566086 0.1450389064848423 0.2579921595752239 0.612881500273943 0.5503941811621189 0.7027011960744858 0.681745121255517 0.24246043153107166 0.2981361001729965 0.02195165678858757 0.10680225491523743 0.9413154497742653 0.6108828671276569 0.9479686040431261 0.17599895782768726 0.6629563421010971 0.5875700414180756 0.05703482776880264] ]", + "[ 21/8 → 11/4 | s:saw note:A#3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.16019348427653313 0.47758268006145954 0.9749126061797142 0.8025561925023794 0.3600110989063978 0.324309878051281 0.5655391272157431 0.13364790193736553 0.3045873437076807 0.3814875278621912 0.14350778982043266 0.6017840709537268 0.11792952008545399 0.7185723781585693 0.6789924055337906 0.43416675738990307 0.9378792773932219 0.48162082210183144 0.37555896304547787 0.4686833508312702] ]", + "[ 11/4 → 23/8 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.6332327704876661 0.7342763151973486 0.16935866326093674 0.4754706546664238 0.528122790157795 0.21934260986745358 0.7476693484932184 0.2995396424084902 0.46688378043472767 0.9741295631974936 0.9878341723233461 0.9837898630648851 0.15651432052254677 0.2896903567016125 0.7395600061863661 0.3490046579390764 0.014511989429593086 0.6857758145779371 0.7405510656535625 0.4224672969430685] ]", + "[ 23/8 → 3/1 | s:saw note:G#3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.01625417172908783 0.28409438766539097 0.3075305689126253 0.40343056432902813 0.5556836389005184 0.6926821582019329 0.5635769125074148 0.017873913049697876 0.5601800158619881 0.28681862354278564 0.8022358678281307 0.13467839546501637 0.02351163513958454 0.1987263821065426 0.4515750799328089 0.0009761657565832138 0.11555273085832596 0.25053937546908855 0.29571316950023174 0.15751986764371395] ]", + "[ 3/1 → 25/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.21728911064565182 0.7605195846408606 0.11240843310952187 0.4372697602957487 0.15630115941166878 0.1653979979455471 0.5303416550159454 0.07218753732740879 0.47459222190082073 0.04051801189780235 0.8742353077977896 0.9356274232268333 0.6662059463560581 0.4364917427301407 0.4163493011146784 0.9820694588124752 0.09098831936717033 0.41974459774792194 0.05833998881280422 0.2601191997528076] ]", + "[ 25/8 → 13/4 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.34324387833476067 0.14098095521330833 0.012934491038322449 0.827436288818717 0.5283997394144535 0.8942463714629412 0.4924008697271347 0.9298017006367445 0.18076439201831818 0.5243716649711132 0.015810951590538025 0.7476964127272367 0.6734520178288221 0.3267945274710655 0.9760967157781124 0.9397075213491917 0.7225867230445147 0.938586825504899 0.9692913070321083 0.5398030783981085] ]", + "[ 13/4 → 27/8 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] phases:[0.9923650715500116 0.7797339502722025 0.1992435697466135 0.9005183521658182 0.718992218375206 0.23745290748775005 0.28872333094477654 0.35448253713548183 0.6209003105759621 0.3761858306825161 0.019171399995684624 0.7563913837075233 0.1559751983731985 0.4236980527639389 0.08094430714845657 0.4560011047869921 0.5958948992192745 0.37061405926942825 0.18633292242884636 0.346891064196825] ]", + "[ 27/8 → 7/2 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.40869180113077164 0.4018078800290823 0.34696186147630215 0.9572948440909386 0.400035472586751 0.3531211093068123 0.7240961641073227 0.8125612027943134 0.3587487991899252 0.1545814611017704 0.8934940584003925 0.9094721674919128 0.2336172368377447 0.17333386465907097 0.227950319647789 0.12384821102023125 0.5896956156939268 0.3293947223573923 0.619540523737669 0.8746719229966402] ]", + "[ 7/2 → 29/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.40994881466031075 0.6455810181796551 0.35704658553004265 0.2732649203389883 0.31587288342416286 0.23243548162281513 0.22569947130978107 0.17241661623120308 0.7624858524650335 0.02111036144196987 0.6966175157576799 0.32112877257168293 0.2512516509741545 0.08602019399404526 0.16260642930865288 0.2627844326198101 0.1954369619488716 0.16302869468927383 0.4046020060777664 0.0904023926705122] ]", + "[ 29/8 → 15/4 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] phases:[0.9391485787928104 0.3380728308111429 0.7723016366362572 0.998674126341939 0.7193406894803047 0.6963680125772953 0.9260678570717573 0.7829763628542423 0.42278125509619713 0.913721015676856 0.38873230293393135 0.7281809840351343 0.952108234167099 0.649707704782486 0.9456792045384645 0.4331315904855728 0.63712502643466 0.9180345926433802 0.7379776351153851 0.7914005517959595] ]", + "[ 15/4 → 31/8 | s:saw note:B4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.8121673222631216 0.25548945367336273 0.5759696904569864 0.6668333616107702 0.9233786761760712 0.11143362522125244 0.9734930321574211 0.369325939565897 0.6248745918273926 0.719582824036479 0.24051696807146072 0.20336504466831684 0.6860735435038805 0.10649923048913479 0.8736641593277454 0.042930178344249725 0.09769343212246895 0.7966452036052942 0.5882443580776453 0.8942952174693346] ]", + "[ 31/8 → 4/1 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.7361665386706591 0.7657648548483849 0.21474426984786987 0.8228576295077801 0.5647660344839096 0.08148551359772682 0.9714624118059874 0.13992334716022015 0.9609981551766396 0.6844500284641981 0.23610286228358746 0.5974335502833128 0.20678778178989887 0.5261937081813812 0.7417268306016922 0.9810918122529984 0.8138748407363892 0.9389897901564837 0.7420910261571407 0.48839940316975117] ]", +] +`; + exports[`runs examples > example "pianoroll" example index 0 1`] = ` [ "[ 0/1 → 1/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]", @@ -8052,6 +8195,75 @@ exports[`runs examples > example "rand" example index 0 1`] = ` ] `; +exports[`runs examples > example "randL" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | s:saw note:F1 partials:[0 0 0 0 0 0 0 0] ]", + "[ 1/16 → 1/8 | s:saw note:Bb2 partials:[0.8426077850162983 0.0886186733841896 0.9342893119901419 0.06848422810435295 0.6248505394905806 0.5372848063707352 0.8283301629126072 0.7969411127269268] ]", + "[ 1/8 → 3/16 | s:saw note:G2 partials:[0.6852155700325966 0.17723728902637959 0.8521428182721138 0.5022399611771107 0.9780306946486235 0.3186125475913286 0.1851645242422819 0.8495976086705923] ]", + "[ 3/16 → 1/4 | s:saw note:Ab1 partials:[0.20066574029624462 0.17195586115121841 0.2159541044384241 0.17005567252635956 0.6841662060469389 0.07906394638121128 0.004815371707081795 0.027107616886496544] ]", + "[ 1/4 → 5/16 | s:saw note:C2 partials:[0.36975969187915325 0.18273563869297504 0.012781793251633644 0.5423613861203194 0.12467948533594608 0.7188410349190235 0.856887087225914 0.8818203993141651] ]", + "[ 5/16 → 3/8 | s:saw note:Eb2 partials:[0.5675661638379097 0.15932588279247284 0.31376867927610874 0.32359412126243114 0.3281281068921089 0.011714376509189606 0.4433113746345043 0.68459103256464] ]", + "[ 3/8 → 7/16 | s:saw note:C2 partials:[0.40139251574873924 0.15799915604293346 0.9604704882949591 0.03873417526483536 0.4157217647880316 0.27756719291210175 0.8800551909953356 0.5692523363977671] ]", + "[ 7/16 → 1/2 | s:saw note:Bb1 partials:[0.3015887886285782 0.22311350144445896 0.47645803540945053 0.001545613631606102 0.53841289319098 0.330710094422102 0.2747743520885706 0.9471044968813658] ]", + "[ 1/2 → 9/16 | s:saw note:Bb1 partials:[0.2604806162416935 0.3654713351279497 0.9900747090578079 0.22572625242173672 0.9328999016433954 0.5273839123547077 0.7047769222408533 0.7311522178351879] ]", + "[ 9/16 → 5/8 | s:saw note:G1 partials:[0.16399178467690945 0.9559339117258787 0.21830322034657001 0.4903192054480314 0.2504696864634752 0.7302105724811554 0.6580934692174196 0.9855979979038239] ]", + "[ 5/8 → 11/16 | s:saw note:G1 partials:[0.1356358677148819 0.5403102282434702 0.07321739941835403 0.9646544624119997 0.7707359045743942 0.46888123638927937 0.9489036612212658 0.366748945787549] ]", + "[ 11/16 → 3/4 | s:saw note:C2 partials:[0.408811716362834 0.9613851886242628 0.7683626655489206 0.5862949229776859 0.9568102769553661 0.7154356613755226 0.6283384170383215 0.2193678840994835] ]", + "[ 3/4 → 13/16 | s:saw note:Ab1 partials:[0.19582648016512394 0.0024610888212919235 0.08272589556872845 0.09592138417065144 0.9678201265633106 0.3249557167291641 0.9463537875562906 0.20766610652208328] ]", + "[ 13/16 → 7/8 | s:saw note:Ab2 partials:[0.7516226731240749 0.15610851533710957 0.9039078876376152 0.8602268267422915 0.928601048886776 0.4479671400040388 0.5080656576901674 0.562442347407341] ]", + "[ 7/8 → 15/16 | s:saw note:C2 partials:[0.3976310808211565 0.13476407527923584 0.963376859202981 0.6579397786408663 0.5203975513577461 0.865439185872674 0.5583905670791864 0.2736878804862499] ]", + "[ 15/16 → 1/1 | s:saw note:G2 partials:[0.7029578909277916 0.6351367030292749 0.26143294386565685 0.6411824338138103 0.5676082745194435 0.4803342465311289 0.5140306428074837 0.9477859679609537] ]", + "[ 1/1 → 17/16 | s:saw note:Eb2 partials:[0.5195421651005745 0.9349692352116108 0.1718774326145649 0.9601866770535707 0.2802200373262167 0.8732441551983356 0.01810968853533268 0.42737805284559727] ]", + "[ 17/16 → 9/8 | s:saw note:Eb2 partials:[0.5486328881233931 0.7088070474565029 0.07877279818058014 0.9422610364854336 0.7696988433599472 0.8289324026554823 0.5925844628363848 0.3920764606446028] ]", + "[ 9/8 → 19/16 | s:saw note:G2 partials:[0.6724895145744085 0.21410975232720375 0.5229047331959009 0.20957534946501255 0.7402758821845055 0.2681974694132805 0.4059371296316385 0.9422509074211121] ]", + "[ 19/16 → 5/4 | s:saw note:F1 partials:[0.08174665085971355 0.2762963864952326 0.5428560189902782 0.29452037811279297 0.17946280725300312 0.366960596293211 0.26327736489474773 0.5002023074775934] ]", + "[ 5/4 → 21/16 | s:saw note:G2 partials:[0.7287282031029463 0.9341024849563837 0.5098182689398527 0.5608645845204592 0.6812942810356617 0.5015129633247852 0.4602120481431484 0.927369873970747] ]", + "[ 21/16 → 11/8 | s:saw note:F1 partials:[0.08104278706014156 0.5200720727443695 0.5363391060382128 0.9824271816760302 0.10652091167867184 0.23473932035267353 0.7656102329492569 0.4850195776671171] ]", + "[ 11/8 → 23/16 | s:saw note:Ab1 partials:[0.18280375190079212 0.40527783520519733 0.30504362285137177 0.7622128054499626 0.842598931863904 0.053796686232089996 0.5124214049428701 0.9178454764187336] ]", + "[ 23/16 → 3/2 | s:saw note:Eb2 partials:[0.5081270858645439 0.1421387754380703 0.8852288406342268 0.9066353775560856 0.7700740322470665 0.35437702015042305 0.20032598450779915 0.963155921548605] ]", + "[ 3/2 → 25/16 | s:saw note:F2 partials:[0.6084080748260021 0.4424846563488245 0.755185678601265 0.04328125901520252 0.04244415462017059 0.3994515985250473 0.787989066913724 0.3180440980941057] ]", + "[ 25/16 → 13/8 | s:saw note:Ab1 partials:[0.17092766426503658 0.7808954659849405 0.8234915658831596 0.8643151633441448 0.9781719036400318 0.5102015696465969 0.018259897828102112 0.25844234600663185] ]", + "[ 13/8 → 27/16 | s:saw note:Db2 partials:[0.49675471149384975 0.2965749856084585 0.09848833456635475 0.19172007404267788 0.22677889838814735 0.9841996673494577 0.11156083643436432 0.6079028844833374] ]", + "[ 27/16 → 7/4 | s:saw note:Bb1 partials:[0.29589061066508293 0.13011129200458527 0.8021425940096378 0.38040103763341904 0.33274981752038 0.29745868407189846 0.9898250997066498 0.743482418358326] ]", + "[ 7/4 → 29/16 | s:saw note:Ab1 partials:[0.20473789609968662 0.7480021081864834 0.30757393687963486 0.5827204789966345 0.22094514779746532 0.6370698790997267 0.16573002003133297 0.0638319905847311] ]", + "[ 29/16 → 15/8 | s:saw note:Db2 partials:[0.4695742893964052 0.6690364442765713 0.6216684486716986 0.41342394426465034 0.9508822970092297 0.8403679896146059 0.91759910620749 0.6591395419090986] ]", + "[ 15/8 → 31/16 | s:saw note:F2 partials:[0.5945571791380644 0.3962489552795887 0.563431927934289 0.4742323160171509 0.4185752831399441 0.9703940469771624 0.5282467231154442 0.6896266285330057] ]", + "[ 31/16 → 2/1 | s:saw note:Bb2 partials:[0.8672592639923096 0.3438429981470108 0.9795673936605453 0.46547594852745533 0.9770395755767822 0.36851615831255913 0.7725539114326239 0.876962523907423] ]", + "[ 2/1 → 33/16 | s:saw note:C3 partials:[0.9595271199941635 0.5427457969635725 0.45864437520504 0.8057199101895094 0.18388565629720688 0.17221237532794476 0.8838487900793552 0.4976818822324276] ]", + "[ 33/16 → 17/8 | s:saw note:Bb1 partials:[0.3324784208089113 0.6005446836352348 0.1564352661371231 0.765217661857605 0.12899602390825748 0.20880493707954884 0.9156261626631021 0.5550615340471268] ]", + "[ 17/8 → 35/16 | s:saw note:Bb2 partials:[0.9033902939409018 0.22919894196093082 0.8388008493930101 0.9108077362179756 0.32246968522667885 0.16122018359601498 0.31932324543595314 0.18524732999503613] ]", + "[ 35/16 → 9/4 | s:saw note:F1 partials:[0.037000780925154686 0.30360451713204384 0.7516817897558212 0.17065968923270702 0.24669718369841576 0.978821286931634 0.9620356522500515 0.12417634017765522] ]", + "[ 9/4 → 37/16 | s:saw note:C2 partials:[0.34548251144587994 0.30419893004000187 0.10205957666039467 0.9906709585338831 0.3156223688274622 0.3745057098567486 0.7668170686811209 0.9379972033202648] ]", + "[ 37/16 → 19/8 | s:saw note:F2 partials:[0.6649543270468712 0.41613837145268917 0.9078915324062109 0.2232209537178278 0.2918607946485281 0.4931973237544298 0.7035325299948454 0.29280414804816246] ]", + "[ 19/8 → 39/16 | s:saw note:Bb2 partials:[0.8365066405385733 0.5369812995195389 0.2263860274106264 0.21759207546710968 0.2172116208821535 0.09454222768545151 0.8075542915612459 0.9017798062413931] ]", + "[ 39/16 → 5/2 | s:saw note:G1 partials:[0.14231421053409576 0.6801821701228619 0.7958894111216068 0.9494249671697617 0.179213372990489 0.22531690262258053 0.7458387855440378 0.3595987129956484] ]", + "[ 5/2 → 41/16 | s:saw note:Db2 partials:[0.45861607417464256 0.6807645913213491 0.07009582966566086 0.1450389064848423 0.2579921595752239 0.612881500273943 0.5503941811621189 0.7027011960744858] ]", + "[ 41/16 → 21/8 | s:saw note:F2 partials:[0.6068673655390739 0.8489279896020889 0.027898555621504784 0.90520747192204 0.18605150654911995 0.30939464271068573 0.16843407973647118 0.5127317048609257] ]", + "[ 21/8 → 43/16 | s:saw note:G1 partials:[0.16019348427653313 0.47758268006145954 0.9749126061797142 0.8025561925023794 0.3600110989063978 0.324309878051281 0.5655391272157431 0.13364790193736553] ]", + "[ 43/16 → 11/4 | s:saw note:Eb2 partials:[0.538035349920392 0.4103843830525875 0.6443832442164421 0.7532152868807316 0.32128027081489563 0.29064710810780525 0.5972099266946316 0.8252892810851336] ]", + "[ 11/4 → 45/16 | s:saw note:F2 partials:[0.6332327704876661 0.7342763151973486 0.16935866326093674 0.4754706546664238 0.528122790157795 0.21934260986745358 0.7476693484932184 0.2995396424084902] ]", + "[ 45/16 → 23/8 | s:saw note:Bb2 partials:[0.8912940509617329 0.5256196465343237 0.25639201514422894 0.46860250271856785 0.08611978217959404 0.6175916157662868 0.990638293325901 0.11189854890108109] ]", + "[ 23/8 → 47/16 | s:saw note:F1 partials:[0.01625417172908783 0.28409438766539097 0.3075305689126253 0.40343056432902813 0.5556836389005184 0.6926821582019329 0.5635769125074148 0.017873913049697876] ]", + "[ 47/16 → 3/1 | s:saw note:Bb1 partials:[0.2830589488148689 0.3613663297146559 0.6216250211000443 0.6083405166864395 0.27202711440622807 0.771099541336298 0.3156145606189966 0.736228458583355] ]", + "[ 3/1 → 49/16 | s:saw note:Ab1 partials:[0.21728911064565182 0.7605195846408606 0.11240843310952187 0.4372697602957487 0.15630115941166878 0.1653979979455471 0.5303416550159454 0.07218753732740879] ]", + "[ 49/16 → 25/8 | s:saw note:F1 partials:[0.012518879026174545 0.6565517522394657 0.783835593611002 0.2481316588819027 0.27786846831440926 0.6153149046003819 0.39914070069789886 0.2175555843859911] ]", + "[ 25/8 → 51/16 | s:saw note:C2 partials:[0.34324387833476067 0.14098095521330833 0.012934491038322449 0.827436288818717 0.5283997394144535 0.8942463714629412 0.4924008697271347 0.9298017006367445] ]", + "[ 51/16 → 13/4 | s:saw note:Bb2 partials:[0.9057559575885534 0.5731389932334423 0.06561482697725296 0.02156665176153183 0.4685337021946907 0.000788738951086998 0.6995994579046965 0.9893404543399811] ]", + "[ 13/4 → 53/16 | s:saw note:C3 partials:[0.9923650715500116 0.7797339502722025 0.1992435697466135 0.9005183521658182 0.718992218375206 0.23745290748775005 0.28872333094477654 0.35448253713548183] ]", + "[ 53/16 → 27/8 | s:saw note:Bb1 partials:[0.3235324025154114 0.5165992900729179 0.6231792941689491 0.8034896682947874 0.6663950439542532 0.4207208137959242 0.9748435858637094 0.27676148526370525] ]", + "[ 27/8 → 55/16 | s:saw note:C2 partials:[0.40869180113077164 0.4018078800290823 0.34696186147630215 0.9572948440909386 0.400035472586751 0.3531211093068123 0.7240961641073227 0.8125612027943134] ]", + "[ 55/16 → 7/2 | s:saw note:Ab2 partials:[0.775677714496851 0.0502743236720562 0.3246541116386652 0.503994770348072 0.8252716399729252 0.89872563816607 0.060319963842630386 0.25497629307210445] ]", + "[ 7/2 → 57/16 | s:saw note:C2 partials:[0.40994881466031075 0.6455810181796551 0.35704658553004265 0.2732649203389883 0.31587288342416286 0.23243548162281513 0.22569947130978107 0.17241661623120308] ]", + "[ 57/16 → 29/8 | s:saw note:Ab2 partials:[0.8133465722203255 0.8327706772834063 0.32914630882442 0.23131784796714783 0.7526696771383286 0.1361286472529173 0.11441296897828579 0.36366880126297474] ]", + "[ 29/8 → 59/16 | s:saw note:C3 partials:[0.9391485787928104 0.3380728308111429 0.7723016366362572 0.998674126341939 0.7193406894803047 0.6963680125772953 0.9260678570717573 0.7829763628542423] ]", + "[ 59/16 → 15/4 | s:saw note:C3 partials:[0.9731374885886908 0.0494034755975008 0.990720022469759 0.9819309785962105 0.22877203114330769 0.7265191469341516 0.4922411348670721 0.7549834884703159] ]", + "[ 15/4 → 61/16 | s:saw note:Ab2 partials:[0.8121673222631216 0.25548945367336273 0.5759696904569864 0.6668333616107702 0.9233786761760712 0.11143362522125244 0.9734930321574211 0.369325939565897] ]", + "[ 61/16 → 31/8 | s:saw note:G1 partials:[0.1008925698697567 0.7558664139360189 0.1523460578173399 0.6523381881415844 0.9690635204315186 0.7343240566551685 0.9544064309448004 0.9150135722011328] ]", + "[ 31/8 → 63/16 | s:saw note:G2 partials:[0.7361665386706591 0.7657648548483849 0.21474426984786987 0.8228576295077801 0.5647660344839096 0.08148551359772682 0.9714624118059874 0.13992334716022015] ]", + "[ 63/16 → 4/1 | s:saw note:Bb1 partials:[0.3056422360241413 0.8881660811603069 0.7683485243469477 0.08641545102000237 0.5427133180201054 0.20803124643862247 0.5347504671663046 0.5977730695158243] ]", +] +`; + exports[`runs examples > example "range" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:hh cutoff:2250 ]", From 35016b58476b17d6b9a2c98b922df2d1fa9e76f6 Mon Sep 17 00:00:00 2001 From: Aria Date: Wed, 15 Oct 2025 14:56:20 -0500 Subject: [PATCH 3/8] Mention range in docstring --- packages/core/controls.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 327f8df5..77e77caa 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -2380,7 +2380,7 @@ export const scrub = register( * Can also be used to create a new synth via `s('user').partials(...)` * * @name partials - * @param {number[] | Pattern} partials List of magnitudes for partials. 0th entry is the first harmonic (i.e. DC offset is skipped) + * @param {number[] | Pattern} partials List of [0, 1] magnitudes for partials. 0th entry is the first harmonic (i.e. DC offset is skipped) * @example * s("user").seg(16).n(irand(8)).scale("A:major") * .partials([1, 0, 1, 0, 0, 1]) @@ -2394,7 +2394,7 @@ export const { partials } = registerControl('partials'); * Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases * * @name phases - * @param {number[] | Pattern} phases List of phases for partials. 0th entry is the first phase (i.e. DC offset is skipped) + * @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the first phase (i.e. DC offset is skipped) * @example * s("saw").seg(8).n(irand(12)).scale("G#:minor") * .partials(binaryL(256)) From fd03d1fdfbc57d7b1e4b8c14bdb414d44bdc0ec8 Mon Sep 17 00:00:00 2001 From: Aria Date: Wed, 15 Oct 2025 15:14:31 -0500 Subject: [PATCH 4/8] Update conditional to allow to control user waveforms --- packages/superdough/synth.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 0294762f..10df1776 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -472,7 +472,7 @@ export function getOscillator(s, t, value) { } s = s === 'user' && !partials ? 'triangle' : s; // If no partials are given, use stock waveforms - if (!partials || !partials?.length || s === 'sine') { + if (!partials || partials?.length === 0 || s === 'sine') { o = getAudioContext().createOscillator(); o.type = s || 'triangle'; } From cc10122b7184d4271c4a166673a76726854ac1c7 Mon Sep 17 00:00:00 2001 From: Aria Date: Sun, 16 Nov 2025 19:20:42 -0600 Subject: [PATCH 5/8] Testing out approach to allow list patterns in partials --- packages/core/controls.mjs | 28 --------------- packages/core/pattern.mjs | 51 ++++++++++++++++++++++++++ website/src/pages/learn/synths.mdx | 57 ++++++++++++++++++++++++++---- 3 files changed, 101 insertions(+), 35 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 1d19a94b..9a2be7f4 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -2583,31 +2583,3 @@ export const scrub = register( }, false, ); - -/** - * Scale the magnitude of the harmonics of one of the core synths ('sine', 'tri', 'saw', ..) - * - * Can also be used to create a new synth via `s('user').partials(...)` - * - * @name partials - * @param {number[] | Pattern} partials List of [0, 1] magnitudes for partials. 0th entry is the first harmonic (i.e. DC offset is skipped) - * @example - * s("user").seg(16).n(irand(8)).scale("A:major") - * .partials([1, 0, 1, 0, 0, 1]) - * @example - * s("saw").seg(8).n(irand(12)).scale("G#:minor") - * .partials(binaryL(256)) - */ -export const { partials } = registerControl('partials'); - -/** - * Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases - * - * @name phases - * @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the first phase (i.e. DC offset is skipped) - * @example - * s("saw").seg(8).n(irand(12)).scale("G#:minor") - * .partials(binaryL(256)) - * .phases(randL(20)) - */ -export const { phases } = registerControl('phases'); diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index e2976990..f08667cf 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3624,3 +3624,54 @@ for (const name of distAlgoNames) { return this.distort(argsPat); }; } + +/** + * Turns a list of patterns into a single pattern which outputs list-values + * + * @name parray + * @returns Pattern + */ +export const parray = (pats) => { + const pack = (...xs) => xs; + let acc = pure(curry(pack, null, pats.length)); + for (const p of pats) acc = acc.appBoth(reify(p)); + return acc; +}; + +/** + * Scale the magnitude of the harmonics of one of the core synths ('sine', 'tri', 'saw', ..) + * + * Can also be used to create a new synth via `s('user').partials(...)` + * + * @name partials + * @param {number[] | Pattern} partials List of [0, 1] magnitudes for partials. 0th entry is the first harmonic (i.e. DC offset is skipped) + * @example + * s("user").seg(16).n(irand(8)).scale("A:major") + * .partials([1, 0, 1, 0, 0, 1]) + * @example + * s("saw").seg(8).n(irand(12)).scale("G#:minor") + * .partials(binaryL(256)) + */ +export const { partials } = register('partials', (list, pat) => { + if (Array.isArray(list)) { + list = parray(list); + } + return pat.withValue((v) => ({...v, partials: list})); +}); + +/** + * Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases + * + * @name phases + * @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the first phase (i.e. DC offset is skipped) + * @example + * s("saw").seg(8).n(irand(12)).scale("G#:minor") + * .partials(binaryL(256)) + * .phases(randL(20)) + */ +export const { phases } = register('phases', (list, pat) => { + if (Array.isArray(list)) { + list = parray(list); + } + return pat.withValue((v) => ({...v, phases: list})); +}); diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 0fcc4136..1a65568d 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -48,28 +48,71 @@ You can also use the `crackle` type to play some subtle noise crackles. You can ### Additive Synthesis -To tame the harsh sound of the basic waveforms, we can set the `n` control to limit the overtones of the waveform: +Waveforms are often composed of several [harmonics](https://en.wikipedia.org/wiki/Harmonic) above a fundamental frequency, lying at integer multiples. These overtones combine to give a sound its unique timbral quality. + +For the basic waveforms, we offer you control over these harmonics with the `partials` and `phases` functions. + +#### Partials + +`partials` refers to the magnitude of each harmonic relative to the fundamental frequency. They can thus be used to spectrally filter these waveforms and tame some of their harshness: >".fast(2)) .sound("sawtooth") -.n("<32 16 8 4>") +.partials([1, 1, 0, 1]) ._scope()`} /> -When the `n` control is used on a basic waveform, it defines the number of harmonic partials the sound is getting. -You can also set `n` directly in mini notation with `sound`: +`partials` can also be used to construct _new_ waveforms not present in our basic set with the 'user' sound source: >".fast(2)) -.sound("sawtooth:<32 16 8 4>") +.sound("user") +.partials([1, 0, 0.3, 0, 0.1, 0, 0, 0.3]) ._scope()`} /> -Note for tidal users: `n` in tidal is synonymous to `note` for synths only. -In strudel, this is not the case, where `n` will always change timbre, be it though different samples or different waveforms. +We may algorithmically construct lists of partials with Javascript code like: + +>".fast(2)) +.sound("user") +.partials(new Array(numHarmonics).fill(1)) +._scope()`} +/> + +This approach can act as a form of bandlimiting. `partials` is also compatible with pattern functions designed to produce lists, like `randL` or `binaryL`: + +>".fast(2)) +.sound("user") +.partials(randL(8)) +._scope()`} +/> + +Note that the first value in the `partials` array controls the magnitude of the fundamental harmonic rather than the DC offset, which is fixed at 0. + +#### Phases + +We mentioned that our sounds can be broken into a constituent set of harmonics above a fundamental frequency. These are defined by two values: their magnitude (how loud they are) and their [phase](https://en.wikipedia.org/wiki/Phase_(waves)), which can be thought of as which point in its cycle each sine wave is initialized at when we begin adding them. + +These phases too can be declared in Strudel and can give your sounds interesting depth. + +>".fast(2)) +.sound("user") +.partials(randL(8)) +.phases(randL(8).late(0.3)) +._scope()`} +/> ## Vibrato From 7a3bea6f9087105ed0be0982798b9dd26c9ed089 Mon Sep 17 00:00:00 2001 From: Aria Date: Sun, 16 Nov 2025 19:38:59 -0600 Subject: [PATCH 6/8] Working version --- packages/core/pattern.mjs | 5 +++-- website/src/pages/learn/synths.mdx | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index f08667cf..cd8c5871 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3653,10 +3653,11 @@ export const parray = (pats) => { * .partials(binaryL(256)) */ export const { partials } = register('partials', (list, pat) => { + debugger; if (Array.isArray(list)) { list = parray(list); } - return pat.withValue((v) => ({...v, partials: list})); + return pat.withValue((v) => (l) => ({...v, partials: l})).appLeft(list); }); /** @@ -3673,5 +3674,5 @@ export const { phases } = register('phases', (list, pat) => { if (Array.isArray(list)) { list = parray(list); } - return pat.withValue((v) => ({...v, phases: list})); + return pat.withValue((v) => (l) => ({...v, phases: l})).appLeft(list); }); diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 1a65568d..b209dc9c 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -96,6 +96,16 @@ note("c2 >".fast(2)) ._scope()`} /> +and with lists _of_ patterns: + +>".fast(4)) +.sound("user") +.partials([1, 0, "0 1", "0 1 0.3", rand]) +._scope()`} +/> + Note that the first value in the `partials` array controls the magnitude of the fundamental harmonic rather than the DC offset, which is fixed at 0. #### Phases From f5ed6dbe79a683dac229ed095ac0c4ff592c72ea Mon Sep 17 00:00:00 2001 From: Aria Date: Sun, 16 Nov 2025 20:28:22 -0600 Subject: [PATCH 7/8] Top level functions --- packages/core/pattern.mjs | 36 +++++++++++++++------ packages/core/test/pattern.test.mjs | 4 +-- website/src/components/Header/Search.css | 4 +-- website/src/pages/learn/synths.mdx | 40 +++++++++++++++--------- 4 files changed, 56 insertions(+), 28 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index cd8c5871..3c21bd1b 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3644,35 +3644,51 @@ export const parray = (pats) => { * Can also be used to create a new synth via `s('user').partials(...)` * * @name partials - * @param {number[] | Pattern} partials List of [0, 1] magnitudes for partials. 0th entry is the first harmonic (i.e. DC offset is skipped) + * @param {number[] | Pattern} magnitudes List of [0, 1] magnitudes for partials. 0th entry is the fundamental harmonic (i.e. DC offset is skipped) * @example * s("user").seg(16).n(irand(8)).scale("A:major") * .partials([1, 0, 1, 0, 0, 1]) * @example * s("saw").seg(8).n(irand(12)).scale("G#:minor") - * .partials(binaryL(256)) + * .partials(randL(16)) */ -export const { partials } = register('partials', (list, pat) => { - debugger; +register('partials', (list, pat) => { if (Array.isArray(list)) { list = parray(list); } - return pat.withValue((v) => (l) => ({...v, partials: l})).appLeft(list); + return pat.withValue((v) => (l) => ({ ...v, partials: l })).appLeft(list); }); +// Also create a top-level function +export const partials = (list) => { + if (Array.isArray(list)) { + list = parray(list); + } + return list.as('partials'); +}; + /** * Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases * * @name phases - * @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the first phase (i.e. DC offset is skipped) + * @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the fundamental phase (i.e. DC offset is skipped) * @example * s("saw").seg(8).n(irand(12)).scale("G#:minor") - * .partials(binaryL(256)) - * .phases(randL(20)) + * .partials(randL(16)) + * .phases(randL(16)) */ -export const { phases } = register('phases', (list, pat) => { +register('phases', (list, pat) => { if (Array.isArray(list)) { list = parray(list); } - return pat.withValue((v) => (l) => ({...v, phases: l})).appLeft(list); + pat = pat ?? pure({}); + return pat.withValue((v) => (l) => ({ ...v, phases: l })).appLeft(list); }); + +// Also create a top-level function +export const phases = (list) => { + if (Array.isArray(list)) { + list = parray(list); + } + return list.as('phases'); +}; diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 625f4075..1df5c877 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -796,7 +796,7 @@ describe('Pattern', () => { }); }); describe('apply', () => { - (it('Can apply a function', () => { + it('Can apply a function', () => { expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle()); }), it('Can apply a pattern of functions', () => { @@ -804,7 +804,7 @@ describe('Pattern', () => { expect(sequence('a', 'b').apply(fast(2), fast(3)).firstCycle()).toStrictEqual( sequence('a', 'b').fast(2, 3).firstCycle(), ); - })); + }); }); describe('layer', () => { it('Can layer up multiple functions', () => { diff --git a/website/src/components/Header/Search.css b/website/src/components/Header/Search.css index b14146cb..456ef9f6 100644 --- a/website/src/components/Header/Search.css +++ b/website/src/components/Header/Search.css @@ -18,8 +18,8 @@ --docsearch-modal-background: var(--background); --docsearch-muted-color: color-mix(in srgb, var(--foreground), #fff 30%); --docsearch-key-gradient: var(--foreground); - --docsearch-key-shadow: - inset 0 -2px 0 0 var(--gutterForeground), inset 0 0 1px 1px var(--foreground), 0 1px 2px 1px var(--gutterBackground); + --docsearch-key-shadow: inset 0 -2px 0 0 var(--gutterForeground), inset 0 0 1px 1px var(--foreground), + 0 1px 2px 1px var(--gutterBackground); } .dark { --docsearch-muted-color: color-mix(in srgb, var(--foreground), #000 30%); diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index b209dc9c..6fc5b692 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -48,7 +48,7 @@ You can also use the `crackle` type to play some subtle noise crackles. You can ### Additive Synthesis -Waveforms are often composed of several [harmonics](https://en.wikipedia.org/wiki/Harmonic) above a fundamental frequency, lying at integer multiples. These overtones combine to give a sound its unique timbral quality. +Periodic waveforms are composed of several [harmonics](https://en.wikipedia.org/wiki/Harmonic) above a fundamental frequency, lying at integer multiples. These overtones combine to give a sound its unique timbral quality. For the basic waveforms, we offer you control over these harmonics with the `partials` and `phases` functions. @@ -60,7 +60,7 @@ For the basic waveforms, we offer you control over these harmonics with the `par client:idle tune={`note("c2 >".fast(2)) .sound("sawtooth") -.partials([1, 1, 0, 1]) +.partials([1, 1, "<1 0>", "<1 0>", "<1 0>", "<1 0>", "<1 0>"]) ._scope()`} /> @@ -74,25 +74,38 @@ For the basic waveforms, we offer you control over these harmonics with the `par ._scope()`} /> -We may algorithmically construct lists of partials with Javascript code like: +We may algorithmically construct lists of magnitudes with Javascript code like: >".fast(2)) -.sound("user") +.sound("saw") .partials(new Array(numHarmonics).fill(1)) ._scope()`} /> -This approach can act as a form of bandlimiting. `partials` is also compatible with pattern functions designed to produce lists, like `randL` or `binaryL`: +which acts as a spectral filter or >".fast(2)) + tune={`note("c2 >").fast(2) .sound("user") -.partials(randL(8)) +.partials(new Array(50).fill(0) + .map((_, idx) => ((-1) ** (idx + 1)) / (idx + 1)) +) +._scope()`} +/> + +which recovers a familiar waveform. + +`partials` is also compatible with pattern functions designed to produce lists, like `randL` or `binaryL`: + +>").fast(2) +.sound("user") +.partials(randL(10)) ._scope()`} /> @@ -110,17 +123,16 @@ Note that the first value in the `partials` array controls the magnitude of the #### Phases -We mentioned that our sounds can be broken into a constituent set of harmonics above a fundamental frequency. These are defined by two values: their magnitude (how loud they are) and their [phase](https://en.wikipedia.org/wiki/Phase_(waves)), which can be thought of as which point in its cycle each sine wave is initialized at when we begin adding them. +Earlier, we mentioned that periodic waveforms can be broken into a set of harmonics above a fundamental frequency. Each harmonic has two defining properties: its magnitude (how loud it is) and its phase, which determines where in its cycle that sine wave starts when the waveform is built. These phases too can be declared in Strudel and can give your sounds interesting depth. >".fast(2)) -.sound("user") -.partials(randL(8)) -.phases(randL(8).late(0.3)) + tune={`note("c2 >".fast(2)) +.sound("saw") +.partials(randL(100)) +.phases(randL(100)) ._scope()`} /> From 641bd2f356f16072dd69e75509697294e479ac38 Mon Sep 17 00:00:00 2001 From: Aria Date: Sun, 16 Nov 2025 22:17:53 -0600 Subject: [PATCH 8/8] Properly patternify everything --- packages/core/pattern.mjs | 45 +++--- test/__snapshots__/examples.test.mjs.snap | 160 +++++++++++++--------- website/src/pages/learn/synths.mdx | 13 +- 3 files changed, 123 insertions(+), 95 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 3c21bd1b..4737f0db 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3638,6 +3638,13 @@ export const parray = (pats) => { return acc; }; +const _ensureListPattern = (list) => { + if (Array.isArray(list)) { + return parray(list); + } + return reify(list); +}; + /** * Scale the magnitude of the harmonics of one of the core synths ('sine', 'tri', 'saw', ..) * @@ -3650,21 +3657,15 @@ export const parray = (pats) => { * .partials([1, 0, 1, 0, 0, 1]) * @example * s("saw").seg(8).n(irand(12)).scale("G#:minor") - * .partials(randL(16)) + * .partials(binaryL(irand(256).add("1"))) */ -register('partials', (list, pat) => { - if (Array.isArray(list)) { - list = parray(list); - } - return pat.withValue((v) => (l) => ({ ...v, partials: l })).appLeft(list); -}); +Pattern.prototype.partials = function (list) { + return this.withValue((v) => (l) => ({ ...v, partials: l })).appLeft(_ensureListPattern(list)); +}; // Also create a top-level function export const partials = (list) => { - if (Array.isArray(list)) { - list = parray(list); - } - return list.as('partials'); + return _ensureListPattern(list).as('partials'); }; /** @@ -3673,22 +3674,16 @@ export const partials = (list) => { * @name phases * @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the fundamental phase (i.e. DC offset is skipped) * @example - * s("saw").seg(8).n(irand(12)).scale("G#:minor") - * .partials(randL(16)) - * .phases(randL(16)) + * // Phase cancellation + * s("saw").seg(8).n(irand(12)).scale("G#1:minor") + * .partials(partials([1, 1, 1])) + * .superimpose(x => x.phases([0.5, 0.5, 0.5])) */ -register('phases', (list, pat) => { - if (Array.isArray(list)) { - list = parray(list); - } - pat = pat ?? pure({}); - return pat.withValue((v) => (l) => ({ ...v, phases: l })).appLeft(list); -}); +Pattern.prototype.phases = function (list) { + return this.withValue((v) => (l) => ({ ...v, phases: l })).appLeft(_ensureListPattern(list)); +}; // Also create a top-level function export const phases = (list) => { - if (Array.isArray(list)) { - list = parray(list); - } - return list.as('phases'); + return _ensureListPattern(list).as('phases'); }; diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 4b4c36e9..32e61c31 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -7290,38 +7290,38 @@ exports[`runs examples > example "partials" example index 0 1`] = ` exports[`runs examples > example "partials" example index 1 1`] = ` [ - "[ 0/1 → 1/8 | s:saw note:G#3 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 1/8 → 1/4 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 1/4 → 3/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 3/8 → 1/2 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 1/2 → 5/8 | s:saw note:C#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 5/8 → 3/4 | s:saw note:A#3 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 3/4 → 7/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 7/8 → 1/1 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 1/1 → 9/8 | s:saw note:F#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 9/8 → 5/4 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 5/4 → 11/8 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 11/8 → 3/2 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 3/2 → 13/8 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 13/8 → 7/4 | s:saw note:E4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 7/4 → 15/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 15/8 → 2/1 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 2/1 → 17/8 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 17/8 → 9/4 | s:saw note:C#5 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 9/4 → 19/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 19/8 → 5/2 | s:saw note:C#5 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 5/2 → 21/8 | s:saw note:E4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 21/8 → 11/4 | s:saw note:A#3 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 11/4 → 23/8 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 23/8 → 3/1 | s:saw note:G#3 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 3/1 → 25/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 25/8 → 13/4 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 13/4 → 27/8 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 27/8 → 7/2 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 7/2 → 29/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 29/8 → 15/4 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 15/4 → 31/8 | s:saw note:B4 partials:[1 0 0 0 0 0 0 0 0] ]", - "[ 31/8 → 4/1 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] ]", + "[ 0/1 → 1/8 | s:saw note:G#3 partials:[1] ]", + "[ 1/8 → 1/4 | s:saw note:A#4 partials:[1 0 1 1 0 0 0 0] ]", + "[ 1/4 → 3/8 | s:saw note:D#4 partials:[1 0 1 1 1 1 1] ]", + "[ 3/8 → 1/2 | s:saw note:D#4 partials:[1 1 0 0 1 1 1] ]", + "[ 1/2 → 5/8 | s:saw note:C#4 partials:[1 0 0 0 0 1 1] ]", + "[ 5/8 → 3/4 | s:saw note:A#3 partials:[1 0 0 0 1 1] ]", + "[ 3/4 → 7/8 | s:saw note:B3 partials:[1 1 0 0 1 1] ]", + "[ 7/8 → 1/1 | s:saw note:D#4 partials:[1 1 0 0 1 1 0] ]", + "[ 1/1 → 9/8 | s:saw note:F#4 partials:[1 0 0 0 0 1 1 0] ]", + "[ 9/8 → 5/4 | s:saw note:A#4 partials:[1 0 1 0 1 1 0 1] ]", + "[ 5/4 → 11/8 | s:saw note:A#4 partials:[1 0 1 1 1 0 1 1] ]", + "[ 11/8 → 3/2 | s:saw note:B3 partials:[1 0 1 1 1 1] ]", + "[ 3/2 → 13/8 | s:saw note:G#4 partials:[1 0 0 1 1 1 0 0] ]", + "[ 13/8 → 7/4 | s:saw note:E4 partials:[1 0 0 0 0 0 0 0] ]", + "[ 7/4 → 15/8 | s:saw note:B3 partials:[1 1 0 1 0 1] ]", + "[ 15/8 → 2/1 | s:saw note:G#4 partials:[1 0 0 1 1 0 0 1] ]", + "[ 2/1 → 17/8 | s:saw note:D#5 partials:[1 1 1 1 0 1 1 0] ]", + "[ 17/8 → 9/4 | s:saw note:C#5 partials:[1 1 1 0 1 0 0 0] ]", + "[ 9/4 → 19/8 | s:saw note:D#4 partials:[1 0 1 1 0 0 1] ]", + "[ 19/8 → 5/2 | s:saw note:C#5 partials:[1 1 0 1 0 1 1 1] ]", + "[ 5/2 → 21/8 | s:saw note:E4 partials:[1 1 1 0 1 1 0] ]", + "[ 21/8 → 11/4 | s:saw note:A#3 partials:[1 0 1 0 1 0] ]", + "[ 11/4 → 23/8 | s:saw note:G#4 partials:[1 0 1 0 0 0 1 1] ]", + "[ 23/8 → 3/1 | s:saw note:G#3 partials:[1 0 1] ]", + "[ 3/1 → 25/8 | s:saw note:B3 partials:[1 1 1 0 0 0] ]", + "[ 25/8 → 13/4 | s:saw note:D#4 partials:[1 0 1 1 0 0 0] ]", + "[ 13/4 → 27/8 | s:saw note:D#5 partials:[1 1 1 1 1 1 1 1] ]", + "[ 27/8 → 7/2 | s:saw note:D#4 partials:[1 1 0 1 0 0 1] ]", + "[ 7/2 → 29/8 | s:saw note:D#4 partials:[1 1 0 1 0 0 1] ]", + "[ 29/8 → 15/4 | s:saw note:D#5 partials:[1 1 1 1 0 0 0 1] ]", + "[ 15/4 → 31/8 | s:saw note:B4 partials:[1 1 0 1 0 0 0 0] ]", + "[ 31/8 → 4/1 | s:saw note:A#4 partials:[1 0 1 1 1 1 0 1] ]", ] `; @@ -7580,38 +7580,70 @@ exports[`runs examples > example "phasersweep" example index 0 1`] = ` exports[`runs examples > example "phases" example index 0 1`] = ` [ - "[ 0/1 → 1/8 | s:saw note:G#3 partials:[1 0 0 0 0 0 0 0 0] phases:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] ]", - "[ 1/8 → 1/4 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.6852155700325966 0.17723728902637959 0.8521428182721138 0.5022399611771107 0.9780306946486235 0.3186125475913286 0.1851645242422819 0.8495976086705923 0.9832699615508318 0.8203876558691263 0.7365445382893085 0.6727468091994524 0.4794053863734007 0.35482912696897984 0.5666771996766329 0.4527092967182398 0.5699347071349621 0.7358296867460012 0.5444891396909952 0.11270620673894882] ]", - "[ 1/4 → 3/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.36975969187915325 0.18273563869297504 0.012781793251633644 0.5423613861203194 0.12467948533594608 0.7188410349190235 0.856887087225914 0.8818203993141651 0.8917219173163176 0.110306391492486 0.07148400321602821 0.8493648078292608 0.9100127145648003 0.4820226952433586 0.037094997242093086 0.9857278112322092 0.23439379036426544 0.46701666340231895 0.15832693874835968 0.6921216659247875] ]", - "[ 3/8 → 1/2 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.40139251574873924 0.15799915604293346 0.9604704882949591 0.03873417526483536 0.4157217647880316 0.27756719291210175 0.8800551909953356 0.5692523363977671 0.11026228219270706 0.7190891150385141 0.404962956905365 0.50828124769032 0.9681975357234478 0.34809120930731297 0.9345223866403103 0.9851128943264484 0.11768617853522301 0.538263589143753 0.6650313660502434 0.02540053054690361] ]", - "[ 1/2 → 5/8 | s:saw note:C#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.2604806162416935 0.3654713351279497 0.9900747090578079 0.22572625242173672 0.9328999016433954 0.5273839123547077 0.7047769222408533 0.7311522178351879 0.42673745937645435 0.7099553178995848 0.5597201306372881 0.8968746215105057 0.15695763938128948 0.3792166206985712 0.4147114269435406 0.38739512488245964 0.7585489805787802 0.3875726908445358 0.867314238101244 0.1866922564804554] ]", - "[ 5/8 → 3/4 | s:saw note:A#3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.1356358677148819 0.5403102282434702 0.07321739941835403 0.9646544624119997 0.7707359045743942 0.46888123638927937 0.9489036612212658 0.366748945787549 0.5029341224581003 0.7194344792515039 0.282692551612854 0.7303404528647661 0.7155798356980085 0.637981578707695 0.39654020965099335 0.008769871667027473 0.5857728160917759 0.3513293471187353 0.43080931156873703 0.518209295347333] ]", - "[ 3/4 → 7/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.19582648016512394 0.0024610888212919235 0.08272589556872845 0.09592138417065144 0.9678201265633106 0.3249557167291641 0.9463537875562906 0.20766610652208328 0.19126702845096588 0.4482936095446348 0.23314787074923515 0.07724925130605698 0.8329483829438686 0.39804019033908844 0.2529231123626232 0.889951054006815 0.716364661231637 0.3905949704349041 0.990752438083291 0.275751456618309] ]", - "[ 7/8 → 1/1 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.3976310808211565 0.13476407527923584 0.963376859202981 0.6579397786408663 0.5203975513577461 0.865439185872674 0.5583905670791864 0.2736878804862499 0.17021040990948677 0.3456706069409847 0.5848060417920351 0.9889458417892456 0.9307208992540836 0.7148868907243013 0.7419579364359379 0.8103639334440231 0.3331365995109081 0.19130694679915905 0.03261224366724491 0.808204049244523] ]", - "[ 1/1 → 9/8 | s:saw note:F#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.5195421651005745 0.9349692352116108 0.1718774326145649 0.9601866770535707 0.2802200373262167 0.8732441551983356 0.01810968853533268 0.42737805284559727 0.26281314715743065 0.7521175239235163 0.568607984110713 0.5046361442655325 0.03172125294804573 0.3706745784729719 0.9402780849486589 0.3995086643844843 0.35769628174602985 0.023460431024432182 0.043030962347984314 0.6526827793568373] ]", - "[ 9/8 → 5/4 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.6724895145744085 0.21410975232720375 0.5229047331959009 0.20957534946501255 0.7402758821845055 0.2681974694132805 0.4059371296316385 0.9422509074211121 0.6584139950573444 0.26589646376669407 0.16059227287769318 0.17166719026863575 0.6234225388616323 0.020276052877306938 0.4836352560669184 0.8991366866976023 0.1860280428081751 0.06238717399537563 0.4383583478629589 0.4902789145708084] ]", - "[ 5/4 → 11/8 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.7287282031029463 0.9341024849563837 0.5098182689398527 0.5608645845204592 0.6812942810356617 0.5015129633247852 0.4602120481431484 0.927369873970747 0.19313151575624943 0.5240397155284882 0.444337897002697 0.19362097792327404 0.7068767864257097 0.920799495652318 0.2648108974099159 0.9700228478759527 0.20331068895757198 0.38390261493623257 0.0038731005042791367 0.9156702514737844] ]", - "[ 11/8 → 3/2 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.18280375190079212 0.40527783520519733 0.30504362285137177 0.7622128054499626 0.842598931863904 0.053796686232089996 0.5124214049428701 0.9178454764187336 0.09979427233338356 0.8280061967670918 0.5572535842657089 0.0622002687305212 0.9205668028444052 0.7738517206162214 0.04644870012998581 0.2117986585944891 0.26436166651546955 0.26421429589390755 0.4649084359407425 0.10490566119551659] ]", - "[ 3/2 → 13/8 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.6084080748260021 0.4424846563488245 0.755185678601265 0.04328125901520252 0.04244415462017059 0.3994515985250473 0.787989066913724 0.3180440980941057 0.30072982981801033 0.24217353947460651 0.7830625418573618 0.7733921892940998 0.7720277719199657 0.4402343425899744 0.21046430803835392 0.38635879568755627 0.5671729445457458 0.9510521050542593 0.010756833478808403 0.5728995501995087] ]", - "[ 13/8 → 7/4 | s:saw note:E4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.49675471149384975 0.2965749856084585 0.09848833456635475 0.19172007404267788 0.22677889838814735 0.9841996673494577 0.11156083643436432 0.6079028844833374 0.9412728808820248 0.9376902114599943 0.3611182738095522 0.5901201106607914 0.26860327646136284 0.9782364591956139 0.18694544956088066 0.004641396924853325 0.42020696587860584 0.06209231913089752 0.4790260009467602 0.21088780276477337] ]", - "[ 7/4 → 15/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.20473789609968662 0.7480021081864834 0.30757393687963486 0.5827204789966345 0.22094514779746532 0.6370698790997267 0.16573002003133297 0.0638319905847311 0.12625465169548988 0.4565841108560562 0.46095744147896767 0.646710304543376 0.045173922553658485 0.4833248760551214 0.1326297614723444 0.5275116711854935 0.9871038906276226 0.424171743914485 0.761672617867589 0.08582597225904465] ]", - "[ 15/8 → 2/1 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.5945571791380644 0.3962489552795887 0.563431927934289 0.4742323160171509 0.4185752831399441 0.9703940469771624 0.5282467231154442 0.6896266285330057 0.6941124945878983 0.2423656489700079 0.225077323615551 0.5445358455181122 0.014737963676452637 0.2848400343209505 0.14955217391252518 0.9782383926212788 0.6327074971050024 0.23327310755848885 0.4324392694979906 0.4326172359287739] ]", - "[ 2/1 → 17/8 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] phases:[0.9595271199941635 0.5427457969635725 0.45864437520504 0.8057199101895094 0.18388565629720688 0.17221237532794476 0.8838487900793552 0.4976818822324276 0.7543560564517975 0.34416236728429794 0.8638174068182707 0.5697487238794565 0.005243342369794846 0.6361143626272678 0.6039986703544855 0.38708674535155296 0.5463927835226059 0.5717255529016256 0.15141930803656578 0.22688637301325798] ]", - "[ 17/8 → 9/4 | s:saw note:C#5 partials:[1 0 0 0 0 0 0 0 0] phases:[0.9033902939409018 0.22919894196093082 0.8388008493930101 0.9108077362179756 0.32246968522667885 0.16122018359601498 0.31932324543595314 0.18524732999503613 0.19110161997377872 0.5346284378319979 0.38531880639493465 0.7401201985776424 0.07450124807655811 0.18417961709201336 0.5768439751118422 0.7161206863820553 0.5836263168603182 0.9591280855238438 0.7728104889392853 0.0006709620356559753] ]", - "[ 9/4 → 19/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.34548251144587994 0.30419893004000187 0.10205957666039467 0.9906709585338831 0.3156223688274622 0.3745057098567486 0.7668170686811209 0.9379972033202648 0.689277408644557 0.8690325897186995 0.36728161945939064 0.9668608456850052 0.7341883443295956 0.32575040124356747 0.8095720671117306 0.3182998225092888 0.38158727809786797 0.5534052699804306 0.5973556581884623 0.752589326351881] ]", - "[ 19/8 → 5/2 | s:saw note:C#5 partials:[1 0 0 0 0 0 0 0 0] phases:[0.8365066405385733 0.5369812995195389 0.2263860274106264 0.21759207546710968 0.2172116208821535 0.09454222768545151 0.8075542915612459 0.9017798062413931 0.46291174553334713 0.01372767798602581 0.3509599883109331 0.4871185813099146 0.3525365497916937 0.6883120182901621 0.19577431678771973 0.287217665463686 0.025726469233632088 0.11798650585114956 0.8464976660907269 0.981348654255271] ]", - "[ 5/2 → 21/8 | s:saw note:E4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.45861607417464256 0.6807645913213491 0.07009582966566086 0.1450389064848423 0.2579921595752239 0.612881500273943 0.5503941811621189 0.7027011960744858 0.681745121255517 0.24246043153107166 0.2981361001729965 0.02195165678858757 0.10680225491523743 0.9413154497742653 0.6108828671276569 0.9479686040431261 0.17599895782768726 0.6629563421010971 0.5875700414180756 0.05703482776880264] ]", - "[ 21/8 → 11/4 | s:saw note:A#3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.16019348427653313 0.47758268006145954 0.9749126061797142 0.8025561925023794 0.3600110989063978 0.324309878051281 0.5655391272157431 0.13364790193736553 0.3045873437076807 0.3814875278621912 0.14350778982043266 0.6017840709537268 0.11792952008545399 0.7185723781585693 0.6789924055337906 0.43416675738990307 0.9378792773932219 0.48162082210183144 0.37555896304547787 0.4686833508312702] ]", - "[ 11/4 → 23/8 | s:saw note:G#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.6332327704876661 0.7342763151973486 0.16935866326093674 0.4754706546664238 0.528122790157795 0.21934260986745358 0.7476693484932184 0.2995396424084902 0.46688378043472767 0.9741295631974936 0.9878341723233461 0.9837898630648851 0.15651432052254677 0.2896903567016125 0.7395600061863661 0.3490046579390764 0.014511989429593086 0.6857758145779371 0.7405510656535625 0.4224672969430685] ]", - "[ 23/8 → 3/1 | s:saw note:G#3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.01625417172908783 0.28409438766539097 0.3075305689126253 0.40343056432902813 0.5556836389005184 0.6926821582019329 0.5635769125074148 0.017873913049697876 0.5601800158619881 0.28681862354278564 0.8022358678281307 0.13467839546501637 0.02351163513958454 0.1987263821065426 0.4515750799328089 0.0009761657565832138 0.11555273085832596 0.25053937546908855 0.29571316950023174 0.15751986764371395] ]", - "[ 3/1 → 25/8 | s:saw note:B3 partials:[1 0 0 0 0 0 0 0 0] phases:[0.21728911064565182 0.7605195846408606 0.11240843310952187 0.4372697602957487 0.15630115941166878 0.1653979979455471 0.5303416550159454 0.07218753732740879 0.47459222190082073 0.04051801189780235 0.8742353077977896 0.9356274232268333 0.6662059463560581 0.4364917427301407 0.4163493011146784 0.9820694588124752 0.09098831936717033 0.41974459774792194 0.05833998881280422 0.2601191997528076] ]", - "[ 25/8 → 13/4 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.34324387833476067 0.14098095521330833 0.012934491038322449 0.827436288818717 0.5283997394144535 0.8942463714629412 0.4924008697271347 0.9298017006367445 0.18076439201831818 0.5243716649711132 0.015810951590538025 0.7476964127272367 0.6734520178288221 0.3267945274710655 0.9760967157781124 0.9397075213491917 0.7225867230445147 0.938586825504899 0.9692913070321083 0.5398030783981085] ]", - "[ 13/4 → 27/8 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] phases:[0.9923650715500116 0.7797339502722025 0.1992435697466135 0.9005183521658182 0.718992218375206 0.23745290748775005 0.28872333094477654 0.35448253713548183 0.6209003105759621 0.3761858306825161 0.019171399995684624 0.7563913837075233 0.1559751983731985 0.4236980527639389 0.08094430714845657 0.4560011047869921 0.5958948992192745 0.37061405926942825 0.18633292242884636 0.346891064196825] ]", - "[ 27/8 → 7/2 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.40869180113077164 0.4018078800290823 0.34696186147630215 0.9572948440909386 0.400035472586751 0.3531211093068123 0.7240961641073227 0.8125612027943134 0.3587487991899252 0.1545814611017704 0.8934940584003925 0.9094721674919128 0.2336172368377447 0.17333386465907097 0.227950319647789 0.12384821102023125 0.5896956156939268 0.3293947223573923 0.619540523737669 0.8746719229966402] ]", - "[ 7/2 → 29/8 | s:saw note:D#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.40994881466031075 0.6455810181796551 0.35704658553004265 0.2732649203389883 0.31587288342416286 0.23243548162281513 0.22569947130978107 0.17241661623120308 0.7624858524650335 0.02111036144196987 0.6966175157576799 0.32112877257168293 0.2512516509741545 0.08602019399404526 0.16260642930865288 0.2627844326198101 0.1954369619488716 0.16302869468927383 0.4046020060777664 0.0904023926705122] ]", - "[ 29/8 → 15/4 | s:saw note:D#5 partials:[1 0 0 0 0 0 0 0 0] phases:[0.9391485787928104 0.3380728308111429 0.7723016366362572 0.998674126341939 0.7193406894803047 0.6963680125772953 0.9260678570717573 0.7829763628542423 0.42278125509619713 0.913721015676856 0.38873230293393135 0.7281809840351343 0.952108234167099 0.649707704782486 0.9456792045384645 0.4331315904855728 0.63712502643466 0.9180345926433802 0.7379776351153851 0.7914005517959595] ]", - "[ 15/4 → 31/8 | s:saw note:B4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.8121673222631216 0.25548945367336273 0.5759696904569864 0.6668333616107702 0.9233786761760712 0.11143362522125244 0.9734930321574211 0.369325939565897 0.6248745918273926 0.719582824036479 0.24051696807146072 0.20336504466831684 0.6860735435038805 0.10649923048913479 0.8736641593277454 0.042930178344249725 0.09769343212246895 0.7966452036052942 0.5882443580776453 0.8942952174693346] ]", - "[ 31/8 → 4/1 | s:saw note:A#4 partials:[1 0 0 0 0 0 0 0 0] phases:[0.7361665386706591 0.7657648548483849 0.21474426984786987 0.8228576295077801 0.5647660344839096 0.08148551359772682 0.9714624118059874 0.13992334716022015 0.9609981551766396 0.6844500284641981 0.23610286228358746 0.5974335502833128 0.20678778178989887 0.5261937081813812 0.7417268306016922 0.9810918122529984 0.8138748407363892 0.9389897901564837 0.7420910261571407 0.48839940316975117] ]", + "[ 0/1 → 1/8 | s:saw note:G#1 partials:{partials:1} ]", + "[ 0/1 → 1/8 | s:saw note:G#1 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 1/8 → 1/4 | s:saw note:A#2 partials:{partials:1} ]", + "[ 1/8 → 1/4 | s:saw note:A#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 1/4 → 3/8 | s:saw note:D#2 partials:{partials:1} ]", + "[ 1/4 → 3/8 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 3/8 → 1/2 | s:saw note:D#2 partials:{partials:1} ]", + "[ 3/8 → 1/2 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 1/2 → 5/8 | s:saw note:C#2 partials:{partials:1} ]", + "[ 1/2 → 5/8 | s:saw note:C#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 5/8 → 3/4 | s:saw note:A#1 partials:{partials:1} ]", + "[ 5/8 → 3/4 | s:saw note:A#1 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 3/4 → 7/8 | s:saw note:B1 partials:{partials:1} ]", + "[ 3/4 → 7/8 | s:saw note:B1 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 7/8 → 1/1 | s:saw note:D#2 partials:{partials:1} ]", + "[ 7/8 → 1/1 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 1/1 → 9/8 | s:saw note:F#2 partials:{partials:1} ]", + "[ 1/1 → 9/8 | s:saw note:F#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 9/8 → 5/4 | s:saw note:A#2 partials:{partials:1} ]", + "[ 9/8 → 5/4 | s:saw note:A#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 5/4 → 11/8 | s:saw note:A#2 partials:{partials:1} ]", + "[ 5/4 → 11/8 | s:saw note:A#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 11/8 → 3/2 | s:saw note:B1 partials:{partials:1} ]", + "[ 11/8 → 3/2 | s:saw note:B1 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 3/2 → 13/8 | s:saw note:G#2 partials:{partials:1} ]", + "[ 3/2 → 13/8 | s:saw note:G#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 13/8 → 7/4 | s:saw note:E2 partials:{partials:1} ]", + "[ 13/8 → 7/4 | s:saw note:E2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 7/4 → 15/8 | s:saw note:B1 partials:{partials:1} ]", + "[ 7/4 → 15/8 | s:saw note:B1 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 15/8 → 2/1 | s:saw note:G#2 partials:{partials:1} ]", + "[ 15/8 → 2/1 | s:saw note:G#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 2/1 → 17/8 | s:saw note:D#3 partials:{partials:1} ]", + "[ 2/1 → 17/8 | s:saw note:D#3 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 17/8 → 9/4 | s:saw note:C#3 partials:{partials:1} ]", + "[ 17/8 → 9/4 | s:saw note:C#3 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 9/4 → 19/8 | s:saw note:D#2 partials:{partials:1} ]", + "[ 9/4 → 19/8 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 19/8 → 5/2 | s:saw note:C#3 partials:{partials:1} ]", + "[ 19/8 → 5/2 | s:saw note:C#3 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 5/2 → 21/8 | s:saw note:E2 partials:{partials:1} ]", + "[ 5/2 → 21/8 | s:saw note:E2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 21/8 → 11/4 | s:saw note:A#1 partials:{partials:1} ]", + "[ 21/8 → 11/4 | s:saw note:A#1 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 11/4 → 23/8 | s:saw note:G#2 partials:{partials:1} ]", + "[ 11/4 → 23/8 | s:saw note:G#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 23/8 → 3/1 | s:saw note:G#1 partials:{partials:1} ]", + "[ 23/8 → 3/1 | s:saw note:G#1 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 3/1 → 25/8 | s:saw note:B1 partials:{partials:1} ]", + "[ 3/1 → 25/8 | s:saw note:B1 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 25/8 → 13/4 | s:saw note:D#2 partials:{partials:1} ]", + "[ 25/8 → 13/4 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 13/4 → 27/8 | s:saw note:D#3 partials:{partials:1} ]", + "[ 13/4 → 27/8 | s:saw note:D#3 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 27/8 → 7/2 | s:saw note:D#2 partials:{partials:1} ]", + "[ 27/8 → 7/2 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 7/2 → 29/8 | s:saw note:D#2 partials:{partials:1} ]", + "[ 7/2 → 29/8 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 29/8 → 15/4 | s:saw note:D#3 partials:{partials:1} ]", + "[ 29/8 → 15/4 | s:saw note:D#3 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 15/4 → 31/8 | s:saw note:B2 partials:{partials:1} ]", + "[ 15/4 → 31/8 | s:saw note:B2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", + "[ 31/8 → 4/1 | s:saw note:A#2 partials:{partials:1} ]", + "[ 31/8 → 4/1 | s:saw note:A#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]", ] `; diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 6fc5b692..56b28c0e 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -85,7 +85,7 @@ note("c2 >".fast(2)) ._scope()`} /> -which acts as a spectral filter or +which acts as a spectral filter. Or: >".fast(2)) -.sound("saw") -.partials(randL(100)) -.phases(randL(100)) -._scope()`} + tune={`s("saw").seg(16).n(irand(12)).scale("F1:minor") + .penv(48).panchor(0).pdec(0.05) + .delay(0.25).room(0.25) + .compressor(-20).vib(0.3) + .partials(randL(200)) + .phases(randL(200))`} /> ## Vibrato