From 275931969e62f19f5edec01cbd63b9e940a37281 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 22 Aug 2022 22:15:32 +0200 Subject: [PATCH] scheduler error correction failed attempt --- packages/core/clockworker.mjs | 2 +- packages/core/scheduler.mjs | 32 +++++++++++++------ packages/react/examples/nano-repl/src/App.jsx | 4 +-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/packages/core/clockworker.mjs b/packages/core/clockworker.mjs index c56f38a4..97940d3e 100644 --- a/packages/core/clockworker.mjs +++ b/packages/core/clockworker.mjs @@ -12,7 +12,7 @@ const createWorker = (func) => new Worker(urlifyFunction(func)); // this is just a setInterval with a counter, running in a worker export class ClockWorker { worker; - interval = 1 / 32; // query span, use powers of 2 to get better float accuracy + interval = 1 / 10; // query span, use powers of 2 to get better float accuracy tick = 0; constructor(callback, interval = this.interval) { this.interval = interval; diff --git a/packages/core/scheduler.mjs b/packages/core/scheduler.mjs index 8bcef060..c2c7448a 100644 --- a/packages/core/scheduler.mjs +++ b/packages/core/scheduler.mjs @@ -13,21 +13,22 @@ export class Scheduler { phase = 0; cps = 1; lastTime; + error = 0; constructor({ interval, onTrigger, onError, latency = 0.1, getTime }) { this.worker = new ClockWorker((_, interval) => { try { - let error = 0; - // measure time between last and current callback and calculate deviation from extected interval - const time = getTime?.(); - if (time && this.lastTime) { - error = time - this.lastTime - interval; // how off is the callback? positive = too late - // console.log('ms error', error * 1000); - } - this.lastTime = time; const begin = this.phase; const end = this.phase + interval * this.cps; this.phase = end; const haps = this.pattern.queryArc(begin, end); + // this.log(begin, end, haps); + // measure time between last and current callback and calculate deviation from extected interval + const time = getTime?.(); + if (time && this.lastTime) { + const diff = time - this.lastTime; + this.error = diff - interval; + } + this.lastTime = time; haps.forEach((hap) => { if (typeof hap.value?.cps === 'number') { this.setCps(hap.value?.cps); @@ -35,7 +36,16 @@ export class Scheduler { if (!hap.part.begin.equals(hap.whole.begin)) { return; } - const deadline = (hap.whole.begin - begin) / this.cps + latency - error; + // console.log('error', this.error); + const deadline = (hap.whole.begin - begin) / this.cps + latency - this.error; + // const deadline = hap.whole.begin - begin + latency; // - error; + if (deadline < 0) { + console.warn( + `deadline ${deadline.toFixed( + 2, + )} is below zero! latency ${latency}s, interval ${interval}s, error ${this.error.toFixed(2)}s`, + ); + } // TODO: use legato / duration of objectified value const duration = hap.duration / this.cps; onTrigger?.(hap, deadline, duration); @@ -68,4 +78,8 @@ export class Scheduler { setCps(cps = 1) { this.cps = cps; } + 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/react/examples/nano-repl/src/App.jsx b/packages/react/examples/nano-repl/src/App.jsx index e1ca3e42..f0756d74 100644 --- a/packages/react/examples/nano-repl/src/App.jsx +++ b/packages/react/examples/nano-repl/src/App.jsx @@ -47,7 +47,7 @@ stack( .superimpose(x=>x.add(.04)) // add second, slightly detuned voice .add(perlin.range(0,.5)) // random pitch variation .n() // wrap in "n" - .s('sawtooth') // waveform + .s('square') // waveform .gain(.16) // turn down .cutoff(500) // fixed cutoff .attack(1) // slowly fade in @@ -67,9 +67,9 @@ stack( // await prebake(); const ctx = getAudioContext(); - function App() { const [code, setCode] = useState(defaultTune); + // const [code, setCode] = useState(`"c3".note().slow(2)`); const { scheduler, evaluate, schedulerError, evalError, isDirty } = useStrudel({ code, defaultOutput: webaudioOutput,