From 16472b59ddef33bc7fdb332cbcf23ecc6e09be48 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 8 May 2024 00:05:05 -0400 Subject: [PATCH] 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; }