diff --git a/packages/core/clockworker.mjs b/packages/core/clockworker.mjs deleted file mode 100644 index ec09ef8e..00000000 --- a/packages/core/clockworker.mjs +++ /dev/null @@ -1,72 +0,0 @@ -/* -clockworker.mjs - -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 . -*/ - -// helpers to create a worker dynamically without needing a server / extra file -const stringifyFunction = (func) => '(' + func + ')();'; -const urlifyFunction = (func) => URL.createObjectURL(new Blob([stringifyFunction(func)], { type: 'text/javascript' })); -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 / 20; // query span, use divisors of 1000 to get better accuracy - tick = 0; - constructor(callback, interval) { - // for some reason the constructor default value won't do in prod build - this.interval = interval || this.interval; - this.worker = createWorker(() => { - // we cannot use closures here! - let interval; - let timerID = null; // this is clock #1 (the sloppy js clock) - const clear = () => { - if (timerID) { - clearInterval(timerID); - timerID = null; - } - }; - const start = () => { - clear(); - if (!interval) { - throw new Error('no interval set! call worker.postMessage({interval}) before starting.'); - } - postMessage('tick'); - timerID = setInterval(() => postMessage('tick'), interval * 1000); - }; - self.onmessage = function (e) { - if (e.data == 'start') { - start(); - } else if (e.data.interval) { - interval = e.data.interval; - if (timerID) { - start(); - } - } else if (e.data == 'stop') { - clear(); - } - }; - }); - this.setInterval(this.interval); - // const round = (n, d) => Math.round(n * d) / d; - this.worker.onmessage = (e) => { - if (e.data === 'tick') { - // callback with query span, using clock #2 (the audio clock) - callback(this.tick++, this.interval); - } - }; - } - start() { - // console.log('start...'); - this.worker.postMessage('start'); - } - stop() { - // console.log('stop...'); - this.worker.postMessage('stop'); - this.tick = 0; - } - setInterval(interval) { - this.worker.postMessage({ interval }); - } -} diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index e462f96b..66105d0a 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -1,10 +1,9 @@ /* cyclist.mjs - -Copyright (C) 2022 Strudel contributors - 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 { ClockWorker } from './clockworker.mjs'; import createClock from './zyklus.mjs'; export class Cyclist { diff --git a/packages/core/index.mjs b/packages/core/index.mjs index e567f185..33a95a77 100644 --- a/packages/core/index.mjs +++ b/packages/core/index.mjs @@ -15,8 +15,6 @@ export * from './state.mjs'; export * from './timespan.mjs'; export * from './util.mjs'; export * from './speak.mjs'; -export * from './clockworker.mjs'; -export * from './scheduler.mjs'; export * from './evaluate.mjs'; export * from './repl.mjs'; export { default as drawLine } from './drawLine.mjs'; diff --git a/packages/core/scheduler.mjs b/packages/core/scheduler.mjs deleted file mode 100644 index 9118a761..00000000 --- a/packages/core/scheduler.mjs +++ /dev/null @@ -1,90 +0,0 @@ -/* -scheduler.mjs - -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 { ClockWorker } from './clockworker.mjs'; - -export class Scheduler { - worker; - pattern; - started = false; - phase = 0; - cps = 1; - getTime; - lastTime; - constructor({ interval, onTrigger, onError, latency = 0.2, getTime }) { - this.worker = new ClockWorker((_, interval) => { - try { - // goals: - // - first query should start from zero - // - next query must start where the other left off - // - queries must be synced to the interval clock => no drifting - const time = getTime(); - if (!this.lastTime) { - this.lastTime = time; - return; - } - const passed = time - this.lastTime; // how much time passed since the last callback? - this.lastTime = time; - const begin = this.phase; // begin where we left off last time || 0 - const end = this.phase + passed * this.cps; - this.phase = end; // remember where we left off fro next query - - const haps = this.pattern.queryArc(begin, end); // get haps - // schedule each hap - haps.forEach((hap) => { - if (typeof hap.value?.cps === 'number') { - this.setCps(hap.value?.cps); - } - // skip haps without onset - if (!hap.part.begin.equals(hap.whole.begin)) { - return; - } - // the following line took me ages to come up with.. handle with care - const scheduled = time + (hap.whole.begin - begin) / this.cps - passed + interval + latency; - const duration = hap.duration / this.cps; // TODO: use legato / duration of objectified value - const now = getTime(); - const deadline = scheduled - now; - if (scheduled < now) { - console.warn(`deadline ${deadline.toFixed(2)} is below zero! latency ${latency}s, interval ${interval}s`); - return; - } - onTrigger?.(hap, deadline, duration); - }); - } catch (err) { - console.warn('scheduler error', err); - onError?.(err); - } - }, interval); - } - start() { - if (!this.pattern) { - throw new Error('Scheduler: no pattern set! call .setPattern first.'); - } - this.worker.start(); - this.started = true; - } - pause() { - this.worker.stop(); - delete this.lastTime; - this.started = false; - } - stop() { - this.phase = 0; - delete this.lastTime; - this.worker.stop(); - this.started = false; - } - setPattern(pat) { - this.pattern = pat; - } - 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('')}`); - } -}