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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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 4cc2de9b7efd37d941625a9805cb7117c2b4a2ef Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 5 Jul 2025 00:39:23 -0400 Subject: [PATCH 18/30] 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 d9e44dcda61d724d26585e6a537eb54b636b51ce Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 6 Jul 2025 00:59:39 -0400 Subject: [PATCH 19/30] 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 ce5f425142852573a68349371870ed1165e2d125 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 8 Jul 2025 00:26:15 -0400 Subject: [PATCH 20/30] 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 21/30] 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 22/30] 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 23/30] 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 63fcce79e000f971badc5e7ddb770a91b8519680 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 8 Jul 2025 21:24:27 -0400 Subject: [PATCH 24/30] 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 25/30] 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 26/30] 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 27/30] 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 735f57e27610a17113b9e13a2c8b3bfe2408757d Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 10 Jul 2025 23:18:57 -0400 Subject: [PATCH 28/30] 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 34874862ab283ec700caf1e102e0acd26f08dd14 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 18 Jul 2025 22:03:33 -0400 Subject: [PATCH 29/30] 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 30/30] 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 ]",