From 496d60dc1cd83cbb7901996632879f058cfdae8a Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 3 May 2024 14:56:16 -0400 Subject: [PATCH 01/75] testing --- packages/core/controls.mjs | 11 +++++ packages/core/cyclist.mjs | 2 +- packages/core/neocyclist.mjs | 11 ++++- packages/core/repl.mjs | 6 +-- packages/superdough/superdough.mjs | 5 ++- packages/superdough/worklets.mjs | 65 ++++++++++++++++++++++++++++++ packages/webaudio/webaudio.mjs | 7 ++-- 7 files changed, 98 insertions(+), 9 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 166f0ac23..2364cd994 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -430,6 +430,17 @@ export const { crush } = registerControl('crush'); */ export const { coarse } = registerControl('coarse'); +/** + * modulate the output gain of a sound with a continuous wave + * + * @name gainmod + * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. + * @example + * s("triangle").gainmod("2:1:0") + * + */ +export const { gainmod } = registerControl('gainmod'); + /** * Allows you to set the output channels on the interface * diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 08b893752..d61267475 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -56,7 +56,7 @@ export class Cyclist { // the following line is dumb and only here for backwards compatibility // see https://github.com/tidalcycles/strudel/pull/1004 const deadline = targetTime - phase; - onTrigger?.(hap, deadline, duration, this.cps, targetTime); + onTrigger?.(hap, deadline, duration, this.cps, targetTime, end); } }); } catch (e) { diff --git a/packages/core/neocyclist.mjs b/packages/core/neocyclist.mjs index 4600d0ef9..6bc0a8404 100644 --- a/packages/core/neocyclist.mjs +++ b/packages/core/neocyclist.mjs @@ -4,6 +4,7 @@ Copyright (C) 2022 Strudel contributors - see . */ +import Fraction from 'fraction.js'; import { logger } from './logger.mjs'; export class NeoCyclist { @@ -86,7 +87,15 @@ export class NeoCyclist { this.latency + this.worker_time_dif; const duration = hap.duration / this.cps; - onTrigger?.(hap, 0, duration, this.cps, targetTime); + onTrigger?.( + hap, + 0, + duration, + this.cps, + targetTime, + this.cycle, + // + Fraction(this.latency).div(this.cps) + ); } }); }; diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 5b7bcd71b..af5935787 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -178,15 +178,15 @@ export function repl({ export const getTrigger = ({ getTime, defaultOutput }) => - async (hap, deadline, duration, cps, t) => { + async (hap, deadline, duration, cps, t, cycle = 0) => { // TODO: get rid of deadline after https://github.com/tidalcycles/strudel/pull/1004 try { if (!hap.context.onTrigger || !hap.context.dominantTrigger) { - await defaultOutput(hap, deadline, duration, cps, t); + await defaultOutput(hap, deadline, duration, cps, t, cycle); } if (hap.context.onTrigger) { // call signature of output / onTrigger is different... - await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t); + await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t, cycle); } } catch (err) { logger(`[cyclist] error: ${err.message}`, 'error'); diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index c0ba96e06..138a3a38d 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -260,7 +260,8 @@ export function resetGlobalEffects() { analysersData = {}; } -export const superdough = async (value, t, hapDuration) => { +export const superdough = async (value, t, hapDuration, cps, cycle) => { + console.log({ cps, cycle }); const ac = getAudioContext(); if (typeof value !== 'object') { throw new Error( @@ -322,6 +323,7 @@ export const superdough = async (value, t, hapDuration) => { phasercenter, // coarse, + gainmod, crush, shape, shapevol = 1, @@ -470,6 +472,7 @@ export const superdough = async (value, t, hapDuration) => { crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush })); shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol })); distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); + gainmod !== undefined && chain.push(getWorklet(ac, 'gainmod-processor', { speed: gainmod, cps, cycle })); compressorThreshold !== undefined && chain.push( diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index bab28987e..f29d1028f 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -1,6 +1,36 @@ // coarse, crush, and shape processors adapted from dktr0's webdirt: https://github.com/dktr0/WebDirt/blob/5ce3d698362c54d6e1b68acc47eb2955ac62c793/dist/AudioWorklets.js // LICENSE GNU General Public License v3.0 see https://github.com/dktr0/WebDirt/blob/main/LICENSE +const linearEnvelope = (startVal, EndVal, startTime, endTime, currentTime, hold = 0) => { + let min = 0.001; + currentTime = currentTime - hold; + if (startTime > currentTime) { + return 1; + } + if (currentTime > endTime) { + return min; + } + // change relative start time to 0 to prevent numeric overflow + currentTime = currentTime - startTime; + endTime = endTime - startTime; + startTime = 0; + + let x1 = startTime; + let y1 = startVal; + let x2 = endTime; + let y2 = EndVal; + + // Calculate the growth or decay rate (b) + let b = y1 / y2 / (x1 - x2); + + // Calculate the initial value (a) + let a = y1 / (b * x1); + + let x = currentTime; + // calculate y for any x + return a * (b * x); +}; + class CoarseProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [{ name: 'coarse', defaultValue: 1 }]; @@ -62,6 +92,41 @@ class CrushProcessor extends AudioWorkletProcessor { } registerProcessor('crush-processor', CrushProcessor); +class GainModProcessor extends AudioWorkletProcessor { + static get parameterDescriptors() { + return [ + { name: 'cps', defaultValue: 0.5 }, + { name: 'speed', defaultValue: 0.5 }, + { name: 'cycle', defaultValue: 0 }, + ]; + } + + constructor() { + super(); + } + + process(inputs, outputs, parameters) { + const input = inputs[0]; + const output = outputs[0]; + const blockSize = 128; + + let crush = parameters.crush[0] ?? 8; + crush = Math.max(1, crush); + + if (input[0] == null || output[0] == null) { + return false; + } + for (let n = 0; n < blockSize; n++) { + for (let i = 0; i < input.length; i++) { + const x = Math.pow(2, crush - 1); + output[i][n] = Math.round(input[i][n] * x) / x; + } + } + return true; + } +} +registerProcessor('gainmod-processor', GainModProcessor); + class ShapeProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [ diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index f82a436a9..36d11b096 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -17,9 +17,9 @@ const hap2value = (hap) => { export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps); // uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004 -export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => - superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration); - +export const webaudioOutput = (hap, deadline, hapDuration, cps, t, cycle) => { + return superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration, cps, cycle); +}; Pattern.prototype.webaudio = function () { return this.onTrigger(webaudioOutputTrigger); }; @@ -30,6 +30,7 @@ export function webaudioScheduler(options = {}) { defaultOutput: webaudioOutput, ...options, }; + const { defaultOutput, getTime } = options; return new strudel.Cyclist({ ...options, From 66a27a609ca3bc26c9f6bd962fad9429d2e686b5 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 3 May 2024 14:58:20 -0400 Subject: [PATCH 02/75] implementing --- packages/superdough/worklets.mjs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index f29d1028f..9800a8ed1 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -106,23 +106,21 @@ class GainModProcessor extends AudioWorkletProcessor { } process(inputs, outputs, parameters) { - const input = inputs[0]; - const output = outputs[0]; - const blockSize = 128; - - let crush = parameters.crush[0] ?? 8; - crush = Math.max(1, crush); - - if (input[0] == null || output[0] == null) { - return false; - } - for (let n = 0; n < blockSize; n++) { - for (let i = 0; i < input.length; i++) { - const x = Math.pow(2, crush - 1); - output[i][n] = Math.round(input[i][n] * x) / x; - } - } - return true; + // const input = inputs[0]; + // const output = outputs[0]; + // const blockSize = 128; + // let crush = parameters.crush[0] ?? 8; + // crush = Math.max(1, crush); + // if (input[0] == null || output[0] == null) { + // return false; + // } + // for (let n = 0; n < blockSize; n++) { + // for (let i = 0; i < input.length; i++) { + // const x = Math.pow(2, crush - 1); + // output[i][n] = Math.round(input[i][n] * x) / x; + // } + // } + // return true; } } registerProcessor('gainmod-processor', GainModProcessor); From 0d900c01342d0d6d860b67b2ea8a40867de21fb3 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 5 May 2024 12:02:51 -0400 Subject: [PATCH 03/75] sine test --- packages/superdough/worklets.mjs | 47 ++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 9800a8ed1..be4d92c41 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -91,6 +91,14 @@ class CrushProcessor extends AudioWorkletProcessor { } } registerProcessor('crush-processor', CrushProcessor); +const sine = (phase, dt) => { + return Math.sin(Math.PI * 2 * phase); + + // return Math.sin(phase); +}; +const getModulator = (frequency, values, ct) => { + Math.sin(); +}; class GainModProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { @@ -103,23 +111,44 @@ class GainModProcessor extends AudioWorkletProcessor { constructor() { super(); + this.phase; + this.inc = 0; + } + + incrementPhase(dt) { + this.phase += dt; + if (this.phase > 1.0) { + this.phase = this.phase - 1; + } } process(inputs, outputs, parameters) { - // const input = inputs[0]; - // const output = outputs[0]; - // const blockSize = 128; + const speed = parameters['speed'][0]; + const cps = parameters['cps'][0]; + const cycle = parameters['cycle'][0]; + const frequency = speed * cps; + if (this.phase == null) { + this.phase = (cycle * cps * frequency) % 1; + } + + const input = inputs[0]; + const output = outputs[0]; + const blockSize = 128; // let crush = parameters.crush[0] ?? 8; // crush = Math.max(1, crush); // if (input[0] == null || output[0] == null) { // return false; // } - // for (let n = 0; n < blockSize; n++) { - // for (let i = 0; i < input.length; i++) { - // const x = Math.pow(2, crush - 1); - // output[i][n] = Math.round(input[i][n] * x) / x; - // } - // } + const dt = frequency / sampleRate; + for (let n = 0; n < blockSize; n++) { + for (let i = 0; i < input.length; i++) { + const modval = sine(this.phase, dt); + + output[i][n] = input[i][n] * modval; + } + this.incrementPhase(dt); + this.inc += 1; + } // return true; } } From 3159f665604089b4233b140441e1630e7a872b12 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 5 May 2024 22:52:18 -0400 Subject: [PATCH 04/75] working --- packages/superdough/superdough.mjs | 15 +++- packages/superdough/worklets.mjs | 140 ++++++++++++++--------------- 2 files changed, 82 insertions(+), 73 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 138a3a38d..b6e06e635 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -261,7 +261,6 @@ export function resetGlobalEffects() { } export const superdough = async (value, t, hapDuration, cps, cycle) => { - console.log({ cps, cycle }); const ac = getAudioContext(); if (typeof value !== 'object') { throw new Error( @@ -282,6 +281,8 @@ export const superdough = async (value, t, hapDuration, cps, cycle) => { } // destructure let { + amdepth = 1, + amshape = 'tri', s = 'triangle', bank, source, @@ -326,6 +327,7 @@ export const superdough = async (value, t, hapDuration, cps, cycle) => { gainmod, crush, shape, + shapevol = 1, distort, distortvol = 1, @@ -472,7 +474,16 @@ export const superdough = async (value, t, hapDuration, cps, cycle) => { crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush })); shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol })); distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); - gainmod !== undefined && chain.push(getWorklet(ac, 'gainmod-processor', { speed: gainmod, cps, cycle })); + gainmod !== undefined && + chain.push( + getWorklet(ac, 'gainmod-processor', { + speed: gainmod, + // depth: amdepth, shape: amshape, + + cps, + cycle, + }), + ); compressorThreshold !== undefined && chain.push( diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index be4d92c41..2f1375f01 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -1,34 +1,70 @@ // coarse, crush, and shape processors adapted from dktr0's webdirt: https://github.com/dktr0/WebDirt/blob/5ce3d698362c54d6e1b68acc47eb2955ac62c793/dist/AudioWorklets.js // LICENSE GNU General Public License v3.0 see https://github.com/dktr0/WebDirt/blob/main/LICENSE -const linearEnvelope = (startVal, EndVal, startTime, endTime, currentTime, hold = 0) => { - let min = 0.001; - currentTime = currentTime - hold; - if (startTime > currentTime) { - return 1; +const clamp = (num, min, max) => Math.min(Math.max(num, min), max); +// adjust waveshape to remove frequencies above nyquist to prevent aliasing +// referenced from https://www.kvraudio.com/forum/viewtopic.php?t=375517 +function polyBlep(phase, dt) { + // 0 <= phase < 1 + if (phase < dt) { + phase /= dt; + // 2 * (phase - phase^2/2 - 0.5) + return phase + phase - phase * phase - 1; } - if (currentTime > endTime) { - return min; + + // -1 < phase < 0 + else if (phase > 1 - dt) { + phase = (phase - 1) / dt; + // 2 * (phase^2/2 + phase + 0.5) + return phase * phase + phase + phase + 1; } - // change relative start time to 0 to prevent numeric overflow - currentTime = currentTime - startTime; - endTime = endTime - startTime; - startTime = 0; - let x1 = startTime; - let y1 = startVal; - let x2 = endTime; - let y2 = EndVal; + // 0 otherwise + else { + return 0; + } +} - // Calculate the growth or decay rate (b) - let b = y1 / y2 / (x1 - x2); +const waveshapes = { + sine(phase) { + return Math.sin(Math.PI * 2 * phase); + }, + custom(phase, values = [0, 1]) { + const numParts = values.length - 1; + const currPart = Math.floor(phase * numParts); - // Calculate the initial value (a) - let a = y1 / (b * x1); - - let x = currentTime; - // calculate y for any x - return a * (b * x); + const partLength = 1 / numParts; + const startVal = clamp(values[currPart], 0, 1); + const endVal = clamp(values[currPart + 1], 0, 1); + const y2 = endVal; + const y1 = startVal; + const x1 = 0; + const x2 = partLength; + const slope = (y2 - y1) / (x2 - x1); + return slope * (phase - partLength * currPart) + startVal; + }, + sawblep(phase, dt) { + const v = 2 * phase - 1; + return v - polyBlep(phase, dt); + }, + ramp(phase) { + return phase; + }, + saw(phase) { + return 1 - phase; + }, + tri(phase, skew = 0.5) { + if (phase >= skew) { + return 1 - ((phase / (1 - skew)) % 1); + } + return (phase / skew) % 1; + }, + square(phase, skew = 0.5) { + if (phase >= skew) { + return 1; + } + return 0; + }, }; class CoarseProcessor extends AudioWorkletProcessor { @@ -91,14 +127,6 @@ class CrushProcessor extends AudioWorkletProcessor { } } registerProcessor('crush-processor', CrushProcessor); -const sine = (phase, dt) => { - return Math.sin(Math.PI * 2 * phase); - - // return Math.sin(phase); -}; -const getModulator = (frequency, values, ct) => { - Math.sin(); -}; class GainModProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { @@ -106,13 +134,14 @@ class GainModProcessor extends AudioWorkletProcessor { { name: 'cps', defaultValue: 0.5 }, { name: 'speed', defaultValue: 0.5 }, { name: 'cycle', defaultValue: 0 }, + // { name: 'shape', defaultValue: 'tri' }, + // { name: 'depth', defaultValue: 1 }, ]; } constructor() { super(); this.phase; - this.inc = 0; } incrementPhase(dt) { @@ -126,30 +155,27 @@ class GainModProcessor extends AudioWorkletProcessor { const speed = parameters['speed'][0]; const cps = parameters['cps'][0]; const cycle = parameters['cycle'][0]; + + const blockSize = 128; const frequency = speed * cps; if (this.phase == null) { - this.phase = (cycle * cps * frequency) % 1; + const secondsPassed = cycle / cps; + this.phase = Math.max(0, (secondsPassed * frequency) % 1); } const input = inputs[0]; const output = outputs[0]; - const blockSize = 128; - // let crush = parameters.crush[0] ?? 8; - // crush = Math.max(1, crush); - // if (input[0] == null || output[0] == null) { - // return false; - // } + const dt = frequency / sampleRate; for (let n = 0; n < blockSize; n++) { for (let i = 0; i < input.length; i++) { - const modval = sine(this.phase, dt); + const modval = waveshapes.tri(this.phase, 0.99); output[i][n] = input[i][n] * modval; } this.incrementPhase(dt); - this.inc += 1; } - // return true; + return true; } } registerProcessor('gainmod-processor', GainModProcessor); @@ -222,34 +248,6 @@ class DistortProcessor extends AudioWorkletProcessor { } registerProcessor('distort-processor', DistortProcessor); -// adjust waveshape to remove frequencies above nyquist to prevent aliasing -// referenced from https://www.kvraudio.com/forum/viewtopic.php?t=375517 -const polyBlep = (phase, dt) => { - // 0 <= phase < 1 - if (phase < dt) { - phase /= dt; - // 2 * (phase - phase^2/2 - 0.5) - return phase + phase - phase * phase - 1; - } - - // -1 < phase < 0 - else if (phase > 1 - dt) { - phase = (phase - 1) / dt; - // 2 * (phase^2/2 + phase + 0.5) - return phase * phase + phase + phase + 1; - } - - // 0 otherwise - else { - return 0; - } -}; - -const saw = (phase, dt) => { - const v = 2 * phase - 1; - return v - polyBlep(phase, dt); -}; - function lerp(a, b, n) { return n * (b - a) + a; } @@ -349,7 +347,7 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor { for (let i = 0; i < output[0].length; i++) { this.phase[n] = this.phase[n] ?? Math.random(); - const v = saw(this.phase[n], dt); + const v = waveshapes.sawblep(this.phase[n], dt); output[0][i] = output[0][i] + v * gainL; output[1][i] = output[1][i] + v * gainR; From 7df663e87dac1c28220fc2cac21ab68ab976b5f9 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 5 May 2024 23:54:30 -0400 Subject: [PATCH 05/75] memory leaking --- packages/core/controls.mjs | 31 +++++++++++++++++++++++++----- packages/superdough/superdough.mjs | 11 ++++++----- packages/superdough/worklets.mjs | 9 +++++---- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 2364cd994..b99f83564 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -431,16 +431,37 @@ export const { crush } = registerControl('crush'); export const { coarse } = registerControl('coarse'); /** - * modulate the output gain of a sound with a continuous wave + * modulate the amplitude of a sound with a continuous waveform * - * @name gainmod - * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. + * @name am + * @param {number | Pattern} speed modulation speed in cycles * @example - * s("triangle").gainmod("2:1:0") + * s("triangle").am("2").amshape("").amdepth(.5) * */ -export const { gainmod } = registerControl('gainmod'); +export const { am } = registerControl(['am', 'amdepth', 'amshape']); +/** + * depth of amplitude modulation + * + * @name amdepth + * @param {number | Pattern} depth + * @example + * s("triangle").am(1).amdepth("1") + * + */ +export const { amdepth } = registerControl('amdepth'); + +/** + * shape of amplitude modulation + * + * @name amshape + * @param {number | Pattern} shape tri | square | sine | saw | ramp + * @example + * note("{f g c d}%16").am(4).amshape("ramp").s("sawtooth") + * + */ +export const { amshape } = registerControl('amshape'); /** * Allows you to set the output channels on the interface * diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index b6e06e635..13c138dcc 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -324,7 +324,7 @@ export const superdough = async (value, t, hapDuration, cps, cycle) => { phasercenter, // coarse, - gainmod, + am, crush, shape, @@ -474,11 +474,12 @@ export const superdough = async (value, t, hapDuration, cps, cycle) => { crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush })); shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol })); distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); - gainmod !== undefined && + am !== undefined && chain.push( - getWorklet(ac, 'gainmod-processor', { - speed: gainmod, - // depth: amdepth, shape: amshape, + getWorklet(ac, 'am-processor', { + speed: am, + depth: amdepth, + // shape: amshape, cps, cycle, diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 2f1375f01..b3e32576a 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -128,14 +128,14 @@ class CrushProcessor extends AudioWorkletProcessor { } registerProcessor('crush-processor', CrushProcessor); -class GainModProcessor extends AudioWorkletProcessor { +class AMProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [ { name: 'cps', defaultValue: 0.5 }, { name: 'speed', defaultValue: 0.5 }, { name: 'cycle', defaultValue: 0 }, // { name: 'shape', defaultValue: 'tri' }, - // { name: 'depth', defaultValue: 1 }, + { name: 'depth', defaultValue: 1 }, ]; } @@ -155,6 +155,7 @@ class GainModProcessor extends AudioWorkletProcessor { const speed = parameters['speed'][0]; const cps = parameters['cps'][0]; const cycle = parameters['cycle'][0]; + const depth = clamp(parameters['depth'][0], 0, 1); const blockSize = 128; const frequency = speed * cps; @@ -169,7 +170,7 @@ class GainModProcessor extends AudioWorkletProcessor { const dt = frequency / sampleRate; for (let n = 0; n < blockSize; n++) { for (let i = 0; i < input.length; i++) { - const modval = waveshapes.tri(this.phase, 0.99); + const modval = waveshapes.tri(this.phase, 0.99) * depth + (1 - depth); output[i][n] = input[i][n] * modval; } @@ -178,7 +179,7 @@ class GainModProcessor extends AudioWorkletProcessor { return true; } } -registerProcessor('gainmod-processor', GainModProcessor); +registerProcessor('am-processor', AMProcessor); class ShapeProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { From 16472b59ddef33bc7fdb332cbcf23ecc6e09be48 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 8 May 2024 00:05:05 -0400 Subject: [PATCH 06/75] fix cyclist clock cycle --- packages/core/cyclist.mjs | 4 +- packages/core/neocyclist.mjs | 11 +-- packages/superdough/worklets.mjs | 158 +++++++++++++++++-------------- 3 files changed, 92 insertions(+), 81 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index d61267475..b33e9cc36 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -34,6 +34,8 @@ export class Cyclist { try { const begin = this.lastEnd; + const cycle = this.now(); + this.lastBegin = begin; const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; this.lastEnd = end; @@ -56,7 +58,7 @@ export class Cyclist { // the following line is dumb and only here for backwards compatibility // see https://github.com/tidalcycles/strudel/pull/1004 const deadline = targetTime - phase; - onTrigger?.(hap, deadline, duration, this.cps, targetTime, end); + onTrigger?.(hap, deadline, duration, this.cps, targetTime, cycle); } }); } catch (e) { diff --git a/packages/core/neocyclist.mjs b/packages/core/neocyclist.mjs index 6bc0a8404..8f9b269e0 100644 --- a/packages/core/neocyclist.mjs +++ b/packages/core/neocyclist.mjs @@ -4,7 +4,6 @@ Copyright (C) 2022 Strudel contributors - see . */ -import Fraction from 'fraction.js'; import { logger } from './logger.mjs'; export class NeoCyclist { @@ -87,15 +86,7 @@ export class NeoCyclist { this.latency + this.worker_time_dif; const duration = hap.duration / this.cps; - onTrigger?.( - hap, - 0, - duration, - this.cps, - targetTime, - this.cycle, - // + Fraction(this.latency).div(this.cps) - ); + onTrigger?.(hap, 0, duration, this.cps, targetTime, this.cycle); } }); }; diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index b3e32576a..107e9bf7f 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -2,6 +2,7 @@ // LICENSE GNU General Public License v3.0 see https://github.com/dktr0/WebDirt/blob/main/LICENSE const clamp = (num, min, max) => Math.min(Math.max(num, min), max); +const blockSize = 128; // adjust waveshape to remove frequencies above nyquist to prevent aliasing // referenced from https://www.kvraudio.com/forum/viewtopic.php?t=375517 function polyBlep(phase, dt) { @@ -67,6 +68,64 @@ const waveshapes = { }, }; +class AMProcessor extends AudioWorkletProcessor { + static get parameterDescriptors() { + return [ + { name: 'cps', defaultValue: 0.5 }, + { name: 'speed', defaultValue: 0.5 }, + { name: 'cycle', defaultValue: 0 }, + // { name: 'shape', defaultValue: 'tri' }, + { name: 'depth', defaultValue: 1 }, + ]; + } + + constructor() { + super(); + this.phase; + this.started = false; + } + + incrementPhase(dt) { + this.phase += dt; + if (this.phase > 1.0) { + this.phase = this.phase - 1; + } + } + + process(inputs, outputs, parameters) { + const input = inputs[0]; + const output = outputs[0]; + const hasInput = !(input[0] === undefined); + if (this.started && !hasInput) { + return false; + } + this.started = hasInput; + + const speed = parameters['speed'][0]; + const cps = parameters['cps'][0]; + const cycle = parameters['cycle'][0]; + const depth = clamp(parameters['depth'][0], 0, 1); + const blockSize = 128; + const frequency = speed * cps; + if (this.phase == null) { + const secondsPassed = cycle / cps; + this.phase = Math.max(0, (secondsPassed * frequency) % 1); + } + + const dt = frequency / sampleRate; + for (let n = 0; n < blockSize; n++) { + for (let i = 0; i < input.length; i++) { + const modval = waveshapes.tri(this.phase, 0.99) * depth + (1 - depth); + + output[i][n] = input[i][n] * modval; + } + this.incrementPhase(dt); + } + return true; + } +} +registerProcessor('am-processor', AMProcessor); + class CoarseProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [{ name: 'coarse', defaultValue: 1 }]; @@ -74,19 +133,21 @@ class CoarseProcessor extends AudioWorkletProcessor { constructor() { super(); + this.started = false; } process(inputs, outputs, parameters) { const input = inputs[0]; const output = outputs[0]; - const blockSize = 128; + + const hasInput = !(input[0] === undefined); + if (this.started && !hasInput) { + return false; + } + this.started = hasInput; let coarse = parameters.coarse[0] ?? 0; coarse = Math.max(1, coarse); - - if (input[0] == null || output[0] == null) { - return false; - } for (let n = 0; n < blockSize; n++) { for (let i = 0; i < input.length; i++) { output[i][n] = n % coarse === 0 ? input[i][n] : output[i][n - 1]; @@ -104,19 +165,22 @@ class CrushProcessor extends AudioWorkletProcessor { constructor() { super(); + this.started = false; } process(inputs, outputs, parameters) { const input = inputs[0]; const output = outputs[0]; - const blockSize = 128; + + const hasInput = !(input[0] === undefined); + if (this.started && !hasInput) { + return false; + } + this.started = hasInput; let crush = parameters.crush[0] ?? 8; crush = Math.max(1, crush); - if (input[0] == null || output[0] == null) { - return false; - } for (let n = 0; n < blockSize; n++) { for (let i = 0; i < input.length; i++) { const x = Math.pow(2, crush - 1); @@ -128,59 +192,6 @@ class CrushProcessor extends AudioWorkletProcessor { } registerProcessor('crush-processor', CrushProcessor); -class AMProcessor extends AudioWorkletProcessor { - static get parameterDescriptors() { - return [ - { name: 'cps', defaultValue: 0.5 }, - { name: 'speed', defaultValue: 0.5 }, - { name: 'cycle', defaultValue: 0 }, - // { name: 'shape', defaultValue: 'tri' }, - { name: 'depth', defaultValue: 1 }, - ]; - } - - constructor() { - super(); - this.phase; - } - - incrementPhase(dt) { - this.phase += dt; - if (this.phase > 1.0) { - this.phase = this.phase - 1; - } - } - - process(inputs, outputs, parameters) { - const speed = parameters['speed'][0]; - const cps = parameters['cps'][0]; - const cycle = parameters['cycle'][0]; - const depth = clamp(parameters['depth'][0], 0, 1); - - const blockSize = 128; - const frequency = speed * cps; - if (this.phase == null) { - const secondsPassed = cycle / cps; - this.phase = Math.max(0, (secondsPassed * frequency) % 1); - } - - const input = inputs[0]; - const output = outputs[0]; - - const dt = frequency / sampleRate; - for (let n = 0; n < blockSize; n++) { - for (let i = 0; i < input.length; i++) { - const modval = waveshapes.tri(this.phase, 0.99) * depth + (1 - depth); - - output[i][n] = input[i][n] * modval; - } - this.incrementPhase(dt); - } - return true; - } -} -registerProcessor('am-processor', AMProcessor); - class ShapeProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [ @@ -191,21 +202,24 @@ class ShapeProcessor extends AudioWorkletProcessor { constructor() { super(); + this.started = false; } process(inputs, outputs, parameters) { const input = inputs[0]; const output = outputs[0]; - const blockSize = 128; + + const hasInput = !(input[0] === undefined); + if (this.started && !hasInput) { + return false; + } + this.started = hasInput; let shape = parameters.shape[0]; shape = shape < 1 ? shape : 1.0 - 4e-10; shape = (2.0 * shape) / (1.0 - shape); const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); - if (input[0] == null || output[0] == null) { - return false; - } for (let n = 0; n < blockSize; n++) { for (let i = 0; i < input.length; i++) { output[i][n] = (((1 + shape) * input[i][n]) / (1 + shape * Math.abs(input[i][n]))) * postgain; @@ -226,19 +240,22 @@ class DistortProcessor extends AudioWorkletProcessor { constructor() { super(); + this.started = false; } process(inputs, outputs, parameters) { const input = inputs[0]; const output = outputs[0]; - const blockSize = 128; + + const hasInput = !(input[0] === undefined); + if (this.started && !hasInput) { + return false; + } + this.started = hasInput; const shape = Math.expm1(parameters.distort[0]); const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); - if (input[0] == null || output[0] == null) { - return false; - } for (let n = 0; n < blockSize; n++) { for (let i = 0; i < input.length; i++) { output[i][n] = (((1 + shape) * input[i][n]) / (1 + shape * Math.abs(input[i][n]))) * postgain; @@ -249,6 +266,7 @@ class DistortProcessor extends AudioWorkletProcessor { } registerProcessor('distort-processor', DistortProcessor); +// SUPERSAW function lerp(a, b, n) { return n * (b - a) + a; } From 6c05a4eb7becfb47e322eff6b6a00d351d5dc888 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 9 May 2024 00:21:21 -0400 Subject: [PATCH 07/75] fixed tri shape --- packages/core/controls.mjs | 23 ++++++++++++++++++++++- packages/core/cyclist.mjs | 5 ++++- packages/superdough/superdough.mjs | 8 ++++++-- packages/superdough/worklets.mjs | 24 ++++++++++++++---------- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index b99f83564..46523588a 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -439,7 +439,7 @@ export const { coarse } = registerControl('coarse'); * s("triangle").am("2").amshape("").amdepth(.5) * */ -export const { am } = registerControl(['am', 'amdepth', 'amshape']); +export const { am } = registerControl(['am', 'amdepth', 'amskew', 'amphase']); /** * depth of amplitude modulation @@ -451,6 +451,27 @@ export const { am } = registerControl(['am', 'amdepth', 'amshape']); * */ export const { amdepth } = registerControl('amdepth'); +/** + * alter the shape of the modulation waveform + * + * @name amskew + * @param {number | Pattern} amount between 0 & 1, the shape of the waveform + * @example + * note("{f a c e}%16").am(4).amskew("<.5 0 1>") + * + */ +export const { amskew } = registerControl('amskew'); + +/** + * alter the phase of the modulation waveform + * + * @name amphase + * @param {number | Pattern} offset the offset in cycles of the modulation + * @example + * note("{f a c e}%16").am(4).amphase("<0 .25 .66>") + * + */ +export const { amphase } = registerControl('amskew'); /** * shape of amplitude modulation diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index b33e9cc36..41e31ce5c 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -34,13 +34,16 @@ export class Cyclist { try { const begin = this.lastEnd; - const cycle = this.now(); this.lastBegin = begin; const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; + this.lastEnd = end; + this.lastTick = phase; + const cycle = this.now(); + if (phase < t) { // avoid querying haps that are in the past anyway console.log(`skip query: too late`); diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 13c138dcc..25859278d 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -281,8 +281,10 @@ export const superdough = async (value, t, hapDuration, cps, cycle) => { } // destructure let { + am, amdepth = 1, - amshape = 'tri', + amskew = 0.5, + amphase = 0, s = 'triangle', bank, source, @@ -324,7 +326,7 @@ export const superdough = async (value, t, hapDuration, cps, cycle) => { phasercenter, // coarse, - am, + crush, shape, @@ -479,6 +481,8 @@ export const superdough = async (value, t, hapDuration, cps, cycle) => { getWorklet(ac, 'am-processor', { speed: am, depth: amdepth, + skew: amskew, + phaseoffset: amphase, // shape: amshape, cps, diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 107e9bf7f..a3a5eaf5c 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -1,7 +1,7 @@ // coarse, crush, and shape processors adapted from dktr0's webdirt: https://github.com/dktr0/WebDirt/blob/5ce3d698362c54d6e1b68acc47eb2955ac62c793/dist/AudioWorklets.js // LICENSE GNU General Public License v3.0 see https://github.com/dktr0/WebDirt/blob/main/LICENSE - -const clamp = (num, min, max) => Math.min(Math.max(num, min), max); +import { clamp, _mod } from './util.mjs'; +// const clamp = (num, min, max) => Math.min(Math.max(num, min), max); const blockSize = 128; // adjust waveshape to remove frequencies above nyquist to prevent aliasing // referenced from https://www.kvraudio.com/forum/viewtopic.php?t=375517 @@ -55,16 +55,17 @@ const waveshapes = { return 1 - phase; }, tri(phase, skew = 0.5) { + const x = 1 - skew; if (phase >= skew) { - return 1 - ((phase / (1 - skew)) % 1); + return 1 / x - phase / x; } - return (phase / skew) % 1; + return phase / skew; }, square(phase, skew = 0.5) { if (phase >= skew) { - return 1; + return 0; } - return 0; + return 1; }, }; @@ -74,8 +75,9 @@ class AMProcessor extends AudioWorkletProcessor { { name: 'cps', defaultValue: 0.5 }, { name: 'speed', defaultValue: 0.5 }, { name: 'cycle', defaultValue: 0 }, - // { name: 'shape', defaultValue: 'tri' }, + { name: 'skew', defaultValue: 0.5 }, { name: 'depth', defaultValue: 1 }, + { name: 'phaseoffset', defaultValue: 0 }, ]; } @@ -104,18 +106,20 @@ class AMProcessor extends AudioWorkletProcessor { const speed = parameters['speed'][0]; const cps = parameters['cps'][0]; const cycle = parameters['cycle'][0]; - const depth = clamp(parameters['depth'][0], 0, 1); + const depth = parameters['depth'][0]; + const skew = parameters['skew'][0]; + const phaseoffset = parameters['phaseoffset'][0]; const blockSize = 128; const frequency = speed * cps; if (this.phase == null) { const secondsPassed = cycle / cps; - this.phase = Math.max(0, (secondsPassed * frequency) % 1); + this.phase = _mod(secondsPassed * frequency + phaseoffset, 1); } const dt = frequency / sampleRate; for (let n = 0; n < blockSize; n++) { for (let i = 0; i < input.length; i++) { - const modval = waveshapes.tri(this.phase, 0.99) * depth + (1 - depth); + const modval = clamp(waveshapes.tri(this.phase, skew) * depth + (1 - depth), 0, 1); output[i][n] = input[i][n] * modval; } From 6a33032da75daac665357ad460f3066e7a1b92a9 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 9 May 2024 23:26:04 -0400 Subject: [PATCH 08/75] formatting --- packages/core/controls.mjs | 2 +- packages/superdough/worklets.mjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 46523588a..b8cdb6ed7 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -471,7 +471,7 @@ export const { amskew } = registerControl('amskew'); * note("{f a c e}%16").am(4).amphase("<0 .25 .66>") * */ -export const { amphase } = registerControl('amskew'); +export const { amphase } = registerControl('amphase'); /** * shape of amplitude modulation diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index a3a5eaf5c..3fa89ca63 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -109,7 +109,7 @@ class AMProcessor extends AudioWorkletProcessor { const depth = parameters['depth'][0]; const skew = parameters['skew'][0]; const phaseoffset = parameters['phaseoffset'][0]; - const blockSize = 128; + const frequency = speed * cps; if (this.phase == null) { const secondsPassed = cycle / cps; From cba96049a3de9eb1baa9982bf5873831870b4595 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 10 May 2024 00:03:52 -0400 Subject: [PATCH 09/75] fixed duplicate variable --- packages/superdough/worklets.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 99eca92b5..3fa89ca63 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -130,7 +130,6 @@ class AMProcessor extends AudioWorkletProcessor { } registerProcessor('am-processor', AMProcessor); -const blockSize = 128; class CoarseProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [{ name: 'coarse', defaultValue: 1 }]; From 19df19375d146f037107358d5f5222f0217d6747 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 13 May 2024 10:33:18 -0400 Subject: [PATCH 10/75] kinda fixed it, need to find cause of incorrect cps calculation --- packages/core/cyclist.mjs | 17 +++++++++-------- packages/core/neocyclist.mjs | 3 --- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 41e31ce5c..046b6ff7d 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -8,7 +8,7 @@ import createClock from './zyklus.mjs'; import { logger } from './logger.mjs'; export class Cyclist { - constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1, setInterval, clearInterval }) { + constructor({ interval = 0.05, onTrigger, onToggle, onError, getTime, latency = 0.1, setInterval, clearInterval }) { this.started = false; this.cps = 0.5; this.num_ticks_since_cps_change = 0; @@ -35,14 +35,11 @@ export class Cyclist { try { const begin = this.lastEnd; - this.lastBegin = begin; const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; - - this.lastEnd = end; - - this.lastTick = phase; - - const cycle = this.now(); + let cycle = this.now(); + //magic number that fixes cycle calcuation, cycle is probably not being calculated correctly + const modifier = this.cps * -interval; + cycle = cycle + modifier; if (phase < t) { // avoid querying haps that are in the past anyway @@ -60,10 +57,14 @@ export class Cyclist { const duration = hap.duration / this.cps; // the following line is dumb and only here for backwards compatibility // see https://github.com/tidalcycles/strudel/pull/1004 + const deadline = targetTime - phase; onTrigger?.(hap, deadline, duration, this.cps, targetTime, cycle); } }); + this.lastBegin = begin; + this.lastEnd = end; + this.lastTick = phase; } catch (e) { logger(`[cyclist] error: ${e.message}`); onError?.(e); diff --git a/packages/core/neocyclist.mjs b/packages/core/neocyclist.mjs index 8f9b269e0..6a92050c5 100644 --- a/packages/core/neocyclist.mjs +++ b/packages/core/neocyclist.mjs @@ -10,11 +10,8 @@ export class NeoCyclist { constructor({ onTrigger, onToggle, getTime }) { this.started = false; this.cps = 0.5; - this.lastTick = 0; // absolute time when last tick (clock callback) happened this.getTime = getTime; // get absolute time this.time_at_last_tick_message = 0; - - this.num_cycles_at_cps_change = 0; this.onToggle = onToggle; this.latency = 0.1; // fixed trigger time offset this.cycle = 0; From e024ae2142797ea8a31aa4dea229003514640b5b Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 13 May 2024 20:42:59 -0400 Subject: [PATCH 11/75] almost figured it out --- packages/core/cyclist.mjs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 046b6ff7d..75f92090c 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -36,11 +36,13 @@ export class Cyclist { const begin = this.lastEnd; const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; - let cycle = this.now(); + let cycle2 = this.now(); //magic number that fixes cycle calcuation, cycle is probably not being calculated correctly const modifier = this.cps * -interval; - cycle = cycle + modifier; - + cycle2 = cycle2 + modifier; + // let cycle = begin + (phase - t) * this.cps; + // cycle = begin t + // console.log({ phase, t, cycle2 }); if (phase < t) { // avoid querying haps that are in the past anyway console.log(`skip query: too late`); @@ -49,7 +51,9 @@ export class Cyclist { // query the pattern for events const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); - + let mod2 = t - phase - this.latency; + mod2 = mod2 * this.cps; + console.log(this.clock.duration); haps.forEach((hap) => { if (hap.hasOnset()) { const targetTime = @@ -59,9 +63,14 @@ export class Cyclist { // see https://github.com/tidalcycles/strudel/pull/1004 const deadline = targetTime - phase; + // console.log(); + let cycle = hap.whole.begin.valueOf() + mod2; + + // console.log({ cycle, cycle2, mod2 }); onTrigger?.(hap, deadline, duration, this.cps, targetTime, cycle); } }); + this.lastBegin = begin; this.lastEnd = end; this.lastTick = phase; From c6c5a3035c90f4a19e5cbd747bae5ccbff77806c Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 13 May 2024 21:06:24 -0400 Subject: [PATCH 12/75] fixed non synced clock --- packages/core/controls.mjs | 4 ++-- packages/core/cyclist.mjs | 23 +++++------------------ 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index b8cdb6ed7..06e808be4 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -434,12 +434,13 @@ export const { coarse } = registerControl('coarse'); * modulate the amplitude of a sound with a continuous waveform * * @name am + * @synonyms tremelo * @param {number | Pattern} speed modulation speed in cycles * @example * s("triangle").am("2").amshape("").amdepth(.5) * */ -export const { am } = registerControl(['am', 'amdepth', 'amskew', 'amphase']); +export const { am, tremolo } = registerControl(['am', 'amdepth', 'amskew', 'amphase'], 'tremolo'); /** * depth of amplitude modulation @@ -1582,7 +1583,6 @@ export const { zmod } = registerControl('zmod'); // like crush but scaled differently export const { zcrush } = registerControl('zcrush'); export const { zdelay } = registerControl('zdelay'); -export const { tremolo } = registerControl('tremolo'); export const { zzfx } = registerControl('zzfx'); /** diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 75f92090c..5e2ee378a 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -34,15 +34,7 @@ export class Cyclist { try { const begin = this.lastEnd; - const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; - let cycle2 = this.now(); - //magic number that fixes cycle calcuation, cycle is probably not being calculated correctly - const modifier = this.cps * -interval; - cycle2 = cycle2 + modifier; - // let cycle = begin + (phase - t) * this.cps; - // cycle = begin t - // console.log({ phase, t, cycle2 }); if (phase < t) { // avoid querying haps that are in the past anyway console.log(`skip query: too late`); @@ -51,23 +43,18 @@ export class Cyclist { // query the pattern for events const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); - let mod2 = t - phase - this.latency; - mod2 = mod2 * this.cps; - console.log(this.clock.duration); + const cycle_gap = (t - phase - this.latency) * this.cps; + haps.forEach((hap) => { if (hap.hasOnset()) { const targetTime = (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps + this.seconds_at_cps_change + latency; const duration = hap.duration / this.cps; + const cycle = hap.whole.begin.valueOf() + cycle_gap; // the following line is dumb and only here for backwards compatibility // see https://github.com/tidalcycles/strudel/pull/1004 - - const deadline = targetTime - phase; - // console.log(); - let cycle = hap.whole.begin.valueOf() + mod2; - - // console.log({ cycle, cycle2, mod2 }); - onTrigger?.(hap, deadline, duration, this.cps, targetTime, cycle); + const _deadline = targetTime - phase; + onTrigger?.(hap, _deadline, duration, this.cps, targetTime, cycle); } }); From 4ba9d47bfffe26246c2836e816bfaf6006f9060a Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 13 May 2024 21:10:43 -0400 Subject: [PATCH 13/75] fix test --- packages/superdough/worklets.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 3fa89ca63..81a9c9086 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -115,12 +115,11 @@ class AMProcessor extends AudioWorkletProcessor { const secondsPassed = cycle / cps; this.phase = _mod(secondsPassed * frequency + phaseoffset, 1); } - + // eslint-disable-next-line no-undef const dt = frequency / sampleRate; for (let n = 0; n < blockSize; n++) { for (let i = 0; i < input.length; i++) { const modval = clamp(waveshapes.tri(this.phase, skew) * depth + (1 - depth), 0, 1); - output[i][n] = input[i][n] * modval; } this.incrementPhase(dt); From d5ab6b7211201ac0437ec1d9ace20a48de9d3015 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 13 May 2024 22:38:20 -0400 Subject: [PATCH 14/75] fix tests --- test/__snapshots__/examples.test.mjs.snap | 375 +++++++++++++--------- 1 file changed, 225 insertions(+), 150 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 30db05a20..4c98231e5 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -779,6 +779,24 @@ exports[`runs examples > example "always" example index 0 1`] = ` ] `; +exports[`runs examples > example "am" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | s:triangle am:2 amshape:tri amdepth:0.5 ]", + "[ 1/1 → 2/1 | s:triangle am:2 amshape:saw amdepth:0.5 ]", + "[ 2/1 → 3/1 | s:triangle am:2 amshape:ramp amdepth:0.5 ]", + "[ 3/1 → 4/1 | s:triangle am:2 amshape:square amdepth:0.5 ]", +] +`; + +exports[`runs examples > example "amdepth" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | s:triangle am:1 amdepth:1 ]", + "[ 1/1 → 2/1 | s:triangle am:1 amdepth:1 ]", + "[ 2/1 → 3/1 | s:triangle am:1 amdepth:1 ]", + "[ 3/1 → 4/1 | s:triangle am:1 amdepth:1 ]", +] +`; + exports[`runs examples > example "amp" example index 0 1`] = ` [ "[ (0/1 → 1/12) ⇝ 1/8 | s:bd amp:0.1 ]", @@ -840,6 +858,213 @@ exports[`runs examples > example "amp" example index 0 1`] = ` ] `; +exports[`runs examples > example "amphase" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:f am:4 amphase:0 ]", + "[ 1/16 → 1/8 | note:a am:4 amphase:0 ]", + "[ 1/8 → 3/16 | note:c am:4 amphase:0 ]", + "[ 3/16 → 1/4 | note:e am:4 amphase:0 ]", + "[ 1/4 → 5/16 | note:f am:4 amphase:0 ]", + "[ 5/16 → 3/8 | note:a am:4 amphase:0 ]", + "[ 3/8 → 7/16 | note:c am:4 amphase:0 ]", + "[ 7/16 → 1/2 | note:e am:4 amphase:0 ]", + "[ 1/2 → 9/16 | note:f am:4 amphase:0 ]", + "[ 9/16 → 5/8 | note:a am:4 amphase:0 ]", + "[ 5/8 → 11/16 | note:c am:4 amphase:0 ]", + "[ 11/16 → 3/4 | note:e am:4 amphase:0 ]", + "[ 3/4 → 13/16 | note:f am:4 amphase:0 ]", + "[ 13/16 → 7/8 | note:a am:4 amphase:0 ]", + "[ 7/8 → 15/16 | note:c am:4 amphase:0 ]", + "[ 15/16 → 1/1 | note:e am:4 amphase:0 ]", + "[ 1/1 → 17/16 | note:f am:4 amphase:0.25 ]", + "[ 17/16 → 9/8 | note:a am:4 amphase:0.25 ]", + "[ 9/8 → 19/16 | note:c am:4 amphase:0.25 ]", + "[ 19/16 → 5/4 | note:e am:4 amphase:0.25 ]", + "[ 5/4 → 21/16 | note:f am:4 amphase:0.25 ]", + "[ 21/16 → 11/8 | note:a am:4 amphase:0.25 ]", + "[ 11/8 → 23/16 | note:c am:4 amphase:0.25 ]", + "[ 23/16 → 3/2 | note:e am:4 amphase:0.25 ]", + "[ 3/2 → 25/16 | note:f am:4 amphase:0.25 ]", + "[ 25/16 → 13/8 | note:a am:4 amphase:0.25 ]", + "[ 13/8 → 27/16 | note:c am:4 amphase:0.25 ]", + "[ 27/16 → 7/4 | note:e am:4 amphase:0.25 ]", + "[ 7/4 → 29/16 | note:f am:4 amphase:0.25 ]", + "[ 29/16 → 15/8 | note:a am:4 amphase:0.25 ]", + "[ 15/8 → 31/16 | note:c am:4 amphase:0.25 ]", + "[ 31/16 → 2/1 | note:e am:4 amphase:0.25 ]", + "[ 2/1 → 33/16 | note:f am:4 amphase:0.66 ]", + "[ 33/16 → 17/8 | note:a am:4 amphase:0.66 ]", + "[ 17/8 → 35/16 | note:c am:4 amphase:0.66 ]", + "[ 35/16 → 9/4 | note:e am:4 amphase:0.66 ]", + "[ 9/4 → 37/16 | note:f am:4 amphase:0.66 ]", + "[ 37/16 → 19/8 | note:a am:4 amphase:0.66 ]", + "[ 19/8 → 39/16 | note:c am:4 amphase:0.66 ]", + "[ 39/16 → 5/2 | note:e am:4 amphase:0.66 ]", + "[ 5/2 → 41/16 | note:f am:4 amphase:0.66 ]", + "[ 41/16 → 21/8 | note:a am:4 amphase:0.66 ]", + "[ 21/8 → 43/16 | note:c am:4 amphase:0.66 ]", + "[ 43/16 → 11/4 | note:e am:4 amphase:0.66 ]", + "[ 11/4 → 45/16 | note:f am:4 amphase:0.66 ]", + "[ 45/16 → 23/8 | note:a am:4 amphase:0.66 ]", + "[ 23/8 → 47/16 | note:c am:4 amphase:0.66 ]", + "[ 47/16 → 3/1 | note:e am:4 amphase:0.66 ]", + "[ 3/1 → 49/16 | note:f am:4 amphase:0 ]", + "[ 49/16 → 25/8 | note:a am:4 amphase:0 ]", + "[ 25/8 → 51/16 | note:c am:4 amphase:0 ]", + "[ 51/16 → 13/4 | note:e am:4 amphase:0 ]", + "[ 13/4 → 53/16 | note:f am:4 amphase:0 ]", + "[ 53/16 → 27/8 | note:a am:4 amphase:0 ]", + "[ 27/8 → 55/16 | note:c am:4 amphase:0 ]", + "[ 55/16 → 7/2 | note:e am:4 amphase:0 ]", + "[ 7/2 → 57/16 | note:f am:4 amphase:0 ]", + "[ 57/16 → 29/8 | note:a am:4 amphase:0 ]", + "[ 29/8 → 59/16 | note:c am:4 amphase:0 ]", + "[ 59/16 → 15/4 | note:e am:4 amphase:0 ]", + "[ 15/4 → 61/16 | note:f am:4 amphase:0 ]", + "[ 61/16 → 31/8 | note:a am:4 amphase:0 ]", + "[ 31/8 → 63/16 | note:c am:4 amphase:0 ]", + "[ 63/16 → 4/1 | note:e am:4 amphase:0 ]", +] +`; + +exports[`runs examples > example "amshape" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 1/16 → 1/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 1/8 → 3/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 3/16 → 1/4 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 1/4 → 5/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 5/16 → 3/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 3/8 → 7/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 7/16 → 1/2 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 1/2 → 9/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 9/16 → 5/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 5/8 → 11/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 11/16 → 3/4 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 3/4 → 13/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 13/16 → 7/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 7/8 → 15/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 15/16 → 1/1 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 1/1 → 17/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 17/16 → 9/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 9/8 → 19/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 19/16 → 5/4 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 5/4 → 21/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 21/16 → 11/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 11/8 → 23/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 23/16 → 3/2 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 3/2 → 25/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 25/16 → 13/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 13/8 → 27/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 27/16 → 7/4 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 7/4 → 29/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 29/16 → 15/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 15/8 → 31/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 31/16 → 2/1 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 2/1 → 33/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 33/16 → 17/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 17/8 → 35/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 35/16 → 9/4 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 9/4 → 37/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 37/16 → 19/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 19/8 → 39/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 39/16 → 5/2 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 5/2 → 41/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 41/16 → 21/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 21/8 → 43/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 43/16 → 11/4 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 11/4 → 45/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 45/16 → 23/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 23/8 → 47/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 47/16 → 3/1 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 3/1 → 49/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 49/16 → 25/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 25/8 → 51/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 51/16 → 13/4 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 13/4 → 53/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 53/16 → 27/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 27/8 → 55/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 55/16 → 7/2 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 7/2 → 57/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 57/16 → 29/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 29/8 → 59/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 59/16 → 15/4 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 15/4 → 61/16 | note:f am:4 amshape:ramp s:sawtooth ]", + "[ 61/16 → 31/8 | note:g am:4 amshape:ramp s:sawtooth ]", + "[ 31/8 → 63/16 | note:c am:4 amshape:ramp s:sawtooth ]", + "[ 63/16 → 4/1 | note:d am:4 amshape:ramp s:sawtooth ]", +] +`; + +exports[`runs examples > example "amskew" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:f am:4 amskew:0.5 ]", + "[ 1/16 → 1/8 | note:a am:4 amskew:0.5 ]", + "[ 1/8 → 3/16 | note:c am:4 amskew:0.5 ]", + "[ 3/16 → 1/4 | note:e am:4 amskew:0.5 ]", + "[ 1/4 → 5/16 | note:f am:4 amskew:0.5 ]", + "[ 5/16 → 3/8 | note:a am:4 amskew:0.5 ]", + "[ 3/8 → 7/16 | note:c am:4 amskew:0.5 ]", + "[ 7/16 → 1/2 | note:e am:4 amskew:0.5 ]", + "[ 1/2 → 9/16 | note:f am:4 amskew:0.5 ]", + "[ 9/16 → 5/8 | note:a am:4 amskew:0.5 ]", + "[ 5/8 → 11/16 | note:c am:4 amskew:0.5 ]", + "[ 11/16 → 3/4 | note:e am:4 amskew:0.5 ]", + "[ 3/4 → 13/16 | note:f am:4 amskew:0.5 ]", + "[ 13/16 → 7/8 | note:a am:4 amskew:0.5 ]", + "[ 7/8 → 15/16 | note:c am:4 amskew:0.5 ]", + "[ 15/16 → 1/1 | note:e am:4 amskew:0.5 ]", + "[ 1/1 → 17/16 | note:f am:4 amskew:0 ]", + "[ 17/16 → 9/8 | note:a am:4 amskew:0 ]", + "[ 9/8 → 19/16 | note:c am:4 amskew:0 ]", + "[ 19/16 → 5/4 | note:e am:4 amskew:0 ]", + "[ 5/4 → 21/16 | note:f am:4 amskew:0 ]", + "[ 21/16 → 11/8 | note:a am:4 amskew:0 ]", + "[ 11/8 → 23/16 | note:c am:4 amskew:0 ]", + "[ 23/16 → 3/2 | note:e am:4 amskew:0 ]", + "[ 3/2 → 25/16 | note:f am:4 amskew:0 ]", + "[ 25/16 → 13/8 | note:a am:4 amskew:0 ]", + "[ 13/8 → 27/16 | note:c am:4 amskew:0 ]", + "[ 27/16 → 7/4 | note:e am:4 amskew:0 ]", + "[ 7/4 → 29/16 | note:f am:4 amskew:0 ]", + "[ 29/16 → 15/8 | note:a am:4 amskew:0 ]", + "[ 15/8 → 31/16 | note:c am:4 amskew:0 ]", + "[ 31/16 → 2/1 | note:e am:4 amskew:0 ]", + "[ 2/1 → 33/16 | note:f am:4 amskew:1 ]", + "[ 33/16 → 17/8 | note:a am:4 amskew:1 ]", + "[ 17/8 → 35/16 | note:c am:4 amskew:1 ]", + "[ 35/16 → 9/4 | note:e am:4 amskew:1 ]", + "[ 9/4 → 37/16 | note:f am:4 amskew:1 ]", + "[ 37/16 → 19/8 | note:a am:4 amskew:1 ]", + "[ 19/8 → 39/16 | note:c am:4 amskew:1 ]", + "[ 39/16 → 5/2 | note:e am:4 amskew:1 ]", + "[ 5/2 → 41/16 | note:f am:4 amskew:1 ]", + "[ 41/16 → 21/8 | note:a am:4 amskew:1 ]", + "[ 21/8 → 43/16 | note:c am:4 amskew:1 ]", + "[ 43/16 → 11/4 | note:e am:4 amskew:1 ]", + "[ 11/4 → 45/16 | note:f am:4 amskew:1 ]", + "[ 45/16 → 23/8 | note:a am:4 amskew:1 ]", + "[ 23/8 → 47/16 | note:c am:4 amskew:1 ]", + "[ 47/16 → 3/1 | note:e am:4 amskew:1 ]", + "[ 3/1 → 49/16 | note:f am:4 amskew:0.5 ]", + "[ 49/16 → 25/8 | note:a am:4 amskew:0.5 ]", + "[ 25/8 → 51/16 | note:c am:4 amskew:0.5 ]", + "[ 51/16 → 13/4 | note:e am:4 amskew:0.5 ]", + "[ 13/4 → 53/16 | note:f am:4 amskew:0.5 ]", + "[ 53/16 → 27/8 | note:a am:4 amskew:0.5 ]", + "[ 27/8 → 55/16 | note:c am:4 amskew:0.5 ]", + "[ 55/16 → 7/2 | note:e am:4 amskew:0.5 ]", + "[ 7/2 → 57/16 | note:f am:4 amskew:0.5 ]", + "[ 57/16 → 29/8 | note:a am:4 amskew:0.5 ]", + "[ 29/8 → 59/16 | note:c am:4 amskew:0.5 ]", + "[ 59/16 → 15/4 | note:e am:4 amskew:0.5 ]", + "[ 15/4 → 61/16 | note:f am:4 amskew:0.5 ]", + "[ 61/16 → 31/8 | note:a am:4 amskew:0.5 ]", + "[ 31/8 → 63/16 | note:c am:4 amskew:0.5 ]", + "[ 63/16 → 4/1 | note:e am:4 amskew:0.5 ]", +] +`; + exports[`runs examples > example "apply" example index 0 1`] = ` [ "[ 0/1 → 1/1 | note:C3 ]", @@ -5035,72 +5260,6 @@ exports[`runs examples > example "ply" example index 0 1`] = ` ] `; -exports[`runs examples > example "polymeter" example index 0 1`] = ` -[ - "[ 0/1 → 1/3 | c ]", - "[ 0/1 → 1/3 | c2 ]", - "[ 1/3 → 2/3 | eb ]", - "[ 1/3 → 2/3 | g2 ]", - "[ 2/3 → 1/1 | g ]", - "[ 2/3 → 1/1 | c2 ]", - "[ 1/1 → 4/3 | c ]", - "[ 1/1 → 4/3 | g2 ]", - "[ 4/3 → 5/3 | eb ]", - "[ 4/3 → 5/3 | c2 ]", - "[ 5/3 → 2/1 | g ]", - "[ 5/3 → 2/1 | g2 ]", - "[ 2/1 → 7/3 | c ]", - "[ 2/1 → 7/3 | c2 ]", - "[ 7/3 → 8/3 | eb ]", - "[ 7/3 → 8/3 | g2 ]", - "[ 8/3 → 3/1 | g ]", - "[ 8/3 → 3/1 | c2 ]", - "[ 3/1 → 10/3 | c ]", - "[ 3/1 → 10/3 | g2 ]", - "[ 10/3 → 11/3 | eb ]", - "[ 10/3 → 11/3 | c2 ]", - "[ 11/3 → 4/1 | g ]", - "[ 11/3 → 4/1 | g2 ]", -] -`; - -exports[`runs examples > example "polymeterSteps" example index 0 1`] = ` -[ - "[ 0/1 → 1/4 | c ]", - "[ 0/1 → 1/4 | e ]", - "[ 1/4 → 1/2 | d ]", - "[ 1/4 → 1/2 | f ]", - "[ 1/2 → 3/4 | c ]", - "[ 1/2 → 3/4 | g ]", - "[ 3/4 → 1/1 | d ]", - "[ 3/4 → 1/1 | e ]", - "[ 1/1 → 5/4 | c ]", - "[ 1/1 → 5/4 | f ]", - "[ 5/4 → 3/2 | d ]", - "[ 5/4 → 3/2 | g ]", - "[ 3/2 → 7/4 | c ]", - "[ 3/2 → 7/4 | e ]", - "[ 7/4 → 2/1 | d ]", - "[ 7/4 → 2/1 | f ]", - "[ 2/1 → 9/4 | c ]", - "[ 2/1 → 9/4 | g ]", - "[ 9/4 → 5/2 | d ]", - "[ 9/4 → 5/2 | e ]", - "[ 5/2 → 11/4 | c ]", - "[ 5/2 → 11/4 | f ]", - "[ 11/4 → 3/1 | d ]", - "[ 11/4 → 3/1 | g ]", - "[ 3/1 → 13/4 | c ]", - "[ 3/1 → 13/4 | e ]", - "[ 13/4 → 7/2 | d ]", - "[ 13/4 → 7/2 | f ]", - "[ 7/2 → 15/4 | c ]", - "[ 7/2 → 15/4 | g ]", - "[ 15/4 → 4/1 | d ]", - "[ 15/4 → 4/1 | e ]", -] -`; - exports[`runs examples > example "postgain" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", @@ -7216,31 +7375,6 @@ exports[`runs examples > example "stack" example index 0 2`] = ` ] `; -exports[`runs examples > example "stepcat" example index 0 1`] = ` -[ - "[ 0/1 → 1/5 | s:bd ]", - "[ 1/5 → 2/5 | s:cp ]", - "[ 2/5 → 3/5 | s:bd ]", - "[ 3/5 → 4/5 | s:mt ]", - "[ 4/5 → 1/1 | s:bd ]", - "[ 1/1 → 6/5 | s:bd ]", - "[ 6/5 → 7/5 | s:cp ]", - "[ 7/5 → 8/5 | s:bd ]", - "[ 8/5 → 9/5 | s:mt ]", - "[ 9/5 → 2/1 | s:bd ]", - "[ 2/1 → 11/5 | s:bd ]", - "[ 11/5 → 12/5 | s:cp ]", - "[ 12/5 → 13/5 | s:bd ]", - "[ 13/5 → 14/5 | s:mt ]", - "[ 14/5 → 3/1 | s:bd ]", - "[ 3/1 → 16/5 | s:bd ]", - "[ 16/5 → 17/5 | s:cp ]", - "[ 17/5 → 18/5 | s:bd ]", - "[ 18/5 → 19/5 | s:mt ]", - "[ 19/5 → 4/1 | s:bd ]", -] -`; - exports[`runs examples > example "steps" example index 0 1`] = ` [ "[ 0/1 → 1/4 | s:bd ]", @@ -7554,65 +7688,6 @@ exports[`runs examples > example "swingBy" example index 0 1`] = ` ] `; -exports[`runs examples > example "timecat" example index 0 1`] = ` -[ - "[ 0/1 → 3/4 | note:e3 ]", - "[ 3/4 → 1/1 | note:g3 ]", - "[ 1/1 → 7/4 | note:e3 ]", - "[ 7/4 → 2/1 | note:g3 ]", - "[ 2/1 → 11/4 | note:e3 ]", - "[ 11/4 → 3/1 | note:g3 ]", - "[ 3/1 → 15/4 | note:e3 ]", - "[ 15/4 → 4/1 | note:g3 ]", -] -`; - -exports[`runs examples > example "timecat" example index 1 1`] = ` -[ - "[ 0/1 → 1/5 | s:bd ]", - "[ 1/5 → 2/5 | s:sd ]", - "[ 2/5 → 3/5 | s:cp ]", - "[ 3/5 → 4/5 | s:hh ]", - "[ 4/5 → 1/1 | s:hh ]", - "[ 1/1 → 6/5 | s:bd ]", - "[ 6/5 → 7/5 | s:sd ]", - "[ 7/5 → 8/5 | s:cp ]", - "[ 8/5 → 9/5 | s:hh ]", - "[ 9/5 → 2/1 | s:hh ]", - "[ 2/1 → 11/5 | s:bd ]", - "[ 11/5 → 12/5 | s:sd ]", - "[ 12/5 → 13/5 | s:cp ]", - "[ 13/5 → 14/5 | s:hh ]", - "[ 14/5 → 3/1 | s:hh ]", - "[ 3/1 → 16/5 | s:bd ]", - "[ 16/5 → 17/5 | s:sd ]", - "[ 17/5 → 18/5 | s:cp ]", - "[ 18/5 → 19/5 | s:hh ]", - "[ 19/5 → 4/1 | s:hh ]", -] -`; - -exports[`runs examples > example "toTactus" example index 0 1`] = ` -[ - "[ 0/1 → 1/4 | s:bd ]", - "[ 1/4 → 1/2 | s:sd ]", - "[ 1/2 → 3/4 | s:cp ]", - "[ 3/4 → 1/1 | s:bd ]", - "[ 1/1 → 5/4 | s:sd ]", - "[ 5/4 → 3/2 | s:cp ]", - "[ 3/2 → 7/4 | s:bd ]", - "[ 7/4 → 2/1 | s:sd ]", - "[ 2/1 → 9/4 | s:cp ]", - "[ 9/4 → 5/2 | s:bd ]", - "[ 5/2 → 11/4 | s:sd ]", - "[ 11/4 → 3/1 | s:cp ]", - "[ 3/1 → 13/4 | s:bd ]", - "[ 13/4 → 7/2 | s:sd ]", - "[ 7/2 → 15/4 | s:cp ]", - "[ 15/4 → 4/1 | s:bd ]", -] -`; - exports[`runs examples > example "transpose" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:C2 ]", From 4a181d5b335611833b1b38dbe039ae4f520224d1 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 13 May 2024 23:02:36 -0400 Subject: [PATCH 15/75] updated non synced clock to work more like synced clock --- packages/core/cyclist.mjs | 93 ++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 5e2ee378a..893a7619e 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -11,60 +11,53 @@ export class Cyclist { constructor({ interval = 0.05, onTrigger, onToggle, onError, getTime, latency = 0.1, setInterval, clearInterval }) { this.started = false; this.cps = 0.5; - this.num_ticks_since_cps_change = 0; - this.lastTick = 0; // absolute time when last tick (clock callback) happened - this.lastBegin = 0; // query begin of last tick - this.lastEnd = 0; // query end of last tick + this.time_at_last_tick_message = 0; + this.cycle = 0; this.getTime = getTime; // get absolute time this.num_cycles_at_cps_change = 0; this.seconds_at_cps_change; // clock phase when cps was changed + this.num_ticks_since_cps_change = 0; this.onToggle = onToggle; this.latency = latency; // fixed trigger time offset + + this.interval = interval; + this.clock = createClock( getTime, // called slightly before each cycle - (phase, duration, _, t) => { - if (this.num_ticks_since_cps_change === 0) { - this.num_cycles_at_cps_change = this.lastEnd; - this.seconds_at_cps_change = phase; - } - this.num_ticks_since_cps_change++; - const seconds_since_cps_change = this.num_ticks_since_cps_change * duration; - const num_cycles_since_cps_change = seconds_since_cps_change * this.cps; + (phase, duration, _, time) => { + const num_seconds_since_cps_change = this.num_ticks_since_cps_change * duration; + const tickdeadline = phase - time; + const lastTick = time + tickdeadline; + const num_cycles_since_cps_change = num_seconds_since_cps_change * this.cps; + const begin = this.num_cycles_at_cps_change + num_cycles_since_cps_change; + const secondsSinceLastTick = time - lastTick - duration; + const eventLength = duration * this.cps; + const end = begin + eventLength; + this.cycle = begin + secondsSinceLastTick * this.cps; + const num_seconds_at_cps_change = this.num_cycles_at_cps_change / this.cps; + const time_dif = time - (num_seconds_at_cps_change + num_seconds_since_cps_change) + tickdeadline; - try { - const begin = this.lastEnd; - const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; - if (phase < t) { - // avoid querying haps that are in the past anyway - console.log(`skip query: too late`); - return; + if (this.started === false) { + return; + } + + const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); + + //account for latency and tick duration when using cycle calculations for audio downstream + const cycle_gap = (this.latency - duration) * this.cps; + + haps.forEach((hap) => { + if (hap.hasOnset()) { + let targetTime = (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps; + targetTime = targetTime + num_seconds_at_cps_change + this.latency + time_dif; + const duration = hap.duration / this.cps; + onTrigger?.(hap, tickdeadline, duration, this.cps, targetTime, this.cycle - cycle_gap); } + }); - // query the pattern for events - const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); - const cycle_gap = (t - phase - this.latency) * this.cps; - - haps.forEach((hap) => { - if (hap.hasOnset()) { - const targetTime = - (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps + this.seconds_at_cps_change + latency; - const duration = hap.duration / this.cps; - const cycle = hap.whole.begin.valueOf() + cycle_gap; - // the following line is dumb and only here for backwards compatibility - // see https://github.com/tidalcycles/strudel/pull/1004 - const _deadline = targetTime - phase; - onTrigger?.(hap, _deadline, duration, this.cps, targetTime, cycle); - } - }); - - this.lastBegin = begin; - this.lastEnd = end; - this.lastTick = phase; - } catch (e) { - logger(`[cyclist] error: ${e.message}`); - onError?.(e); - } + this.time_at_last_tick_message = this.getTime(); + this.num_ticks_since_cps_change++; }, interval, // duration of each cycle 0.1, @@ -77,16 +70,24 @@ export class Cyclist { if (!this.started) { return 0; } - const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration; - return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency; + const gap = (this.getTime() - this.time_at_last_tick_message) * this.cps; + return this.cycle + gap; } + + setCycle = (cycle) => { + this.num_ticks_since_cps_change = 0; + this.num_cycles_at_cps_change = cycle; + }; setStarted(v) { this.started = v; + + this.setCycle(0); this.onToggle?.(v); } start() { this.num_ticks_since_cps_change = 0; this.num_cycles_at_cps_change = 0; + if (!this.pattern) { throw new Error('Scheduler: no pattern set! call .setPattern first.'); } @@ -115,6 +116,8 @@ export class Cyclist { if (this.cps === cps) { return; } + const num_seconds_since_cps_change = this.num_ticks_since_cps_change * this.interval; + this.num_cycles_at_cps_change = this.num_cycles_at_cps_change + num_seconds_since_cps_change * cps; this.cps = cps; this.num_ticks_since_cps_change = 0; } From 8ad48e1633ff5c26e1d29b7257c96f855f4764f0 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 13 May 2024 23:02:48 -0400 Subject: [PATCH 16/75] remove unesseccary time call --- packages/core/cyclist.mjs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 893a7619e..55dcb35b6 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -26,6 +26,9 @@ export class Cyclist { getTime, // called slightly before each cycle (phase, duration, _, time) => { + if (this.started === false) { + return; + } const num_seconds_since_cps_change = this.num_ticks_since_cps_change * duration; const tickdeadline = phase - time; const lastTick = time + tickdeadline; @@ -38,14 +41,9 @@ export class Cyclist { const num_seconds_at_cps_change = this.num_cycles_at_cps_change / this.cps; const time_dif = time - (num_seconds_at_cps_change + num_seconds_since_cps_change) + tickdeadline; - if (this.started === false) { - return; - } - - const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); - //account for latency and tick duration when using cycle calculations for audio downstream const cycle_gap = (this.latency - duration) * this.cps; + const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); haps.forEach((hap) => { if (hap.hasOnset()) { @@ -55,8 +53,7 @@ export class Cyclist { onTrigger?.(hap, tickdeadline, duration, this.cps, targetTime, this.cycle - cycle_gap); } }); - - this.time_at_last_tick_message = this.getTime(); + this.time_at_last_tick_message = time; this.num_ticks_since_cps_change++; }, interval, // duration of each cycle From bee8c812548e795b8ce04324b3929ec1afe52b89 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 13 May 2024 23:14:30 -0400 Subject: [PATCH 17/75] simplify targettime --- packages/core/cyclist.mjs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 55dcb35b6..a170ca3a5 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -38,17 +38,15 @@ export class Cyclist { const eventLength = duration * this.cps; const end = begin + eventLength; this.cycle = begin + secondsSinceLastTick * this.cps; - const num_seconds_at_cps_change = this.num_cycles_at_cps_change / this.cps; - const time_dif = time - (num_seconds_at_cps_change + num_seconds_since_cps_change) + tickdeadline; //account for latency and tick duration when using cycle calculations for audio downstream const cycle_gap = (this.latency - duration) * this.cps; - const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); + const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); haps.forEach((hap) => { if (hap.hasOnset()) { let targetTime = (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps; - targetTime = targetTime + num_seconds_at_cps_change + this.latency + time_dif; + targetTime = targetTime + this.latency + tickdeadline + time - num_seconds_since_cps_change; const duration = hap.duration / this.cps; onTrigger?.(hap, tickdeadline, duration, this.cps, targetTime, this.cycle - cycle_gap); } From 006ce9d1da2af36364d2acce6f4aada71a965766 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 2 May 2025 23:33:58 -0400 Subject: [PATCH 18/75] delaytime --- packages/superdough/superdough.mjs | 8 ++++---- packages/superdough/util.mjs | 4 ++++ packages/webaudio/webaudio.mjs | 5 +++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index a1dc30b7c..2a205e18d 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -7,7 +7,7 @@ This program is free software: you can redistribute it and/or modify it under th import './feedbackdelay.mjs'; import './reverb.mjs'; import './vowel.mjs'; -import { clamp, nanFallback, _mod } from './util.mjs'; +import { clamp, nanFallback, _mod, cycleToSeconds } from './util.mjs'; import workletsUrl from './worklets.mjs?audioworklet'; import { createFilter, gainNode, getCompressor, getWorklet } from './helpers.mjs'; import { map } from 'nanostores'; @@ -132,7 +132,7 @@ const defaultDefaultValues = { distortvol: 1, delay: 0, delayfeedback: 0.5, - delaytime: 0.25, + delaytime: 3 / 16, orbit: 1, i: 1, velocity: 1, @@ -429,7 +429,7 @@ export function resetGlobalEffects() { let activeSoundSources = new Map(); -export const superdough = async (value, t, hapDuration) => { +export const superdough = async (value, t, hapDuration, cps = 1) => { const ac = getAudioContext(); t = typeof t === 'string' && t.startsWith('=') ? Number(t.slice(1)) : ac.currentTime + t; let { stretch } = value; @@ -699,7 +699,7 @@ export const superdough = async (value, t, hapDuration) => { // delay let delaySend; if (delay > 0 && delaytime > 0 && delayfeedback > 0) { - const delyNode = getDelay(orbit, delaytime, delayfeedback, t); + const delyNode = getDelay(orbit, cycleToSeconds(delaytime, cps), delayfeedback, t); delaySend = effectSend(post, delyNode, delay); audioNodes.push(delaySend); } diff --git a/packages/superdough/util.mjs b/packages/superdough/util.mjs index 4e3c7e41e..ea63a16f0 100644 --- a/packages/superdough/util.mjs +++ b/packages/superdough/util.mjs @@ -68,3 +68,7 @@ export const _mod = (n, m) => ((n % m) + m) % m; export const getSoundIndex = (n, numSounds) => { return _mod(Math.round(nanFallback(n, 0)), numSounds); }; + +export function cycleToSeconds(cycle, cps) { + return cycle / cps; +} diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 44a683480..3ce4f91b3 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -17,8 +17,9 @@ const hap2value = (hap) => { export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps); // uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004 -export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => - superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration); +export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => { + return superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration,cps); +}; Pattern.prototype.webaudio = function () { return this.onTrigger(webaudioOutputTrigger); From 94257e81ee6c43053ffa739eff88c1454ba36604 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 2 May 2025 23:44:55 -0400 Subject: [PATCH 19/75] lint --- packages/webaudio/webaudio.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 3ce4f91b3..640449db0 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -18,7 +18,7 @@ const hap2value = (hap) => { export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps); // uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004 export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => { - return superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration,cps); + return superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration, cps); }; Pattern.prototype.webaudio = function () { From ae15bb72740000bd1ac563cf4a0f96d2e9c73d1f Mon Sep 17 00:00:00 2001 From: Alice Wyan <121136+wyan@users.noreply.github.com> Date: Mon, 9 Jun 2025 15:03:41 +0200 Subject: [PATCH 20/75] Add browser cache explanation in samples.mdx Several users (including me) have been bitten by this, so it's perhaps worth mentioning in the docs that the strudel.json file gets cached by the browser and doesn't reload properly unless forced. --- website/src/pages/learn/samples.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index 890de3aae..88a0a3b29 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -163,6 +163,12 @@ The last section could be written as: } ``` +Please note that browsers will often cache `strudel.json` on first load, and keep using the cached +version even if the orginal has been updated. If this bites you (for example while developing a new +sample pack), you can force the browser to download a new copy by i.e. changing capitalization of one +character in the URL, or by removing it from cache (deleting cache in browser Privacy settings, or from +the dev console if you're technically minded, or by using a cache deleting extension). + ## Github Shortcut Because loading samples from github is common, there is a shortcut: From 117cf7e18aa3e481ffde92964fc41df3439d83c7 Mon Sep 17 00:00:00 2001 From: Alice Wyan <121136+wyan@users.noreply.github.com> Date: Mon, 9 Jun 2025 19:45:21 +0200 Subject: [PATCH 21/75] Update samples.mdx --- website/src/pages/learn/samples.mdx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index 88a0a3b29..bc6627d29 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -166,8 +166,15 @@ The last section could be written as: Please note that browsers will often cache `strudel.json` on first load, and keep using the cached version even if the orginal has been updated. If this bites you (for example while developing a new sample pack), you can force the browser to download a new copy by i.e. changing capitalization of one -character in the URL, or by removing it from cache (deleting cache in browser Privacy settings, or from -the dev console if you're technically minded, or by using a cache deleting extension). +character in the URL, or adding a URL attribute, such as: +```javascript +samples('https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/strudel.json?version=2') +``` +that gets ignored by GitHub (but changes the URL, forcing the browser to reload every time we increase +the version number). + +It is also possible, of course, to just remove it from cache (deleting cache in browser Privacy settings, +or from the dev console if you're technically minded, or by using a cache deleting extension). ## Github Shortcut From 1b7fbecf50c8385497005cd8c8df36bc004f8f0e Mon Sep 17 00:00:00 2001 From: Alice Wyan <121136+wyan@users.noreply.github.com> Date: Tue, 10 Jun 2025 13:20:21 +0200 Subject: [PATCH 22/75] Fix style issue --- website/src/pages/learn/samples.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index bc6627d29..daaecd06c 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -167,9 +167,11 @@ Please note that browsers will often cache `strudel.json` on first load, and kee version even if the orginal has been updated. If this bites you (for example while developing a new sample pack), you can force the browser to download a new copy by i.e. changing capitalization of one character in the URL, or adding a URL attribute, such as: + ```javascript -samples('https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/strudel.json?version=2') +samples('https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/strudel.json?version=2'); ``` + that gets ignored by GitHub (but changes the URL, forcing the browser to reload every time we increase the version number). From fe9a91d1e4d9933d21f9aa028851a62f348ebe1e Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 24 Jun 2025 23:22:42 +0100 Subject: [PATCH 23/75] add unjoin, into and chunkinto --- packages/core/pattern.mjs | 41 ++++++++++++++ test/__snapshots__/examples.test.mjs.snap | 69 +++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index beefc5971..0ad1f77c9 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -869,6 +869,47 @@ export class Pattern { console.log(drawLine(this)); return this; } + + ////////////////////////////////////////////////////////////////////// + // methods relating to breaking patterns into subcycles + + // Breaks a pattern into a pattern of patterns, according to the structure of the given binary pattern. + unjoin(pieces, func = id) { + return pieces.withHap((hap) => + hap.withValue((v) => (v ? func(this.ribbon(hap.whole.begin, hap.whole.duration)) : this)), + ); + } + + /** + * Breaks a pattern into pieces according to the structure of a given pattern. + * True values in the given pattern cause the corresponding subcycle of the + * source pattern to be looped, and for an (optional) given function to be + * applied. False values result in the corresponding part of the source pattern + * to be played unchanged. + * @name into + * @memberof Pattern + * @example + * sound("bd sd ht lt").into("1 0", hurry(2)) + */ + into(pieces, func) { + return this.unjoin(pieces, func).innerJoin(); + } + + /** + * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. + * @name chunkInto + * @synonym chunkinto + * @memberof Pattern + * @example + * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) + * .bank("tr909") + */ + chunkInto(n, func) { + return this.into(fastcat(true, ...Array(n - 1).fill(false)).iterback(4), func); + } + chunkinto(n, func) { + return this.chunkInto(n, func); + } } ////////////////////////////////////////////////////////////////////// diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index f3607cb48..d0a622940 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1889,6 +1889,46 @@ exports[`runs examples > example "chunkBack" example index 0 1`] = ` ] `; +exports[`runs examples > example "chunkInto" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | s:bd speed:2 bank:tr909 ]", + "[ 1/16 → 1/8 | s:sd speed:2 bank:tr909 ]", + "[ 1/8 → 3/16 | s:bd speed:2 bank:tr909 ]", + "[ 3/16 → 1/4 | s:sd speed:2 bank:tr909 ]", + "[ 1/4 → 3/8 | s:ht bank:tr909 ]", + "[ 3/8 → 1/2 | s:lt bank:tr909 ]", + "[ 1/2 → 5/8 | s:bd bank:tr909 ]", + "[ 3/4 → 7/8 | s:cp bank:tr909 ]", + "[ 7/8 → 1/1 | s:lt bank:tr909 ]", + "[ 1/1 → 9/8 | s:bd bank:tr909 ]", + "[ 9/8 → 5/4 | s:sd bank:tr909 ]", + "[ 5/4 → 21/16 | s:ht speed:2 bank:tr909 ]", + "[ 21/16 → 11/8 | s:lt speed:2 bank:tr909 ]", + "[ 11/8 → 23/16 | s:ht speed:2 bank:tr909 ]", + "[ 23/16 → 3/2 | s:lt speed:2 bank:tr909 ]", + "[ 3/2 → 13/8 | s:bd bank:tr909 ]", + "[ 7/4 → 15/8 | s:cp bank:tr909 ]", + "[ 15/8 → 2/1 | s:lt bank:tr909 ]", + "[ 2/1 → 17/8 | s:bd bank:tr909 ]", + "[ 17/8 → 9/4 | s:sd bank:tr909 ]", + "[ 9/4 → 19/8 | s:ht bank:tr909 ]", + "[ 19/8 → 5/2 | s:lt bank:tr909 ]", + "[ 5/2 → 41/16 | s:bd speed:2 bank:tr909 ]", + "[ 21/8 → 43/16 | s:bd speed:2 bank:tr909 ]", + "[ 11/4 → 23/8 | s:cp bank:tr909 ]", + "[ 23/8 → 3/1 | s:lt bank:tr909 ]", + "[ 3/1 → 25/8 | s:bd bank:tr909 ]", + "[ 25/8 → 13/4 | s:sd bank:tr909 ]", + "[ 13/4 → 27/8 | s:ht bank:tr909 ]", + "[ 27/8 → 7/2 | s:lt bank:tr909 ]", + "[ 7/2 → 29/8 | s:bd bank:tr909 ]", + "[ 15/4 → 61/16 | s:cp speed:2 bank:tr909 ]", + "[ 61/16 → 31/8 | s:lt speed:2 bank:tr909 ]", + "[ 31/8 → 63/16 | s:cp speed:2 bank:tr909 ]", + "[ 63/16 → 4/1 | s:lt speed:2 bank:tr909 ]", +] +`; + exports[`runs examples > example "clip" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c s:piano clip:0.5 ]", @@ -4391,6 +4431,35 @@ exports[`runs examples > example "inside" example index 0 1`] = ` ] `; +exports[`runs examples > example "into" example index 0 1`] = ` +[ + "[ 0/1 → 1/8 | s:bd speed:2 ]", + "[ 1/8 → 1/4 | s:sd speed:2 ]", + "[ 1/4 → 3/8 | s:bd speed:2 ]", + "[ 3/8 → 1/2 | s:sd speed:2 ]", + "[ 1/2 → 3/4 | s:ht ]", + "[ 3/4 → 1/1 | s:lt ]", + "[ 1/1 → 9/8 | s:bd speed:2 ]", + "[ 9/8 → 5/4 | s:sd speed:2 ]", + "[ 5/4 → 11/8 | s:bd speed:2 ]", + "[ 11/8 → 3/2 | s:sd speed:2 ]", + "[ 3/2 → 7/4 | s:ht ]", + "[ 7/4 → 2/1 | s:lt ]", + "[ 2/1 → 17/8 | s:bd speed:2 ]", + "[ 17/8 → 9/4 | s:sd speed:2 ]", + "[ 9/4 → 19/8 | s:bd speed:2 ]", + "[ 19/8 → 5/2 | s:sd speed:2 ]", + "[ 5/2 → 11/4 | s:ht ]", + "[ 11/4 → 3/1 | s:lt ]", + "[ 3/1 → 25/8 | s:bd speed:2 ]", + "[ 25/8 → 13/4 | s:sd speed:2 ]", + "[ 13/4 → 27/8 | s:bd speed:2 ]", + "[ 27/8 → 7/2 | s:sd speed:2 ]", + "[ 7/2 → 15/4 | s:ht ]", + "[ 15/4 → 4/1 | s:lt ]", +] +`; + exports[`runs examples > example "invert" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:bd ]", From e19fd2444704b1767f415d68c897d3da0058eb6a Mon Sep 17 00:00:00 2001 From: Khalid Date: Wed, 25 Jun 2025 08:44:12 -0400 Subject: [PATCH 24/75] Case insensitive search in the reference tab --- website/src/repl/components/panel/Reference.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index ab925eb91..42a03a02e 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -21,7 +21,8 @@ export function Reference() { return true; } - return entry.name.includes(search) || (entry.synonyms?.some((s) => s.includes(search)) ?? false); + const lowCaseSearch = search.toLowerCase(); + return entry.name.toLowerCase().includes(lowCaseSearch) || (entry.synonyms?.some((s) => s.includes(lowCaseSearch)) ?? false); }); }, [search]); From 09d05abd707fefb77bdceb3f0d7dee35227e87ba Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 27 Jun 2025 15:54:39 -0400 Subject: [PATCH 25/75] delaysync --- packages/core/controls.mjs | 18 +++++++++++++++++- packages/superdough/superdough.mjs | 2 +- packages/webaudio/webaudio.mjs | 1 - 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 3a7faf081..c37960d41 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -985,15 +985,31 @@ export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', * */ export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', 'dt'); -/* // TODO: test + +/** + * Sets the time of the delay effect in cycles. + * + * @name delaysync + * @param {number | Pattern} cycles delay length in cycles + * @synonyms delayt, dt + * @example + * s("bd bd").delay(.25).delaysync("<1 2 3 5>".div(8)) + * + */ +export const { delaysync } = registerControl('delaysync'); + +/** * Specifies whether delaytime is calculated relative to cps. * * @name lock * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. + * @superdirtOnly * @example * s("sd").delay().lock(1).osc() * + * */ + export const { lock } = registerControl('lock'); /** * Set detune for stacked voices of supported oscillators diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index fc75276a8..14f46711b 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -548,7 +548,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5) => { compressorRelease, } = value; - delaytime = delaytime ?? cycleToSeconds(delaysync) + delaytime = delaytime ?? cycleToSeconds(delaysync, cps); const orbitChannels = mapChannelNumbers( multiChannelOrbits && orbit > 0 ? [orbit * 2 - 1, orbit * 2] : getDefaultValue('channels'), diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 67490cd23..91e28defd 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -21,7 +21,6 @@ export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => { return superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration, cps); }; - Pattern.prototype.webaudio = function () { return this.onTrigger(webaudioOutputTrigger); }; From 7a53f6c021b89d53664a222b0f46b0a17c58a4f4 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 27 Jun 2025 15:56:37 -0400 Subject: [PATCH 26/75] add test --- test/__snapshots__/examples.test.mjs.snap | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index f3607cb48..ed3b5f793 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -2505,6 +2505,19 @@ exports[`runs examples > example "delayfeedback" example index 0 1`] = ` ] `; +exports[`runs examples > example "delaysync" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd delay:0.25 delaysync:0.125 ]", + "[ 1/2 → 1/1 | s:bd delay:0.25 delaysync:0.125 ]", + "[ 1/1 → 3/2 | s:bd delay:0.25 delaysync:0.25 ]", + "[ 3/2 → 2/1 | s:bd delay:0.25 delaysync:0.25 ]", + "[ 2/1 → 5/2 | s:bd delay:0.25 delaysync:0.375 ]", + "[ 5/2 → 3/1 | s:bd delay:0.25 delaysync:0.375 ]", + "[ 3/1 → 7/2 | s:bd delay:0.25 delaysync:0.625 ]", + "[ 7/2 → 4/1 | s:bd delay:0.25 delaysync:0.625 ]", +] +`; + exports[`runs examples > example "delaytime" example index 0 1`] = ` [ "[ 0/1 → 1/2 | s:bd delay:0.25 delaytime:0.125 ]", @@ -5016,6 +5029,15 @@ exports[`runs examples > example "linger" example index 0 1`] = ` ] `; +exports[`runs examples > example "lock" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | delay:{s:sd} lock:1 ]", + "[ 1/1 → 2/1 | delay:{s:sd} lock:1 ]", + "[ 2/1 → 3/1 | delay:{s:sd} lock:1 ]", + "[ 3/1 → 4/1 | delay:{s:sd} lock:1 ]", +] +`; + exports[`runs examples > example "loop" example index 0 1`] = ` [ "[ 0/1 → 1/1 | s:casio loop:1 ]", From ff5b11f5edee691e5cb126f6a88d0fc4471bc4d0 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 27 Jun 2025 22:44:15 +0100 Subject: [PATCH 27/75] test + bugfix for chunkinto --- packages/core/pattern.mjs | 36 ++++++++++++++--------------- packages/core/test/pattern.test.mjs | 23 ++++++++++++++++++ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 10527a1a8..c8c744628 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -894,22 +894,6 @@ export class Pattern { into(pieces, func) { return this.unjoin(pieces, func).innerJoin(); } - - /** - * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. - * @name chunkInto - * @synonym chunkinto - * @memberof Pattern - * @example - * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) - * .bank("tr909") - */ - chunkInto(n, func) { - return this.into(fastcat(true, ...Array(n - 1).fill(false)).iterback(4), func); - } - chunkinto(n, func) { - return this.chunkInto(n, func); - } } ////////////////////////////////////////////////////////////////////// @@ -1858,9 +1842,9 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f const newWhole = !hap.whole ? undefined : new TimeSpan( - newPart.begin.sub(begin.sub(hap.whole.begin).div(factor)), - newPart.end.add(hap.whole.end.sub(end).div(factor)), - ); + newPart.begin.sub(begin.sub(hap.whole.begin).div(factor)), + newPart.end.add(hap.whole.end.sub(end).div(factor)), + ); return new Hap(newWhole, newPart, hap.value, hap.context); }; return pat.withQuerySpanMaybe(qf).withHap(ef).splitQueries(); @@ -2535,6 +2519,20 @@ export const { fastchunk, fastChunk } = register( true, ); +/** + * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. + * @name chunkInto + * @synonym chunkinto + * @memberof Pattern + * @example + * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) + * .bank("tr909") + */ +export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], + function (n, func, pat) { + return pat.into(fastcat(true, ...Array(n - 1).fill(false)).iterback(n), func); + }); + // TODO - redefine elsewhere in terms of mask export const bypass = register( 'bypass', diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 874d45132..8d26a990c 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -1271,4 +1271,27 @@ describe('Pattern', () => { ); }); }); + describe('unjoin', () => { + it('destructures a pattern into subcycles', () => { + sameFirst( + fastcat('a', 'b', 'c', 'd').unjoin(fastcat(true, fastcat(true, true))).fmap(fast(2)).join(), + fastcat('a', 'b', 'a', 'b', 'c', 'c', 'd', 'd') + ) + }) + }) + describe('into', () => { + it('applies a function to subcycles of a pattern', () => { + sameFirst( + fastcat('a', 'b', 'c', 'd').into(fastcat(fastcat('true', 'true'), 'true'), fast(2)), + fastcat('a', 'a', 'b', 'b', 'c', 'd', 'c', 'd') + ) + }) + }), + describe('chunkinto', () => { + it('chunks into subcycles', () => { + sameFirst(fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), + fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')) + ) + }) + }) }); From f44caf90966e34271f155f366721a64b060c1f97 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 27 Jun 2025 22:51:47 +0100 Subject: [PATCH 28/75] tests and tweaks, and add chunkBackInto --- packages/core/pattern.mjs | 16 +++++++++++++++- packages/core/test/pattern.test.mjs | 23 +++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index c8c744628..6d42c51fd 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2530,7 +2530,21 @@ export const { fastchunk, fastChunk } = register( */ export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], function (n, func, pat) { - return pat.into(fastcat(true, ...Array(n - 1).fill(false)).iterback(n), func); + return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iterback(n), func); + }); + +/** + * Like `chunkInto`, but moves backwards through the chunks. + * @name chunkBackInto + * @synonym chunkbackinto + * @memberof Pattern + * @example + * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) + * .bank("tr909") + */ +export const { chunkbackinto, chunkBackInto } = register(['chunkbackinto', 'chunkBackInto'], + function (n, func, pat) { + return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iter(n)._early(1), func); }); // TODO - redefine elsewhere in terms of mask diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 8d26a990c..4ab519a75 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -1278,7 +1278,7 @@ describe('Pattern', () => { fastcat('a', 'b', 'a', 'b', 'c', 'c', 'd', 'd') ) }) - }) + }); describe('into', () => { it('applies a function to subcycles of a pattern', () => { sameFirst( @@ -1286,12 +1286,19 @@ describe('Pattern', () => { fastcat('a', 'a', 'b', 'b', 'c', 'd', 'c', 'd') ) }) - }), - describe('chunkinto', () => { - it('chunks into subcycles', () => { - sameFirst(fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), - fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')) - ) - }) + }); + describe('chunkinto', () => { + it('chunks into subcycles', () => { + sameFirst(fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), + fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')) + ) }) + }); + describe('chunkbackinto', () => { + it('chunks into subcycles backwards', () => { + sameFirst(fastcat('a', 'b', 'c').chunkBackInto(3, fast(2)).fast(3), + fastcat('a', 'b', fastcat('c', 'c'), 'a', fastcat('b', 'b'), 'c', fastcat('a', 'a'), 'b', 'c') + ) + }) + }); }); From 27073d6c17264dd3218102bd72bcb96d6a0186d6 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 27 Jun 2025 22:56:19 +0100 Subject: [PATCH 29/75] format --- packages/core/pattern.mjs | 25 ++++++++++++--------- packages/core/test/pattern.test.mjs | 35 ++++++++++++++++------------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 6d42c51fd..cbaf8a8a2 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1842,9 +1842,9 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f const newWhole = !hap.whole ? undefined : new TimeSpan( - newPart.begin.sub(begin.sub(hap.whole.begin).div(factor)), - newPart.end.add(hap.whole.end.sub(end).div(factor)), - ); + newPart.begin.sub(begin.sub(hap.whole.begin).div(factor)), + newPart.end.add(hap.whole.end.sub(end).div(factor)), + ); return new Hap(newWhole, newPart, hap.value, hap.context); }; return pat.withQuerySpanMaybe(qf).withHap(ef).splitQueries(); @@ -2528,10 +2528,9 @@ export const { fastchunk, fastChunk } = register( * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) * .bank("tr909") */ -export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], - function (n, func, pat) { - return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iterback(n), func); - }); +export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], function (n, func, pat) { + return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iterback(n), func); +}); /** * Like `chunkInto`, but moves backwards through the chunks. @@ -2542,10 +2541,14 @@ export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) * .bank("tr909") */ -export const { chunkbackinto, chunkBackInto } = register(['chunkbackinto', 'chunkBackInto'], - function (n, func, pat) { - return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iter(n)._early(1), func); - }); +export const { chunkbackinto, chunkBackInto } = register(['chunkbackinto', 'chunkBackInto'], function (n, func, pat) { + return pat.into( + fastcat(true, ...Array(n - 1).fill(false)) + ._iter(n) + ._early(1), + func, + ); +}); // TODO - redefine elsewhere in terms of mask export const bypass = register( diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 4ab519a75..93c4168c9 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -1274,31 +1274,36 @@ describe('Pattern', () => { describe('unjoin', () => { it('destructures a pattern into subcycles', () => { sameFirst( - fastcat('a', 'b', 'c', 'd').unjoin(fastcat(true, fastcat(true, true))).fmap(fast(2)).join(), - fastcat('a', 'b', 'a', 'b', 'c', 'c', 'd', 'd') - ) - }) + fastcat('a', 'b', 'c', 'd') + .unjoin(fastcat(true, fastcat(true, true))) + .fmap(fast(2)) + .join(), + fastcat('a', 'b', 'a', 'b', 'c', 'c', 'd', 'd'), + ); + }); }); describe('into', () => { it('applies a function to subcycles of a pattern', () => { sameFirst( fastcat('a', 'b', 'c', 'd').into(fastcat(fastcat('true', 'true'), 'true'), fast(2)), - fastcat('a', 'a', 'b', 'b', 'c', 'd', 'c', 'd') - ) - }) + fastcat('a', 'a', 'b', 'b', 'c', 'd', 'c', 'd'), + ); + }); }); describe('chunkinto', () => { it('chunks into subcycles', () => { - sameFirst(fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), - fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')) - ) - }) + sameFirst( + fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), + fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')), + ); + }); }); describe('chunkbackinto', () => { it('chunks into subcycles backwards', () => { - sameFirst(fastcat('a', 'b', 'c').chunkBackInto(3, fast(2)).fast(3), - fastcat('a', 'b', fastcat('c', 'c'), 'a', fastcat('b', 'b'), 'c', fastcat('a', 'a'), 'b', 'c') - ) - }) + sameFirst( + fastcat('a', 'b', 'c').chunkBackInto(3, fast(2)).fast(3), + fastcat('a', 'b', fastcat('c', 'c'), 'a', fastcat('b', 'b'), 'c', fastcat('a', 'a'), 'b', 'c'), + ); + }); }); }); From df06248c54709e68d39d4f4ae607af4509742842 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 27 Jun 2025 22:59:01 +0100 Subject: [PATCH 30/75] snapshot --- test/__snapshots__/examples.test.mjs.snap | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index d0a622940..2d20cff4c 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1889,6 +1889,46 @@ exports[`runs examples > example "chunkBack" example index 0 1`] = ` ] `; +exports[`runs examples > example "chunkBackInto" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | s:bd speed:2 bank:tr909 ]", + "[ 1/16 → 1/8 | s:sd speed:2 bank:tr909 ]", + "[ 1/8 → 3/16 | s:bd speed:2 bank:tr909 ]", + "[ 3/16 → 1/4 | s:sd speed:2 bank:tr909 ]", + "[ 1/4 → 3/8 | s:ht bank:tr909 ]", + "[ 3/8 → 1/2 | s:lt bank:tr909 ]", + "[ 1/2 → 5/8 | s:bd bank:tr909 ]", + "[ 3/4 → 7/8 | s:cp bank:tr909 ]", + "[ 7/8 → 1/1 | s:lt bank:tr909 ]", + "[ 1/1 → 9/8 | s:bd bank:tr909 ]", + "[ 9/8 → 5/4 | s:sd bank:tr909 ]", + "[ 5/4 → 21/16 | s:ht speed:2 bank:tr909 ]", + "[ 21/16 → 11/8 | s:lt speed:2 bank:tr909 ]", + "[ 11/8 → 23/16 | s:ht speed:2 bank:tr909 ]", + "[ 23/16 → 3/2 | s:lt speed:2 bank:tr909 ]", + "[ 3/2 → 13/8 | s:bd bank:tr909 ]", + "[ 7/4 → 15/8 | s:cp bank:tr909 ]", + "[ 15/8 → 2/1 | s:lt bank:tr909 ]", + "[ 2/1 → 17/8 | s:bd bank:tr909 ]", + "[ 17/8 → 9/4 | s:sd bank:tr909 ]", + "[ 9/4 → 19/8 | s:ht bank:tr909 ]", + "[ 19/8 → 5/2 | s:lt bank:tr909 ]", + "[ 5/2 → 41/16 | s:bd speed:2 bank:tr909 ]", + "[ 21/8 → 43/16 | s:bd speed:2 bank:tr909 ]", + "[ 11/4 → 23/8 | s:cp bank:tr909 ]", + "[ 23/8 → 3/1 | s:lt bank:tr909 ]", + "[ 3/1 → 25/8 | s:bd bank:tr909 ]", + "[ 25/8 → 13/4 | s:sd bank:tr909 ]", + "[ 13/4 → 27/8 | s:ht bank:tr909 ]", + "[ 27/8 → 7/2 | s:lt bank:tr909 ]", + "[ 7/2 → 29/8 | s:bd bank:tr909 ]", + "[ 15/4 → 61/16 | s:cp speed:2 bank:tr909 ]", + "[ 61/16 → 31/8 | s:lt speed:2 bank:tr909 ]", + "[ 31/8 → 63/16 | s:cp speed:2 bank:tr909 ]", + "[ 63/16 → 4/1 | s:lt speed:2 bank:tr909 ]", +] +`; + exports[`runs examples > example "chunkInto" example index 0 1`] = ` [ "[ 0/1 → 1/16 | s:bd speed:2 bank:tr909 ]", From 77bab433e0c897048e874ad97f0721733a55948e Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 28 Jun 2025 14:06:16 -0400 Subject: [PATCH 31/75] cf --- website/src/repl/components/panel/Reference.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index 42a03a02e..1b617341b 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -21,8 +21,11 @@ export function Reference() { return true; } - const lowCaseSearch = search.toLowerCase(); - return entry.name.toLowerCase().includes(lowCaseSearch) || (entry.synonyms?.some((s) => s.includes(lowCaseSearch)) ?? false); + const lowCaseSearch = search.toLowerCase(); + return ( + entry.name.toLowerCase().includes(lowCaseSearch) || + (entry.synonyms?.some((s) => s.includes(lowCaseSearch)) ?? false) + ); }); }, [search]); From f2330a2307fcdba42b55fe976635dce3e941cfcc Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 29 Jun 2025 20:18:15 +0200 Subject: [PATCH 32/75] add 1.1.0 release notes --- .../blog/release-1.1.0-bananensplit.mdx | 317 ++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 website/src/content/blog/release-1.1.0-bananensplit.mdx diff --git a/website/src/content/blog/release-1.1.0-bananensplit.mdx b/website/src/content/blog/release-1.1.0-bananensplit.mdx new file mode 100644 index 000000000..dcc547682 --- /dev/null +++ b/website/src/content/blog/release-1.1.0-bananensplit.mdx @@ -0,0 +1,317 @@ +--- +title: 'Release Notes v1.1.0' +description: '' +date: '2024-06-02' +tags: ['meta'] +author: froos +--- + +These are the release notes for Strudel 1.1.0 aka "Bananensplit". + +The last release was over 19 weeks ago, so a lot of things have happened! + +First, here's a little demo, teasing some of the new features: + +import { Youtube } from '@src/components/Youtube'; + + + +Let's write up some of the highlights: + +## New DSP Features + +### Stereo Supersaw + +with spread, unison, and detune parameters + +```plaintext +note("d f a a# a d3").fast(2) +.s("supersaw").spread(".8").detune(.3).unison("2 7") +``` + +### Analog "ladder" filter type + +works great for acid basslines and vibey tones + +```plaintext +note("{d d d a a# d3 f4}%16".sub(12)).gain(1).s("sawtooth") +.lpf(200).lpenv(slider(1.36,0,8)).lpq(7).distort("1.5:.7")` +.ftype('ladder') +``` + +### stereo distortion effect + +```plaintext +note("{g g a# g g4}%8".add("{0 7 12 0}%8")).lpf(500) +.s("supersaw").dist("4:.2") +``` + +## Editor Features + +### inline viz + +The editor now supports multiple visuals within the code, using the `_` prefix for viz functions: + +Screenshot 2024-06-01 at 01 23 51 + +- `._pianoroll()`: inline pianoroll +- `._punchcard()`: inline punchcard +- `._scope()`: inline scope +- `._pitchwheel()`: inline pitchwheel + +For more info, check out the new [Visual Feedback Page](https://strudel.cc/learn/visual-feedback/) + +### label notation + +This new notation simplifies writing patterns at the top level: + +```plaintext +d1: s("bd*4") +d2: s("[- hh]*4") +``` + +This is equivalent to: + +```plaintext +stack( + s("bd*4"), + s("[- hh]*4") +) +``` + +The labels you choose are arbitrary, the above `d1` and `d2` are a typical thing you'd write in tidal, for example `d1 $ s "bd*4"`. +If the same label is used multiple times, the last one wins: + +```plaintext +d1: s("bd*4") +d1: s("[- hh]*4") // <-- only this plays +``` + +There is a special label anonymous label `$`, which can appear multiple times without overriding itself: + +```plaintext +// both of these will play: +$: s("bd*4") +$: s("[- hh]*4") +``` + +You can mute a pattern by prefixing `_`: + +```plaintext +_$: s("bd*4") // <-- this one is muted +$: s("[- hh]*4") +``` + +To run a transformation on all patterns, you can use `all`: + +```plaintext +$: s("bd*4") +$: s("[- hh]*4") +all(x=>x.room(.5)) +``` + +This notation is now the recommended way to [play patterns in parallel](https://strudel.cc/workshop/first-notes/#playing-multiple-patterns) + +### Clock sync between multiple instances + +timing has received a major overhaul, and is now much more accurate on all browsers. Additionally, you can now sync timing across multiple windows. +![Screenshot 2024-06-02 at 11 24 40 PM](https://codeberg.org/uzu/strudel/assets/47068718/840be744-a13e-4d7b-ab09-50d3a70b1f85) + +### Better sample upload support + +you can now upload large amounts of samples much faster across all browsers including on IOS devices. supported filetypes now include: ogg flac mp3 wav aac m4a + +### experimental tidal syntax + +The new `tidal` function allows you to write strudel patterns in tidal syntax: + +```plaintext +await initTidal() + +tidal` +d1 $ s "bd*4" + +d2 $ s "[- hh]*4" +` +``` + +As we're looking to improve compatibility with tidal, we're happy to hear feedback. + +## breaking changes + +This release comes with a bunch of breaking changes. If you find your patterns to sound different, check out the PRs below for guidance on how to update them. Most of these changes shouldn't affect a lot of patterns. +In case of doubt, add the line `// @version 1.0` to your old pattern. +If you're having problems, please let us know! + +- remove legacy legato + duration implementations by @felixroos in https://codeberg.org/uzu/strudel/pull/965 +- Velocity in value by @felixroos in https://codeberg.org/uzu/strudel/pull/974 +- use ireal as default voicing dict by @felixroos https://codeberg.org/uzu/strudel/pull/967 +- Color in hap value by @felixroos https://codeberg.org/uzu/strudel/pull/1007 +- rename trig -> reset, trigzero -> restart by @felixroos https://codeberg.org/uzu/strudel/pull/1010 +- remove dangerous arithmetic feature by @felixroos https://codeberg.org/uzu/strudel/pull/1030 +- change fanchor to 0 by @daslyfe https://codeberg.org/uzu/strudel/pull/1107 + +## superdough features + +- replace shape with distort in learn doc by @daslyfe https://codeberg.org/uzu/strudel/pull/982 +- Worklet Improvents / fixes by @daslyfe https://codeberg.org/uzu/strudel/pull/963 +- supersaw oscillator by @daslyfe https://codeberg.org/uzu/strudel/pull/978 +- Add analog-style ladder filter by @daslyfe https://codeberg.org/uzu/strudel/pull/1103 +- Calculate phaser modulation phase based on time by @daslyfe https://codeberg.org/uzu/strudel/pull/1110 +- rollback phaser by @daslyfe https://codeberg.org/uzu/strudel/pull/1113 + +## editor / ui features + +- 'Enable Bracket Matching' option in Codemirror by @eefano https://codeberg.org/uzu/strudel/pull/956 +- REPL sync between windows by @daslyfe https://codeberg.org/uzu/strudel/pull/900 +- inline viz / widgets package by @felixroos https://codeberg.org/uzu/strudel/pull/989 +- Inline punchcard + spiral by @felixroos https://codeberg.org/uzu/strudel/pull/1008 +- More fonts by @felixroos https://codeberg.org/uzu/strudel/pull/1023 +- better theme integration for visuals + various fixes by @felixroos https://codeberg.org/uzu/strudel/pull/1024 +- add setting for sync flag by @felixroos https://codeberg.org/uzu/strudel/pull/1025 +- add closeBrackets setting by @felixroos https://codeberg.org/uzu/strudel/pull/1031 +- add font file types to offline cache by @felixroos https://codeberg.org/uzu/strudel/pull/1032 +- pitchwheel visual by @felixroos https://codeberg.org/uzu/strudel/pull/1041 +- repl: set document.title from @title by @kasparsj https://codeberg.org/uzu/strudel/pull/1090 +- Samples tab improvements by @daslyfe https://codeberg.org/uzu/strudel/pull/1102 + +## language features + +- pickOut(), pickRestart(), pickReset() by @eefano https://codeberg.org/uzu/strudel/pull/950 +- Auto await samples by @felixroos https://codeberg.org/uzu/strudel/pull/955 +- feat: can now invert euclid pulses with negative numbers by @felixroos https://codeberg.org/uzu/strudel/pull/959 +- Nested controls by @felixroos https://codeberg.org/uzu/strudel/pull/973 +- alias - for ~ by @yaxu https://codeberg.org/uzu/strudel/pull/981 +- Beat-oriented functionality by @yaxu https://codeberg.org/uzu/strudel/pull/976 +- Labeled statements by @felixroos https://codeberg.org/uzu/strudel/pull/991 +- accidentals in scale degrees by @eefano https://codeberg.org/uzu/strudel/pull/1000 +- Feature: tactus marking by @yaxu https://codeberg.org/uzu/strudel/pull/1021 +- Tactus tidy by @yaxu https://codeberg.org/uzu/strudel/pull/1027 +- Wax, wane, taper and taperlist by @yaxu https://codeberg.org/uzu/strudel/pull/1042 +- transpose: support all combinations of numbers and strings for notes and intervals by @felixroos https://codeberg.org/uzu/strudel/pull/1048 +- anonymous patterns + muting by @felixroos https://codeberg.org/uzu/strudel/pull/1059 +- add swing + swingBy by @felixroos https://codeberg.org/uzu/strudel/pull/1038 +- Stepwise functions from Tidal by @yaxu https://codeberg.org/uzu/strudel/pull/1060 +- Tactus tweaks - fixes for maintaining tactus and highlight locations by @yaxu https://codeberg.org/uzu/strudel/pull/1065 +- Fix stepjoin by @yaxu https://codeberg.org/uzu/strudel/pull/1067 +- More tactus tidying by @yaxu https://codeberg.org/uzu/strudel/pull/1071 +- Tactus calculation toggle and breaking change to tactus calculation in fast/slow/hurry by @yaxu https://codeberg.org/uzu/strudel/pull/1081 +- hs2js package / tidal parser by @felixroos https://codeberg.org/uzu/strudel/pull/870 +- Add the mousex and mousey signal by @Enelg52 https://codeberg.org/uzu/strudel/pull/1112 +- can now access strudelMirror from repl by @felixroos https://codeberg.org/uzu/strudel/pull/1117 + +## sampler + +If you have nodejs installed on your system, you can now use [@strudel/sampler](https://www.npmjs.com/package/@strudel/sampler) to serve samples from disk to the REPL or flok. + +- local sample server cli by @felixroos https://codeberg.org/uzu/strudel/pull/1033 +- Fix sampler paths by @felixroos https://codeberg.org/uzu/strudel/pull/1034 +- Fix sampler windows by @felixroos https://codeberg.org/uzu/strudel/pull/1108 +- fix sampler on windows by @geikha https://codeberg.org/uzu/strudel/pull/1109 + +## docs + +- V1 release notes by @felixroos https://codeberg.org/uzu/strudel/pull/935 +- Minor documentation error: Update first-sounds.mdx by @mhetrick https://codeberg.org/uzu/strudel/pull/941 +- Update synths.mdx by @andresgottlieb https://codeberg.org/uzu/strudel/pull/984 +- using strudel in your project guide + cleanup examples by @felixroos https://codeberg.org/uzu/strudel/pull/1006 +- Document signals by @ilesinge https://codeberg.org/uzu/strudel/pull/1015 +- improve tutorial + custom samples doc by @felixroos https://codeberg.org/uzu/strudel/pull/1053 +- fix cr typo on first-sounds.mdx by @cleary https://codeberg.org/uzu/strudel/pull/1068 +- fix first sounds typo by @cleary https://codeberg.org/uzu/strudel/pull/1069 +- add `<...>` to first-sounds.mdx recap by @cleary https://codeberg.org/uzu/strudel/pull/1070 +- add nesting to `off` example variation in pattern-effects.mdx by @cleary https://codeberg.org/uzu/strudel/pull/1075 +- fix translation issue in first-effects.mdx by @cleary https://codeberg.org/uzu/strudel/pull/1072 +- add signals to recap in first-effects.mdx by @cleary https://codeberg.org/uzu/strudel/pull/1073 +- fix docs on alignment.mdx by @diegodorado https://codeberg.org/uzu/strudel/pull/1076 +- fix little dub tune example by @lukad https://codeberg.org/uzu/strudel/pull/1104 +- clarify `off` in pattern-effects.mdx by @cleary https://codeberg.org/uzu/strudel/pull/1074 +- Fixes drawPianoroll import in codemirror example by @giohappy https://codeberg.org/uzu/strudel/pull/1116 +- Migrate tutorial fanchor by @felixroos https://codeberg.org/uzu/strudel/pull/1122 + +## internals + +- remove cjs builds by @felixroos https://codeberg.org/uzu/strudel/pull/945 +- controls refactoring: simplify exports by @felixroos https://codeberg.org/uzu/strudel/pull/962 +- move canvas related helpers from core to new draw package by @felixroos https://codeberg.org/uzu/strudel/pull/971 +- remove canvas, externalize samples, delete junk by @felixroos https://codeberg.org/uzu/strudel/pull/1003 +- Improve performance of ! (replicate) by @yaxu https://codeberg.org/uzu/strudel/pull/1084 +- Benchmarks by @yaxu https://codeberg.org/uzu/strudel/pull/1079 + +## fixes + +- fix midi issue on firefox and added quote error by @Enelg52 https://codeberg.org/uzu/strudel/pull/936 +- fix: pianoroll sorting by @felixroos https://codeberg.org/uzu/strudel/pull/938 +- account for cps in midi time duration by @daslyfe https://codeberg.org/uzu/strudel/pull/954 +- fix script importable packages (web + repl) by @felixroos https://codeberg.org/uzu/strudel/pull/957 +- fix: reset global fx on pattern change by @felixroos https://codeberg.org/uzu/strudel/pull/960 +- add debounce to logger by @felixroos https://codeberg.org/uzu/strudel/pull/968 +- fix for transpose(): preserve hap value object structure by @eefano https://codeberg.org/uzu/strudel/pull/966 +- fix: clear hydra on reset by @felixroos https://codeberg.org/uzu/strudel/pull/983 +- little fix for withVal by @eefano https://codeberg.org/uzu/strudel/pull/980 +- fix: share now shares what's visible instead of active by @felixroos https://codeberg.org/uzu/strudel/pull/985 +- Fix pure mini highlight by @yaxu https://codeberg.org/uzu/strudel/pull/994 +- fix: await injectPatternMethods by @felixroos https://codeberg.org/uzu/strudel/pull/1012 +- update undocumented script by @felixroos https://codeberg.org/uzu/strudel/pull/1013 +- eliminate chromium clock jitter by @felixroos https://codeberg.org/uzu/strudel/pull/1004 +- Repl sync fixes by @daslyfe https://codeberg.org/uzu/strudel/pull/1014 +- hotfix for 1017 by @daslyfe https://codeberg.org/uzu/strudel/pull/1020 +- fix cyclist fizzling out by @felixroos https://codeberg.org/uzu/strudel/pull/1046 +- Midi Time hotfix for scheduler updates by @daslyfe https://codeberg.org/uzu/strudel/pull/1047 +- fix: do not reset cc input values on each eval by @felixroos https://codeberg.org/uzu/strudel/pull/1054 +- Fix wchooseCycles not picking the whole pattern by @ilesinge https://codeberg.org/uzu/strudel/pull/1061 +- fix OSC timing for recent scheduler updates by @daslyfe https://codeberg.org/uzu/strudel/pull/1062 +- clarify license by @yaxu https://codeberg.org/uzu/strudel/pull/1064 +- fix failing format test by @daslyfe https://codeberg.org/uzu/strudel/pull/1077 +- fix: url parsing with extra params by @felixroos https://codeberg.org/uzu/strudel/pull/1083 +- fix: csound + dough timing by @felixroos https://codeberg.org/uzu/strudel/pull/1086 +- fix: missing events due to premature worklet cleanup by @felixroos https://codeberg.org/uzu/strudel/pull/1089 +- Use sessionStorage for viewingPatternData and activePattern by @kasparsj https://codeberg.org/uzu/strudel/pull/1091 +- osc: couple of fixes by @kasparsj https://codeberg.org/uzu/strudel/pull/1093 +- web package fixes by @felixroos https://codeberg.org/uzu/strudel/pull/1044 +- Fix audio worklets by @daslyfe https://codeberg.org/uzu/strudel/pull/1114 +- fix: use full repl in web package by @felixroos https://codeberg.org/uzu/strudel/pull/1119 +- [BUG FIX] Audio worklets sometimes dont load by @daslyfe https://codeberg.org/uzu/strudel/pull/1121 + +## New Contributors + +- @mhetrick made their first contribution https://codeberg.org/uzu/strudel/pull/941 +- @eefano made their first contribution https://codeberg.org/uzu/strudel/pull/956 +- @Enelg52 made their first contribution https://codeberg.org/uzu/strudel/pull/936 +- @andresgottlieb made their first contribution https://codeberg.org/uzu/strudel/pull/984 +- @cleary made their first contribution https://codeberg.org/uzu/strudel/pull/1068 +- @diegodorado made their first contribution https://codeberg.org/uzu/strudel/pull/1076 +- @lukad made their first contribution https://codeberg.org/uzu/strudel/pull/1104 +- @giohappy made their first contribution https://codeberg.org/uzu/strudel/pull/1116 + +A huge thanks to all contributors!!! + +## Packages + +- @strudel/codemirror@1.1.0 +- @strudel/core@1.1.0 +- @strudel/csound@1.1.0 +- @strudel/draw@1.1.0 +- @strudel/embed@1.1.0 +- hs2js@0.1.0 +- @strudel/hydra@1.1.0 +- @strudel/midi@1.1.0 +- @strudel/mini@1.1.0 +- @strudel/osc@1.1.0 +- @strudel/repl@1.1.0 +- @strudel/sampler@0.1.0 +- @strudel/serial@1.1.0 +- @strudel/soundfonts@1.1.0 +- superdough@1.1.0 +- @strudel/tidal@0.1.0 +- @strudel/tonal@1.1.0 +- @strudel/transpiler@1.1.0 +- @strudel/web@1.1.0 +- @strudel/webaudio@1.1.0 +- @strudel/xen@1.1.0 + +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v1.0.0...v1.1.0 From eac6f27ca4e79a0b21c2c48e6cc3bdf3331e1be5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 29 Jun 2025 21:29:11 +0200 Subject: [PATCH 33/75] add 1.2.0 release notes --- .../blog/release-1.2.0-kardinalschnitten.mdx | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 website/src/content/blog/release-1.2.0-kardinalschnitten.mdx diff --git a/website/src/content/blog/release-1.2.0-kardinalschnitten.mdx b/website/src/content/blog/release-1.2.0-kardinalschnitten.mdx new file mode 100644 index 000000000..e58e27aec --- /dev/null +++ b/website/src/content/blog/release-1.2.0-kardinalschnitten.mdx @@ -0,0 +1,187 @@ +--- +title: 'Release Notes v1.2.0' +description: '' +date: '2025-05-01' +tags: ['meta'] +author: froos +--- + +## What's Changed + +## highlights + +- [stepwise functions](https://strudel.cc/learn/stepwise/) ([PR](https://github.com/tidalcycles/strudel/pull/1262)) +- [midimaps](https://strudel.cc/learn/input-output/#midimaps) ([PR](https://github.com/tidalcycles/strudel/pull/1274)) +- [spectrum](https://strudel.cc/learn/visual-feedback/#spectrum) ([PR](https://github.com/tidalcycles/strudel/pull/1213)) +- [mqtt](https://strudel.cc/learn/input-output/#mqtt) ([PR](https://github.com/tidalcycles/strudel/pull/1224)) +- pulse oscillator (todo: https://github.com/tidalcycles/strudel/issues/1336) ([PR](https://github.com/tidalcycles/strudel/pull/1304)) +- theme improvements + +## breaking changes + +- [breaking change] Sample signals from query onset, rather than midpoint by @yaxu in https://github.com/tidalcycles/strudel/pull/1278 +- change behaviour of polymeter, and remove polymeterSteps by @yaxu in https://github.com/tidalcycles/strudel/pull/1302 +- Polish, rename, and document stepwise functions by @yaxu in https://github.com/tidalcycles/strudel/pull/1262 + +### superdough + +- feat: Create Pulse Oscillator with variable PWM by @daslyfe in https://github.com/tidalcycles/strudel/pull/1304 +- add num samples (edited numbers) by @yaxu in https://github.com/tidalcycles/strudel/pull/1309 +- Add num samples from 0 up to 20 by @yaxu in https://github.com/tidalcycles/strudel/pull/1310 +- feat: add max polyphony feature for superdough by @daslyfe in https://github.com/tidalcycles/strudel/pull/1317 + +### docs + +- doc: visual functions + refactor onPaint by @felixroos in https://github.com/tidalcycles/strudel/pull/1125 +- Labeled statements doc by @felixroos in https://github.com/tidalcycles/strudel/pull/1126 +- Correct spelling mistakes by @EdwardBetts in https://github.com/tidalcycles/strudel/pull/1183 +- remove redundant example for cat, update snapshot by @kdiab in https://github.com/tidalcycles/strudel/pull/1189 +- chore: Edit run locally instructions in README.md by @ChinoUkaegbu in https://github.com/tidalcycles/strudel/pull/1206 +- suggested changes to voicings.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/1231 +- Documentation for all/each, and bugfix for each by @yaxu in https://github.com/tidalcycles/strudel/pull/1233 +- Update documentation for param value modification by @gillespi314 in https://github.com/tidalcycles/strudel/pull/1238 +- fix docs for beat function by @daslyfe in https://github.com/tidalcycles/strudel/pull/1248 +- understand voicings page by @felixroos in https://github.com/tidalcycles/strudel/pull/1230 +- add reference package by @felixroos in https://github.com/tidalcycles/strudel/pull/1252 +- Stepwise documentation tweaks, with mridangam samples by @yaxu in https://github.com/tidalcycles/strudel/pull/1275 +- showcase tweaks by @yaxu in https://github.com/tidalcycles/strudel/pull/1291 +- Signpost licenses for source code and samples a bit more, ref #1277 by @yaxu in https://github.com/tidalcycles/strudel/pull/1289 +- Fix misplaced ending sentence by @makmanalp in https://github.com/tidalcycles/strudel/pull/1296 +- Fix typo pattnr by @ReneNyffenegger in https://github.com/tidalcycles/strudel/pull/1316 +- update docs to reflect import sounds tab change by @hpunq in https://github.com/tidalcycles/strudel/pull/1332 + +### ui improvements + +- Udels (MultiFrame Strudel) Revisited by @daslyfe in https://github.com/tidalcycles/strudel/pull/1132 +- Create audio target selector for OSC/Superdirt by @daslyfe in https://github.com/tidalcycles/strudel/pull/1160 +- Add a search bar to the REPL Reference tab by @netux in https://github.com/tidalcycles/strudel/pull/1165 +- Adding search bar (soundtab.jsx) by @Bubobubobubobubo in https://github.com/tidalcycles/strudel/pull/1185 +- add 2 new ui settings by @felixroos in https://github.com/tidalcycles/strudel/pull/1200 +- Theme glowup by @felixroos in https://github.com/tidalcycles/strudel/pull/1268 +- Create Pattern Page Pagination by @daslyfe in https://github.com/tidalcycles/strudel/pull/1287 +- feat: Theme improvements by @daslyfe in https://github.com/tidalcycles/strudel/pull/1295 +- feat: new themes + theme improvements by @daslyfe in https://github.com/tidalcycles/strudel/pull/1326 +- Add new "import-sounds" tab with explanation on folder import by @hpunq in https://github.com/tidalcycles/strudel/pull/1329 +- Add Icon to import sample button by @daslyfe in https://github.com/tidalcycles/strudel/pull/1331 +- better spacing in zen mode by @felixroos in https://github.com/tidalcycles/strudel/pull/1147 +- Screenreader improvements by @yaxu in https://github.com/tidalcycles/strudel/pull/1158 +- colorize console + tweak header by @felixroos in https://github.com/tidalcycles/strudel/pull/1203 +- Menu Panel Improvements! by @daslyfe in https://github.com/tidalcycles/strudel/pull/1193 +- Make panel hover behavior optional by @daslyfe in https://github.com/tidalcycles/strudel/pull/1199 +- REPL: solo and sync configuration by @bthj in https://github.com/tidalcycles/strudel/pull/1214 +- enhancement: make error messages easier to read by @daslyfe in https://github.com/tidalcycles/strudel/pull/1315 + +### mqtt + +- MQTT support by @yaxu in https://github.com/tidalcycles/strudel/pull/1224 +- MQTT - if password isn't provided, prompt for one by @yaxu in https://github.com/tidalcycles/strudel/pull/1249 +- MQTT - support adding hap duration and cps metadata to JSON messages by @yaxu in https://github.com/tidalcycles/strudel/pull/1279 +- make mqtt topic patternable by @yaxu in https://github.com/tidalcycles/strudel/pull/1280 +- Bugfix: update mqtt connections dictionary by @yaxu in https://github.com/tidalcycles/strudel/pull/1281 +- mqtt bugfix - connection check by @yaxu in https://github.com/tidalcycles/strudel/pull/1282 + +### new functions + +- Add scramble and shuffle by @yaxu in https://github.com/tidalcycles/strudel/pull/1167 +- polyJoin by @yaxu in https://github.com/tidalcycles/strudel/pull/1168 +- Add seqPLoop from Tidal by @yaxu in https://github.com/tidalcycles/strudel/pull/1182 +- add filter + filterWhen + within by @felixroos in https://github.com/tidalcycles/strudel/pull/1039 +- Add bite function by @yaxu in https://github.com/tidalcycles/strudel/pull/1187 +- markcss by @felixroos in https://github.com/tidalcycles/strudel/pull/1202 +- "beat" function for "step sequencer" style rhythm notation by @daslyfe in https://github.com/tidalcycles/strudel/pull/1237 +- Add s_zip for 'cat'-ing patterns together step-by-step, bugfix `steps` by @yaxu in https://github.com/tidalcycles/strudel/pull/1208 +- "stretch" function (phase vocoder) by @daslyfe in https://github.com/tidalcycles/strudel/pull/1130 +- add basic spectrum function by @felixroos in https://github.com/tidalcycles/strudel/pull/1213 +- Add onKey function for custom key commands for patterns by @daslyfe in https://github.com/tidalcycles/strudel/pull/1235 +- Add binary and binaryN by @heerman in https://github.com/tidalcycles/strudel/pull/1226 +- midimaps by @felixroos in https://github.com/tidalcycles/strudel/pull/1274 +- small feat: Add alias for segment and ribbon by @daslyfe in https://github.com/tidalcycles/strudel/pull/1314 +- feat: Create scrub function for scrubbing an audio file by @daslyfe in https://github.com/tidalcycles/strudel/pull/1321 +- feat: Improve gain curve by @daslyfe in https://github.com/tidalcycles/strudel/pull/1318 +- Chop chop by @yaxu in https://github.com/tidalcycles/strudel/pull/1078 + +### more + +- Make `all()` post-stack again, and add `each()` for pre-stack by @yaxu in https://github.com/tidalcycles/strudel/pull/1229 +- Add stepBind, and some toplevel aliases for binds and withValue by @yaxu in https://github.com/tidalcycles/strudel/pull/1241 +- Make cps patternable by @eefano in https://github.com/tidalcycles/strudel/pull/1001 +- Allow wchooseCycles probabilities to be patterned by @yaxu in https://github.com/tidalcycles/strudel/pull/1292 +- @strudel/sampler improvements by @felixroos in https://github.com/tidalcycles/strudel/pull/1288 + +### refactor + +- export comment commands by @felixroos in https://github.com/tidalcycles/strudel/pull/113 + 6 +- containerize/seperate out boolean checks for repl types/Repl logic into bespoke components. by @daslyfe in https://github.com/tidalcycles/strudel/pull/1163 +- Improve + simplify neocyclist timing by @daslyfe in https://github.com/tidalcycles/strudel/pull/1164 +- Make phaser control consistent with superdirt by @daslyfe in https://github.com/tidalcycles/strudel/pull/1178 +- Revert "Make phaser control consistent with superdirt" by @daslyfe in https://github.com/tidalcycles/strudel/pull/1179 +- make phaser control match superdirt by @daslyfe in https://github.com/tidalcycles/strudel/pull/1180 +- refactor sampler by @felixroos in https://github.com/tidalcycles/strudel/pull/1101 +- update lockfile + minor versions by @felixroos in https://github.com/tidalcycles/strudel/pull/1198 +- Preserve tactus for 'degrade' and friends, and tidy up 'pick' and friends by @yaxu in https://github.com/tidalcycles/strudel/pull/1205 +- Apply `all` function to individual patterns rather than final stack by @yaxu in https://github.com/tidalcycles/strudel/pull/1209 +- Revert "Fix sometimes" by @yaxu in https://github.com/tidalcycles/strudel/pull/1267 +- patchday by @felixroos in https://github.com/tidalcycles/strudel/pull/1264 +- Rename repeat back to extend by @yaxu in https://github.com/tidalcycles/strudel/pull/1285 +- Send delta in OSC message in seconds, to match tidal/superdirt by @yaxu in https://github.com/tidalcycles/strudel/pull/1323 + +### fixes + +- Fix clock worker dependency path in module builds by @matthewkaney in https://github.com/tidalcycles/strudel/pull/1129 +- Fix bug in Fraction.lcm by @yaxu in https://github.com/tidalcycles/strudel/pull/1133 +- Fix tactus marking in mininotation by @yaxu in https://github.com/tidalcycles/strudel/pull/1144 +- Fix loopAt tactus by @yaxu in https://github.com/tidalcycles/strudel/pull/1145 +- Fix OSC clock jitter by @daslyfe in https://github.com/tidalcycles/strudel/pull/1157 +- [CORS HOTFIX] by @daslyfe in https://github.com/tidalcycles/strudel/pull/1162 +- Fixes fit so it works after a chop or slice by @yaxu in https://github.com/tidalcycles/strudel/pull/1171 +- fix sample speed when using splice and fit with superdirt by @daslyfe in https://github.com/tidalcycles/strudel/pull/1172 +- handle midin device not found error by @felixroos in https://github.com/tidalcycles/strudel/pull/1146 +- Fix serial timing by @yaxu in https://github.com/tidalcycles/strudel/pull/1188 +- Fix regression for d1, p1, p(n) by @yaxu in https://github.com/tidalcycles/strudel/pull/1227 +- Fix sometimes by @yaxu in https://github.com/tidalcycles/strudel/pull/1243 +- Fix sf2 timing by @felixroos in https://github.com/tidalcycles/strudel/pull/1272 +- Fix "squeezejoin" and functions using it, including "bite" by @yaxu in https://github.com/tidalcycles/strudel/pull/1286 +- Fixes inverted triangle wave by renaming it to "itri", making non-inverted "tri" by @yaxu in https://github.com/tidalcycles/strudel/pull/1283 +- Hotfix: prevent undefined pattern code from crashing strudel on load by @daslyfe in https://github.com/tidalcycles/strudel/pull/1297 +- Fix test error #1297 by @nkymut in https://github.com/tidalcycles/strudel/pull/1298 +- bugfix zoom stepcount by @yaxu in https://github.com/tidalcycles/strudel/pull/1301 +- bugfix: Allow single param to be used in the as function by @daslyfe in https://github.com/tidalcycles/strudel/pull/1312 +- fix: replace empty spaces in registered sound keys by @daslyfe in https://github.com/tidalcycles/strudel/pull/1319 +- FIX: Multichannel Audio by @daslyfe in https://github.com/tidalcycles/strudel/pull/1322 +- fix: udels header by @daslyfe in https://github.com/tidalcycles/strudel/pull/1325 +- fix: disable astro toolbar by default by @daslyfe in https://github.com/tidalcycles/strudel/pull/1324 +- FIX: sound import order by @daslyfe in https://github.com/tidalcycles/strudel/pull/1333` + +## New Contributors + +- @EdwardBetts made their first contribution in https://github.com/tidalcycles/strudel/pull/1183 +- @netux made their first contribution in https://github.com/tidalcycles/strudel/pull/1165 +- @kdiab made their first contribution in https://github.com/tidalcycles/strudel/pull/1189 + +**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v1.1.0...v1.1.1 + +## packages + +- @strudel/codemirror@1.2.0 +- @strudel/core@1.2.0 +- @strudel/csound@1.2.0 +- @strudel/draw@1.2.0 +- @strudel/gamepad@1.2.0 +- @strudel/hydra@1.2.0 +- @strudel/midi@1.2.0 +- @strudel/mini@1.2.0 +- @strudel/motion@1.2.0 +- @strudel/mqtt@1.2.0 +- @strudel/osc@1.2.0 +- @strudel/reference@1.2.0 +- @strudel/repl@1.2.0 +- @strudel/sampler@0.2.0 +- @strudel/serial@1.2.0 +- @strudel/soundfonts@1.2.0 +- superdough@1.2.0 +- @strudel/tonal@1.2.0 +- @strudel/transpiler@1.2.0 +- @strudel/web@1.2.0 +- @strudel/webaudio@1.2.0 +- @strudel/xen@1.2.0 From 5a72ea94b15e1905e82dd210ac18f63ebae9b5cc Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 30 Jun 2025 05:15:30 +0200 Subject: [PATCH 34/75] fix: format --- website/src/content/blog/release-1.2.0-kardinalschnitten.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/website/src/content/blog/release-1.2.0-kardinalschnitten.mdx b/website/src/content/blog/release-1.2.0-kardinalschnitten.mdx index e58e27aec..e215d4532 100644 --- a/website/src/content/blog/release-1.2.0-kardinalschnitten.mdx +++ b/website/src/content/blog/release-1.2.0-kardinalschnitten.mdx @@ -110,8 +110,7 @@ author: froos ### refactor -- export comment commands by @felixroos in https://github.com/tidalcycles/strudel/pull/113 - 6 +- expose comment commands by @felixroos in https://github.com/tidalcycles/strudel/pull/1136 - containerize/seperate out boolean checks for repl types/Repl logic into bespoke components. by @daslyfe in https://github.com/tidalcycles/strudel/pull/1163 - Improve + simplify neocyclist timing by @daslyfe in https://github.com/tidalcycles/strudel/pull/1164 - Make phaser control consistent with superdirt by @daslyfe in https://github.com/tidalcycles/strudel/pull/1178 From 2e3356978610400136a983740d013cb7305809c4 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 30 Jun 2025 05:48:08 +0200 Subject: [PATCH 35/75] add setDefault + resetDefaults to superdough --- packages/superdough/superdough.mjs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 14f46711b..0db5e2b99 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -150,6 +150,17 @@ const defaultDefaultValues = { fft: 8, }; +const defaultDefaultDefaultValues = Object.freeze({ ...defaultDefaultValues }); + +export function setDefault(control, value) { + const main = getControlName(control); + defaultDefaultValues[main] = value; +} + +export function resetDefaults() { + defaultDefaultValues = { ...defaultDefaultDefaultValues }; +} + let defaultControls = new Map(Object.entries(defaultDefaultValues)); export function setDefaultValue(key, value) { From e3680b96deab28811e2c8c63be09839242493002 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 30 Jun 2025 10:18:54 +0200 Subject: [PATCH 36/75] refactor: remove first param of all onTrigger calls --- packages/core/pattern.mjs | 2 +- packages/core/repl.mjs | 2 +- packages/core/speak.mjs | 2 +- packages/csound/index.mjs | 6 +++--- packages/desktopbridge/midibridge.mjs | 2 +- packages/desktopbridge/oscbridge.mjs | 2 +- packages/midi/midi.mjs | 2 +- packages/osc/osc.mjs | 2 +- packages/soundfonts/sfumato.mjs | 2 +- packages/superdough/dspworklet.mjs | 2 +- packages/superdough/superdough.mjs | 2 +- packages/webaudio/webaudio.mjs | 8 ++------ test/testtunes.mjs | 22 ---------------------- 13 files changed, 15 insertions(+), 41 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index cbaf8a8a2..d9bb94075 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3263,7 +3263,7 @@ export const slice = register( * s("bd!8").onTriggerTime((hap) => {console.info(hap)}) */ Pattern.prototype.onTriggerTime = function (func) { - return this.onTrigger((t_deprecate, hap, currentTime, cps = 1, targetTime) => { + return this.onTrigger((hap, currentTime, _cps, targetTime) => { const diff = targetTime - currentTime; window.setTimeout(() => { func(hap); diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index dcc5fcba4..117b1d046 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -252,7 +252,7 @@ export const getTrigger = } if (hap.context.onTrigger) { // call signature of output / onTrigger is different... - await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t); + await hap.context.onTrigger(hap, getTime(), cps, t); } } catch (err) { logger(`[cyclist] error: ${err.message}`, 'error'); diff --git a/packages/core/speak.mjs b/packages/core/speak.mjs index 7e548a73b..e0f60184e 100644 --- a/packages/core/speak.mjs +++ b/packages/core/speak.mjs @@ -32,7 +32,7 @@ function triggerSpeech(words, lang, voice) { } export const speak = register('speak', function (lang, voice, pat) { - return pat.onTrigger((_, hap) => { + return pat.onTrigger((hap) => { triggerSpeech(hap.value, lang, voice); }); }); diff --git a/packages/csound/index.mjs b/packages/csound/index.mjs index 1eeaf6fa5..e44765227 100644 --- a/packages/csound/index.mjs +++ b/packages/csound/index.mjs @@ -23,7 +23,7 @@ export const csound = register('csound', (instrument, pat) => { instrument = instrument || 'triangle'; init(); // not async to support csound inside other patterns + to be able to call pattern methods after it // TODO: find a alternative way to wait for csound to load (to wait with first time playback) - return pat.onTrigger((time_deprecate, hap, currentTime, _cps, targetTime) => { + return pat.onTrigger((hap, currentTime, _cps, targetTime) => { if (!_csound) { logger('[csound] not loaded yet', 'warning'); return; @@ -142,7 +142,7 @@ export const csoundm = register('csoundm', (instrument, pat) => { p1 = `"${instrument}"`; } init(); // not async to support csound inside other patterns + to be able to call pattern methods after it - return pat.onTrigger((tidal_time, hap) => { + return pat.onTrigger((hap, currentTime, _cps, targetTime) => { if (!_csound) { logger('[csound] not loaded yet', 'warning'); return; @@ -151,7 +151,7 @@ export const csoundm = register('csoundm', (instrument, pat) => { throw new Error('csound only support objects as hap values'); } // Time in seconds counting from now. - const p2 = tidal_time - getAudioContext().currentTime; + const p2 = targetTime - currentTime; const p3 = hap.duration.valueOf() + 0; const frequency = getFrequency(hap); let { gain = 1, velocity = 0.9 } = hap.value; diff --git a/packages/desktopbridge/midibridge.mjs b/packages/desktopbridge/midibridge.mjs index 471c826ea..2436d8ad7 100644 --- a/packages/desktopbridge/midibridge.mjs +++ b/packages/desktopbridge/midibridge.mjs @@ -6,7 +6,7 @@ const OFF_MESSAGE = 0x80; const CC_MESSAGE = 0xb0; Pattern.prototype.midi = function (output) { - return this.onTrigger((time_deprecate, hap, currentTime, cps, targetTime) => { + return this.onTrigger((hap, currentTime, cps, targetTime) => { let { note, nrpnn, nrpv, ccn, ccv, velocity = 0.9, gain = 1 } = hap.value; //magic number to get audio engine to line up, can probably be calculated somehow const latencyMs = 34; diff --git a/packages/desktopbridge/oscbridge.mjs b/packages/desktopbridge/oscbridge.mjs index 9bead6d19..2568c78e5 100644 --- a/packages/desktopbridge/oscbridge.mjs +++ b/packages/desktopbridge/oscbridge.mjs @@ -4,7 +4,7 @@ import { Invoke } from './utils.mjs'; const collator = new ClockCollator({}); -export async function oscTriggerTauri(t_deprecate, hap, currentTime, cps = 1, targetTime) { +export async function oscTriggerTauri(hap, currentTime, cps = 1, targetTime) { const controls = parseControlsFromHap(hap, cps); const params = []; const timestamp = collator.calculateTimestamp(currentTime, targetTime); diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index af2dd3a62..d0e092938 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -333,7 +333,7 @@ Pattern.prototype.midi = function (midiport, options = {}) { logger(`Midi device disconnected! Available: ${getMidiDeviceNamesString(outputs)}`), }); - return this.onTrigger((time_deprecate, hap, currentTime, cps, targetTime) => { + return this.onTrigger((hap, currentTime, cps, targetTime) => { if (!WebMidi.enabled) { logger('Midi not enabled'); return; diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index ac70b9e73..8262e5569 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -60,7 +60,7 @@ export function parseControlsFromHap(hap, cps) { const collator = new ClockCollator({}); -export async function oscTrigger(t_deprecate, hap, currentTime, cps = 1, targetTime) { +export async function oscTrigger(hap, currentTime, cps = 1, targetTime) { const osc = await connect(); const controls = parseControlsFromHap(hap, cps); const keyvals = Object.entries(controls).flat(); diff --git a/packages/soundfonts/sfumato.mjs b/packages/soundfonts/sfumato.mjs index 6d60f80dd..4d5e7ef45 100644 --- a/packages/soundfonts/sfumato.mjs +++ b/packages/soundfonts/sfumato.mjs @@ -3,7 +3,7 @@ import { getAudioContext, registerSound } from '@strudel/webaudio'; import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato'; Pattern.prototype.soundfont = function (sf, n = 0) { - return this.onTrigger((time_deprecate, h, ct, cps, targetTime) => { + return this.onTrigger((h, ct, cps, targetTime) => { const ctx = getAudioContext(); const note = getPlayableNoteValue(h); const preset = sf.presets[n % sf.presets.length]; diff --git a/packages/superdough/dspworklet.mjs b/packages/superdough/dspworklet.mjs index ed5c1e7e0..5b02ad298 100644 --- a/packages/superdough/dspworklet.mjs +++ b/packages/superdough/dspworklet.mjs @@ -74,6 +74,6 @@ export const dough = async (code) => { worklet.node.connect(ac.destination); }; -export function doughTrigger(time_deprecate, hap, currentTime, cps, targetTime) { +export function doughTrigger(hap, currentTime, cps, targetTime) { window.postMessage({ time: targetTime, dough: hap.value, currentTime, duration: hap.duration, cps }); } diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 14f46711b..003f58bbe 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -451,8 +451,8 @@ function mapChannelNumbers(channels) { } export const superdough = async (value, t, hapDuration, cps = 0.5) => { + // new: t is always expected to be the absolute target onset time const ac = getAudioContext(); - t = typeof t === 'string' && t.startsWith('=') ? Number(t.slice(1)) : ac.currentTime + t; let { stretch } = value; if (stretch != null) { //account for phase vocoder latency diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 91e28defd..01ad9fb8b 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -15,14 +15,10 @@ const hap2value = (hap) => { return hap.value; }; -export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps); // uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004 +// TODO: refactor output callbacks to eliminate deadline export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => { - return superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration, cps); -}; - -Pattern.prototype.webaudio = function () { - return this.onTrigger(webaudioOutputTrigger); + return superdough(hap2value(hap), t, hapDuration, cps); }; export function webaudioRepl(options = {}) { diff --git a/test/testtunes.mjs b/test/testtunes.mjs index fc887610e..3690d315b 100644 --- a/test/testtunes.mjs +++ b/test/testtunes.mjs @@ -359,28 +359,6 @@ stack( "[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25) ).note().piano().slow(2)`; -/* -export const customTrigger = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos -stack( - freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)").layer(x=>x.mul("1.006,.995"))), - freq("440(5,8)".clip(.18).mul("<1 3/4 2 2/3>")).gain(perlin.range(.2,.8)) -).s("/2") - .onTrigger((t,hap,ct)=>{ - const ac = Tone.getContext().rawContext; - t = ac.currentTime + t - ct; - const { freq, s, gain = 1 } = hap.value; - const master = ac.createGain(); - master.gain.value = 0.1 * gain; - master.connect(ac.destination); - const o = ac.createOscillator(); - o.type = s || 'triangle'; - o.frequency.value = Number(freq); - o.connect(master); - o.start(t); - o.stop(t + hap.duration); -}).stack(s("bd(3,8),hh*4,~ sd").webdirt())`; */ - export const swimmingWithSoundfonts = `// Koji Kondo - Swimming (Super Mario World) stack( n( From c063fb6b1ca7f2cd32fe626b9545702e8b65ec24 Mon Sep 17 00:00:00 2001 From: tj-mueller Date: Mon, 30 Jun 2025 11:51:39 +0200 Subject: [PATCH 37/75] correct rd to cr --- website/src/pages/de/workshop/first-sounds.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/de/workshop/first-sounds.mdx b/website/src/pages/de/workshop/first-sounds.mdx index 3695843b0..64a58db02 100644 --- a/website/src/pages/de/workshop/first-sounds.mdx +++ b/website/src/pages/de/workshop/first-sounds.mdx @@ -95,7 +95,7 @@ Diese Kombinationen von Buchstaben stehen für verschiedene Teile eines Schlagze - `mt` = **m**iddle tom - `ht` = **h**igh tom - `rd` = **r**i**d**e cymbal -- `rd` = **cr**ash cymbal +- `cr` = **cr**ash cymbal Probier verschiedene Sounds aus! From 504fe9ed4029438ba0214a5b730ab869a4eaaa60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aurel=20B=C3=ADl=C3=BD?= Date: Mon, 30 Jun 2025 12:17:24 +0200 Subject: [PATCH 38/75] document setcps in cycles reference --- website/src/pages/understand/cycles.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/src/pages/understand/cycles.mdx b/website/src/pages/understand/cycles.mdx index 1c0a778a6..bcf987c4d 100644 --- a/website/src/pages/understand/cycles.mdx +++ b/website/src/pages/understand/cycles.mdx @@ -67,6 +67,8 @@ Or using 2 beats per cycle: s("bd sd, hh*4")`} /> +You can use the `setcps` method to set the global tempo in cycles per second. `setcpm(x)` is the same as `setcps(x / 60)`. + To set a specific bpm, use `setcpm(bpm/bpc)` From 34771f03d424b021bdc683a8227dfffdab050222 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 4 Jul 2025 22:17:23 +0200 Subject: [PATCH 39/75] some clarification comments --- packages/core/cyclist.mjs | 1 + packages/core/repl.mjs | 1 + undocumented.json | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index f28dc604c..fec819548 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -67,6 +67,7 @@ export class Cyclist { // the following line is dumb and only here for backwards compatibility // see https://codeberg.org/uzu/strudel/pulls/1004 const deadline = targetTime - phase; + // this onTrigger has another signature onTrigger?.(hap, deadline, duration, this.cps, targetTime); if (hap.value.cps !== undefined && this.cps != hap.value.cps) { this.cps = hap.value.cps; diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 117b1d046..aeaa6418e 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -245,6 +245,7 @@ export function repl({ export const getTrigger = ({ getTime, defaultOutput }) => async (hap, deadline, duration, cps, t) => { + // ^ this signature is different from hap.context.onTrigger, as set by Pattern.onTrigger(onTrigger) // TODO: get rid of deadline after https://codeberg.org/uzu/strudel/pulls/1004 try { if (!hap.context.onTrigger || !hap.context.dominantTrigger) { diff --git a/undocumented.json b/undocumented.json index 8a38f205f..3f5a3be30 100644 --- a/undocumented.json +++ b/undocumented.json @@ -647,7 +647,6 @@ "evaluate" ], "/packages/webaudio/webaudio.mjs": [ - "webaudioOutputTrigger", "webaudioOutput", "webaudioRepl" ], From 168269a839f54ca95dab38097f9ecbb87b90259c Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 4 Jul 2025 22:29:18 +0200 Subject: [PATCH 40/75] disable fm for supersaw --- packages/superdough/synth.mjs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 13cfa13a5..88e14e5ab 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -121,7 +121,10 @@ export function registerSynthSounds() { const gainAdjustment = 1 / Math.sqrt(voices); getPitchEnvelope(o.parameters.get('detune'), value, begin, holdend); const vibratoOscillator = getVibratoOscillator(o.parameters.get('detune'), value, begin); - const fm = applyFM(o.parameters.get('frequency'), value, begin); + // const fm = applyFM(o.parameters.get('frequency'), value, begin); + // https://codeberg.org/uzu/strudel/issues/1428 + // if you think about re-enabling this, please test with fm > 1 first + // it's like 10x gain, so it's really dangerous let envGain = gainNode(1); envGain = o.connect(envGain); @@ -133,7 +136,7 @@ export function registerSynthSounds() { destroyAudioWorkletNode(o); envGain.disconnect(); onended(); - fm?.stop(); + // fm?.stop(); vibratoOscillator?.stop(); }, begin, From 4cc2de9b7efd37d941625a9805cb7117c2b4a2ef Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 5 Jul 2025 00:39:23 -0400 Subject: [PATCH 41/75] wip --- packages/core/cyclist.mjs | 62 +++++++++++------------------- packages/core/repl.mjs | 4 +- packages/superdough/superdough.mjs | 43 ++++++++++++++------- packages/superdough/util.mjs | 4 ++ 4 files changed, 59 insertions(+), 54 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 072e97abb..f28dc604c 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -22,44 +22,38 @@ export class Cyclist { this.started = false; this.beforeStart = beforeStart; this.cps = 0.5; - this.time_at_last_tick_message = 0; - this.cycle = 0; + this.num_ticks_since_cps_change = 0; + this.lastTick = 0; // absolute time when last tick (clock callback) happened + this.lastBegin = 0; // query begin of last tick + this.lastEnd = 0; // query end of last tick this.getTime = getTime; // get absolute time this.num_cycles_at_cps_change = 0; this.seconds_at_cps_change; // clock phase when cps was changed - this.num_ticks_since_cps_change = 0; this.onToggle = onToggle; this.latency = latency; // fixed trigger time offset - - this.interval = interval; - this.clock = createClock( getTime, // called slightly before each cycle - (phase, duration, _, time) => { - if (this.started === false) { - return; + (phase, duration, _, t) => { + if (this.num_ticks_since_cps_change === 0) { + this.num_cycles_at_cps_change = this.lastEnd; + this.seconds_at_cps_change = phase; } - const num_seconds_since_cps_change = this.num_ticks_since_cps_change * duration; - const tickdeadline = phase - time; - const lastTick = time + tickdeadline; - const num_cycles_since_cps_change = num_seconds_since_cps_change * this.cps; - const begin = this.num_cycles_at_cps_change + num_cycles_since_cps_change; - const secondsSinceLastTick = time - lastTick - duration; - const eventLength = duration * this.cps; - const end = begin + eventLength; - this.cycle = begin + secondsSinceLastTick * this.cps; + this.num_ticks_since_cps_change++; + const seconds_since_cps_change = this.num_ticks_since_cps_change * duration; + const num_cycles_since_cps_change = seconds_since_cps_change * this.cps; - //account for latency and tick duration when using cycle calculations for audio downstream - const cycle_gap = (this.latency - duration) * this.cps; + try { + const begin = this.lastEnd; + this.lastBegin = begin; + const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; + this.lastEnd = end; + this.lastTick = phase; - const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); - haps.forEach((hap) => { - if (hap.hasOnset()) { - let targetTime = (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps; - targetTime = targetTime + this.latency + tickdeadline + time - num_seconds_since_cps_change; - const duration = hap.duration / this.cps; - onTrigger?.(hap, tickdeadline, duration, this.cps, targetTime, this.cycle - cycle_gap); + if (phase < t) { + // avoid querying haps that are in the past anyway + console.log(`skip query: too late`); + return; } // query the pattern for events @@ -96,25 +90,17 @@ export class Cyclist { if (!this.started) { return 0; } - const gap = (this.getTime() - this.time_at_last_tick_message) * this.cps; - return this.cycle + gap; + const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration; + return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency; } - - setCycle = (cycle) => { - this.num_ticks_since_cps_change = 0; - this.num_cycles_at_cps_change = cycle; - }; setStarted(v) { this.started = v; - - this.setCycle(0); this.onToggle?.(v); } async start() { await this.beforeStart?.(); this.num_ticks_since_cps_change = 0; this.num_cycles_at_cps_change = 0; - if (!this.pattern) { throw new Error('Scheduler: no pattern set! call .setPattern first.'); } @@ -143,8 +129,6 @@ export class Cyclist { if (this.cps === cps) { return; } - const num_seconds_since_cps_change = this.num_ticks_since_cps_change * this.interval; - this.num_cycles_at_cps_change = this.num_cycles_at_cps_change + num_seconds_since_cps_change * cps; this.cps = cps; this.num_ticks_since_cps_change = 0; } diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index d5cab857d..dcc5fcba4 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -248,11 +248,11 @@ export const getTrigger = // TODO: get rid of deadline after https://codeberg.org/uzu/strudel/pulls/1004 try { if (!hap.context.onTrigger || !hap.context.dominantTrigger) { - await defaultOutput(hap, deadline, duration, cps, t, cycle); + await defaultOutput(hap, deadline, duration, cps, t); } if (hap.context.onTrigger) { // call signature of output / onTrigger is different... - await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t, cycle); + await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t); } } catch (err) { logger(`[cyclist] error: ${err.message}`, 'error'); diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index c59a5c0d2..e4296c6b7 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -7,7 +7,7 @@ This program is free software: you can redistribute it and/or modify it under th import './feedbackdelay.mjs'; import './reverb.mjs'; import './vowel.mjs'; -import { clamp, nanFallback, _mod, cycleToSeconds } from './util.mjs'; +import { clamp, nanFallback, _mod, cycleToSeconds, secondsToCycle } from './util.mjs'; import workletsUrl from './worklets.mjs?audioworklet'; import { createFilter, gainNode, getCompressor, getWorklet } from './helpers.mjs'; import { map } from 'nanostores'; @@ -482,6 +482,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5) => { amdepth = 1, amskew = 0.5, amphase = 0, + amshape = 1, s = getDefaultValue('s'), bank, source, @@ -707,19 +708,35 @@ export const superdough = async (value, t, hapDuration, cps = 0.5) => { crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush })); shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol })); distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); - am !== undefined && - chain.push( - getWorklet(ac, 'am-processor', { - speed: am, - depth: amdepth, - skew: amskew, - phaseoffset: amphase, - // shape: amshape, + // am !== undefined && + // chain.push( + // getWorklet(ac, 'am-processor', { + // speed: am, + // depth: amdepth, + // skew: amskew, + // phaseoffset: amphase, + // // shape: amshape, - cps, - cycle, - }), - ); + // cps, + // cycle, + // }), + // ); + + if (am !== undefined) { + const amGain = new GainNode(ac, { gain: 1 }); + const frequency = cycleToSeconds(am, cps) + const phaseoffset = cycleToSeconds(amphase, cps) + const lfo = getLfo(ac, t, t + hapDuration, { + skew: amskew, + frequency: am * cps, + depth: amdepth, + // dcoffset: 0, + shape: amshape, + phaseoffset + }) + lfo.connect(amGain.gain) + chain.push(amGain) + } compressorThreshold !== undefined && chain.push( diff --git a/packages/superdough/util.mjs b/packages/superdough/util.mjs index ea63a16f0..f4d59024e 100644 --- a/packages/superdough/util.mjs +++ b/packages/superdough/util.mjs @@ -72,3 +72,7 @@ export const getSoundIndex = (n, numSounds) => { export function cycleToSeconds(cycle, cps) { return cycle / cps; } + +export function secondsToCycle(t, cps) { + return t * cps; +} From 05bcd5e32fd706969aa9c9c343ed7bf43cc528e3 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 5 Jul 2025 09:01:48 +0100 Subject: [PATCH 42/75] Fix randrun and deps including shuffle. Fixes #1441 --- packages/core/signal.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index c933e7fe2..6bd6fde67 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -264,7 +264,7 @@ export const randrun = (n) => { const rands = timeToRands(t.floor().add(0.5), n); const nums = rands .map((n, i) => [n, i]) - .sort((a, b) => a[0] > b[0] - a[0] < b[0]) + .sort((a, b) => (a[0] > b[0]) - (a[0] < b[0])) .map((x) => x[1]); const i = t.cyclePos().mul(n).floor() % n; return nums[i]; From 82b14b529201f72b35e9603da43a62de22bb19ad Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 5 Jul 2025 11:13:40 +0200 Subject: [PATCH 43/75] fix: lint --- packages/superdough/superdough.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 0db5e2b99..8ff64ca8c 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -126,7 +126,7 @@ export const getAudioDevices = async () => { return devicesMap; }; -const defaultDefaultValues = { +let defaultDefaultValues = { s: 'triangle', gain: 0.8, postgain: 1, @@ -153,8 +153,8 @@ const defaultDefaultValues = { const defaultDefaultDefaultValues = Object.freeze({ ...defaultDefaultValues }); export function setDefault(control, value) { - const main = getControlName(control); - defaultDefaultValues[main] = value; + // const main = getControlName(control); // we cant do this because superdough is independent of strudel/core + defaultDefaultValues[control] = value; } export function resetDefaults() { From a901e2e3875288626ab2136b85ab969ec60847e6 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 5 Jul 2025 10:28:20 +0100 Subject: [PATCH 44/75] snapshot --- test/__snapshots__/examples.test.mjs.snap | 48 +++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index cc45848bf..36c7ca58b 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -8813,46 +8813,46 @@ exports[`runs examples > example "shrink" example index 3 1`] = ` exports[`runs examples > example "shuffle" example index 0 1`] = ` [ - "[ 0/1 → 1/4 | note:c s:piano ]", + "[ 0/1 → 1/4 | note:e s:piano ]", "[ 1/4 → 1/2 | note:d s:piano ]", - "[ 1/2 → 3/4 | note:e s:piano ]", - "[ 3/4 → 1/1 | note:f s:piano ]", - "[ 1/1 → 5/4 | note:c s:piano ]", - "[ 5/4 → 3/2 | note:d s:piano ]", - "[ 3/2 → 7/4 | note:e s:piano ]", - "[ 7/4 → 2/1 | note:f s:piano ]", - "[ 2/1 → 9/4 | note:c s:piano ]", - "[ 9/4 → 5/2 | note:d s:piano ]", + "[ 1/2 → 3/4 | note:f s:piano ]", + "[ 3/4 → 1/1 | note:c s:piano ]", + "[ 1/1 → 5/4 | note:e s:piano ]", + "[ 5/4 → 3/2 | note:c s:piano ]", + "[ 3/2 → 7/4 | note:f s:piano ]", + "[ 7/4 → 2/1 | note:d s:piano ]", + "[ 2/1 → 9/4 | note:d s:piano ]", + "[ 9/4 → 5/2 | note:c s:piano ]", "[ 5/2 → 11/4 | note:e s:piano ]", "[ 11/4 → 3/1 | note:f s:piano ]", "[ 3/1 → 13/4 | note:c s:piano ]", - "[ 13/4 → 7/2 | note:d s:piano ]", - "[ 7/2 → 15/4 | note:e s:piano ]", - "[ 15/4 → 4/1 | note:f s:piano ]", + "[ 13/4 → 7/2 | note:e s:piano ]", + "[ 7/2 → 15/4 | note:f s:piano ]", + "[ 15/4 → 4/1 | note:d s:piano ]", ] `; exports[`runs examples > example "shuffle" example index 1 1`] = ` [ - "[ 0/1 → 1/8 | note:c s:piano ]", + "[ 0/1 → 1/8 | note:e s:piano ]", "[ 1/8 → 1/4 | note:d s:piano ]", - "[ 1/4 → 3/8 | note:e s:piano ]", - "[ 3/8 → 1/2 | note:f s:piano ]", + "[ 1/4 → 3/8 | note:f s:piano ]", + "[ 3/8 → 1/2 | note:c s:piano ]", "[ 1/2 → 1/1 | note:g s:piano ]", - "[ 1/1 → 9/8 | note:c s:piano ]", - "[ 9/8 → 5/4 | note:d s:piano ]", - "[ 5/4 → 11/8 | note:e s:piano ]", - "[ 11/8 → 3/2 | note:f s:piano ]", + "[ 1/1 → 9/8 | note:e s:piano ]", + "[ 9/8 → 5/4 | note:c s:piano ]", + "[ 5/4 → 11/8 | note:f s:piano ]", + "[ 11/8 → 3/2 | note:d s:piano ]", "[ 3/2 → 2/1 | note:g s:piano ]", - "[ 2/1 → 17/8 | note:c s:piano ]", - "[ 17/8 → 9/4 | note:d s:piano ]", + "[ 2/1 → 17/8 | note:d s:piano ]", + "[ 17/8 → 9/4 | note:c s:piano ]", "[ 9/4 → 19/8 | note:e s:piano ]", "[ 19/8 → 5/2 | note:f s:piano ]", "[ 5/2 → 3/1 | note:g s:piano ]", "[ 3/1 → 25/8 | note:c s:piano ]", - "[ 25/8 → 13/4 | note:d s:piano ]", - "[ 13/4 → 27/8 | note:e s:piano ]", - "[ 27/8 → 7/2 | note:f s:piano ]", + "[ 25/8 → 13/4 | note:e s:piano ]", + "[ 13/4 → 27/8 | note:f s:piano ]", + "[ 27/8 → 7/2 | note:d s:piano ]", "[ 7/2 → 4/1 | note:g s:piano ]", ] `; From 664d76cf1b1756f1cd01155c3120642793297f62 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 5 Jul 2025 11:32:16 +0200 Subject: [PATCH 45/75] resetDefaults when editor is reset --- website/src/repl/useReplContext.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/src/repl/useReplContext.jsx b/website/src/repl/useReplContext.jsx index 12621c50a..ac30fe342 100644 --- a/website/src/repl/useReplContext.jsx +++ b/website/src/repl/useReplContext.jsx @@ -13,6 +13,7 @@ import { resetGlobalEffects, resetLoadedSounds, initAudioOnFirstClick, + resetDefaults, } from '@strudel/webaudio'; import { setVersionDefaultsFrom } from './util.mjs'; import { StrudelMirror, defaultSettings } from '@strudel/codemirror'; @@ -181,6 +182,7 @@ export function useReplContext() { const resetEditor = async () => { (await getModule('@strudel/tonal'))?.resetVoicings(); + resetDefaults(); resetGlobalEffects(); clearCanvas(); clearHydra(); From d9e44dcda61d724d26585e6a537eb54b636b51ce Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 6 Jul 2025 00:59:39 -0400 Subject: [PATCH 46/75] sync without cyclist changes --- packages/core/controls.mjs | 16 +++- packages/core/cyclist copy.mjs | 140 +++++++++++++++++++++++++++++ packages/core/cyclist.mjs | 114 ++++++++++------------- packages/core/neocyclist.mjs | 2 +- packages/core/repl.mjs | 6 +- packages/superdough/superdough.mjs | 42 +++++++-- packages/superdough/worklets.mjs | 15 +++- packages/webaudio/webaudio.mjs | 8 +- 8 files changed, 261 insertions(+), 82 deletions(-) create mode 100644 packages/core/cyclist copy.mjs diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 66dee4f1c..12bcc3565 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -448,13 +448,23 @@ export const { coarse } = registerControl('coarse'); * modulate the amplitude of a sound with a continuous waveform * * @name am - * @synonyms tremelo - * @param {number | Pattern} speed modulation speed in cycles + * @param {number | Pattern} speed modulation speed in HZ * @example * s("triangle").am("2").amshape("").amdepth(.5) * */ -export const { am, tremolo } = registerControl(['am', 'amdepth', 'amskew', 'amphase'], 'tremolo'); +export const { am, } = registerControl(['am', 'amdepth', 'amskew', 'amphase'],); + +/** + * modulate the amplitude of a sound with a continuous waveform + * + * @name amsync + * @param {number | Pattern} cycles modulation speed in cycles + * @example + * s("triangle").am("2").amshape("").amdepth(.5) + * + */ +export const { amsync } = registerControl(['amsync', 'amdepth', 'amskew', 'amphase']); /** * depth of amplitude modulation diff --git a/packages/core/cyclist copy.mjs b/packages/core/cyclist copy.mjs new file mode 100644 index 000000000..fec819548 --- /dev/null +++ b/packages/core/cyclist copy.mjs @@ -0,0 +1,140 @@ +/* +cyclist.mjs - event scheduler for a single strudel instance. for multi-instance scheduler, see - see +Copyright (C) 2022 Strudel contributors - see +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ + +import createClock from './zyklus.mjs'; +import { logger } from './logger.mjs'; + +export class Cyclist { + constructor({ + interval, + onTrigger, + onToggle, + onError, + getTime, + latency = 0.1, + setInterval, + clearInterval, + beforeStart, + }) { + this.started = false; + this.beforeStart = beforeStart; + this.cps = 0.5; + this.num_ticks_since_cps_change = 0; + this.lastTick = 0; // absolute time when last tick (clock callback) happened + this.lastBegin = 0; // query begin of last tick + this.lastEnd = 0; // query end of last tick + this.getTime = getTime; // get absolute time + this.num_cycles_at_cps_change = 0; + this.seconds_at_cps_change; // clock phase when cps was changed + this.onToggle = onToggle; + this.latency = latency; // fixed trigger time offset + this.clock = createClock( + getTime, + // called slightly before each cycle + (phase, duration, _, t) => { + if (this.num_ticks_since_cps_change === 0) { + this.num_cycles_at_cps_change = this.lastEnd; + this.seconds_at_cps_change = phase; + } + this.num_ticks_since_cps_change++; + const seconds_since_cps_change = this.num_ticks_since_cps_change * duration; + const num_cycles_since_cps_change = seconds_since_cps_change * this.cps; + + try { + const begin = this.lastEnd; + this.lastBegin = begin; + const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; + this.lastEnd = end; + this.lastTick = phase; + + if (phase < t) { + // avoid querying haps that are in the past anyway + console.log(`skip query: too late`); + return; + } + + // query the pattern for events + const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); + + haps.forEach((hap) => { + if (hap.hasOnset()) { + const targetTime = + (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps + this.seconds_at_cps_change + latency; + const duration = hap.duration / this.cps; + // the following line is dumb and only here for backwards compatibility + // see https://codeberg.org/uzu/strudel/pulls/1004 + const deadline = targetTime - phase; + // this onTrigger has another signature + onTrigger?.(hap, deadline, duration, this.cps, targetTime); + if (hap.value.cps !== undefined && this.cps != hap.value.cps) { + this.cps = hap.value.cps; + this.num_ticks_since_cps_change = 0; + } + } + }); + } catch (e) { + logger(`[cyclist] error: ${e.message}`); + onError?.(e); + } + }, + interval, // duration of each cycle + 0.1, + 0.1, + setInterval, + clearInterval, + ); + } + now() { + if (!this.started) { + return 0; + } + const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration; + return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency; + } + setStarted(v) { + this.started = v; + this.onToggle?.(v); + } + async start() { + await this.beforeStart?.(); + this.num_ticks_since_cps_change = 0; + this.num_cycles_at_cps_change = 0; + if (!this.pattern) { + throw new Error('Scheduler: no pattern set! call .setPattern first.'); + } + logger('[cyclist] start'); + this.clock.start(); + this.setStarted(true); + } + pause() { + logger('[cyclist] pause'); + this.clock.pause(); + this.setStarted(false); + } + stop() { + logger('[cyclist] stop'); + this.clock.stop(); + this.lastEnd = 0; + this.setStarted(false); + } + async setPattern(pat, autostart = false) { + this.pattern = pat; + if (autostart && !this.started) { + await this.start(); + } + } + setCps(cps = 0.5) { + if (this.cps === cps) { + return; + } + this.cps = cps; + this.num_ticks_since_cps_change = 0; + } + log(begin, end, haps) { + const onsets = haps.filter((h) => h.hasOnset()); + console.log(`${begin.toFixed(4)} - ${end.toFixed(4)} ${Array(onsets.length).fill('I').join('')}`); + } +} diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index fec819548..43e1f508e 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -1,6 +1,6 @@ /* -cyclist.mjs - event scheduler for a single strudel instance. for multi-instance scheduler, see - see -Copyright (C) 2022 Strudel contributors - see +cyclist.mjs - event scheduler for a single strudel instance. for multi-instance scheduler, see - see +Copyright (C) 2022 Strudel contributors - see This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ @@ -8,77 +8,50 @@ import createClock from './zyklus.mjs'; import { logger } from './logger.mjs'; export class Cyclist { - constructor({ - interval, - onTrigger, - onToggle, - onError, - getTime, - latency = 0.1, - setInterval, - clearInterval, - beforeStart, - }) { + constructor({ interval = 0.05, onTrigger, onToggle, onError, getTime, latency = 0.1, setInterval, clearInterval }) { this.started = false; - this.beforeStart = beforeStart; this.cps = 0.5; - this.num_ticks_since_cps_change = 0; - this.lastTick = 0; // absolute time when last tick (clock callback) happened - this.lastBegin = 0; // query begin of last tick - this.lastEnd = 0; // query end of last tick + this.time_at_last_tick_message = 0; + this.cycle = 0; this.getTime = getTime; // get absolute time this.num_cycles_at_cps_change = 0; this.seconds_at_cps_change; // clock phase when cps was changed + this.num_ticks_since_cps_change = 0; this.onToggle = onToggle; this.latency = latency; // fixed trigger time offset + + this.interval = interval; + this.clock = createClock( getTime, // called slightly before each cycle - (phase, duration, _, t) => { - if (this.num_ticks_since_cps_change === 0) { - this.num_cycles_at_cps_change = this.lastEnd; - this.seconds_at_cps_change = phase; + (lastTick, duration, _, time) => { + if (this.started === false) { + return; } - this.num_ticks_since_cps_change++; - const seconds_since_cps_change = this.num_ticks_since_cps_change * duration; - const num_cycles_since_cps_change = seconds_since_cps_change * this.cps; + const num_seconds_since_cps_change = this.num_ticks_since_cps_change * duration; + const tickdeadline = lastTick - time; + const num_cycles_since_cps_change = num_seconds_since_cps_change * this.cps; + const begin = this.num_cycles_at_cps_change + num_cycles_since_cps_change; + const secondsSinceLastTick = time - lastTick - duration; + const eventLength = duration * this.cps; + const end = begin + eventLength; + this.cycle = begin + secondsSinceLastTick * this.cps; - try { - const begin = this.lastEnd; - this.lastBegin = begin; - const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; - this.lastEnd = end; - this.lastTick = phase; + //account for latency and tick duration when using cycle calculations for audio downstream + const cycle_gap = (this.latency - duration) * this.cps; - if (phase < t) { - // avoid querying haps that are in the past anyway - console.log(`skip query: too late`); - return; + const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); + haps.forEach((hap) => { + if (hap.hasOnset()) { + let targetTime = (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps; + targetTime = targetTime + this.latency + tickdeadline + time - num_seconds_since_cps_change; + const duration = hap.duration / this.cps; + onTrigger?.(hap, tickdeadline, duration, this.cps, targetTime, this.cycle - cycle_gap); } - - // query the pattern for events - const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); - - haps.forEach((hap) => { - if (hap.hasOnset()) { - const targetTime = - (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps + this.seconds_at_cps_change + latency; - const duration = hap.duration / this.cps; - // the following line is dumb and only here for backwards compatibility - // see https://codeberg.org/uzu/strudel/pulls/1004 - const deadline = targetTime - phase; - // this onTrigger has another signature - onTrigger?.(hap, deadline, duration, this.cps, targetTime); - if (hap.value.cps !== undefined && this.cps != hap.value.cps) { - this.cps = hap.value.cps; - this.num_ticks_since_cps_change = 0; - } - } - }); - } catch (e) { - logger(`[cyclist] error: ${e.message}`); - onError?.(e); - } + }); + this.time_at_last_tick_message = time; + this.num_ticks_since_cps_change++; }, interval, // duration of each cycle 0.1, @@ -91,17 +64,24 @@ export class Cyclist { if (!this.started) { return 0; } - const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration; - return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency; + const gap = (this.getTime() - this.time_at_last_tick_message) * this.cps; + return this.cycle + gap; } + + setCycle = (cycle) => { + this.num_ticks_since_cps_change = 0; + this.num_cycles_at_cps_change = cycle; + }; setStarted(v) { this.started = v; + + this.setCycle(0); this.onToggle?.(v); } - async start() { - await this.beforeStart?.(); + start() { this.num_ticks_since_cps_change = 0; this.num_cycles_at_cps_change = 0; + if (!this.pattern) { throw new Error('Scheduler: no pattern set! call .setPattern first.'); } @@ -120,16 +100,18 @@ export class Cyclist { this.lastEnd = 0; this.setStarted(false); } - async setPattern(pat, autostart = false) { + setPattern(pat, autostart = false) { this.pattern = pat; if (autostart && !this.started) { - await this.start(); + this.start(); } } setCps(cps = 0.5) { if (this.cps === cps) { return; } + const num_seconds_since_cps_change = this.num_ticks_since_cps_change * this.interval; + this.num_cycles_at_cps_change = this.num_cycles_at_cps_change + num_seconds_since_cps_change * cps; this.cps = cps; this.num_ticks_since_cps_change = 0; } @@ -137,4 +119,4 @@ export class Cyclist { const onsets = haps.filter((h) => h.hasOnset()); console.log(`${begin.toFixed(4)} - ${end.toFixed(4)} ${Array(onsets.length).fill('I').join('')}`); } -} +} \ No newline at end of file diff --git a/packages/core/neocyclist.mjs b/packages/core/neocyclist.mjs index 261f08acb..4e6d385d8 100644 --- a/packages/core/neocyclist.mjs +++ b/packages/core/neocyclist.mjs @@ -45,7 +45,7 @@ export class NeoCyclist { const timeUntilTrigger = cycleToSeconds(hap.whole.begin - this.cycle, this.cps); const targetTime = timeUntilTrigger + currentTime + this.latency; const duration = cycleToSeconds(hap.duration, this.cps); - onTrigger?.(hap, 0, duration, this.cps, targetTime); + onTrigger?.(hap, 0, duration, this.cps, targetTime, this.cycle); } }); }; diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index aeaa6418e..5e696c85f 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -244,16 +244,16 @@ export function repl({ export const getTrigger = ({ getTime, defaultOutput }) => - async (hap, deadline, duration, cps, t) => { + async (hap, deadline, duration, cps, t, cycle = 0) => { // ^ this signature is different from hap.context.onTrigger, as set by Pattern.onTrigger(onTrigger) // TODO: get rid of deadline after https://codeberg.org/uzu/strudel/pulls/1004 try { if (!hap.context.onTrigger || !hap.context.dominantTrigger) { - await defaultOutput(hap, deadline, duration, cps, t); + await defaultOutput(hap, deadline, duration, cps, t, cycle); } if (hap.context.onTrigger) { // call signature of output / onTrigger is different... - await hap.context.onTrigger(hap, getTime(), cps, t); + await hap.context.onTrigger(hap, getTime(), cps, t, cycle); } } catch (err) { logger(`[cyclist] error: ${err.message}`, 'error'); diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index bbc7d8a1c..79c1ff117 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -45,6 +45,8 @@ export function setGainCurve(newGainCurveFunc) { gainCurveFunc = newGainCurveFunc; } + + function aliasBankMap(aliasMap) { // Make all bank keys lower case for case insensitivity for (const key in aliasMap) { @@ -325,12 +327,29 @@ function getDelay(orbit, delaytime, delayfeedback, t, channels) { return delays[orbit]; } -export function getLfo(audioContext, time, end, properties = {}) { +export function getLfo(audioContext, begin, end, properties = {}) { return getWorklet(audioContext, 'lfo-processor', { frequency: 1, depth: 1, skew: 0, phaseoffset: 0, + time: begin, + begin, + end, + shape: 1, + dcoffset: -0.5, + ...properties, + }); +} + +export function getSyncedLfo(audioContext, time, end, cps, cycle, properties = {}) { + const frequency = cycle/cps + + return getWorklet(audioContext, 'lfo-processor', { + frequency, + depth: 1, + skew: 0, + phaseoffset: 0, time, end, shape: 1, @@ -450,9 +469,10 @@ function mapChannelNumbers(channels) { return (Array.isArray(channels) ? channels : [channels]).map((ch) => ch - 1); } -export const superdough = async (value, t, hapDuration, cps = 0.5) => { +export const superdough = async (value, t, hapDuration, cps = 0.5, cycle) => { // new: t is always expected to be the absolute target onset time const ac = getAudioContext(); + let { stretch } = value; if (stretch != null) { //account for phase vocoder latency @@ -707,11 +727,12 @@ export const superdough = async (value, t, hapDuration, cps = 0.5) => { coarse !== undefined && chain.push(getWorklet(ac, 'coarse-processor', { coarse })); crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush })); shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol })); - distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); + // distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); // am !== undefined && // chain.push( // getWorklet(ac, 'am-processor', { // speed: am, + // begin: t, // depth: amdepth, // skew: amskew, // phaseoffset: amphase, @@ -724,12 +745,21 @@ export const superdough = async (value, t, hapDuration, cps = 0.5) => { if (am !== undefined) { const amGain = new GainNode(ac, { gain: 1 }); - const frequency = cycleToSeconds(am, cps) + const frequency = cps / am const phaseoffset = cycleToSeconds(amphase, cps) + + + const time = cycle / cps + + // console.info(cycle, time, frequency) + + + const lfo = getLfo(ac, t, t + hapDuration, { skew: amskew, - frequency: am * cps, - depth: amdepth, + frequency, + depth: amdepth, + time, // dcoffset: 0, shape: amshape, phaseoffset diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index ffa2165b4..366be8a5e 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -85,6 +85,7 @@ const waveShapeNames = Object.keys(waveshapes); class LFOProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [ + { name: 'begin', defaultValue: 0 }, { name: 'time', defaultValue: 0 }, { name: 'end', defaultValue: 0 }, { name: 'frequency', defaultValue: 0.5 }, @@ -109,10 +110,14 @@ class LFOProcessor extends AudioWorkletProcessor { } process(inputs, outputs, parameters) { + const begin = parameters['begin'][0] // eslint-disable-next-line no-undef if (currentTime >= parameters.end[0]) { return false; } + if (currentTime <= begin) { + return true; + } const output = outputs[0]; const frequency = parameters['frequency'][0]; @@ -159,6 +164,8 @@ class CoarseProcessor extends AudioWorkletProcessor { const input = inputs[0]; const output = outputs[0]; + + const hasInput = !(input[0] === undefined); if (this.started && !hasInput) { return false; @@ -899,6 +906,7 @@ registerProcessor('byte-beat-processor', ByteBeatProcessor); class AMProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [ + { name: 'begin', defaultValue: 0 }, { name: 'cps', defaultValue: 0.5 }, { name: 'speed', defaultValue: 0.5 }, { name: 'cycle', defaultValue: 0 }, @@ -925,10 +933,15 @@ class AMProcessor extends AudioWorkletProcessor { const input = inputs[0]; const output = outputs[0]; const hasInput = !(input[0] === undefined); + + if (this.started && !hasInput) { return false; } this.started = hasInput; + if (currentTime <= parameters.begin[0]) { + return true; + } const speed = parameters['speed'][0]; const cps = parameters['cps'][0]; @@ -937,7 +950,7 @@ class AMProcessor extends AudioWorkletProcessor { const skew = parameters['skew'][0]; const phaseoffset = parameters['phaseoffset'][0]; - const frequency = speed * cps; + const frequency = cps / speed if (this.phase == null) { const secondsPassed = cycle / cps; this.phase = _mod(secondsPassed * frequency + phaseoffset, 1); diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 01ad9fb8b..78441ee55 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -17,8 +17,12 @@ const hap2value = (hap) => { // uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004 // TODO: refactor output callbacks to eliminate deadline -export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => { - return superdough(hap2value(hap), t, hapDuration, cps); +export const webaudioOutput = (hap, deadline, hapDuration, cps, t, cycle) => { + + return superdough(hap2value(hap), t, hapDuration, cps, + hap.whole.begin.valueOf() + // cycle +); }; export function webaudioRepl(options = {}) { From a7a283a99e1505005e45ccf0e7df0f5be83391e2 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 6 Jul 2025 17:00:33 +0200 Subject: [PATCH 47/75] doc: add version tagging guide to contributing.md --- CONTRIBUTING.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f455741f5..a7f65b5bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -150,6 +150,7 @@ Important: Always publish with `pnpm`, as `npm` does not support overriding main ## useful commands + ```sh #regenerate the test snapshots (ex: when updating or creating new pattern functions) pnpm snapshot @@ -160,6 +161,81 @@ pnpm run osc #build the standalone version pnpm tauri build ``` + +## version tag patching + +here's a little guide on how to patch patterns in the database to prevent breaking old patterns due to breaking changes in newer versions. + +the general tactic is to use `// @version x.y` to tag a pattern with a specific strudel version. when a pattern is evaluated, this metadata will de-activate any breaking changes that came after the specified version. +for example, in version 1.1, the default value for `fanchor` was changed from `0.5` to `0`. +if play a pattern that was made before that change, sounds that use filter evenlopes can sound very different, so by adding `// @version 1.0` will make it sound like it used to. +before releasing a new version with breaking changes, we can edit all patterns in the database, inserting the version tag they were created under: + +as an example, to release version 1.2, do the following: + +1. get date range + +```sh +# get date of last version: +git log -1 --format=%aI @strudel/core@1.1.0 +# 2024-05-31T23:07:26+02:00 + +# get date of current version: +git log -1 --format=%aI @strudel/core@1.2.0 +# 2025-05-01T12:39:24+02:00 +# might also use todays timestamp if version is not yet released +``` + +now we know, all patterns between these 2 dates have to receive a version tag (unless they already have one). + +2. get patterns in question + +```sql +SELECT * +FROM code_v1 +WHERE code NOT LIKE '%@version%' +AND created_at > '2024-05-31T23:07:26+02:00' +AND created_at < '2025-05-01T12:39:24+02:00' +ORDER BY created_at ASC; +``` + +this gives us all unversioned patterns that were saved between 1.1.0 and 1.2.0. in this case, it's 9373 patterns! + +3. insert version tags + +we are now ready to insert the version tag to these patterns. +before updating thousands of patterns, it's probably a good idea to test if a single one gets udpated: + +```sql +UPDATE code_v1 +SET code = code || E'\n// @version 1.1' +WHERE hash = 'Ns2sMB40yIw4'; +``` + +after [verifying](https://strudel.cc/?Ns2sMB40yIw4) that the version tag has been added, let's insert it everywhere: + +```sql +UPDATE code_v1 +SET code = code || E'\n// @version 1.1' +WHERE code NOT LIKE '%@version%' +AND created_at > '2024-05-31T23:07:26+02:00' +AND created_at < '2025-05-01T12:39:24+02:00' +``` + +4. verify + +we can verify that the edits worked by querying all patterns that contain the new version tag: + +```sql +SELECT * +FROM code_v1 +WHERE code LIKE '%@version 1.1%' +AND created_at > '2024-05-31T23:07:26+02:00' +AND created_at < '2025-05-01T12:39:24+02:00' +ORDER BY created_at ASC; +``` + + ## Have Fun Remember to have fun, and that this project is driven by the passion of volunteers! From 72b582c605bd68beae398877a9c8f41e8dffc716 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 7 Jul 2025 22:20:04 +0200 Subject: [PATCH 48/75] fix: remove github contributor avatars --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index d54302ac6..854e2c317 100644 --- a/README.md +++ b/README.md @@ -40,12 +40,6 @@ Licensing info for the default sound banks can be found over on the [dough-sampl There are many ways to contribute to this project! See [contribution guide](./CONTRIBUTING.md). - - - - -Made with [contrib.rocks](https://contrib.rocks). - ## Community There is a #strudel channel on the TidalCycles discord: From 81a9811803bfdbe89f721da1bbf5a327173d267a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 7 Jul 2025 22:23:16 +0200 Subject: [PATCH 49/75] add logo --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 854e2c317..640fc24c9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # strudel + + Live coding patterns on the web https://strudel.cc/ From 681eed3d0e544062099a78089912aaad82f8670e Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 7 Jul 2025 22:23:40 +0200 Subject: [PATCH 50/75] revert add logo --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 640fc24c9..854e2c317 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # strudel - - Live coding patterns on the web https://strudel.cc/ From a6ad58a3755cda9e049c1378002b735311c3be11 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 7 Jul 2025 23:12:15 +0200 Subject: [PATCH 51/75] fix: remove first gm_synth_bass_1, as it doesn't work in safari --- packages/soundfonts/gm.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/soundfonts/gm.mjs b/packages/soundfonts/gm.mjs index c0e57dcb0..d8b7687d7 100644 --- a/packages/soundfonts/gm.mjs +++ b/packages/soundfonts/gm.mjs @@ -537,7 +537,7 @@ export default { ], gm_synth_bass_1: [ // Synth Bass 1: Bass - '0380_Aspirin_sf2_file', + // '0380_Aspirin_sf2_file', // broken in safari https://codeberg.org/uzu/strudel/issues/1384 '0380_Chaos_sf2_file', '0380_FluidR3_GM_sf2_file', // 0380_GeneralUserGS_sf2_file // laut From 671a22fc221e920e9550f8c8ee13abda1b3d6f96 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 7 Jul 2025 23:28:41 +0200 Subject: [PATCH 52/75] chore: add link to contributors --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 854e2c317..4aa90fd38 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Licensing info for the default sound banks can be found over on the [dough-sampl ## Contributing -There are many ways to contribute to this project! See [contribution guide](./CONTRIBUTING.md). +There are many ways to contribute to this project! See [contribution guide](./CONTRIBUTING.md). You can find the full list of contributors [here](https://codeberg.org/uzu/strudel/activity/contributors). ## Community From ce5f425142852573a68349371870ed1165e2d125 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 8 Jul 2025 00:26:15 -0400 Subject: [PATCH 53/75] fully implemented --- packages/core/controls.mjs | 33 ++- packages/core/cyclist.mjs | 7 +- packages/core/repl.mjs | 6 +- packages/superdough/superdough.mjs | 97 +++---- packages/superdough/worklets.mjs | 79 +----- packages/webaudio/webaudio.mjs | 15 +- test/__snapshots__/examples.test.mjs.snap | 296 ++++++++++------------ 7 files changed, 227 insertions(+), 306 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 12bcc3565..a0f5d10e3 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -448,12 +448,13 @@ export const { coarse } = registerControl('coarse'); * modulate the amplitude of a sound with a continuous waveform * * @name am + * @synonym tremolo * @param {number | Pattern} speed modulation speed in HZ * @example * s("triangle").am("2").amshape("").amdepth(.5) * */ -export const { am, } = registerControl(['am', 'amdepth', 'amskew', 'amphase'],); +export const { am, tremolo } = registerControl(['am', 'amdepth', 'amskew', 'amphase'], 'tremolo'); /** * modulate the amplitude of a sound with a continuous waveform @@ -461,7 +462,7 @@ export const { am, } = registerControl(['am', 'amdepth', 'amskew', 'amphase'],); * @name amsync * @param {number | Pattern} cycles modulation speed in cycles * @example - * s("triangle").am("2").amshape("").amdepth(.5) + * s("supersaw").amsync(1/4).amskew("<0 .5 1>").amdepth(2) * */ export const { amsync } = registerControl(['amsync', 'amdepth', 'amskew', 'amphase']); @@ -482,7 +483,7 @@ export const { amdepth } = registerControl('amdepth'); * @name amskew * @param {number | Pattern} amount between 0 & 1, the shape of the waveform * @example - * note("{f a c e}%16").am(4).amskew("<.5 0 1>") + * note("{f a c e}%16").am(5).amskew(.5).amdepth("<1 0.5 2>").att(.01).rel(.03) * */ export const { amskew } = registerControl('amskew'); @@ -493,7 +494,7 @@ export const { amskew } = registerControl('amskew'); * @name amphase * @param {number | Pattern} offset the offset in cycles of the modulation * @example - * note("{f a c e}%16").am(4).amphase("<0 .25 .66>") + * note("{f a c e}%16").s("sawtooth").am(4).amphase("<0 .25 .66>") * */ export const { amphase } = registerControl('amphase'); @@ -517,6 +518,30 @@ export const { amshape } = registerControl('amshape'); * note("{f g g c d a a#}%16".sub(17)).s("supersaw").lpenv(8).lpf(150).lpq(.8).ftype('ladder').drive("<.5 4>") * */ + +// TODO: SUPRADOUGH implement post orbit "pump" sidechain effect +// /** +// * modulate the amplitude of an orbit to create a "sidechain" like effect +// * +// * @name pump +// * @param {number | Pattern} speed modulation speed in cycles +// * @example +// * note("{f g c d}%16").s("sawtooth").pump(".25:.75") +// * +// */ +// export const { pump } = registerControl(['pump', 'pumpdepth']); + +// /** +// * modulate the amplitude of an orbit to create a "sidechain" like effect +// * +// * @name pumpdepth +// * @param {number | Pattern} depth depth of modulation from 0 to 1 +// * @example +// * note("{f g c d}%16").s("sawtooth").pump(".25").depth("<.25 .5 .75 1>") +// * +// */ +// export const { pumpdepth } = registerControl('pumpdepth'); + export const { drive } = registerControl('drive'); /** diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 43e1f508e..6864301db 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -38,16 +38,13 @@ export class Cyclist { const end = begin + eventLength; this.cycle = begin + secondsSinceLastTick * this.cps; - //account for latency and tick duration when using cycle calculations for audio downstream - const cycle_gap = (this.latency - duration) * this.cps; - const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); haps.forEach((hap) => { if (hap.hasOnset()) { let targetTime = (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps; targetTime = targetTime + this.latency + tickdeadline + time - num_seconds_since_cps_change; const duration = hap.duration / this.cps; - onTrigger?.(hap, tickdeadline, duration, this.cps, targetTime, this.cycle - cycle_gap); + onTrigger?.(hap, tickdeadline, duration, this.cps, targetTime); } }); this.time_at_last_tick_message = time; @@ -119,4 +116,4 @@ export class Cyclist { const onsets = haps.filter((h) => h.hasOnset()); console.log(`${begin.toFixed(4)} - ${end.toFixed(4)} ${Array(onsets.length).fill('I').join('')}`); } -} \ No newline at end of file +} diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 5e696c85f..aeaa6418e 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -244,16 +244,16 @@ export function repl({ export const getTrigger = ({ getTime, defaultOutput }) => - async (hap, deadline, duration, cps, t, cycle = 0) => { + async (hap, deadline, duration, cps, t) => { // ^ this signature is different from hap.context.onTrigger, as set by Pattern.onTrigger(onTrigger) // TODO: get rid of deadline after https://codeberg.org/uzu/strudel/pulls/1004 try { if (!hap.context.onTrigger || !hap.context.dominantTrigger) { - await defaultOutput(hap, deadline, duration, cps, t, cycle); + await defaultOutput(hap, deadline, duration, cps, t); } if (hap.context.onTrigger) { // call signature of output / onTrigger is different... - await hap.context.onTrigger(hap, getTime(), cps, t, cycle); + await hap.context.onTrigger(hap, getTime(), cps, t); } } catch (err) { logger(`[cyclist] error: ${err.message}`, 'error'); diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 79c1ff117..ccb69cceb 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -45,8 +45,6 @@ export function setGainCurve(newGainCurveFunc) { gainCurveFunc = newGainCurveFunc; } - - function aliasBankMap(aliasMap) { // Make all bank keys lower case for case insensitivity for (const key in aliasMap) { @@ -328,22 +326,26 @@ function getDelay(orbit, delaytime, delayfeedback, t, channels) { } export function getLfo(audioContext, begin, end, properties = {}) { + const { dcoffset = -0.5, depth = 1 } = properties; return getWorklet(audioContext, 'lfo-processor', { frequency: 1, - depth: 1, + depth, skew: 0, phaseoffset: 0, time: begin, begin, end, shape: 1, - dcoffset: -0.5, + dcoffset, + min: dcoffset - depth * 0.5, + max: dcoffset + depth * 0.5, + curve: 1, ...properties, }); } export function getSyncedLfo(audioContext, time, end, cps, cycle, properties = {}) { - const frequency = cycle/cps + const frequency = cycle / cps; return getWorklet(audioContext, 'lfo-processor', { frequency, @@ -499,10 +501,11 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle) => { // destructure let { am, + amsync, amdepth = 1, - amskew = 0.5, + amskew = 1, amphase = 0, - amshape = 1, + amshape = 0, s = getDefaultValue('s'), bank, source, @@ -512,6 +515,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle) => { // filters fanchor = getDefaultValue('fanchor'), drive = 0.69, + release = 0, // low pass cutoff, lpenv, @@ -587,8 +591,11 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle) => { distortvol = applyGainCurve(distortvol); delay = applyGainCurve(delay); velocity = applyGainCurve(velocity); + amdepth = applyGainCurve(amdepth); gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future + const end = t + hapDuration; + const endWithRelease = end + release; const chainID = Math.round(Math.random() * 1000000); // oldest audio nodes will be destroyed if maximum polyphony is exceeded @@ -663,7 +670,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle) => { lprelease, lpenv, t, - t + hapDuration, + end, fanchor, ftype, drive, @@ -687,7 +694,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle) => { hprelease, hpenv, t, - t + hapDuration, + end, fanchor, ); chain.push(hp()); @@ -698,20 +705,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle) => { if (bandf !== undefined) { let bp = () => - createFilter( - ac, - 'bandpass', - bandf, - bandq, - bpattack, - bpdecay, - bpsustain, - bprelease, - bpenv, - t, - t + hapDuration, - fanchor, - ); + createFilter(ac, 'bandpass', bandf, bandq, bpattack, bpdecay, bpsustain, bprelease, bpenv, t, end, fanchor); chain.push(bp()); if (ftype === '24db') { chain.push(bp()); @@ -727,45 +721,32 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle) => { coarse !== undefined && chain.push(getWorklet(ac, 'coarse-processor', { coarse })); crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush })); shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol })); - // distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); - // am !== undefined && - // chain.push( - // getWorklet(ac, 'am-processor', { - // speed: am, - // begin: t, - // depth: amdepth, - // skew: amskew, - // phaseoffset: amphase, - // // shape: amshape, - - // cps, - // cycle, - // }), - // ); + distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); + if (amsync != null) { + am = cps / amsync; + } if (am !== undefined) { - const amGain = new GainNode(ac, { gain: 1 }); - const frequency = cps / am - const phaseoffset = cycleToSeconds(amphase, cps) + // Allow clipping of modulator for more dynamic possiblities, and to prevent speaker overload + // EX: a triangle waveform will clip like this /-\ when the depth is above 1 + const gain = Math.max(1 - amdepth, 0); + const amGain = new GainNode(ac, { gain }); - - const time = cycle / cps - - // console.info(cycle, time, frequency) - - - - const lfo = getLfo(ac, t, t + hapDuration, { - skew: amskew, - frequency, + const time = cycle / cps; + const lfo = getLfo(ac, t, endWithRelease, { + skew: amskew, + frequency: am, depth: amdepth, time, - // dcoffset: 0, - shape: amshape, - phaseoffset - }) - lfo.connect(amGain.gain) - chain.push(amGain) + dcoffset: 0, + shape: amshape, + phaseoffset: amphase, + min: 0, + max: 1, + curve: 1.5, + }); + lfo.connect(amGain.gain); + chain.push(amGain); } compressorThreshold !== undefined && @@ -781,7 +762,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle) => { } // phaser if (phaser !== undefined && phaserdepth > 0) { - const phaserFX = getPhaser(t, t + hapDuration, phaser, phaserdepth, phasercenter, phasersweep); + const phaserFX = getPhaser(t, endWithRelease, phaser, phaserdepth, phasercenter, phasersweep); chain.push(phaserFX); } diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 366be8a5e..466be558a 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -93,7 +93,10 @@ class LFOProcessor extends AudioWorkletProcessor { { name: 'depth', defaultValue: 1 }, { name: 'phaseoffset', defaultValue: 0 }, { name: 'shape', defaultValue: 0 }, + { name: 'curve', defaultValue: 1 }, { name: 'dcoffset', defaultValue: 0 }, + { name: 'min', defaultValue: 0 }, + { name: 'max', defaultValue: 1 }, ]; } @@ -110,7 +113,7 @@ class LFOProcessor extends AudioWorkletProcessor { } process(inputs, outputs, parameters) { - const begin = parameters['begin'][0] + const begin = parameters['begin'][0]; // eslint-disable-next-line no-undef if (currentTime >= parameters.end[0]) { return false; @@ -126,6 +129,9 @@ class LFOProcessor extends AudioWorkletProcessor { const depth = parameters['depth'][0]; const skew = parameters['skew'][0]; const phaseoffset = parameters['phaseoffset'][0]; + const min = parameters['min'][0]; + const max = parameters['max'][0]; + const curve = parameters['curve'][0]; const dcoffset = parameters['dcoffset'][0]; const shape = waveShapeNames[parameters['shape'][0]]; @@ -140,7 +146,7 @@ class LFOProcessor extends AudioWorkletProcessor { for (let n = 0; n < blockSize; n++) { for (let i = 0; i < output.length; i++) { const modval = (waveshapes[shape](this.phase, skew) + dcoffset) * depth; - output[i][n] = modval; + output[i][n] = clamp(Math.pow(modval, curve), min, max); } this.incrementPhase(dt); } @@ -164,8 +170,6 @@ class CoarseProcessor extends AudioWorkletProcessor { const input = inputs[0]; const output = outputs[0]; - - const hasInput = !(input[0] === undefined); if (this.started && !hasInput) { return false; @@ -901,70 +905,3 @@ class ByteBeatProcessor extends AudioWorkletProcessor { } registerProcessor('byte-beat-processor', ByteBeatProcessor); - - -class AMProcessor extends AudioWorkletProcessor { - static get parameterDescriptors() { - return [ - { name: 'begin', defaultValue: 0 }, - { name: 'cps', defaultValue: 0.5 }, - { name: 'speed', defaultValue: 0.5 }, - { name: 'cycle', defaultValue: 0 }, - { name: 'skew', defaultValue: 0.5 }, - { name: 'depth', defaultValue: 1 }, - { name: 'phaseoffset', defaultValue: 0 }, - ]; - } - - constructor() { - super(); - this.phase; - this.started = false; - } - - incrementPhase(dt) { - this.phase += dt; - if (this.phase > 1.0) { - this.phase = this.phase - 1; - } - } - - process(inputs, outputs, parameters) { - const input = inputs[0]; - const output = outputs[0]; - const hasInput = !(input[0] === undefined); - - - if (this.started && !hasInput) { - return false; - } - this.started = hasInput; - if (currentTime <= parameters.begin[0]) { - return true; - } - - const speed = parameters['speed'][0]; - const cps = parameters['cps'][0]; - const cycle = parameters['cycle'][0]; - const depth = parameters['depth'][0]; - const skew = parameters['skew'][0]; - const phaseoffset = parameters['phaseoffset'][0]; - - const frequency = cps / speed - if (this.phase == null) { - const secondsPassed = cycle / cps; - this.phase = _mod(secondsPassed * frequency + phaseoffset, 1); - } - // eslint-disable-next-line no-undef - const dt = frequency / sampleRate; - for (let n = 0; n < blockSize; n++) { - for (let i = 0; i < input.length; i++) { - const modval = clamp(waveshapes.tri(this.phase, skew) * depth + (1 - depth), 0, 1); - output[i][n] = input[i][n] * modval; - } - this.incrementPhase(dt); - } - return true; - } -} -registerProcessor('am-processor', AMProcessor); diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 78441ee55..1e8455b3f 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -17,12 +17,15 @@ const hap2value = (hap) => { // uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004 // TODO: refactor output callbacks to eliminate deadline -export const webaudioOutput = (hap, deadline, hapDuration, cps, t, cycle) => { - - return superdough(hap2value(hap), t, hapDuration, cps, - hap.whole.begin.valueOf() - // cycle -); +export const webaudioOutput = (hap, _deadline, hapDuration, cps, t) => { + return superdough( + hap2value(hap), + t, + hapDuration, + cps, + hap.whole.begin.valueOf(), + // cycle + ); }; export function webaudioRepl(options = {}) { diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 4ddf79eff..50d4f2095 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -860,70 +860,70 @@ exports[`runs examples > example "amp" example index 0 1`] = ` exports[`runs examples > example "amphase" example index 0 1`] = ` [ - "[ 0/1 → 1/16 | note:f am:4 amphase:0 ]", - "[ 1/16 → 1/8 | note:a am:4 amphase:0 ]", - "[ 1/8 → 3/16 | note:c am:4 amphase:0 ]", - "[ 3/16 → 1/4 | note:e am:4 amphase:0 ]", - "[ 1/4 → 5/16 | note:f am:4 amphase:0 ]", - "[ 5/16 → 3/8 | note:a am:4 amphase:0 ]", - "[ 3/8 → 7/16 | note:c am:4 amphase:0 ]", - "[ 7/16 → 1/2 | note:e am:4 amphase:0 ]", - "[ 1/2 → 9/16 | note:f am:4 amphase:0 ]", - "[ 9/16 → 5/8 | note:a am:4 amphase:0 ]", - "[ 5/8 → 11/16 | note:c am:4 amphase:0 ]", - "[ 11/16 → 3/4 | note:e am:4 amphase:0 ]", - "[ 3/4 → 13/16 | note:f am:4 amphase:0 ]", - "[ 13/16 → 7/8 | note:a am:4 amphase:0 ]", - "[ 7/8 → 15/16 | note:c am:4 amphase:0 ]", - "[ 15/16 → 1/1 | note:e am:4 amphase:0 ]", - "[ 1/1 → 17/16 | note:f am:4 amphase:0.25 ]", - "[ 17/16 → 9/8 | note:a am:4 amphase:0.25 ]", - "[ 9/8 → 19/16 | note:c am:4 amphase:0.25 ]", - "[ 19/16 → 5/4 | note:e am:4 amphase:0.25 ]", - "[ 5/4 → 21/16 | note:f am:4 amphase:0.25 ]", - "[ 21/16 → 11/8 | note:a am:4 amphase:0.25 ]", - "[ 11/8 → 23/16 | note:c am:4 amphase:0.25 ]", - "[ 23/16 → 3/2 | note:e am:4 amphase:0.25 ]", - "[ 3/2 → 25/16 | note:f am:4 amphase:0.25 ]", - "[ 25/16 → 13/8 | note:a am:4 amphase:0.25 ]", - "[ 13/8 → 27/16 | note:c am:4 amphase:0.25 ]", - "[ 27/16 → 7/4 | note:e am:4 amphase:0.25 ]", - "[ 7/4 → 29/16 | note:f am:4 amphase:0.25 ]", - "[ 29/16 → 15/8 | note:a am:4 amphase:0.25 ]", - "[ 15/8 → 31/16 | note:c am:4 amphase:0.25 ]", - "[ 31/16 → 2/1 | note:e am:4 amphase:0.25 ]", - "[ 2/1 → 33/16 | note:f am:4 amphase:0.66 ]", - "[ 33/16 → 17/8 | note:a am:4 amphase:0.66 ]", - "[ 17/8 → 35/16 | note:c am:4 amphase:0.66 ]", - "[ 35/16 → 9/4 | note:e am:4 amphase:0.66 ]", - "[ 9/4 → 37/16 | note:f am:4 amphase:0.66 ]", - "[ 37/16 → 19/8 | note:a am:4 amphase:0.66 ]", - "[ 19/8 → 39/16 | note:c am:4 amphase:0.66 ]", - "[ 39/16 → 5/2 | note:e am:4 amphase:0.66 ]", - "[ 5/2 → 41/16 | note:f am:4 amphase:0.66 ]", - "[ 41/16 → 21/8 | note:a am:4 amphase:0.66 ]", - "[ 21/8 → 43/16 | note:c am:4 amphase:0.66 ]", - "[ 43/16 → 11/4 | note:e am:4 amphase:0.66 ]", - "[ 11/4 → 45/16 | note:f am:4 amphase:0.66 ]", - "[ 45/16 → 23/8 | note:a am:4 amphase:0.66 ]", - "[ 23/8 → 47/16 | note:c am:4 amphase:0.66 ]", - "[ 47/16 → 3/1 | note:e am:4 amphase:0.66 ]", - "[ 3/1 → 49/16 | note:f am:4 amphase:0 ]", - "[ 49/16 → 25/8 | note:a am:4 amphase:0 ]", - "[ 25/8 → 51/16 | note:c am:4 amphase:0 ]", - "[ 51/16 → 13/4 | note:e am:4 amphase:0 ]", - "[ 13/4 → 53/16 | note:f am:4 amphase:0 ]", - "[ 53/16 → 27/8 | note:a am:4 amphase:0 ]", - "[ 27/8 → 55/16 | note:c am:4 amphase:0 ]", - "[ 55/16 → 7/2 | note:e am:4 amphase:0 ]", - "[ 7/2 → 57/16 | note:f am:4 amphase:0 ]", - "[ 57/16 → 29/8 | note:a am:4 amphase:0 ]", - "[ 29/8 → 59/16 | note:c am:4 amphase:0 ]", - "[ 59/16 → 15/4 | note:e am:4 amphase:0 ]", - "[ 15/4 → 61/16 | note:f am:4 amphase:0 ]", - "[ 61/16 → 31/8 | note:a am:4 amphase:0 ]", - "[ 31/8 → 63/16 | note:c am:4 amphase:0 ]", - "[ 63/16 → 4/1 | note:e am:4 amphase:0 ]", + "[ 0/1 → 1/16 | note:f s:sawtooth am:4 amphase:0 ]", + "[ 1/16 → 1/8 | note:a s:sawtooth am:4 amphase:0 ]", + "[ 1/8 → 3/16 | note:c s:sawtooth am:4 amphase:0 ]", + "[ 3/16 → 1/4 | note:e s:sawtooth am:4 amphase:0 ]", + "[ 1/4 → 5/16 | note:f s:sawtooth am:4 amphase:0 ]", + "[ 5/16 → 3/8 | note:a s:sawtooth am:4 amphase:0 ]", + "[ 3/8 → 7/16 | note:c s:sawtooth am:4 amphase:0 ]", + "[ 7/16 → 1/2 | note:e s:sawtooth am:4 amphase:0 ]", + "[ 1/2 → 9/16 | note:f s:sawtooth am:4 amphase:0 ]", + "[ 9/16 → 5/8 | note:a s:sawtooth am:4 amphase:0 ]", + "[ 5/8 → 11/16 | note:c s:sawtooth am:4 amphase:0 ]", + "[ 11/16 → 3/4 | note:e s:sawtooth am:4 amphase:0 ]", + "[ 3/4 → 13/16 | note:f s:sawtooth am:4 amphase:0 ]", + "[ 13/16 → 7/8 | note:a s:sawtooth am:4 amphase:0 ]", + "[ 7/8 → 15/16 | note:c s:sawtooth am:4 amphase:0 ]", + "[ 15/16 → 1/1 | note:e s:sawtooth am:4 amphase:0 ]", + "[ 1/1 → 17/16 | note:f s:sawtooth am:4 amphase:0.25 ]", + "[ 17/16 → 9/8 | note:a s:sawtooth am:4 amphase:0.25 ]", + "[ 9/8 → 19/16 | note:c s:sawtooth am:4 amphase:0.25 ]", + "[ 19/16 → 5/4 | note:e s:sawtooth am:4 amphase:0.25 ]", + "[ 5/4 → 21/16 | note:f s:sawtooth am:4 amphase:0.25 ]", + "[ 21/16 → 11/8 | note:a s:sawtooth am:4 amphase:0.25 ]", + "[ 11/8 → 23/16 | note:c s:sawtooth am:4 amphase:0.25 ]", + "[ 23/16 → 3/2 | note:e s:sawtooth am:4 amphase:0.25 ]", + "[ 3/2 → 25/16 | note:f s:sawtooth am:4 amphase:0.25 ]", + "[ 25/16 → 13/8 | note:a s:sawtooth am:4 amphase:0.25 ]", + "[ 13/8 → 27/16 | note:c s:sawtooth am:4 amphase:0.25 ]", + "[ 27/16 → 7/4 | note:e s:sawtooth am:4 amphase:0.25 ]", + "[ 7/4 → 29/16 | note:f s:sawtooth am:4 amphase:0.25 ]", + "[ 29/16 → 15/8 | note:a s:sawtooth am:4 amphase:0.25 ]", + "[ 15/8 → 31/16 | note:c s:sawtooth am:4 amphase:0.25 ]", + "[ 31/16 → 2/1 | note:e s:sawtooth am:4 amphase:0.25 ]", + "[ 2/1 → 33/16 | note:f s:sawtooth am:4 amphase:0.66 ]", + "[ 33/16 → 17/8 | note:a s:sawtooth am:4 amphase:0.66 ]", + "[ 17/8 → 35/16 | note:c s:sawtooth am:4 amphase:0.66 ]", + "[ 35/16 → 9/4 | note:e s:sawtooth am:4 amphase:0.66 ]", + "[ 9/4 → 37/16 | note:f s:sawtooth am:4 amphase:0.66 ]", + "[ 37/16 → 19/8 | note:a s:sawtooth am:4 amphase:0.66 ]", + "[ 19/8 → 39/16 | note:c s:sawtooth am:4 amphase:0.66 ]", + "[ 39/16 → 5/2 | note:e s:sawtooth am:4 amphase:0.66 ]", + "[ 5/2 → 41/16 | note:f s:sawtooth am:4 amphase:0.66 ]", + "[ 41/16 → 21/8 | note:a s:sawtooth am:4 amphase:0.66 ]", + "[ 21/8 → 43/16 | note:c s:sawtooth am:4 amphase:0.66 ]", + "[ 43/16 → 11/4 | note:e s:sawtooth am:4 amphase:0.66 ]", + "[ 11/4 → 45/16 | note:f s:sawtooth am:4 amphase:0.66 ]", + "[ 45/16 → 23/8 | note:a s:sawtooth am:4 amphase:0.66 ]", + "[ 23/8 → 47/16 | note:c s:sawtooth am:4 amphase:0.66 ]", + "[ 47/16 → 3/1 | note:e s:sawtooth am:4 amphase:0.66 ]", + "[ 3/1 → 49/16 | note:f s:sawtooth am:4 amphase:0 ]", + "[ 49/16 → 25/8 | note:a s:sawtooth am:4 amphase:0 ]", + "[ 25/8 → 51/16 | note:c s:sawtooth am:4 amphase:0 ]", + "[ 51/16 → 13/4 | note:e s:sawtooth am:4 amphase:0 ]", + "[ 13/4 → 53/16 | note:f s:sawtooth am:4 amphase:0 ]", + "[ 53/16 → 27/8 | note:a s:sawtooth am:4 amphase:0 ]", + "[ 27/8 → 55/16 | note:c s:sawtooth am:4 amphase:0 ]", + "[ 55/16 → 7/2 | note:e s:sawtooth am:4 amphase:0 ]", + "[ 7/2 → 57/16 | note:f s:sawtooth am:4 amphase:0 ]", + "[ 57/16 → 29/8 | note:a s:sawtooth am:4 amphase:0 ]", + "[ 29/8 → 59/16 | note:c s:sawtooth am:4 amphase:0 ]", + "[ 59/16 → 15/4 | note:e s:sawtooth am:4 amphase:0 ]", + "[ 15/4 → 61/16 | note:f s:sawtooth am:4 amphase:0 ]", + "[ 61/16 → 31/8 | note:a s:sawtooth am:4 amphase:0 ]", + "[ 31/8 → 63/16 | note:c s:sawtooth am:4 amphase:0 ]", + "[ 63/16 → 4/1 | note:e s:sawtooth am:4 amphase:0 ]", ] `; @@ -998,70 +998,79 @@ exports[`runs examples > example "amshape" example index 0 1`] = ` exports[`runs examples > example "amskew" example index 0 1`] = ` [ - "[ 0/1 → 1/16 | note:f am:4 amskew:0.5 ]", - "[ 1/16 → 1/8 | note:a am:4 amskew:0.5 ]", - "[ 1/8 → 3/16 | note:c am:4 amskew:0.5 ]", - "[ 3/16 → 1/4 | note:e am:4 amskew:0.5 ]", - "[ 1/4 → 5/16 | note:f am:4 amskew:0.5 ]", - "[ 5/16 → 3/8 | note:a am:4 amskew:0.5 ]", - "[ 3/8 → 7/16 | note:c am:4 amskew:0.5 ]", - "[ 7/16 → 1/2 | note:e am:4 amskew:0.5 ]", - "[ 1/2 → 9/16 | note:f am:4 amskew:0.5 ]", - "[ 9/16 → 5/8 | note:a am:4 amskew:0.5 ]", - "[ 5/8 → 11/16 | note:c am:4 amskew:0.5 ]", - "[ 11/16 → 3/4 | note:e am:4 amskew:0.5 ]", - "[ 3/4 → 13/16 | note:f am:4 amskew:0.5 ]", - "[ 13/16 → 7/8 | note:a am:4 amskew:0.5 ]", - "[ 7/8 → 15/16 | note:c am:4 amskew:0.5 ]", - "[ 15/16 → 1/1 | note:e am:4 amskew:0.5 ]", - "[ 1/1 → 17/16 | note:f am:4 amskew:0 ]", - "[ 17/16 → 9/8 | note:a am:4 amskew:0 ]", - "[ 9/8 → 19/16 | note:c am:4 amskew:0 ]", - "[ 19/16 → 5/4 | note:e am:4 amskew:0 ]", - "[ 5/4 → 21/16 | note:f am:4 amskew:0 ]", - "[ 21/16 → 11/8 | note:a am:4 amskew:0 ]", - "[ 11/8 → 23/16 | note:c am:4 amskew:0 ]", - "[ 23/16 → 3/2 | note:e am:4 amskew:0 ]", - "[ 3/2 → 25/16 | note:f am:4 amskew:0 ]", - "[ 25/16 → 13/8 | note:a am:4 amskew:0 ]", - "[ 13/8 → 27/16 | note:c am:4 amskew:0 ]", - "[ 27/16 → 7/4 | note:e am:4 amskew:0 ]", - "[ 7/4 → 29/16 | note:f am:4 amskew:0 ]", - "[ 29/16 → 15/8 | note:a am:4 amskew:0 ]", - "[ 15/8 → 31/16 | note:c am:4 amskew:0 ]", - "[ 31/16 → 2/1 | note:e am:4 amskew:0 ]", - "[ 2/1 → 33/16 | note:f am:4 amskew:1 ]", - "[ 33/16 → 17/8 | note:a am:4 amskew:1 ]", - "[ 17/8 → 35/16 | note:c am:4 amskew:1 ]", - "[ 35/16 → 9/4 | note:e am:4 amskew:1 ]", - "[ 9/4 → 37/16 | note:f am:4 amskew:1 ]", - "[ 37/16 → 19/8 | note:a am:4 amskew:1 ]", - "[ 19/8 → 39/16 | note:c am:4 amskew:1 ]", - "[ 39/16 → 5/2 | note:e am:4 amskew:1 ]", - "[ 5/2 → 41/16 | note:f am:4 amskew:1 ]", - "[ 41/16 → 21/8 | note:a am:4 amskew:1 ]", - "[ 21/8 → 43/16 | note:c am:4 amskew:1 ]", - "[ 43/16 → 11/4 | note:e am:4 amskew:1 ]", - "[ 11/4 → 45/16 | note:f am:4 amskew:1 ]", - "[ 45/16 → 23/8 | note:a am:4 amskew:1 ]", - "[ 23/8 → 47/16 | note:c am:4 amskew:1 ]", - "[ 47/16 → 3/1 | note:e am:4 amskew:1 ]", - "[ 3/1 → 49/16 | note:f am:4 amskew:0.5 ]", - "[ 49/16 → 25/8 | note:a am:4 amskew:0.5 ]", - "[ 25/8 → 51/16 | note:c am:4 amskew:0.5 ]", - "[ 51/16 → 13/4 | note:e am:4 amskew:0.5 ]", - "[ 13/4 → 53/16 | note:f am:4 amskew:0.5 ]", - "[ 53/16 → 27/8 | note:a am:4 amskew:0.5 ]", - "[ 27/8 → 55/16 | note:c am:4 amskew:0.5 ]", - "[ 55/16 → 7/2 | note:e am:4 amskew:0.5 ]", - "[ 7/2 → 57/16 | note:f am:4 amskew:0.5 ]", - "[ 57/16 → 29/8 | note:a am:4 amskew:0.5 ]", - "[ 29/8 → 59/16 | note:c am:4 amskew:0.5 ]", - "[ 59/16 → 15/4 | note:e am:4 amskew:0.5 ]", - "[ 15/4 → 61/16 | note:f am:4 amskew:0.5 ]", - "[ 61/16 → 31/8 | note:a am:4 amskew:0.5 ]", - "[ 31/8 → 63/16 | note:c am:4 amskew:0.5 ]", - "[ 63/16 → 4/1 | note:e am:4 amskew:0.5 ]", + "[ 0/1 → 1/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 1/16 → 1/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 1/8 → 3/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 3/16 → 1/4 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 1/4 → 5/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 5/16 → 3/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 3/8 → 7/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 7/16 → 1/2 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 1/2 → 9/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 9/16 → 5/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 5/8 → 11/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 11/16 → 3/4 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 3/4 → 13/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 13/16 → 7/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 7/8 → 15/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 15/16 → 1/1 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 1/1 → 17/16 | note:f am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 17/16 → 9/8 | note:a am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 9/8 → 19/16 | note:c am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 19/16 → 5/4 | note:e am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 5/4 → 21/16 | note:f am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 21/16 → 11/8 | note:a am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 11/8 → 23/16 | note:c am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 23/16 → 3/2 | note:e am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 3/2 → 25/16 | note:f am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 25/16 → 13/8 | note:a am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 13/8 → 27/16 | note:c am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 27/16 → 7/4 | note:e am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 7/4 → 29/16 | note:f am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 29/16 → 15/8 | note:a am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 15/8 → 31/16 | note:c am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 31/16 → 2/1 | note:e am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", + "[ 2/1 → 33/16 | note:f am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 33/16 → 17/8 | note:a am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 17/8 → 35/16 | note:c am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 35/16 → 9/4 | note:e am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 9/4 → 37/16 | note:f am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 37/16 → 19/8 | note:a am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 19/8 → 39/16 | note:c am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 39/16 → 5/2 | note:e am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 5/2 → 41/16 | note:f am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 41/16 → 21/8 | note:a am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 21/8 → 43/16 | note:c am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 43/16 → 11/4 | note:e am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 11/4 → 45/16 | note:f am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 45/16 → 23/8 | note:a am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 23/8 → 47/16 | note:c am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 47/16 → 3/1 | note:e am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", + "[ 3/1 → 49/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 49/16 → 25/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 25/8 → 51/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 51/16 → 13/4 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 13/4 → 53/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 53/16 → 27/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 27/8 → 55/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 55/16 → 7/2 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 7/2 → 57/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 57/16 → 29/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 29/8 → 59/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 59/16 → 15/4 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 15/4 → 61/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 61/16 → 31/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 31/8 → 63/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 63/16 → 4/1 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", +] +`; + +exports[`runs examples > example "amsync" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | s:supersaw amsync:0.25 amskew:0 amdepth:2 ]", + "[ 1/1 → 2/1 | s:supersaw amsync:0.25 amskew:0.5 amdepth:2 ]", + "[ 2/1 → 3/1 | s:supersaw amsync:0.25 amskew:1 amdepth:2 ]", + "[ 3/1 → 4/1 | s:supersaw amsync:0.25 amskew:0 amdepth:2 ]", ] `; @@ -7076,8 +7085,6 @@ exports[`runs examples > example "ply" example index 0 1`] = ` ] `; -<<<<<<< HEAD -======= exports[`runs examples > example "polymeter" example index 0 1`] = ` [ "[ 0/1 → 1/6 | note:c ]", @@ -7131,7 +7138,6 @@ exports[`runs examples > example "polymeter" example index 0 1`] = ` ] `; ->>>>>>> main exports[`runs examples > example "postgain" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", @@ -9785,30 +9791,6 @@ exports[`runs examples > example "stack" example index 1 1`] = ` ] `; -<<<<<<< HEAD -exports[`runs examples > example "stack" example index 0 2`] = ` -[ - "[ 0/1 → 1/2 | note:e4 ]", - "[ 0/1 → 1/1 | note:g3 ]", - "[ 0/1 → 1/1 | note:b3 ]", - "[ 1/2 → 1/1 | note:d4 ]", - "[ 1/1 → 3/2 | note:e4 ]", - "[ 1/1 → 2/1 | note:g3 ]", - "[ 1/1 → 2/1 | note:b3 ]", - "[ 3/2 → 2/1 | note:d4 ]", - "[ 2/1 → 5/2 | note:e4 ]", - "[ 2/1 → 3/1 | note:g3 ]", - "[ 2/1 → 3/1 | note:b3 ]", - "[ 5/2 → 3/1 | note:d4 ]", - "[ 3/1 → 7/2 | note:e4 ]", - "[ 3/1 → 4/1 | note:g3 ]", - "[ 3/1 → 4/1 | note:b3 ]", - "[ 7/2 → 4/1 | note:d4 ]", -] -`; - -exports[`runs examples > example "steps" example index 0 1`] = ` -======= exports[`runs examples > example "stepalt" example index 0 1`] = ` [ "[ 0/1 → 1/5 | s:bd ]", @@ -9835,7 +9817,6 @@ exports[`runs examples > example "stepalt" example index 0 1`] = ` `; exports[`runs examples > example "stepcat" example index 0 1`] = ` ->>>>>>> main [ "[ 0/1 → 3/4 | note:e3 ]", "[ 3/4 → 1/1 | note:g3 ]", @@ -10182,8 +10163,6 @@ exports[`runs examples > example "swingBy" example index 0 1`] = ` ] `; -<<<<<<< HEAD -======= exports[`runs examples > example "sysex" example index 0 1`] = ` [ "[ 0/1 → 1/1 | note:c4 sysexid:119 sysexdata:[1 2 3 4] midichan:1 ]", @@ -10323,7 +10302,6 @@ exports[`runs examples > example "tour" example index 0 1`] = ` ] `; ->>>>>>> main exports[`runs examples > example "transpose" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:C2 ]", From 1dd1ce254f6700e708e4afb838733f17a125301b Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 8 Jul 2025 00:30:00 -0400 Subject: [PATCH 54/75] rm deadcode --- packages/core/cyclist copy.mjs | 140 --------------------------------- 1 file changed, 140 deletions(-) delete mode 100644 packages/core/cyclist copy.mjs diff --git a/packages/core/cyclist copy.mjs b/packages/core/cyclist copy.mjs deleted file mode 100644 index fec819548..000000000 --- a/packages/core/cyclist copy.mjs +++ /dev/null @@ -1,140 +0,0 @@ -/* -cyclist.mjs - event scheduler for a single strudel instance. for multi-instance scheduler, see - see -Copyright (C) 2022 Strudel contributors - see -This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -*/ - -import createClock from './zyklus.mjs'; -import { logger } from './logger.mjs'; - -export class Cyclist { - constructor({ - interval, - onTrigger, - onToggle, - onError, - getTime, - latency = 0.1, - setInterval, - clearInterval, - beforeStart, - }) { - this.started = false; - this.beforeStart = beforeStart; - this.cps = 0.5; - this.num_ticks_since_cps_change = 0; - this.lastTick = 0; // absolute time when last tick (clock callback) happened - this.lastBegin = 0; // query begin of last tick - this.lastEnd = 0; // query end of last tick - this.getTime = getTime; // get absolute time - this.num_cycles_at_cps_change = 0; - this.seconds_at_cps_change; // clock phase when cps was changed - this.onToggle = onToggle; - this.latency = latency; // fixed trigger time offset - this.clock = createClock( - getTime, - // called slightly before each cycle - (phase, duration, _, t) => { - if (this.num_ticks_since_cps_change === 0) { - this.num_cycles_at_cps_change = this.lastEnd; - this.seconds_at_cps_change = phase; - } - this.num_ticks_since_cps_change++; - const seconds_since_cps_change = this.num_ticks_since_cps_change * duration; - const num_cycles_since_cps_change = seconds_since_cps_change * this.cps; - - try { - const begin = this.lastEnd; - this.lastBegin = begin; - const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; - this.lastEnd = end; - this.lastTick = phase; - - if (phase < t) { - // avoid querying haps that are in the past anyway - console.log(`skip query: too late`); - return; - } - - // query the pattern for events - const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); - - haps.forEach((hap) => { - if (hap.hasOnset()) { - const targetTime = - (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps + this.seconds_at_cps_change + latency; - const duration = hap.duration / this.cps; - // the following line is dumb and only here for backwards compatibility - // see https://codeberg.org/uzu/strudel/pulls/1004 - const deadline = targetTime - phase; - // this onTrigger has another signature - onTrigger?.(hap, deadline, duration, this.cps, targetTime); - if (hap.value.cps !== undefined && this.cps != hap.value.cps) { - this.cps = hap.value.cps; - this.num_ticks_since_cps_change = 0; - } - } - }); - } catch (e) { - logger(`[cyclist] error: ${e.message}`); - onError?.(e); - } - }, - interval, // duration of each cycle - 0.1, - 0.1, - setInterval, - clearInterval, - ); - } - now() { - if (!this.started) { - return 0; - } - const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration; - return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency; - } - setStarted(v) { - this.started = v; - this.onToggle?.(v); - } - async start() { - await this.beforeStart?.(); - this.num_ticks_since_cps_change = 0; - this.num_cycles_at_cps_change = 0; - if (!this.pattern) { - throw new Error('Scheduler: no pattern set! call .setPattern first.'); - } - logger('[cyclist] start'); - this.clock.start(); - this.setStarted(true); - } - pause() { - logger('[cyclist] pause'); - this.clock.pause(); - this.setStarted(false); - } - stop() { - logger('[cyclist] stop'); - this.clock.stop(); - this.lastEnd = 0; - this.setStarted(false); - } - async setPattern(pat, autostart = false) { - this.pattern = pat; - if (autostart && !this.started) { - await this.start(); - } - } - setCps(cps = 0.5) { - if (this.cps === cps) { - return; - } - this.cps = cps; - this.num_ticks_since_cps_change = 0; - } - log(begin, end, haps) { - const onsets = haps.filter((h) => h.hasOnset()); - console.log(`${begin.toFixed(4)} - ${end.toFixed(4)} ${Array(onsets.length).fill('I').join('')}`); - } -} From 6327e17feb4b1a7f443134ab24f0e31a086911ec Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 8 Jul 2025 00:44:57 -0400 Subject: [PATCH 55/75] remove unessecary cyclist changes --- packages/core/cyclist.mjs | 112 ++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 6864301db..f28dc604c 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -1,6 +1,6 @@ /* -cyclist.mjs - event scheduler for a single strudel instance. for multi-instance scheduler, see - see -Copyright (C) 2022 Strudel contributors - see +cyclist.mjs - event scheduler for a single strudel instance. for multi-instance scheduler, see - see +Copyright (C) 2022 Strudel contributors - see This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ @@ -8,47 +8,76 @@ import createClock from './zyklus.mjs'; import { logger } from './logger.mjs'; export class Cyclist { - constructor({ interval = 0.05, onTrigger, onToggle, onError, getTime, latency = 0.1, setInterval, clearInterval }) { + constructor({ + interval, + onTrigger, + onToggle, + onError, + getTime, + latency = 0.1, + setInterval, + clearInterval, + beforeStart, + }) { this.started = false; + this.beforeStart = beforeStart; this.cps = 0.5; - this.time_at_last_tick_message = 0; - this.cycle = 0; + this.num_ticks_since_cps_change = 0; + this.lastTick = 0; // absolute time when last tick (clock callback) happened + this.lastBegin = 0; // query begin of last tick + this.lastEnd = 0; // query end of last tick this.getTime = getTime; // get absolute time this.num_cycles_at_cps_change = 0; this.seconds_at_cps_change; // clock phase when cps was changed - this.num_ticks_since_cps_change = 0; this.onToggle = onToggle; this.latency = latency; // fixed trigger time offset - - this.interval = interval; - this.clock = createClock( getTime, // called slightly before each cycle - (lastTick, duration, _, time) => { - if (this.started === false) { - return; + (phase, duration, _, t) => { + if (this.num_ticks_since_cps_change === 0) { + this.num_cycles_at_cps_change = this.lastEnd; + this.seconds_at_cps_change = phase; } - const num_seconds_since_cps_change = this.num_ticks_since_cps_change * duration; - const tickdeadline = lastTick - time; - const num_cycles_since_cps_change = num_seconds_since_cps_change * this.cps; - const begin = this.num_cycles_at_cps_change + num_cycles_since_cps_change; - const secondsSinceLastTick = time - lastTick - duration; - const eventLength = duration * this.cps; - const end = begin + eventLength; - this.cycle = begin + secondsSinceLastTick * this.cps; - - const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); - haps.forEach((hap) => { - if (hap.hasOnset()) { - let targetTime = (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps; - targetTime = targetTime + this.latency + tickdeadline + time - num_seconds_since_cps_change; - const duration = hap.duration / this.cps; - onTrigger?.(hap, tickdeadline, duration, this.cps, targetTime); - } - }); - this.time_at_last_tick_message = time; this.num_ticks_since_cps_change++; + const seconds_since_cps_change = this.num_ticks_since_cps_change * duration; + const num_cycles_since_cps_change = seconds_since_cps_change * this.cps; + + try { + const begin = this.lastEnd; + this.lastBegin = begin; + const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; + this.lastEnd = end; + this.lastTick = phase; + + if (phase < t) { + // avoid querying haps that are in the past anyway + console.log(`skip query: too late`); + return; + } + + // query the pattern for events + const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); + + haps.forEach((hap) => { + if (hap.hasOnset()) { + const targetTime = + (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps + this.seconds_at_cps_change + latency; + const duration = hap.duration / this.cps; + // the following line is dumb and only here for backwards compatibility + // see https://codeberg.org/uzu/strudel/pulls/1004 + const deadline = targetTime - phase; + onTrigger?.(hap, deadline, duration, this.cps, targetTime); + if (hap.value.cps !== undefined && this.cps != hap.value.cps) { + this.cps = hap.value.cps; + this.num_ticks_since_cps_change = 0; + } + } + }); + } catch (e) { + logger(`[cyclist] error: ${e.message}`); + onError?.(e); + } }, interval, // duration of each cycle 0.1, @@ -61,24 +90,17 @@ export class Cyclist { if (!this.started) { return 0; } - const gap = (this.getTime() - this.time_at_last_tick_message) * this.cps; - return this.cycle + gap; + const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration; + return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency; } - - setCycle = (cycle) => { - this.num_ticks_since_cps_change = 0; - this.num_cycles_at_cps_change = cycle; - }; setStarted(v) { this.started = v; - - this.setCycle(0); this.onToggle?.(v); } - start() { + async start() { + await this.beforeStart?.(); this.num_ticks_since_cps_change = 0; this.num_cycles_at_cps_change = 0; - if (!this.pattern) { throw new Error('Scheduler: no pattern set! call .setPattern first.'); } @@ -97,18 +119,16 @@ export class Cyclist { this.lastEnd = 0; this.setStarted(false); } - setPattern(pat, autostart = false) { + async setPattern(pat, autostart = false) { this.pattern = pat; if (autostart && !this.started) { - this.start(); + await this.start(); } } setCps(cps = 0.5) { if (this.cps === cps) { return; } - const num_seconds_since_cps_change = this.num_ticks_since_cps_change * this.interval; - this.num_cycles_at_cps_change = this.num_cycles_at_cps_change + num_seconds_since_cps_change * cps; this.cps = cps; this.num_ticks_since_cps_change = 0; } From 9b3c6b9f9483ccf6fe60a3964bf24afbd9d219db Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 8 Jul 2025 00:46:39 -0400 Subject: [PATCH 56/75] c --- packages/core/cyclist.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index f28dc604c..fec819548 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -67,6 +67,7 @@ export class Cyclist { // the following line is dumb and only here for backwards compatibility // see https://codeberg.org/uzu/strudel/pulls/1004 const deadline = targetTime - phase; + // this onTrigger has another signature onTrigger?.(hap, deadline, duration, this.cps, targetTime); if (hap.value.cps !== undefined && this.cps != hap.value.cps) { this.cps = hap.value.cps; From 8defdc424c94bca5a1341d6d08e4253fd1978f7d Mon Sep 17 00:00:00 2001 From: Dsm0 Date: Tue, 8 Jul 2025 17:57:24 -0700 Subject: [PATCH 57/75] added tab-indent setting --- packages/codemirror/codemirror.mjs | 4 +++- website/src/repl/components/panel/SettingsTab.jsx | 6 ++++++ website/src/settings.mjs | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 193d96b55..6ba1d9c40 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -1,7 +1,7 @@ import { closeBrackets } from '@codemirror/autocomplete'; export { toggleComment, toggleBlockComment, toggleLineComment, toggleBlockCommentByLine } from '@codemirror/commands'; // import { search, highlightSelectionMatches } from '@codemirror/search'; -import { history } from '@codemirror/commands'; +import { history, indentWithTab } from '@codemirror/commands'; import { javascript } from '@codemirror/lang-javascript'; import { defaultHighlightStyle, syntaxHighlighting, bracketMatching } from '@codemirror/language'; import { Compartment, EditorState, Prec } from '@codemirror/state'; @@ -37,6 +37,7 @@ const extensions = { isActiveLineHighlighted: (on) => (on ? [highlightActiveLine(), highlightActiveLineGutter()] : []), isFlashEnabled, keybindings, + isTabIndentationEnabled: (on) => (on ? keymap.of([indentWithTab]) : []), }; const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [key, new Compartment()])); @@ -51,6 +52,7 @@ export const defaultSettings = { isFlashEnabled: true, isTooltipEnabled: false, isLineWrappingEnabled: false, + isTabIndentationEnabled: false, theme: 'strudelTheme', fontFamily: 'monospace', fontSize: 18, diff --git a/website/src/repl/components/panel/SettingsTab.jsx b/website/src/repl/components/panel/SettingsTab.jsx index 5be9b602e..e82d03d87 100644 --- a/website/src/repl/components/panel/SettingsTab.jsx +++ b/website/src/repl/components/panel/SettingsTab.jsx @@ -109,6 +109,7 @@ export function SettingsTab({ started }) { togglePanelTrigger, maxPolyphony, multiChannelOrbits, + isTabIndentationEnabled, } = useSettings(); const shouldAlwaysSync = isUdels(); const canChangeAudioDevice = AudioContext.prototype.setSinkId != null; @@ -262,6 +263,11 @@ export function SettingsTab({ started }) { onChange={(cbEvent) => settingsMap.setKey('isLineWrappingEnabled', cbEvent.target.checked)} value={isLineWrappingEnabled} /> + settingsMap.setKey('isTabIndentationEnabled', cbEvent.target.checked)} + value={isTabIndentationEnabled} + /> settingsMap.setKey('isFlashEnabled', cbEvent.target.checked)} diff --git a/website/src/settings.mjs b/website/src/settings.mjs index 84b433141..6212a6da0 100644 --- a/website/src/settings.mjs +++ b/website/src/settings.mjs @@ -21,6 +21,7 @@ export const defaultSettings = { isSyncEnabled: false, isLineWrappingEnabled: false, isPatternHighlightingEnabled: true, + isTabIndentationEnabled: false, theme: 'strudelTheme', fontFamily: 'monospace', fontSize: 18, @@ -77,6 +78,7 @@ export function useSettings() { isLineWrappingEnabled: parseBoolean(state.isLineWrappingEnabled), isFlashEnabled: parseBoolean(state.isFlashEnabled), isSyncEnabled: isUdels() ? true : parseBoolean(state.isSyncEnabled), + isTabIndentationEnabled: parseBoolean(state.isTabIndentationEnabled), fontSize: Number(state.fontSize), panelPosition: state.activeFooter !== '' && !isUdels() ? state.panelPosition : 'bottom', // <-- keep this 'bottom' where it is! isPanelPinned: parseBoolean(state.isPanelPinned), From 63fcce79e000f971badc5e7ddb770a91b8519680 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 8 Jul 2025 21:24:27 -0400 Subject: [PATCH 58/75] default cycle --- packages/core/neocyclist.mjs | 2 +- packages/superdough/superdough.mjs | 2 +- packages/webaudio/webaudio.mjs | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/core/neocyclist.mjs b/packages/core/neocyclist.mjs index 4e6d385d8..261f08acb 100644 --- a/packages/core/neocyclist.mjs +++ b/packages/core/neocyclist.mjs @@ -45,7 +45,7 @@ export class NeoCyclist { const timeUntilTrigger = cycleToSeconds(hap.whole.begin - this.cycle, this.cps); const targetTime = timeUntilTrigger + currentTime + this.latency; const duration = cycleToSeconds(hap.duration, this.cps); - onTrigger?.(hap, 0, duration, this.cps, targetTime, this.cycle); + onTrigger?.(hap, 0, duration, this.cps, targetTime); } }); }; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 198e7b418..109a521c0 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -482,7 +482,7 @@ function mapChannelNumbers(channels) { return (Array.isArray(channels) ? channels : [channels]).map((ch) => ch - 1); } -export const superdough = async (value, t, hapDuration, cps = 0.5, cycle) => { +export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) => { // new: t is always expected to be the absolute target onset time const ac = getAudioContext(); diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 1e8455b3f..72dc687b0 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -23,8 +23,7 @@ export const webaudioOutput = (hap, _deadline, hapDuration, cps, t) => { t, hapDuration, cps, - hap.whole.begin.valueOf(), - // cycle + hap.whole?.begin.valueOf(), ); }; From edb347e85a1c56d97a76e92e2a1f6cd8c7e13eec Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 8 Jul 2025 22:24:54 -0400 Subject: [PATCH 59/75] format --- packages/webaudio/webaudio.mjs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 72dc687b0..429d2a26b 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -18,13 +18,7 @@ const hap2value = (hap) => { // uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004 // TODO: refactor output callbacks to eliminate deadline export const webaudioOutput = (hap, _deadline, hapDuration, cps, t) => { - return superdough( - hap2value(hap), - t, - hapDuration, - cps, - hap.whole?.begin.valueOf(), - ); + return superdough(hap2value(hap), t, hapDuration, cps, hap.whole?.begin.valueOf()); }; export function webaudioRepl(options = {}) { From bfdfab0a007ef880678e34f964a332551dab50b9 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 9 Jul 2025 09:33:11 +0200 Subject: [PATCH 60/75] fix: @synonym -> @synonyms --- jsdoc/jsdoc-synonyms.js | 2 +- packages/core/controls.mjs | 2 +- packages/core/pattern.mjs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/jsdoc/jsdoc-synonyms.js b/jsdoc/jsdoc-synonyms.js index 0b52420bc..d59c8dac4 100644 --- a/jsdoc/jsdoc-synonyms.js +++ b/jsdoc/jsdoc-synonyms.js @@ -1,5 +1,5 @@ /* -jsdoc-synonyms.js - Add support for @synonym tag +jsdoc-synonyms.js - Add support for @synonyms tag Copyright (C) 2023 Strudel contributors - see This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index a0f5d10e3..5eb8e5da8 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -448,7 +448,7 @@ export const { coarse } = registerControl('coarse'); * modulate the amplitude of a sound with a continuous waveform * * @name am - * @synonym tremolo + * @synonyms tremolo * @param {number | Pattern} speed modulation speed in HZ * @example * s("triangle").am("2").amshape("").amdepth(.5) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index d9bb94075..d59a1285d 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2522,7 +2522,7 @@ export const { fastchunk, fastChunk } = register( /** * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. * @name chunkInto - * @synonym chunkinto + * @synonyms chunkinto * @memberof Pattern * @example * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) @@ -2535,7 +2535,7 @@ export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], fun /** * Like `chunkInto`, but moves backwards through the chunks. * @name chunkBackInto - * @synonym chunkbackinto + * @synonyms chunkbackinto * @memberof Pattern * @example * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) @@ -2565,7 +2565,7 @@ export const bypass = register( * Loops the pattern inside an `offset` for `cycles`. * If you think of the entire span of time in cycles as a ribbon, you can cut a single piece and loop it. * @name ribbon - * @synonym rib + * @synonyms rib * @param {number} offset start point of loop in cycles * @param {number} cycles loop length in cycles * @example From cf7fc23981df38e2fffdc25c675a3ec615188704 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 9 Jul 2025 09:33:28 +0200 Subject: [PATCH 61/75] add new am examples to doc --- website/src/pages/learn/effects.mdx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 2f7eaa7f8..9c96dbe31 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -57,6 +57,34 @@ Each filter has 2 parameters: +# Amplitude Modulation + +Amplitude modulation changes the amplitude (gain) periodically over time. + +## am + + + +## amsync + + + +## amdepth + + + +## amskew + + + +## amphase + + + +## amshape + + + # Amplitude Envelope The amplitude [envelope]() controls the dynamic contour of a sound. From d645cc6fe504ef850e3cb03d1113da7969f5e772 Mon Sep 17 00:00:00 2001 From: Dsm0 Date: Wed, 9 Jul 2025 13:28:22 -0700 Subject: [PATCH 62/75] fixed keybinding presedence issue --- packages/codemirror/keybindings.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/codemirror/keybindings.mjs b/packages/codemirror/keybindings.mjs index 6fe00eda1..05a211636 100644 --- a/packages/codemirror/keybindings.mjs +++ b/packages/codemirror/keybindings.mjs @@ -26,6 +26,5 @@ const keymaps = { export function keybindings(name) { const active = keymaps[name]; - return [keymap.of(defaultKeymap), keymap.of(historyKeymap), active ? active() : []]; - // keymap.of(searchKeymap), + return [active ? active() : [], keymap.of(historyKeymap), keymap.of(defaultKeymap)]; } From bf3de368f070ce875847b76f3da9b01d1bcff0e8 Mon Sep 17 00:00:00 2001 From: Dsm0 Date: Thu, 10 Jul 2025 12:11:10 -0700 Subject: [PATCH 63/75] added codemirror as an option + removed fallback on defaults --- packages/codemirror/keybindings.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/codemirror/keybindings.mjs b/packages/codemirror/keybindings.mjs index 05a211636..1201959a2 100644 --- a/packages/codemirror/keybindings.mjs +++ b/packages/codemirror/keybindings.mjs @@ -21,10 +21,11 @@ const vscodeExtension = (options) => [vscodePlugin].concat(options ?? []); const keymaps = { vim, emacs, + codemirror: () => keymap.of(defaultKeymap), vscode: vscodeExtension, }; export function keybindings(name) { const active = keymaps[name]; - return [active ? active() : [], keymap.of(historyKeymap), keymap.of(defaultKeymap)]; + return [active ? active() : [], keymap.of(historyKeymap)]; } From e7e321fe0300103c52f9e795be6addbec437218e Mon Sep 17 00:00:00 2001 From: Dsm0 Date: Thu, 10 Jul 2025 15:03:22 -0700 Subject: [PATCH 64/75] added multicursor support on ctrl/cmd + click --- packages/codemirror/codemirror.mjs | 3 +++ website/src/repl/components/panel/SettingsTab.jsx | 6 ++++++ website/src/settings.mjs | 2 ++ 3 files changed, 11 insertions(+) diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 6ba1d9c40..c4bdaca3f 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -38,6 +38,8 @@ const extensions = { isFlashEnabled, keybindings, isTabIndentationEnabled: (on) => (on ? keymap.of([indentWithTab]) : []), + isMultiCursorEnabled: (on) => + on ? [EditorState.allowMultipleSelections.of(true), EditorView.clickAddsSelectionRange.of((ev) => ev.metaKey || ev.ctrlKey)] : [], }; const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [key, new Compartment()])); @@ -53,6 +55,7 @@ export const defaultSettings = { isTooltipEnabled: false, isLineWrappingEnabled: false, isTabIndentationEnabled: false, + isMultiCursorEnabled: false, theme: 'strudelTheme', fontFamily: 'monospace', fontSize: 18, diff --git a/website/src/repl/components/panel/SettingsTab.jsx b/website/src/repl/components/panel/SettingsTab.jsx index e82d03d87..cf2978a1a 100644 --- a/website/src/repl/components/panel/SettingsTab.jsx +++ b/website/src/repl/components/panel/SettingsTab.jsx @@ -110,6 +110,7 @@ export function SettingsTab({ started }) { maxPolyphony, multiChannelOrbits, isTabIndentationEnabled, + isMultiCursorEnabled, } = useSettings(); const shouldAlwaysSync = isUdels(); const canChangeAudioDevice = AudioContext.prototype.setSinkId != null; @@ -268,6 +269,11 @@ export function SettingsTab({ started }) { onChange={(cbEvent) => settingsMap.setKey('isTabIndentationEnabled', cbEvent.target.checked)} value={isTabIndentationEnabled} /> + settingsMap.setKey('isMultiCursorEnabled', cbEvent.target.checked)} + value={isMultiCursorEnabled} + /> settingsMap.setKey('isFlashEnabled', cbEvent.target.checked)} diff --git a/website/src/settings.mjs b/website/src/settings.mjs index 6212a6da0..3d99b656c 100644 --- a/website/src/settings.mjs +++ b/website/src/settings.mjs @@ -22,6 +22,7 @@ export const defaultSettings = { isLineWrappingEnabled: false, isPatternHighlightingEnabled: true, isTabIndentationEnabled: false, + isMultiCursorEnabled: false, theme: 'strudelTheme', fontFamily: 'monospace', fontSize: 18, @@ -79,6 +80,7 @@ export function useSettings() { isFlashEnabled: parseBoolean(state.isFlashEnabled), isSyncEnabled: isUdels() ? true : parseBoolean(state.isSyncEnabled), isTabIndentationEnabled: parseBoolean(state.isTabIndentationEnabled), + isMultiCursorEnabled: parseBoolean(state.isMultiCursorEnabled), fontSize: Number(state.fontSize), panelPosition: state.activeFooter !== '' && !isUdels() ? state.panelPosition : 'bottom', // <-- keep this 'bottom' where it is! isPanelPinned: parseBoolean(state.isPanelPinned), From eb4f1f69f6b3cbc20382444e9ddc07d2ef0d2ce0 Mon Sep 17 00:00:00 2001 From: Dsm0 Date: Thu, 10 Jul 2025 17:28:56 -0700 Subject: [PATCH 65/75] pnpm codeformat --- packages/codemirror/codemirror.mjs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index c4bdaca3f..f47f6eba5 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -39,7 +39,12 @@ const extensions = { keybindings, isTabIndentationEnabled: (on) => (on ? keymap.of([indentWithTab]) : []), isMultiCursorEnabled: (on) => - on ? [EditorState.allowMultipleSelections.of(true), EditorView.clickAddsSelectionRange.of((ev) => ev.metaKey || ev.ctrlKey)] : [], + on + ? [ + EditorState.allowMultipleSelections.of(true), + EditorView.clickAddsSelectionRange.of((ev) => ev.metaKey || ev.ctrlKey), + ] + : [], }; const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [key, new Compartment()])); From 735f57e27610a17113b9e13a2c8b3bfe2408757d Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 10 Jul 2025 23:18:57 -0400 Subject: [PATCH 66/75] working --- packages/superdough/superdough.mjs | 18 +++++++++++++----- packages/superdough/worklets.mjs | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 109a521c0..dbee8ae4b 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -28,6 +28,13 @@ export function setMultiChannelOrbits(bool) { multiChannelOrbits = bool == true; } +function getModulationShapeInput(val) { + if (typeof val === 'number') { + return val % 5; + } + return { tri: 0, triangle: 0, sine: 1, ramp: 2, saw: 3, square: 4 }[val] ?? 0; +} + export const soundMap = map(); export function registerSound(key, onTrigger, data = {}) { @@ -337,6 +344,7 @@ function getDelay(orbit, delaytime, delayfeedback, t, channels) { } export function getLfo(audioContext, begin, end, properties = {}) { + const { shape = 0, ...props } = properties; const { dcoffset = -0.5, depth = 1 } = properties; return getWorklet(audioContext, 'lfo-processor', { frequency: 1, @@ -346,12 +354,12 @@ export function getLfo(audioContext, begin, end, properties = {}) { time: begin, begin, end, - shape: 1, + shape: getModulationShapeInput(shape), dcoffset, min: dcoffset - depth * 0.5, max: dcoffset + depth * 0.5, curve: 1, - ...properties, + ...props, }); } @@ -514,9 +522,9 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) am, amsync, amdepth = 1, - amskew = 1, + amskew, amphase = 0, - amshape = 0, + amshape, s = getDefaultValue('s'), bank, source, @@ -745,7 +753,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) const time = cycle / cps; const lfo = getLfo(ac, t, endWithRelease, { - skew: amskew, + skew: amskew ?? (amshape != null ? 0.5 : 1), frequency: am, depth: amdepth, time, diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 466be558a..b4b36f6c9 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -31,7 +31,7 @@ function polyBlep(phase, dt) { return 0; } } - +// The order is important for dough integration const waveshapes = { tri(phase, skew = 0.5) { const x = 1 - skew; From 16e9aca2221a08a5c01fd45649b5967f35a4312c Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 15 Jul 2025 09:33:14 +0200 Subject: [PATCH 67/75] fix: can now use def in mondough --- packages/mondough/mondough.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mondough/mondough.mjs b/packages/mondough/mondough.mjs index 6e8278939..15feb5d86 100644 --- a/packages/mondough/mondough.mjs +++ b/packages/mondough/mondough.mjs @@ -42,6 +42,7 @@ lib['%'] = pace; lib['?'] = degradeBy; // todo: default 0.5 not working.. lib[':'] = tail; lib['..'] = range; +lib['def'] = () => silence; lib['or'] = (...children) => chooseIn(...children); // always has structure but is cyclewise.. e.g. "s oh*8.dec[.04 | .5]" //lib['or'] = (...children) => chooseOut(...children); // "s oh*8.dec[.04 | .5]" is better but "dec[.04 | .5].s oh*8" has no struct From 1fdd87a4f3c4b0ede205d149edfca1325c5bdf78 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 15 Jul 2025 09:35:24 +0200 Subject: [PATCH 68/75] doc: def --- website/src/pages/learn/mondo-notation.mdx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/website/src/pages/learn/mondo-notation.mdx b/website/src/pages/learn/mondo-notation.mdx index 3b00b9902..e2b0c4027 100644 --- a/website/src/pages/learn/mondo-notation.mdx +++ b/website/src/pages/learn/mondo-notation.mdx @@ -176,3 +176,16 @@ $ chord # voicing /> The `$` sign is an alias for `,` so it will create a stack behind the scenes. + +## variables + +using the `def` keyword, you can define variables: + + From 34874862ab283ec700caf1e102e0acd26f08dd14 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 18 Jul 2025 22:03:33 -0400 Subject: [PATCH 69/75] change params --- packages/core/controls.mjs | 56 ++- packages/superdough/superdough.mjs | 33 +- test/__snapshots__/examples.test.mjs.snap | 418 +++++++++++----------- website/src/pages/learn/effects.mdx | 20 +- 4 files changed, 261 insertions(+), 266 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 5eb8e5da8..d09fbbc21 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -447,68 +447,72 @@ export const { coarse } = registerControl('coarse'); /** * modulate the amplitude of a sound with a continuous waveform * - * @name am - * @synonyms tremolo + * @name tremolo + * @synonyms trem * @param {number | Pattern} speed modulation speed in HZ * @example - * s("triangle").am("2").amshape("").amdepth(.5) + * note("d d d# d".fast(4)).s("supersaw").tremolo("<3 2 100> ").tremoloskew("<.5>") * */ -export const { am, tremolo } = registerControl(['am', 'amdepth', 'amskew', 'amphase'], 'tremolo'); +export const { tremolo } = registerControl(['tremolo', 'tremolodepth', 'tremoloskew', 'tremolophase'], 'trem'); /** * modulate the amplitude of a sound with a continuous waveform * - * @name amsync + * @name tremolosync + * @synonyms tremsync * @param {number | Pattern} cycles modulation speed in cycles * @example - * s("supersaw").amsync(1/4).amskew("<0 .5 1>").amdepth(2) + * note("d d d# d".fast(4)).s("supersaw").tremolosync("4").tremoloskew("<1 .5 0>") * */ -export const { amsync } = registerControl(['amsync', 'amdepth', 'amskew', 'amphase']); +export const { tremolosync } = registerControl(['tremolosync', 'tremolodepth', 'tremoloskew', 'tremolophase'], 'tremsync'); /** * depth of amplitude modulation * - * @name amdepth + * @name tremolodepth + * @synonyms tremdepth * @param {number | Pattern} depth * @example - * s("triangle").am(1).amdepth("1") + * note("a1 a1 a#1 a1".fast(4)).s("pulse").tremsync(4).tremolodepth("<1 2 .7>") * */ -export const { amdepth } = registerControl('amdepth'); +export const { tremolodepth } = registerControl('tremolodepth', 'tremdepth'); /** * alter the shape of the modulation waveform * - * @name amskew + * @name tremoloskew + * @synonyms tremskew * @param {number | Pattern} amount between 0 & 1, the shape of the waveform * @example - * note("{f a c e}%16").am(5).amskew(.5).amdepth("<1 0.5 2>").att(.01).rel(.03) + * note("{f a c e}%16").s("sawtooth").tremsync(4).tremoloskew("<.5 0 1>") * */ -export const { amskew } = registerControl('amskew'); +export const { tremoloskew } = registerControl('tremoloskew', 'tremskew'); /** * alter the phase of the modulation waveform * - * @name amphase + * @name tremolophase + * @synonyms tremphase * @param {number | Pattern} offset the offset in cycles of the modulation * @example - * note("{f a c e}%16").s("sawtooth").am(4).amphase("<0 .25 .66>") + * note("{f a c e}%16").s("sawtooth").tremsync(4).tremolophase("<0 .25 .66>") * */ -export const { amphase } = registerControl('amphase'); +export const { tremolophase } = registerControl('tremolophase', 'tremphase'); /** * shape of amplitude modulation * - * @name amshape + * @name tremoloshape * @param {number | Pattern} shape tri | square | sine | saw | ramp * @example - * note("{f g c d}%16").am(4).amshape("ramp").s("sawtooth") + * note("{f g c d}%16").tremsync(4).tremoloshape("").s("sawtooth") * */ -export const { amshape } = registerControl('amshape'); +export const { tremoloshape } = registerControl('tremoloshape', 'tremshape'); /** * filter overdrive for supported filter types * @@ -1650,18 +1654,8 @@ export const { density } = registerControl('density'); // ['modwheel'], export const { expression } = registerControl('expression'); export const { sustainpedal } = registerControl('sustainpedal'); -/* // TODO: doesn't seem to do anything - * - * Tremolo Audio DSP effect - * - * @name tremolodepth - * @param {number | Pattern} depth between 0 and 1 - * @example - * n("0,4,7").tremolodepth("<0 .3 .6 .9>").osc() - * - */ -export const { tremolodepth, tremdp } = registerControl('tremolodepth', 'tremdp'); -export const { tremolorate, tremr } = registerControl('tremolorate', 'tremr'); + + export const { fshift } = registerControl('fshift'); export const { fshiftnote } = registerControl('fshiftnote'); diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index dbee8ae4b..6ce673d5b 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -519,12 +519,12 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) } // destructure let { - am, - amsync, - amdepth = 1, - amskew, - amphase = 0, - amshape, + tremolo, + tremolosync, + tremolodepth = 1, + tremoloskew, + tremolophase = 0, + tremoloshape, s = getDefaultValue('s'), bank, source, @@ -610,7 +610,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) distortvol = applyGainCurve(distortvol); delay = applyGainCurve(delay); velocity = applyGainCurve(velocity); - amdepth = applyGainCurve(amdepth); + tremolodepth = applyGainCurve(tremolodepth); gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future const end = t + hapDuration; @@ -742,24 +742,25 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol })); distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); - if (amsync != null) { - am = cps / amsync; + if (tremolosync != null) { + tremolo = cps * tremolosync; } - if (am !== undefined) { + + if (tremolo !== undefined) { // Allow clipping of modulator for more dynamic possiblities, and to prevent speaker overload // EX: a triangle waveform will clip like this /-\ when the depth is above 1 - const gain = Math.max(1 - amdepth, 0); + const gain = Math.max(1 - tremolodepth, 0); const amGain = new GainNode(ac, { gain }); const time = cycle / cps; const lfo = getLfo(ac, t, endWithRelease, { - skew: amskew ?? (amshape != null ? 0.5 : 1), - frequency: am, - depth: amdepth, + skew: tremoloskew ?? (tremoloshape != null ? 0.5 : 1), + frequency: tremolo, + depth: tremolodepth, time, dcoffset: 0, - shape: amshape, - phaseoffset: amphase, + shape: tremoloshape, + phaseoffset: tremolophase, min: 0, max: 1, curve: 1.5, diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 500392041..7ad7c48b9 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -781,19 +781,19 @@ exports[`runs examples > example "always" example index 0 1`] = ` exports[`runs examples > example "am" example index 0 1`] = ` [ - "[ 0/1 → 1/1 | s:triangle am:2 amshape:tri amdepth:0.5 ]", - "[ 1/1 → 2/1 | s:triangle am:2 amshape:saw amdepth:0.5 ]", - "[ 2/1 → 3/1 | s:triangle am:2 amshape:ramp amdepth:0.5 ]", - "[ 3/1 → 4/1 | s:triangle am:2 amshape:square amdepth:0.5 ]", + "[ 0/1 → 1/1 | s:triangle am:2 tremoloshape:tri tremolodepth:0.5 ]", + "[ 1/1 → 2/1 | s:triangle am:2 tremoloshape:saw tremolodepth:0.5 ]", + "[ 2/1 → 3/1 | s:triangle am:2 tremoloshape:ramp tremolodepth:0.5 ]", + "[ 3/1 → 4/1 | s:triangle am:2 tremoloshape:square tremolodepth:0.5 ]", ] `; -exports[`runs examples > example "amdepth" example index 0 1`] = ` +exports[`runs examples > example "tremolodepth" example index 0 1`] = ` [ - "[ 0/1 → 1/1 | s:triangle am:1 amdepth:1 ]", - "[ 1/1 → 2/1 | s:triangle am:1 amdepth:1 ]", - "[ 2/1 → 3/1 | s:triangle am:1 amdepth:1 ]", - "[ 3/1 → 4/1 | s:triangle am:1 amdepth:1 ]", + "[ 0/1 → 1/1 | s:triangle am:1 tremolodepth:1 ]", + "[ 1/1 → 2/1 | s:triangle am:1 tremolodepth:1 ]", + "[ 2/1 → 3/1 | s:triangle am:1 tremolodepth:1 ]", + "[ 3/1 → 4/1 | s:triangle am:1 tremolodepth:1 ]", ] `; @@ -858,219 +858,219 @@ exports[`runs examples > example "amp" example index 0 1`] = ` ] `; -exports[`runs examples > example "amphase" example index 0 1`] = ` +exports[`runs examples > example "tremolophase" example index 0 1`] = ` [ - "[ 0/1 → 1/16 | note:f s:sawtooth am:4 amphase:0 ]", - "[ 1/16 → 1/8 | note:a s:sawtooth am:4 amphase:0 ]", - "[ 1/8 → 3/16 | note:c s:sawtooth am:4 amphase:0 ]", - "[ 3/16 → 1/4 | note:e s:sawtooth am:4 amphase:0 ]", - "[ 1/4 → 5/16 | note:f s:sawtooth am:4 amphase:0 ]", - "[ 5/16 → 3/8 | note:a s:sawtooth am:4 amphase:0 ]", - "[ 3/8 → 7/16 | note:c s:sawtooth am:4 amphase:0 ]", - "[ 7/16 → 1/2 | note:e s:sawtooth am:4 amphase:0 ]", - "[ 1/2 → 9/16 | note:f s:sawtooth am:4 amphase:0 ]", - "[ 9/16 → 5/8 | note:a s:sawtooth am:4 amphase:0 ]", - "[ 5/8 → 11/16 | note:c s:sawtooth am:4 amphase:0 ]", - "[ 11/16 → 3/4 | note:e s:sawtooth am:4 amphase:0 ]", - "[ 3/4 → 13/16 | note:f s:sawtooth am:4 amphase:0 ]", - "[ 13/16 → 7/8 | note:a s:sawtooth am:4 amphase:0 ]", - "[ 7/8 → 15/16 | note:c s:sawtooth am:4 amphase:0 ]", - "[ 15/16 → 1/1 | note:e s:sawtooth am:4 amphase:0 ]", - "[ 1/1 → 17/16 | note:f s:sawtooth am:4 amphase:0.25 ]", - "[ 17/16 → 9/8 | note:a s:sawtooth am:4 amphase:0.25 ]", - "[ 9/8 → 19/16 | note:c s:sawtooth am:4 amphase:0.25 ]", - "[ 19/16 → 5/4 | note:e s:sawtooth am:4 amphase:0.25 ]", - "[ 5/4 → 21/16 | note:f s:sawtooth am:4 amphase:0.25 ]", - "[ 21/16 → 11/8 | note:a s:sawtooth am:4 amphase:0.25 ]", - "[ 11/8 → 23/16 | note:c s:sawtooth am:4 amphase:0.25 ]", - "[ 23/16 → 3/2 | note:e s:sawtooth am:4 amphase:0.25 ]", - "[ 3/2 → 25/16 | note:f s:sawtooth am:4 amphase:0.25 ]", - "[ 25/16 → 13/8 | note:a s:sawtooth am:4 amphase:0.25 ]", - "[ 13/8 → 27/16 | note:c s:sawtooth am:4 amphase:0.25 ]", - "[ 27/16 → 7/4 | note:e s:sawtooth am:4 amphase:0.25 ]", - "[ 7/4 → 29/16 | note:f s:sawtooth am:4 amphase:0.25 ]", - "[ 29/16 → 15/8 | note:a s:sawtooth am:4 amphase:0.25 ]", - "[ 15/8 → 31/16 | note:c s:sawtooth am:4 amphase:0.25 ]", - "[ 31/16 → 2/1 | note:e s:sawtooth am:4 amphase:0.25 ]", - "[ 2/1 → 33/16 | note:f s:sawtooth am:4 amphase:0.66 ]", - "[ 33/16 → 17/8 | note:a s:sawtooth am:4 amphase:0.66 ]", - "[ 17/8 → 35/16 | note:c s:sawtooth am:4 amphase:0.66 ]", - "[ 35/16 → 9/4 | note:e s:sawtooth am:4 amphase:0.66 ]", - "[ 9/4 → 37/16 | note:f s:sawtooth am:4 amphase:0.66 ]", - "[ 37/16 → 19/8 | note:a s:sawtooth am:4 amphase:0.66 ]", - "[ 19/8 → 39/16 | note:c s:sawtooth am:4 amphase:0.66 ]", - "[ 39/16 → 5/2 | note:e s:sawtooth am:4 amphase:0.66 ]", - "[ 5/2 → 41/16 | note:f s:sawtooth am:4 amphase:0.66 ]", - "[ 41/16 → 21/8 | note:a s:sawtooth am:4 amphase:0.66 ]", - "[ 21/8 → 43/16 | note:c s:sawtooth am:4 amphase:0.66 ]", - "[ 43/16 → 11/4 | note:e s:sawtooth am:4 amphase:0.66 ]", - "[ 11/4 → 45/16 | note:f s:sawtooth am:4 amphase:0.66 ]", - "[ 45/16 → 23/8 | note:a s:sawtooth am:4 amphase:0.66 ]", - "[ 23/8 → 47/16 | note:c s:sawtooth am:4 amphase:0.66 ]", - "[ 47/16 → 3/1 | note:e s:sawtooth am:4 amphase:0.66 ]", - "[ 3/1 → 49/16 | note:f s:sawtooth am:4 amphase:0 ]", - "[ 49/16 → 25/8 | note:a s:sawtooth am:4 amphase:0 ]", - "[ 25/8 → 51/16 | note:c s:sawtooth am:4 amphase:0 ]", - "[ 51/16 → 13/4 | note:e s:sawtooth am:4 amphase:0 ]", - "[ 13/4 → 53/16 | note:f s:sawtooth am:4 amphase:0 ]", - "[ 53/16 → 27/8 | note:a s:sawtooth am:4 amphase:0 ]", - "[ 27/8 → 55/16 | note:c s:sawtooth am:4 amphase:0 ]", - "[ 55/16 → 7/2 | note:e s:sawtooth am:4 amphase:0 ]", - "[ 7/2 → 57/16 | note:f s:sawtooth am:4 amphase:0 ]", - "[ 57/16 → 29/8 | note:a s:sawtooth am:4 amphase:0 ]", - "[ 29/8 → 59/16 | note:c s:sawtooth am:4 amphase:0 ]", - "[ 59/16 → 15/4 | note:e s:sawtooth am:4 amphase:0 ]", - "[ 15/4 → 61/16 | note:f s:sawtooth am:4 amphase:0 ]", - "[ 61/16 → 31/8 | note:a s:sawtooth am:4 amphase:0 ]", - "[ 31/8 → 63/16 | note:c s:sawtooth am:4 amphase:0 ]", - "[ 63/16 → 4/1 | note:e s:sawtooth am:4 amphase:0 ]", + "[ 0/1 → 1/16 | note:f s:sawtooth am:4 tremolophase:0 ]", + "[ 1/16 → 1/8 | note:a s:sawtooth am:4 tremolophase:0 ]", + "[ 1/8 → 3/16 | note:c s:sawtooth am:4 tremolophase:0 ]", + "[ 3/16 → 1/4 | note:e s:sawtooth am:4 tremolophase:0 ]", + "[ 1/4 → 5/16 | note:f s:sawtooth am:4 tremolophase:0 ]", + "[ 5/16 → 3/8 | note:a s:sawtooth am:4 tremolophase:0 ]", + "[ 3/8 → 7/16 | note:c s:sawtooth am:4 tremolophase:0 ]", + "[ 7/16 → 1/2 | note:e s:sawtooth am:4 tremolophase:0 ]", + "[ 1/2 → 9/16 | note:f s:sawtooth am:4 tremolophase:0 ]", + "[ 9/16 → 5/8 | note:a s:sawtooth am:4 tremolophase:0 ]", + "[ 5/8 → 11/16 | note:c s:sawtooth am:4 tremolophase:0 ]", + "[ 11/16 → 3/4 | note:e s:sawtooth am:4 tremolophase:0 ]", + "[ 3/4 → 13/16 | note:f s:sawtooth am:4 tremolophase:0 ]", + "[ 13/16 → 7/8 | note:a s:sawtooth am:4 tremolophase:0 ]", + "[ 7/8 → 15/16 | note:c s:sawtooth am:4 tremolophase:0 ]", + "[ 15/16 → 1/1 | note:e s:sawtooth am:4 tremolophase:0 ]", + "[ 1/1 → 17/16 | note:f s:sawtooth am:4 tremolophase:0.25 ]", + "[ 17/16 → 9/8 | note:a s:sawtooth am:4 tremolophase:0.25 ]", + "[ 9/8 → 19/16 | note:c s:sawtooth am:4 tremolophase:0.25 ]", + "[ 19/16 → 5/4 | note:e s:sawtooth am:4 tremolophase:0.25 ]", + "[ 5/4 → 21/16 | note:f s:sawtooth am:4 tremolophase:0.25 ]", + "[ 21/16 → 11/8 | note:a s:sawtooth am:4 tremolophase:0.25 ]", + "[ 11/8 → 23/16 | note:c s:sawtooth am:4 tremolophase:0.25 ]", + "[ 23/16 → 3/2 | note:e s:sawtooth am:4 tremolophase:0.25 ]", + "[ 3/2 → 25/16 | note:f s:sawtooth am:4 tremolophase:0.25 ]", + "[ 25/16 → 13/8 | note:a s:sawtooth am:4 tremolophase:0.25 ]", + "[ 13/8 → 27/16 | note:c s:sawtooth am:4 tremolophase:0.25 ]", + "[ 27/16 → 7/4 | note:e s:sawtooth am:4 tremolophase:0.25 ]", + "[ 7/4 → 29/16 | note:f s:sawtooth am:4 tremolophase:0.25 ]", + "[ 29/16 → 15/8 | note:a s:sawtooth am:4 tremolophase:0.25 ]", + "[ 15/8 → 31/16 | note:c s:sawtooth am:4 tremolophase:0.25 ]", + "[ 31/16 → 2/1 | note:e s:sawtooth am:4 tremolophase:0.25 ]", + "[ 2/1 → 33/16 | note:f s:sawtooth am:4 tremolophase:0.66 ]", + "[ 33/16 → 17/8 | note:a s:sawtooth am:4 tremolophase:0.66 ]", + "[ 17/8 → 35/16 | note:c s:sawtooth am:4 tremolophase:0.66 ]", + "[ 35/16 → 9/4 | note:e s:sawtooth am:4 tremolophase:0.66 ]", + "[ 9/4 → 37/16 | note:f s:sawtooth am:4 tremolophase:0.66 ]", + "[ 37/16 → 19/8 | note:a s:sawtooth am:4 tremolophase:0.66 ]", + "[ 19/8 → 39/16 | note:c s:sawtooth am:4 tremolophase:0.66 ]", + "[ 39/16 → 5/2 | note:e s:sawtooth am:4 tremolophase:0.66 ]", + "[ 5/2 → 41/16 | note:f s:sawtooth am:4 tremolophase:0.66 ]", + "[ 41/16 → 21/8 | note:a s:sawtooth am:4 tremolophase:0.66 ]", + "[ 21/8 → 43/16 | note:c s:sawtooth am:4 tremolophase:0.66 ]", + "[ 43/16 → 11/4 | note:e s:sawtooth am:4 tremolophase:0.66 ]", + "[ 11/4 → 45/16 | note:f s:sawtooth am:4 tremolophase:0.66 ]", + "[ 45/16 → 23/8 | note:a s:sawtooth am:4 tremolophase:0.66 ]", + "[ 23/8 → 47/16 | note:c s:sawtooth am:4 tremolophase:0.66 ]", + "[ 47/16 → 3/1 | note:e s:sawtooth am:4 tremolophase:0.66 ]", + "[ 3/1 → 49/16 | note:f s:sawtooth am:4 tremolophase:0 ]", + "[ 49/16 → 25/8 | note:a s:sawtooth am:4 tremolophase:0 ]", + "[ 25/8 → 51/16 | note:c s:sawtooth am:4 tremolophase:0 ]", + "[ 51/16 → 13/4 | note:e s:sawtooth am:4 tremolophase:0 ]", + "[ 13/4 → 53/16 | note:f s:sawtooth am:4 tremolophase:0 ]", + "[ 53/16 → 27/8 | note:a s:sawtooth am:4 tremolophase:0 ]", + "[ 27/8 → 55/16 | note:c s:sawtooth am:4 tremolophase:0 ]", + "[ 55/16 → 7/2 | note:e s:sawtooth am:4 tremolophase:0 ]", + "[ 7/2 → 57/16 | note:f s:sawtooth am:4 tremolophase:0 ]", + "[ 57/16 → 29/8 | note:a s:sawtooth am:4 tremolophase:0 ]", + "[ 29/8 → 59/16 | note:c s:sawtooth am:4 tremolophase:0 ]", + "[ 59/16 → 15/4 | note:e s:sawtooth am:4 tremolophase:0 ]", + "[ 15/4 → 61/16 | note:f s:sawtooth am:4 tremolophase:0 ]", + "[ 61/16 → 31/8 | note:a s:sawtooth am:4 tremolophase:0 ]", + "[ 31/8 → 63/16 | note:c s:sawtooth am:4 tremolophase:0 ]", + "[ 63/16 → 4/1 | note:e s:sawtooth am:4 tremolophase:0 ]", ] `; -exports[`runs examples > example "amshape" example index 0 1`] = ` +exports[`runs examples > example "tremoloshape" example index 0 1`] = ` [ - "[ 0/1 → 1/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 1/16 → 1/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 1/8 → 3/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 3/16 → 1/4 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 1/4 → 5/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 5/16 → 3/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 3/8 → 7/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 7/16 → 1/2 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 1/2 → 9/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 9/16 → 5/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 5/8 → 11/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 11/16 → 3/4 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 3/4 → 13/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 13/16 → 7/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 7/8 → 15/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 15/16 → 1/1 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 1/1 → 17/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 17/16 → 9/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 9/8 → 19/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 19/16 → 5/4 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 5/4 → 21/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 21/16 → 11/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 11/8 → 23/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 23/16 → 3/2 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 3/2 → 25/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 25/16 → 13/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 13/8 → 27/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 27/16 → 7/4 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 7/4 → 29/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 29/16 → 15/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 15/8 → 31/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 31/16 → 2/1 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 2/1 → 33/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 33/16 → 17/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 17/8 → 35/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 35/16 → 9/4 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 9/4 → 37/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 37/16 → 19/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 19/8 → 39/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 39/16 → 5/2 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 5/2 → 41/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 41/16 → 21/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 21/8 → 43/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 43/16 → 11/4 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 11/4 → 45/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 45/16 → 23/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 23/8 → 47/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 47/16 → 3/1 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 3/1 → 49/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 49/16 → 25/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 25/8 → 51/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 51/16 → 13/4 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 13/4 → 53/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 53/16 → 27/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 27/8 → 55/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 55/16 → 7/2 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 7/2 → 57/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 57/16 → 29/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 29/8 → 59/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 59/16 → 15/4 | note:d am:4 amshape:ramp s:sawtooth ]", - "[ 15/4 → 61/16 | note:f am:4 amshape:ramp s:sawtooth ]", - "[ 61/16 → 31/8 | note:g am:4 amshape:ramp s:sawtooth ]", - "[ 31/8 → 63/16 | note:c am:4 amshape:ramp s:sawtooth ]", - "[ 63/16 → 4/1 | note:d am:4 amshape:ramp s:sawtooth ]", + "[ 0/1 → 1/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 1/16 → 1/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 1/8 → 3/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 3/16 → 1/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 1/4 → 5/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 5/16 → 3/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 3/8 → 7/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 7/16 → 1/2 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 1/2 → 9/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 9/16 → 5/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 5/8 → 11/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 11/16 → 3/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 3/4 → 13/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 13/16 → 7/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 7/8 → 15/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 15/16 → 1/1 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 1/1 → 17/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 17/16 → 9/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 9/8 → 19/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 19/16 → 5/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 5/4 → 21/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 21/16 → 11/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 11/8 → 23/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 23/16 → 3/2 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 3/2 → 25/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 25/16 → 13/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 13/8 → 27/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 27/16 → 7/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 7/4 → 29/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 29/16 → 15/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 15/8 → 31/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 31/16 → 2/1 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 2/1 → 33/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 33/16 → 17/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 17/8 → 35/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 35/16 → 9/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 9/4 → 37/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 37/16 → 19/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 19/8 → 39/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 39/16 → 5/2 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 5/2 → 41/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 41/16 → 21/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 21/8 → 43/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 43/16 → 11/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 11/4 → 45/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 45/16 → 23/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 23/8 → 47/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 47/16 → 3/1 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 3/1 → 49/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 49/16 → 25/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 25/8 → 51/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 51/16 → 13/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 13/4 → 53/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 53/16 → 27/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 27/8 → 55/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 55/16 → 7/2 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 7/2 → 57/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 57/16 → 29/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 29/8 → 59/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 59/16 → 15/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", + "[ 15/4 → 61/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", + "[ 61/16 → 31/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", + "[ 31/8 → 63/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", + "[ 63/16 → 4/1 | note:d am:4 tremoloshape:ramp s:sawtooth ]", ] `; -exports[`runs examples > example "amskew" example index 0 1`] = ` +exports[`runs examples > example "tremoloskew" example index 0 1`] = ` [ - "[ 0/1 → 1/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 1/16 → 1/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 1/8 → 3/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 3/16 → 1/4 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 1/4 → 5/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 5/16 → 3/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 3/8 → 7/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 7/16 → 1/2 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 1/2 → 9/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 9/16 → 5/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 5/8 → 11/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 11/16 → 3/4 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 3/4 → 13/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 13/16 → 7/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 7/8 → 15/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 15/16 → 1/1 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 1/1 → 17/16 | note:f am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 17/16 → 9/8 | note:a am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 9/8 → 19/16 | note:c am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 19/16 → 5/4 | note:e am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 5/4 → 21/16 | note:f am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 21/16 → 11/8 | note:a am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 11/8 → 23/16 | note:c am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 23/16 → 3/2 | note:e am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 3/2 → 25/16 | note:f am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 25/16 → 13/8 | note:a am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 13/8 → 27/16 | note:c am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 27/16 → 7/4 | note:e am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 7/4 → 29/16 | note:f am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 29/16 → 15/8 | note:a am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 15/8 → 31/16 | note:c am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 31/16 → 2/1 | note:e am:5 amskew:0.5 amdepth:0.5 attack:0.01 release:0.03 ]", - "[ 2/1 → 33/16 | note:f am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 33/16 → 17/8 | note:a am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 17/8 → 35/16 | note:c am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 35/16 → 9/4 | note:e am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 9/4 → 37/16 | note:f am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 37/16 → 19/8 | note:a am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 19/8 → 39/16 | note:c am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 39/16 → 5/2 | note:e am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 5/2 → 41/16 | note:f am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 41/16 → 21/8 | note:a am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 21/8 → 43/16 | note:c am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 43/16 → 11/4 | note:e am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 11/4 → 45/16 | note:f am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 45/16 → 23/8 | note:a am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 23/8 → 47/16 | note:c am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 47/16 → 3/1 | note:e am:5 amskew:0.5 amdepth:2 attack:0.01 release:0.03 ]", - "[ 3/1 → 49/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 49/16 → 25/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 25/8 → 51/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 51/16 → 13/4 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 13/4 → 53/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 53/16 → 27/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 27/8 → 55/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 55/16 → 7/2 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 7/2 → 57/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 57/16 → 29/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 29/8 → 59/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 59/16 → 15/4 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 15/4 → 61/16 | note:f am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 61/16 → 31/8 | note:a am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 31/8 → 63/16 | note:c am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", - "[ 63/16 → 4/1 | note:e am:5 amskew:0.5 amdepth:1 attack:0.01 release:0.03 ]", + "[ 0/1 → 1/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 1/16 → 1/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 1/8 → 3/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 3/16 → 1/4 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 1/4 → 5/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 5/16 → 3/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 3/8 → 7/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 7/16 → 1/2 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 1/2 → 9/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 9/16 → 5/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 5/8 → 11/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 11/16 → 3/4 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 3/4 → 13/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 13/16 → 7/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 7/8 → 15/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 15/16 → 1/1 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 1/1 → 17/16 | note:f am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 17/16 → 9/8 | note:a am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 9/8 → 19/16 | note:c am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 19/16 → 5/4 | note:e am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 5/4 → 21/16 | note:f am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 21/16 → 11/8 | note:a am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 11/8 → 23/16 | note:c am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 23/16 → 3/2 | note:e am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 3/2 → 25/16 | note:f am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 25/16 → 13/8 | note:a am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 13/8 → 27/16 | note:c am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 27/16 → 7/4 | note:e am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 7/4 → 29/16 | note:f am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 29/16 → 15/8 | note:a am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 15/8 → 31/16 | note:c am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 31/16 → 2/1 | note:e am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", + "[ 2/1 → 33/16 | note:f am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 33/16 → 17/8 | note:a am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 17/8 → 35/16 | note:c am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 35/16 → 9/4 | note:e am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 9/4 → 37/16 | note:f am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 37/16 → 19/8 | note:a am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 19/8 → 39/16 | note:c am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 39/16 → 5/2 | note:e am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 5/2 → 41/16 | note:f am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 41/16 → 21/8 | note:a am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 21/8 → 43/16 | note:c am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 43/16 → 11/4 | note:e am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 11/4 → 45/16 | note:f am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 45/16 → 23/8 | note:a am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 23/8 → 47/16 | note:c am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 47/16 → 3/1 | note:e am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", + "[ 3/1 → 49/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 49/16 → 25/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 25/8 → 51/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 51/16 → 13/4 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 13/4 → 53/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 53/16 → 27/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 27/8 → 55/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 55/16 → 7/2 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 7/2 → 57/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 57/16 → 29/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 29/8 → 59/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 59/16 → 15/4 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 15/4 → 61/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 61/16 → 31/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 31/8 → 63/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", + "[ 63/16 → 4/1 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", ] `; -exports[`runs examples > example "amsync" example index 0 1`] = ` +exports[`runs examples > example "tremolosync" example index 0 1`] = ` [ - "[ 0/1 → 1/1 | s:supersaw amsync:0.25 amskew:0 amdepth:2 ]", - "[ 1/1 → 2/1 | s:supersaw amsync:0.25 amskew:0.5 amdepth:2 ]", - "[ 2/1 → 3/1 | s:supersaw amsync:0.25 amskew:1 amdepth:2 ]", - "[ 3/1 → 4/1 | s:supersaw amsync:0.25 amskew:0 amdepth:2 ]", + "[ 0/1 → 1/1 | s:supersaw tremolosync:0.25 tremoloskew:0 tremolodepth:2 ]", + "[ 1/1 → 2/1 | s:supersaw tremolosync:0.25 tremoloskew:0.5 tremolodepth:2 ]", + "[ 2/1 → 3/1 | s:supersaw tremolosync:0.25 tremoloskew:1 tremolodepth:2 ]", + "[ 3/1 → 4/1 | s:supersaw tremolosync:0.25 tremoloskew:0 tremolodepth:2 ]", ] `; diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 9c96dbe31..8f6a3b8aa 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -65,25 +65,25 @@ Amplitude modulation changes the amplitude (gain) periodically over time. -## amsync +## tremolosync - + -## amdepth +## tremolodepth - + -## amskew +## tremoloskew - + -## amphase +## tremolophase - + -## amshape +## tremoloshape - + # Amplitude Envelope From dd04d49c750f79667f987576ff664bbbd841dd58 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 18 Jul 2025 22:04:18 -0400 Subject: [PATCH 70/75] fix test --- packages/core/controls.mjs | 7 +- packages/superdough/superdough.mjs | 2 +- test/__snapshots__/examples.test.mjs.snap | 648 ++++++++++++++-------- 3 files changed, 419 insertions(+), 238 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index d09fbbc21..326343e10 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -466,7 +466,10 @@ export const { tremolo } = registerControl(['tremolo', 'tremolodepth', 'tremolos * note("d d d# d".fast(4)).s("supersaw").tremolosync("4").tremoloskew("<1 .5 0>") * */ -export const { tremolosync } = registerControl(['tremolosync', 'tremolodepth', 'tremoloskew', 'tremolophase'], 'tremsync'); +export const { tremolosync } = registerControl( + ['tremolosync', 'tremolodepth', 'tremoloskew', 'tremolophase'], + 'tremsync', +); /** * depth of amplitude modulation @@ -1655,8 +1658,6 @@ export const { density } = registerControl('density'); export const { expression } = registerControl('expression'); export const { sustainpedal } = registerControl('sustainpedal'); - - export const { fshift } = registerControl('fshift'); export const { fshiftnote } = registerControl('fshiftnote'); export const { fshiftphase } = registerControl('fshiftphase'); diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 6ce673d5b..f3e04a047 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -745,7 +745,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5) if (tremolosync != null) { tremolo = cps * tremolosync; } - + if (tremolo !== undefined) { // Allow clipping of modulator for more dynamic possiblities, and to prevent speaker overload // EX: a triangle waveform will clip like this /-\ when the depth is above 1 diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 7ad7c48b9..e6bfa44aa 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -779,24 +779,6 @@ exports[`runs examples > example "always" example index 0 1`] = ` ] `; -exports[`runs examples > example "am" example index 0 1`] = ` -[ - "[ 0/1 → 1/1 | s:triangle am:2 tremoloshape:tri tremolodepth:0.5 ]", - "[ 1/1 → 2/1 | s:triangle am:2 tremoloshape:saw tremolodepth:0.5 ]", - "[ 2/1 → 3/1 | s:triangle am:2 tremoloshape:ramp tremolodepth:0.5 ]", - "[ 3/1 → 4/1 | s:triangle am:2 tremoloshape:square tremolodepth:0.5 ]", -] -`; - -exports[`runs examples > example "tremolodepth" example index 0 1`] = ` -[ - "[ 0/1 → 1/1 | s:triangle am:1 tremolodepth:1 ]", - "[ 1/1 → 2/1 | s:triangle am:1 tremolodepth:1 ]", - "[ 2/1 → 3/1 | s:triangle am:1 tremolodepth:1 ]", - "[ 3/1 → 4/1 | s:triangle am:1 tremolodepth:1 ]", -] -`; - exports[`runs examples > example "amp" example index 0 1`] = ` [ "[ (0/1 → 1/12) ⇝ 1/8 | s:bd amp:0.1 ]", @@ -858,222 +840,6 @@ exports[`runs examples > example "amp" example index 0 1`] = ` ] `; -exports[`runs examples > example "tremolophase" example index 0 1`] = ` -[ - "[ 0/1 → 1/16 | note:f s:sawtooth am:4 tremolophase:0 ]", - "[ 1/16 → 1/8 | note:a s:sawtooth am:4 tremolophase:0 ]", - "[ 1/8 → 3/16 | note:c s:sawtooth am:4 tremolophase:0 ]", - "[ 3/16 → 1/4 | note:e s:sawtooth am:4 tremolophase:0 ]", - "[ 1/4 → 5/16 | note:f s:sawtooth am:4 tremolophase:0 ]", - "[ 5/16 → 3/8 | note:a s:sawtooth am:4 tremolophase:0 ]", - "[ 3/8 → 7/16 | note:c s:sawtooth am:4 tremolophase:0 ]", - "[ 7/16 → 1/2 | note:e s:sawtooth am:4 tremolophase:0 ]", - "[ 1/2 → 9/16 | note:f s:sawtooth am:4 tremolophase:0 ]", - "[ 9/16 → 5/8 | note:a s:sawtooth am:4 tremolophase:0 ]", - "[ 5/8 → 11/16 | note:c s:sawtooth am:4 tremolophase:0 ]", - "[ 11/16 → 3/4 | note:e s:sawtooth am:4 tremolophase:0 ]", - "[ 3/4 → 13/16 | note:f s:sawtooth am:4 tremolophase:0 ]", - "[ 13/16 → 7/8 | note:a s:sawtooth am:4 tremolophase:0 ]", - "[ 7/8 → 15/16 | note:c s:sawtooth am:4 tremolophase:0 ]", - "[ 15/16 → 1/1 | note:e s:sawtooth am:4 tremolophase:0 ]", - "[ 1/1 → 17/16 | note:f s:sawtooth am:4 tremolophase:0.25 ]", - "[ 17/16 → 9/8 | note:a s:sawtooth am:4 tremolophase:0.25 ]", - "[ 9/8 → 19/16 | note:c s:sawtooth am:4 tremolophase:0.25 ]", - "[ 19/16 → 5/4 | note:e s:sawtooth am:4 tremolophase:0.25 ]", - "[ 5/4 → 21/16 | note:f s:sawtooth am:4 tremolophase:0.25 ]", - "[ 21/16 → 11/8 | note:a s:sawtooth am:4 tremolophase:0.25 ]", - "[ 11/8 → 23/16 | note:c s:sawtooth am:4 tremolophase:0.25 ]", - "[ 23/16 → 3/2 | note:e s:sawtooth am:4 tremolophase:0.25 ]", - "[ 3/2 → 25/16 | note:f s:sawtooth am:4 tremolophase:0.25 ]", - "[ 25/16 → 13/8 | note:a s:sawtooth am:4 tremolophase:0.25 ]", - "[ 13/8 → 27/16 | note:c s:sawtooth am:4 tremolophase:0.25 ]", - "[ 27/16 → 7/4 | note:e s:sawtooth am:4 tremolophase:0.25 ]", - "[ 7/4 → 29/16 | note:f s:sawtooth am:4 tremolophase:0.25 ]", - "[ 29/16 → 15/8 | note:a s:sawtooth am:4 tremolophase:0.25 ]", - "[ 15/8 → 31/16 | note:c s:sawtooth am:4 tremolophase:0.25 ]", - "[ 31/16 → 2/1 | note:e s:sawtooth am:4 tremolophase:0.25 ]", - "[ 2/1 → 33/16 | note:f s:sawtooth am:4 tremolophase:0.66 ]", - "[ 33/16 → 17/8 | note:a s:sawtooth am:4 tremolophase:0.66 ]", - "[ 17/8 → 35/16 | note:c s:sawtooth am:4 tremolophase:0.66 ]", - "[ 35/16 → 9/4 | note:e s:sawtooth am:4 tremolophase:0.66 ]", - "[ 9/4 → 37/16 | note:f s:sawtooth am:4 tremolophase:0.66 ]", - "[ 37/16 → 19/8 | note:a s:sawtooth am:4 tremolophase:0.66 ]", - "[ 19/8 → 39/16 | note:c s:sawtooth am:4 tremolophase:0.66 ]", - "[ 39/16 → 5/2 | note:e s:sawtooth am:4 tremolophase:0.66 ]", - "[ 5/2 → 41/16 | note:f s:sawtooth am:4 tremolophase:0.66 ]", - "[ 41/16 → 21/8 | note:a s:sawtooth am:4 tremolophase:0.66 ]", - "[ 21/8 → 43/16 | note:c s:sawtooth am:4 tremolophase:0.66 ]", - "[ 43/16 → 11/4 | note:e s:sawtooth am:4 tremolophase:0.66 ]", - "[ 11/4 → 45/16 | note:f s:sawtooth am:4 tremolophase:0.66 ]", - "[ 45/16 → 23/8 | note:a s:sawtooth am:4 tremolophase:0.66 ]", - "[ 23/8 → 47/16 | note:c s:sawtooth am:4 tremolophase:0.66 ]", - "[ 47/16 → 3/1 | note:e s:sawtooth am:4 tremolophase:0.66 ]", - "[ 3/1 → 49/16 | note:f s:sawtooth am:4 tremolophase:0 ]", - "[ 49/16 → 25/8 | note:a s:sawtooth am:4 tremolophase:0 ]", - "[ 25/8 → 51/16 | note:c s:sawtooth am:4 tremolophase:0 ]", - "[ 51/16 → 13/4 | note:e s:sawtooth am:4 tremolophase:0 ]", - "[ 13/4 → 53/16 | note:f s:sawtooth am:4 tremolophase:0 ]", - "[ 53/16 → 27/8 | note:a s:sawtooth am:4 tremolophase:0 ]", - "[ 27/8 → 55/16 | note:c s:sawtooth am:4 tremolophase:0 ]", - "[ 55/16 → 7/2 | note:e s:sawtooth am:4 tremolophase:0 ]", - "[ 7/2 → 57/16 | note:f s:sawtooth am:4 tremolophase:0 ]", - "[ 57/16 → 29/8 | note:a s:sawtooth am:4 tremolophase:0 ]", - "[ 29/8 → 59/16 | note:c s:sawtooth am:4 tremolophase:0 ]", - "[ 59/16 → 15/4 | note:e s:sawtooth am:4 tremolophase:0 ]", - "[ 15/4 → 61/16 | note:f s:sawtooth am:4 tremolophase:0 ]", - "[ 61/16 → 31/8 | note:a s:sawtooth am:4 tremolophase:0 ]", - "[ 31/8 → 63/16 | note:c s:sawtooth am:4 tremolophase:0 ]", - "[ 63/16 → 4/1 | note:e s:sawtooth am:4 tremolophase:0 ]", -] -`; - -exports[`runs examples > example "tremoloshape" example index 0 1`] = ` -[ - "[ 0/1 → 1/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 1/16 → 1/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 1/8 → 3/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 3/16 → 1/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 1/4 → 5/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 5/16 → 3/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 3/8 → 7/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 7/16 → 1/2 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 1/2 → 9/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 9/16 → 5/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 5/8 → 11/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 11/16 → 3/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 3/4 → 13/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 13/16 → 7/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 7/8 → 15/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 15/16 → 1/1 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 1/1 → 17/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 17/16 → 9/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 9/8 → 19/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 19/16 → 5/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 5/4 → 21/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 21/16 → 11/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 11/8 → 23/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 23/16 → 3/2 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 3/2 → 25/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 25/16 → 13/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 13/8 → 27/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 27/16 → 7/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 7/4 → 29/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 29/16 → 15/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 15/8 → 31/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 31/16 → 2/1 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 2/1 → 33/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 33/16 → 17/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 17/8 → 35/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 35/16 → 9/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 9/4 → 37/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 37/16 → 19/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 19/8 → 39/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 39/16 → 5/2 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 5/2 → 41/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 41/16 → 21/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 21/8 → 43/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 43/16 → 11/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 11/4 → 45/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 45/16 → 23/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 23/8 → 47/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 47/16 → 3/1 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 3/1 → 49/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 49/16 → 25/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 25/8 → 51/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 51/16 → 13/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 13/4 → 53/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 53/16 → 27/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 27/8 → 55/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 55/16 → 7/2 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 7/2 → 57/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 57/16 → 29/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 29/8 → 59/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 59/16 → 15/4 | note:d am:4 tremoloshape:ramp s:sawtooth ]", - "[ 15/4 → 61/16 | note:f am:4 tremoloshape:ramp s:sawtooth ]", - "[ 61/16 → 31/8 | note:g am:4 tremoloshape:ramp s:sawtooth ]", - "[ 31/8 → 63/16 | note:c am:4 tremoloshape:ramp s:sawtooth ]", - "[ 63/16 → 4/1 | note:d am:4 tremoloshape:ramp s:sawtooth ]", -] -`; - -exports[`runs examples > example "tremoloskew" example index 0 1`] = ` -[ - "[ 0/1 → 1/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 1/16 → 1/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 1/8 → 3/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 3/16 → 1/4 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 1/4 → 5/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 5/16 → 3/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 3/8 → 7/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 7/16 → 1/2 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 1/2 → 9/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 9/16 → 5/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 5/8 → 11/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 11/16 → 3/4 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 3/4 → 13/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 13/16 → 7/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 7/8 → 15/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 15/16 → 1/1 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 1/1 → 17/16 | note:f am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 17/16 → 9/8 | note:a am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 9/8 → 19/16 | note:c am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 19/16 → 5/4 | note:e am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 5/4 → 21/16 | note:f am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 21/16 → 11/8 | note:a am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 11/8 → 23/16 | note:c am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 23/16 → 3/2 | note:e am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 3/2 → 25/16 | note:f am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 25/16 → 13/8 | note:a am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 13/8 → 27/16 | note:c am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 27/16 → 7/4 | note:e am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 7/4 → 29/16 | note:f am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 29/16 → 15/8 | note:a am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 15/8 → 31/16 | note:c am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 31/16 → 2/1 | note:e am:5 tremoloskew:0.5 tremolodepth:0.5 attack:0.01 release:0.03 ]", - "[ 2/1 → 33/16 | note:f am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 33/16 → 17/8 | note:a am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 17/8 → 35/16 | note:c am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 35/16 → 9/4 | note:e am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 9/4 → 37/16 | note:f am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 37/16 → 19/8 | note:a am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 19/8 → 39/16 | note:c am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 39/16 → 5/2 | note:e am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 5/2 → 41/16 | note:f am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 41/16 → 21/8 | note:a am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 21/8 → 43/16 | note:c am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 43/16 → 11/4 | note:e am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 11/4 → 45/16 | note:f am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 45/16 → 23/8 | note:a am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 23/8 → 47/16 | note:c am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 47/16 → 3/1 | note:e am:5 tremoloskew:0.5 tremolodepth:2 attack:0.01 release:0.03 ]", - "[ 3/1 → 49/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 49/16 → 25/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 25/8 → 51/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 51/16 → 13/4 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 13/4 → 53/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 53/16 → 27/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 27/8 → 55/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 55/16 → 7/2 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 7/2 → 57/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 57/16 → 29/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 29/8 → 59/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 59/16 → 15/4 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 15/4 → 61/16 | note:f am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 61/16 → 31/8 | note:a am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 31/8 → 63/16 | note:c am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", - "[ 63/16 → 4/1 | note:e am:5 tremoloskew:0.5 tremolodepth:1 attack:0.01 release:0.03 ]", -] -`; - -exports[`runs examples > example "tremolosync" example index 0 1`] = ` -[ - "[ 0/1 → 1/1 | s:supersaw tremolosync:0.25 tremoloskew:0 tremolodepth:2 ]", - "[ 1/1 → 2/1 | s:supersaw tremolosync:0.25 tremoloskew:0.5 tremolodepth:2 ]", - "[ 2/1 → 3/1 | s:supersaw tremolosync:0.25 tremoloskew:1 tremolodepth:2 ]", - "[ 3/1 → 4/1 | s:supersaw tremolosync:0.25 tremoloskew:0 tremolodepth:2 ]", -] -`; - exports[`runs examples > example "apply" example index 0 1`] = ` [ "[ 0/1 → 1/1 | note:C3 ]", @@ -10344,6 +10110,420 @@ exports[`runs examples > example "transpose" example index 1 1`] = ` ] `; +exports[`runs examples > example "tremolo" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 1/16 → 1/8 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 1/8 → 3/16 | note:d# s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 3/16 → 1/4 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 1/4 → 5/16 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 5/16 → 3/8 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 3/8 → 7/16 | note:d# s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 7/16 → 1/2 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 1/2 → 9/16 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 9/16 → 5/8 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 5/8 → 11/16 | note:d# s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 11/16 → 3/4 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 3/4 → 13/16 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 13/16 → 7/8 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 7/8 → 15/16 | note:d# s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 15/16 → 1/1 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 1/1 → 17/16 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 17/16 → 9/8 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 9/8 → 19/16 | note:d# s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 19/16 → 5/4 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 5/4 → 21/16 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 21/16 → 11/8 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 11/8 → 23/16 | note:d# s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 23/16 → 3/2 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 3/2 → 25/16 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 25/16 → 13/8 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 13/8 → 27/16 | note:d# s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 27/16 → 7/4 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 7/4 → 29/16 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 29/16 → 15/8 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 15/8 → 31/16 | note:d# s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 31/16 → 2/1 | note:d s:supersaw tremolo:2 tremoloskew:0.5 ]", + "[ 2/1 → 33/16 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 33/16 → 17/8 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 17/8 → 35/16 | note:d# s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 35/16 → 9/4 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 9/4 → 37/16 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 37/16 → 19/8 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 19/8 → 39/16 | note:d# s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 39/16 → 5/2 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 5/2 → 41/16 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 41/16 → 21/8 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 21/8 → 43/16 | note:d# s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 43/16 → 11/4 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 11/4 → 45/16 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 45/16 → 23/8 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 23/8 → 47/16 | note:d# s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 47/16 → 3/1 | note:d s:supersaw tremolo:100 tremoloskew:0.5 ]", + "[ 3/1 → 49/16 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 49/16 → 25/8 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 25/8 → 51/16 | note:d# s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 51/16 → 13/4 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 13/4 → 53/16 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 53/16 → 27/8 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 27/8 → 55/16 | note:d# s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 55/16 → 7/2 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 7/2 → 57/16 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 57/16 → 29/8 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 29/8 → 59/16 | note:d# s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 59/16 → 15/4 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 15/4 → 61/16 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 61/16 → 31/8 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 31/8 → 63/16 | note:d# s:supersaw tremolo:3 tremoloskew:0.5 ]", + "[ 63/16 → 4/1 | note:d s:supersaw tremolo:3 tremoloskew:0.5 ]", +] +`; + +exports[`runs examples > example "tremolodepth" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 1/16 → 1/8 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 1/8 → 3/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 3/16 → 1/4 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 1/4 → 5/16 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 5/16 → 3/8 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 3/8 → 7/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 7/16 → 1/2 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 1/2 → 9/16 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 9/16 → 5/8 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 5/8 → 11/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 11/16 → 3/4 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 3/4 → 13/16 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 13/16 → 7/8 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 7/8 → 15/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 15/16 → 1/1 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 1/1 → 17/16 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 17/16 → 9/8 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 9/8 → 19/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 19/16 → 5/4 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 5/4 → 21/16 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 21/16 → 11/8 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 11/8 → 23/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 23/16 → 3/2 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 3/2 → 25/16 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 25/16 → 13/8 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 13/8 → 27/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 27/16 → 7/4 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 7/4 → 29/16 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 29/16 → 15/8 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 15/8 → 31/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 31/16 → 2/1 | note:a1 s:pulse tremolosync:4 tremolodepth:2 ]", + "[ 2/1 → 33/16 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 33/16 → 17/8 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 17/8 → 35/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 35/16 → 9/4 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 9/4 → 37/16 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 37/16 → 19/8 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 19/8 → 39/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 39/16 → 5/2 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 5/2 → 41/16 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 41/16 → 21/8 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 21/8 → 43/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 43/16 → 11/4 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 11/4 → 45/16 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 45/16 → 23/8 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 23/8 → 47/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 47/16 → 3/1 | note:a1 s:pulse tremolosync:4 tremolodepth:0.7 ]", + "[ 3/1 → 49/16 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 49/16 → 25/8 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 25/8 → 51/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 51/16 → 13/4 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 13/4 → 53/16 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 53/16 → 27/8 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 27/8 → 55/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 55/16 → 7/2 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 7/2 → 57/16 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 57/16 → 29/8 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 29/8 → 59/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 59/16 → 15/4 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 15/4 → 61/16 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 61/16 → 31/8 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 31/8 → 63/16 | note:a#1 s:pulse tremolosync:4 tremolodepth:1 ]", + "[ 63/16 → 4/1 | note:a1 s:pulse tremolosync:4 tremolodepth:1 ]", +] +`; + +exports[`runs examples > example "tremolophase" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:f s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 1/16 → 1/8 | note:a s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 1/8 → 3/16 | note:c s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 3/16 → 1/4 | note:e s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 1/4 → 5/16 | note:f s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 5/16 → 3/8 | note:a s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 3/8 → 7/16 | note:c s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 7/16 → 1/2 | note:e s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 1/2 → 9/16 | note:f s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 9/16 → 5/8 | note:a s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 5/8 → 11/16 | note:c s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 11/16 → 3/4 | note:e s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 3/4 → 13/16 | note:f s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 13/16 → 7/8 | note:a s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 7/8 → 15/16 | note:c s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 15/16 → 1/1 | note:e s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 1/1 → 17/16 | note:f s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 17/16 → 9/8 | note:a s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 9/8 → 19/16 | note:c s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 19/16 → 5/4 | note:e s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 5/4 → 21/16 | note:f s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 21/16 → 11/8 | note:a s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 11/8 → 23/16 | note:c s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 23/16 → 3/2 | note:e s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 3/2 → 25/16 | note:f s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 25/16 → 13/8 | note:a s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 13/8 → 27/16 | note:c s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 27/16 → 7/4 | note:e s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 7/4 → 29/16 | note:f s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 29/16 → 15/8 | note:a s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 15/8 → 31/16 | note:c s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 31/16 → 2/1 | note:e s:sawtooth tremolosync:4 tremolophase:0.25 ]", + "[ 2/1 → 33/16 | note:f s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 33/16 → 17/8 | note:a s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 17/8 → 35/16 | note:c s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 35/16 → 9/4 | note:e s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 9/4 → 37/16 | note:f s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 37/16 → 19/8 | note:a s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 19/8 → 39/16 | note:c s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 39/16 → 5/2 | note:e s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 5/2 → 41/16 | note:f s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 41/16 → 21/8 | note:a s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 21/8 → 43/16 | note:c s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 43/16 → 11/4 | note:e s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 11/4 → 45/16 | note:f s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 45/16 → 23/8 | note:a s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 23/8 → 47/16 | note:c s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 47/16 → 3/1 | note:e s:sawtooth tremolosync:4 tremolophase:0.66 ]", + "[ 3/1 → 49/16 | note:f s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 49/16 → 25/8 | note:a s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 25/8 → 51/16 | note:c s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 51/16 → 13/4 | note:e s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 13/4 → 53/16 | note:f s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 53/16 → 27/8 | note:a s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 27/8 → 55/16 | note:c s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 55/16 → 7/2 | note:e s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 7/2 → 57/16 | note:f s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 57/16 → 29/8 | note:a s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 29/8 → 59/16 | note:c s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 59/16 → 15/4 | note:e s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 15/4 → 61/16 | note:f s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 61/16 → 31/8 | note:a s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 31/8 → 63/16 | note:c s:sawtooth tremolosync:4 tremolophase:0 ]", + "[ 63/16 → 4/1 | note:e s:sawtooth tremolosync:4 tremolophase:0 ]", +] +`; + +exports[`runs examples > example "tremoloshape" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:f tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 1/16 → 1/8 | note:g tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 1/8 → 3/16 | note:c tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 3/16 → 1/4 | note:d tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 1/4 → 5/16 | note:f tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 5/16 → 3/8 | note:g tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 3/8 → 7/16 | note:c tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 7/16 → 1/2 | note:d tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 1/2 → 9/16 | note:f tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 9/16 → 5/8 | note:g tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 5/8 → 11/16 | note:c tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 11/16 → 3/4 | note:d tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 3/4 → 13/16 | note:f tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 13/16 → 7/8 | note:g tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 7/8 → 15/16 | note:c tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 15/16 → 1/1 | note:d tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 1/1 → 17/16 | note:f tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 17/16 → 9/8 | note:g tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 9/8 → 19/16 | note:c tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 19/16 → 5/4 | note:d tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 5/4 → 21/16 | note:f tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 21/16 → 11/8 | note:g tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 11/8 → 23/16 | note:c tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 23/16 → 3/2 | note:d tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 3/2 → 25/16 | note:f tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 25/16 → 13/8 | note:g tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 13/8 → 27/16 | note:c tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 27/16 → 7/4 | note:d tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 7/4 → 29/16 | note:f tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 29/16 → 15/8 | note:g tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 15/8 → 31/16 | note:c tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 31/16 → 2/1 | note:d tremolosync:4 tremoloshape:tri s:sawtooth ]", + "[ 2/1 → 33/16 | note:f tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 33/16 → 17/8 | note:g tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 17/8 → 35/16 | note:c tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 35/16 → 9/4 | note:d tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 9/4 → 37/16 | note:f tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 37/16 → 19/8 | note:g tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 19/8 → 39/16 | note:c tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 39/16 → 5/2 | note:d tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 5/2 → 41/16 | note:f tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 41/16 → 21/8 | note:g tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 21/8 → 43/16 | note:c tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 43/16 → 11/4 | note:d tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 11/4 → 45/16 | note:f tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 45/16 → 23/8 | note:g tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 23/8 → 47/16 | note:c tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 47/16 → 3/1 | note:d tremolosync:4 tremoloshape:square s:sawtooth ]", + "[ 3/1 → 49/16 | note:f tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 49/16 → 25/8 | note:g tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 25/8 → 51/16 | note:c tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 51/16 → 13/4 | note:d tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 13/4 → 53/16 | note:f tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 53/16 → 27/8 | note:g tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 27/8 → 55/16 | note:c tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 55/16 → 7/2 | note:d tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 7/2 → 57/16 | note:f tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 57/16 → 29/8 | note:g tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 29/8 → 59/16 | note:c tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 59/16 → 15/4 | note:d tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 15/4 → 61/16 | note:f tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 61/16 → 31/8 | note:g tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 31/8 → 63/16 | note:c tremolosync:4 tremoloshape:sine s:sawtooth ]", + "[ 63/16 → 4/1 | note:d tremolosync:4 tremoloshape:sine s:sawtooth ]", +] +`; + +exports[`runs examples > example "tremoloskew" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 1/16 → 1/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 1/8 → 3/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 3/16 → 1/4 | note:e s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 1/4 → 5/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 5/16 → 3/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 3/8 → 7/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 7/16 → 1/2 | note:e s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 1/2 → 9/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 9/16 → 5/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 5/8 → 11/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 11/16 → 3/4 | note:e s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 3/4 → 13/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 13/16 → 7/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 7/8 → 15/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 15/16 → 1/1 | note:e s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 1/1 → 17/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 17/16 → 9/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 9/8 → 19/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 19/16 → 5/4 | note:e s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 5/4 → 21/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 21/16 → 11/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 11/8 → 23/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 23/16 → 3/2 | note:e s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 3/2 → 25/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 25/16 → 13/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 13/8 → 27/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 27/16 → 7/4 | note:e s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 7/4 → 29/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 29/16 → 15/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 15/8 → 31/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 31/16 → 2/1 | note:e s:sawtooth tremolosync:4 tremoloskew:0 ]", + "[ 2/1 → 33/16 | note:f s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 33/16 → 17/8 | note:a s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 17/8 → 35/16 | note:c s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 35/16 → 9/4 | note:e s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 9/4 → 37/16 | note:f s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 37/16 → 19/8 | note:a s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 19/8 → 39/16 | note:c s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 39/16 → 5/2 | note:e s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 5/2 → 41/16 | note:f s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 41/16 → 21/8 | note:a s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 21/8 → 43/16 | note:c s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 43/16 → 11/4 | note:e s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 11/4 → 45/16 | note:f s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 45/16 → 23/8 | note:a s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 23/8 → 47/16 | note:c s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 47/16 → 3/1 | note:e s:sawtooth tremolosync:4 tremoloskew:1 ]", + "[ 3/1 → 49/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 49/16 → 25/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 25/8 → 51/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 51/16 → 13/4 | note:e s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 13/4 → 53/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 53/16 → 27/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 27/8 → 55/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 55/16 → 7/2 | note:e s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 7/2 → 57/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 57/16 → 29/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 29/8 → 59/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 59/16 → 15/4 | note:e s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 15/4 → 61/16 | note:f s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 61/16 → 31/8 | note:a s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 31/8 → 63/16 | note:c s:sawtooth tremolosync:4 tremoloskew:0.5 ]", + "[ 63/16 → 4/1 | note:e s:sawtooth tremolosync:4 tremoloskew:0.5 ]", +] +`; + +exports[`runs examples > example "tremolosync" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 1/16 → 1/8 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 1/8 → 3/16 | note:d# s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 3/16 → 1/4 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 1/4 → 5/16 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 5/16 → 3/8 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 3/8 → 7/16 | note:d# s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 7/16 → 1/2 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 1/2 → 9/16 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 9/16 → 5/8 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 5/8 → 11/16 | note:d# s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 11/16 → 3/4 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 3/4 → 13/16 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 13/16 → 7/8 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 7/8 → 15/16 | note:d# s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 15/16 → 1/1 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 1/1 → 17/16 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 17/16 → 9/8 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 9/8 → 19/16 | note:d# s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 19/16 → 5/4 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 5/4 → 21/16 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 21/16 → 11/8 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 11/8 → 23/16 | note:d# s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 23/16 → 3/2 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 3/2 → 25/16 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 25/16 → 13/8 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 13/8 → 27/16 | note:d# s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 27/16 → 7/4 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 7/4 → 29/16 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 29/16 → 15/8 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 15/8 → 31/16 | note:d# s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 31/16 → 2/1 | note:d s:supersaw tremolosync:4 tremoloskew:0.5 ]", + "[ 2/1 → 33/16 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 33/16 → 17/8 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 17/8 → 35/16 | note:d# s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 35/16 → 9/4 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 9/4 → 37/16 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 37/16 → 19/8 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 19/8 → 39/16 | note:d# s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 39/16 → 5/2 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 5/2 → 41/16 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 41/16 → 21/8 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 21/8 → 43/16 | note:d# s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 43/16 → 11/4 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 11/4 → 45/16 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 45/16 → 23/8 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 23/8 → 47/16 | note:d# s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 47/16 → 3/1 | note:d s:supersaw tremolosync:4 tremoloskew:0 ]", + "[ 3/1 → 49/16 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 49/16 → 25/8 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 25/8 → 51/16 | note:d# s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 51/16 → 13/4 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 13/4 → 53/16 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 53/16 → 27/8 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 27/8 → 55/16 | note:d# s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 55/16 → 7/2 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 7/2 → 57/16 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 57/16 → 29/8 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 29/8 → 59/16 | note:d# s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 59/16 → 15/4 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 15/4 → 61/16 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 61/16 → 31/8 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 31/8 → 63/16 | note:d# s:supersaw tremolosync:4 tremoloskew:1 ]", + "[ 63/16 → 4/1 | note:d s:supersaw tremolosync:4 tremoloskew:1 ]", +] +`; + exports[`runs examples > example "tri" example index 0 1`] = ` [ "[ 0/1 → 1/8 | note:C3 ]", From 75b6f59212d301e50ee01a33e7647a44ae4657ca Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 20 Jul 2025 23:26:17 -0400 Subject: [PATCH 71/75] fix --- packages/superdough/superdough.mjs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index f3e04a047..96cf3f673 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -363,6 +363,22 @@ export function getLfo(audioContext, begin, end, properties = {}) { }); } +// export function getLfo(audioContext, time, end, properties = {}) { +// return getWorklet(audioContext, 'lfo-processor', { +// frequency: 1, +// depth: 1, +// skew: 0, +// phaseoffset: 0, +// time, +// begin: time, +// end, +// shape: 1, +// dcoffset: -0.5, +// ...properties, +// }); +// } + + export function getSyncedLfo(audioContext, time, end, cps, cycle, properties = {}) { const frequency = cycle / cps; From 9416f317d4a19446411f9e3e9db98cb030e8baf2 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 20 Jul 2025 23:29:01 -0400 Subject: [PATCH 72/75] fixing... --- packages/superdough/superdough.mjs | 54 ++++++++++++------------- packages/superdough/worklets.mjs | 65 +++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 28 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 96cf3f673..a591df0fe 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -343,41 +343,41 @@ function getDelay(orbit, delaytime, delayfeedback, t, channels) { return delays[orbit]; } -export function getLfo(audioContext, begin, end, properties = {}) { - const { shape = 0, ...props } = properties; - const { dcoffset = -0.5, depth = 1 } = properties; - return getWorklet(audioContext, 'lfo-processor', { - frequency: 1, - depth, - skew: 0, - phaseoffset: 0, - time: begin, - begin, - end, - shape: getModulationShapeInput(shape), - dcoffset, - min: dcoffset - depth * 0.5, - max: dcoffset + depth * 0.5, - curve: 1, - ...props, - }); -} - -// export function getLfo(audioContext, time, end, properties = {}) { +// export function getLfo(audioContext, begin, end, properties = {}) { +// const { shape = 0, ...props } = properties; +// const { dcoffset = -0.5, depth = 1 } = properties; // return getWorklet(audioContext, 'lfo-processor', { // frequency: 1, -// depth: 1, +// depth, // skew: 0, // phaseoffset: 0, -// time, -// begin: time, +// time: begin, +// begin, // end, -// shape: 1, -// dcoffset: -0.5, -// ...properties, +// shape: getModulationShapeInput(shape), +// dcoffset, +// min: dcoffset - depth * 0.5, +// max: dcoffset + depth * 0.5, +// curve: 1, +// ...props, // }); // } +export function getLfo(audioContext, time, end, properties = {}) { + return getWorklet(audioContext, 'lfo-processor', { + frequency: 1, + depth: 1, + skew: 0, + phaseoffset: 0, + time, + begin: time, + end, + shape: 1, + dcoffset: -0.5, + ...properties, + }); +} + export function getSyncedLfo(audioContext, time, end, cps, cycle, properties = {}) { const frequency = cycle / cps; diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index b4b36f6c9..25dae03cd 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -81,7 +81,70 @@ function getParamValue(block, param) { } return param[0]; } -const waveShapeNames = Object.keys(waveshapes); +// const waveShapeNames = Object.keys(waveshapes); +// class LFOProcessor extends AudioWorkletProcessor { +// static get parameterDescriptors() { +// return [ +// { name: 'time', defaultValue: 0 }, +// { name: 'end', defaultValue: 0 }, +// { name: 'frequency', defaultValue: 0.5 }, +// { name: 'skew', defaultValue: 0.5 }, +// { name: 'depth', defaultValue: 1 }, +// { name: 'phaseoffset', defaultValue: 0 }, +// { name: 'shape', defaultValue: 0 }, +// { name: 'dcoffset', defaultValue: 0 }, +// ]; +// } + +// constructor() { +// super(); +// this.phase; +// } + +// incrementPhase(dt) { +// this.phase += dt; +// if (this.phase > 1.0) { +// this.phase = this.phase - 1; +// } +// } + +// process(inputs, outputs, parameters) { +// // eslint-disable-next-line no-undef +// if (currentTime >= parameters.end[0]) { +// return false; +// } + +// const output = outputs[0]; +// const frequency = parameters['frequency'][0]; + +// const time = parameters['time'][0]; +// const depth = parameters['depth'][0]; +// const skew = parameters['skew'][0]; +// const phaseoffset = parameters['phaseoffset'][0]; + +// const dcoffset = parameters['dcoffset'][0]; +// const shape = waveShapeNames[parameters['shape'][0]]; + +// const blockSize = output[0].length ?? 0; + +// if (this.phase == null) { +// this.phase = _mod(time * frequency + phaseoffset, 1); +// } +// // eslint-disable-next-line no-undef +// const dt = frequency / sampleRate; +// for (let n = 0; n < blockSize; n++) { +// for (let i = 0; i < output.length; i++) { +// const modval = (waveshapes[shape](this.phase, skew) + dcoffset) * depth; +// output[i][n] = modval; +// } +// this.incrementPhase(dt); +// } + +// return true; +// } +// } +// registerProcessor('lfo-processor', LFOProcessor); +// const waveShapeNames = Object.keys(waveshapes); class LFOProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [ From ccf2a6de52c7a595188c3404e82735f50bb5ed85 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 20 Jul 2025 23:38:16 -0400 Subject: [PATCH 73/75] fixing.. --- packages/superdough/superdough.mjs | 54 +++++++++++++++--------------- packages/superdough/worklets.mjs | 2 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index a591df0fe..96cf3f673 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -343,41 +343,41 @@ function getDelay(orbit, delaytime, delayfeedback, t, channels) { return delays[orbit]; } -// export function getLfo(audioContext, begin, end, properties = {}) { -// const { shape = 0, ...props } = properties; -// const { dcoffset = -0.5, depth = 1 } = properties; -// return getWorklet(audioContext, 'lfo-processor', { -// frequency: 1, -// depth, -// skew: 0, -// phaseoffset: 0, -// time: begin, -// begin, -// end, -// shape: getModulationShapeInput(shape), -// dcoffset, -// min: dcoffset - depth * 0.5, -// max: dcoffset + depth * 0.5, -// curve: 1, -// ...props, -// }); -// } - -export function getLfo(audioContext, time, end, properties = {}) { +export function getLfo(audioContext, begin, end, properties = {}) { + const { shape = 0, ...props } = properties; + const { dcoffset = -0.5, depth = 1 } = properties; return getWorklet(audioContext, 'lfo-processor', { frequency: 1, - depth: 1, + depth, skew: 0, phaseoffset: 0, - time, - begin: time, + time: begin, + begin, end, - shape: 1, - dcoffset: -0.5, - ...properties, + shape: getModulationShapeInput(shape), + dcoffset, + min: dcoffset - depth * 0.5, + max: dcoffset + depth * 0.5, + curve: 1, + ...props, }); } +// export function getLfo(audioContext, time, end, properties = {}) { +// return getWorklet(audioContext, 'lfo-processor', { +// frequency: 1, +// depth: 1, +// skew: 0, +// phaseoffset: 0, +// time, +// begin: time, +// end, +// shape: 1, +// dcoffset: -0.5, +// ...properties, +// }); +// } + export function getSyncedLfo(audioContext, time, end, cps, cycle, properties = {}) { const frequency = cycle / cps; diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 25dae03cd..c9948916a 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -144,7 +144,7 @@ function getParamValue(block, param) { // } // } // registerProcessor('lfo-processor', LFOProcessor); -// const waveShapeNames = Object.keys(waveshapes); +const waveShapeNames = Object.keys(waveshapes); class LFOProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [ From b2f90ecb3ba2fa47fba6cca63afa027df4cd7bb1 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 20 Jul 2025 23:56:30 -0400 Subject: [PATCH 74/75] fix def skew --- packages/superdough/superdough.mjs | 9 ++++++--- packages/superdough/synth.mjs | 2 +- packages/superdough/worklets.mjs | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 96cf3f673..f9e8a1b8c 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -346,10 +346,10 @@ function getDelay(orbit, delaytime, delayfeedback, t, channels) { export function getLfo(audioContext, begin, end, properties = {}) { const { shape = 0, ...props } = properties; const { dcoffset = -0.5, depth = 1 } = properties; - return getWorklet(audioContext, 'lfo-processor', { + const lfoprops = { frequency: 1, depth, - skew: 0, + skew: .5, phaseoffset: 0, time: begin, begin, @@ -360,7 +360,10 @@ export function getLfo(audioContext, begin, end, properties = {}) { max: dcoffset + depth * 0.5, curve: 1, ...props, - }); + } + + console.info(lfoprops) + return getWorklet(audioContext, 'lfo-processor', lfoprops); } // export function getLfo(audioContext, time, end, properties = {}) { diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 88e14e5ab..a95ba9df4 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -279,7 +279,7 @@ export function registerSynthSounds() { getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear'); let lfo; if (pwsweep != 0) { - lfo = getLfo(ac, begin, end, { frequency: pwrate, depth: pwsweep }); + lfo = getLfo(ac, begin, end, { frequency: pwrate, depth: pwsweep, skew: .5, dcoffset: 0 }); lfo.connect(o.parameters.get('pulsewidth')); } let timeoutNode = webAudioTimeout( diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index c9948916a..df3c445ac 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -185,6 +185,8 @@ class LFOProcessor extends AudioWorkletProcessor { return true; } + + const output = outputs[0]; const frequency = parameters['frequency'][0]; From 66828e17d30946b217942e7a89af7a1fe6903642 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 21 Jul 2025 01:11:04 -0400 Subject: [PATCH 75/75] working --- packages/core/cyclist.mjs | 4 +- packages/core/logger.mjs | 6 +++ packages/core/repl.mjs | 4 +- packages/superdough/superdough.mjs | 25 ++-------- packages/superdough/synth.mjs | 2 +- packages/superdough/worklets.mjs | 74 +++--------------------------- 6 files changed, 21 insertions(+), 94 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index fec819548..ad0961032 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import createClock from './zyklus.mjs'; -import { logger } from './logger.mjs'; +import { errorLogger, logger } from './logger.mjs'; export class Cyclist { constructor({ @@ -76,7 +76,7 @@ export class Cyclist { } }); } catch (e) { - logger(`[cyclist] error: ${e.message}`); + errorLogger(e); onError?.(e); } }, diff --git a/packages/core/logger.mjs b/packages/core/logger.mjs index e13bf86ca..635505c59 100644 --- a/packages/core/logger.mjs +++ b/packages/core/logger.mjs @@ -4,6 +4,12 @@ let debounce = 1000, lastMessage, lastTime; +export function errorLogger(e, origin = 'cyclist') { + //TODO: add some kind of debug flag that enables this while in dev mode + // console.error(e) + logger(`[${origin}] error: ${e.message}`); +} + export function logger(message, type, data = {}) { let t = performance.now(); if (lastMessage === message && t - lastTime < debounce) { diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index aeaa6418e..47231af4d 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -1,7 +1,7 @@ import { NeoCyclist } from './neocyclist.mjs'; import { Cyclist } from './cyclist.mjs'; import { evaluate as _evaluate } from './evaluate.mjs'; -import { logger } from './logger.mjs'; +import { errorLogger, logger } from './logger.mjs'; import { setTime } from './time.mjs'; import { evalScope } from './evaluate.mjs'; import { register, Pattern, isPattern, silence, stack } from './pattern.mjs'; @@ -256,6 +256,6 @@ export const getTrigger = await hap.context.onTrigger(hap, getTime(), cps, t); } } catch (err) { - logger(`[cyclist] error: ${err.message}`, 'error'); + errorLogger(err, 'getTrigger'); } }; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index f9e8a1b8c..7d90eb15d 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -349,39 +349,22 @@ export function getLfo(audioContext, begin, end, properties = {}) { const lfoprops = { frequency: 1, depth, - skew: .5, + skew: 0.5, phaseoffset: 0, time: begin, begin, end, shape: getModulationShapeInput(shape), dcoffset, - min: dcoffset - depth * 0.5, - max: dcoffset + depth * 0.5, + min: dcoffset * depth, + max: dcoffset * depth + depth, curve: 1, ...props, - } + }; - console.info(lfoprops) return getWorklet(audioContext, 'lfo-processor', lfoprops); } -// export function getLfo(audioContext, time, end, properties = {}) { -// return getWorklet(audioContext, 'lfo-processor', { -// frequency: 1, -// depth: 1, -// skew: 0, -// phaseoffset: 0, -// time, -// begin: time, -// end, -// shape: 1, -// dcoffset: -0.5, -// ...properties, -// }); -// } - - export function getSyncedLfo(audioContext, time, end, cps, cycle, properties = {}) { const frequency = cycle / cps; diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index a95ba9df4..88e14e5ab 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -279,7 +279,7 @@ export function registerSynthSounds() { getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear'); let lfo; if (pwsweep != 0) { - lfo = getLfo(ac, begin, end, { frequency: pwrate, depth: pwsweep, skew: .5, dcoffset: 0 }); + lfo = getLfo(ac, begin, end, { frequency: pwrate, depth: pwsweep }); lfo.connect(o.parameters.get('pulsewidth')); } let timeoutNode = webAudioTimeout( diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index df3c445ac..e7928a48f 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -81,69 +81,7 @@ function getParamValue(block, param) { } return param[0]; } -// const waveShapeNames = Object.keys(waveshapes); -// class LFOProcessor extends AudioWorkletProcessor { -// static get parameterDescriptors() { -// return [ -// { name: 'time', defaultValue: 0 }, -// { name: 'end', defaultValue: 0 }, -// { name: 'frequency', defaultValue: 0.5 }, -// { name: 'skew', defaultValue: 0.5 }, -// { name: 'depth', defaultValue: 1 }, -// { name: 'phaseoffset', defaultValue: 0 }, -// { name: 'shape', defaultValue: 0 }, -// { name: 'dcoffset', defaultValue: 0 }, -// ]; -// } -// constructor() { -// super(); -// this.phase; -// } - -// incrementPhase(dt) { -// this.phase += dt; -// if (this.phase > 1.0) { -// this.phase = this.phase - 1; -// } -// } - -// process(inputs, outputs, parameters) { -// // eslint-disable-next-line no-undef -// if (currentTime >= parameters.end[0]) { -// return false; -// } - -// const output = outputs[0]; -// const frequency = parameters['frequency'][0]; - -// const time = parameters['time'][0]; -// const depth = parameters['depth'][0]; -// const skew = parameters['skew'][0]; -// const phaseoffset = parameters['phaseoffset'][0]; - -// const dcoffset = parameters['dcoffset'][0]; -// const shape = waveShapeNames[parameters['shape'][0]]; - -// const blockSize = output[0].length ?? 0; - -// if (this.phase == null) { -// this.phase = _mod(time * frequency + phaseoffset, 1); -// } -// // eslint-disable-next-line no-undef -// const dt = frequency / sampleRate; -// for (let n = 0; n < blockSize; n++) { -// for (let i = 0; i < output.length; i++) { -// const modval = (waveshapes[shape](this.phase, skew) + dcoffset) * depth; -// output[i][n] = modval; -// } -// this.incrementPhase(dt); -// } - -// return true; -// } -// } -// registerProcessor('lfo-processor', LFOProcessor); const waveShapeNames = Object.keys(waveshapes); class LFOProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { @@ -185,8 +123,6 @@ class LFOProcessor extends AudioWorkletProcessor { return true; } - - const output = outputs[0]; const frequency = parameters['frequency'][0]; @@ -194,11 +130,12 @@ class LFOProcessor extends AudioWorkletProcessor { const depth = parameters['depth'][0]; const skew = parameters['skew'][0]; const phaseoffset = parameters['phaseoffset'][0]; - const min = parameters['min'][0]; - const max = parameters['max'][0]; + const curve = parameters['curve'][0]; const dcoffset = parameters['dcoffset'][0]; + const min = parameters['min'][0]; + const max = parameters['max'][0]; const shape = waveShapeNames[parameters['shape'][0]]; const blockSize = output[0].length ?? 0; @@ -210,8 +147,9 @@ class LFOProcessor extends AudioWorkletProcessor { const dt = frequency / sampleRate; for (let n = 0; n < blockSize; n++) { for (let i = 0; i < output.length; i++) { - const modval = (waveshapes[shape](this.phase, skew) + dcoffset) * depth; - output[i][n] = clamp(Math.pow(modval, curve), min, max); + let modval = (waveshapes[shape](this.phase, skew) + dcoffset) * depth; + modval = Math.pow(modval, curve); + output[i][n] = clamp(modval, min, max); } this.incrementPhase(dt); }