From 0565558e2107a009615a555efb150047f553280b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 21 Mar 2024 13:27:53 +0100 Subject: [PATCH 01/13] scheduling in a worker, draft --- examples/worker-repl/.gitignore | 24 ++ examples/worker-repl/index.html | 13 + examples/worker-repl/main.js | 89 ++++++ examples/worker-repl/package.json | 15 + examples/worker-repl/worker.mjs | 116 ++++++++ packages/superdough/superdough.mjs | 3 +- pnpm-lock.yaml | 437 +++++++++++++++++++++++++++-- 7 files changed, 677 insertions(+), 20 deletions(-) create mode 100644 examples/worker-repl/.gitignore create mode 100644 examples/worker-repl/index.html create mode 100644 examples/worker-repl/main.js create mode 100644 examples/worker-repl/package.json create mode 100644 examples/worker-repl/worker.mjs diff --git a/examples/worker-repl/.gitignore b/examples/worker-repl/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/examples/worker-repl/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/worker-repl/index.html b/examples/worker-repl/index.html new file mode 100644 index 00000000..97aeb700 --- /dev/null +++ b/examples/worker-repl/index.html @@ -0,0 +1,13 @@ + + + + + + Worker REPL + + +
+ +

click somewhere...

+ + diff --git a/examples/worker-repl/main.js b/examples/worker-repl/main.js new file mode 100644 index 00000000..35454787 --- /dev/null +++ b/examples/worker-repl/main.js @@ -0,0 +1,89 @@ +import workerUrl from './worker.mjs?url'; +import { samples, superdough } from 'superdough'; + +let ctx; +document.addEventListener('click', async () => { + ctx = new AudioContext(); + await samples('github:tidalcycles/dirt-samples'); + initWorker(); +}); + +function initWorker() { + if (!globalThis.Worker) { + throw new Error('initWorker: Worker not supported in this environment'); + } + const worker = new Worker(workerUrl); + worker.postMessage({ origin: performance.timeOrigin }); + // worker.postMessage(1); + let origin; + let minLatency = 0.05; + + let init = performance.now(); + + // performance.timeOrigin; // unix timestamp when the page loads + // Date.now() // unix timestamp (ms since January 1st 1970) + // performance.timeOrigin // unix timestamp when the page loaded + // performance.now() // precise ms since the page loaded + // performance.timeOrigin + performance.now() ~= Date.now() + + // worker: performance.now() // precise ms since the worker loaded + // worker: performance.timeOrigin // unix timestamp when the worker loaded + + worker.onmessage = (e) => { + const { contextTime, performanceTime } = ctx.getOutputTimestamp(); + console.log('e', e.timeStamp, performance.now()); + /* // clock offsets + const { contextTime: t } = outputTimestamp; // audio clock time in seconds + const { performanceTime: now } = outputTimestamp; // estimated system clock time for contextTime + const { origin: workerOrigin } = e.data; // seconds when the worker was loaded + const { phase } = e.data; // target time in seconds of current tick (relative to system clock) + + let workerOriginDiffMs = workerOrigin - performance.timeOrigin; + // console.log('worker created after seconds:', workerOriginDiffMs); + + let workerOriginDiffSeconds = workerOriginDiffMs / 1000; + + let workerPhaseSeconds = phase + workerOriginDiffSeconds; + let nowSeconds = now / 1000; + + let workerDeadline = workerPhaseSeconds - nowSeconds; */ + + // console.log('workerDeadline', workerDeadline); + + // superdough({ s: 'bd' }, workerDeadline); + + e.data.forEach((event, i) => { + let phase = event.phase * 1000; + //let phase = event.tick * 100 + origin; + let diff = (phase - performanceTime) / 1000; + superdough({ s: 'bd' }, contextTime + diff + 2); + }); + + /* const deadline = phase - now; + + const { tickOrigin } = e.data; // ms when the first tick happened + + const { tick, begin, end, haps, cps } = e.data; + + // console.log('phase', phase); + if (tick === 0) { + console.log('set origin', t); + origin = t; + } + // console.log('tick', tick, begin, end, phase, origin); + haps.forEach((hap) => { + let hapBegin = hap.whole.begin; + hapBegin = (hapBegin.s * hapBegin.n) / hapBegin.d; + const stamp = ctx.getOutputTimestamp(); + + let targetTime = origin + hapBegin * cps; + let deadline = t - targetTime + minLatency; + // console.log('--hap, begin:', begin, 'deadline', deadline); + // console.log('hap', hap.value, begin, deadline); + superdough(hap.value, deadline); + }); */ + + // const stamp = ctx?.getOutputTimestamp(); + // console.log('stamp', stamp); + }; +} diff --git a/examples/worker-repl/package.json b/examples/worker-repl/package.json new file mode 100644 index 00000000..742b0683 --- /dev/null +++ b/examples/worker-repl/package.json @@ -0,0 +1,15 @@ +{ + "name": "worker-repl", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^5.2.0" + }, + "dependencies": {"superdough":"workspace:*"} +} diff --git a/examples/worker-repl/worker.mjs b/examples/worker-repl/worker.mjs new file mode 100644 index 00000000..f62e8f97 --- /dev/null +++ b/examples/worker-repl/worker.mjs @@ -0,0 +1,116 @@ +// used to consistently schedule events +const createClock = ( + getTime, + callback, // called slightly before each cycle + duration = 0.05, // duration of each cycle + interval = 0.1, // interval between callbacks + overlap = 0.1, // overlap between callbacks +) => { + let tick = 0; // counts callbacks + let phase = 0; // next callback time + let precision = 10 ** 4; // used to round phase + let minLatency = 0; + const setDuration = (setter) => (duration = setter(duration)); + overlap = overlap || interval / 2; + const onTick = () => { + const t = getTime(); + const lookahead = t + interval + overlap; // the time window for this tick + if (phase === 0) { + phase = t + minLatency; + } + // callback as long as we're inside the lookahead + while (phase < lookahead) { + // phase = Math.round(phase * precision) / precision; + phase >= t && callback(phase, duration, tick, t); + phase < t && console.log('TOO LATE', phase); // what if latency is added from outside? + phase += duration; // increment phase by duration + tick++; + } + }; + let intervalID; + const start = () => { + clear(); // just in case start was called more than once + onTick(); + intervalID = setInterval(onTick, interval * 1000); + }; + const clear = () => intervalID !== undefined && clearInterval(intervalID); + const pause = () => clear(); + const stop = () => { + tick = 0; + phase = 0; + clear(); + }; + const getPhase = () => phase; + // setCallback + return { setDuration, start, stop, pause, duration, interval, getPhase, minLatency }; +}; + +onmessage = async function (e) { + console.log('Worker: Message received from main script', e); + + /* const { seq } = await import('@strudel/core'); + let pat = seq('bd').fast(4).s(); */ + if (!e.data.origin) { + console.log('no origin'); + return; + } + console.log('start!', e.data.origin); + // e.data.origin = unix ms when main document was created + // performance.timeOrigin = unix ms when worker was created + let originDiff = (performance.timeOrigin - e.data.origin) / 1000; + let firstTick; + console.log('originDiff', originDiff); + + let precision = 10 ** 4; + let cps = 0.5; + let interval = 0.05; + let tickOrigin; + let lastEnd = 0; + let bag = []; + let clock = createClock( + () => performance.now() / 1000, + (phase, duration, tick, t) => { + if (tick === 0) { + firstTick = t; + console.log('firstTick', t); + } + // let durationInCycles = duration * cps; + /* phase = lastEnd; */ + // phase = Math.round(phase * precision) / precision; + + bag.push({ tick, duration, phase, cps, origin: performance.timeOrigin }); + //postMessage({ tick, phase, cps, origin: performance.timeOrigin }); + let pack = 4; + if (bag.length === pack) { + console.log('send', performance.now()); + postMessage(bag); + bag = []; + } + + /* phase += duration; + lastEnd = phase; */ + /*if (tick === 0) { + tickOrigin = performance.now(); + } + let begin = lastEnd || 0; + let end = begin + duration * cps; + lastEnd = end; + //console.log('query', begin, phase); + const haps = pat.queryArc(begin, end).filter((hap) => hap.hasOnset()); + if (haps.length) { + postMessage({ tick, phase, begin, end, haps, cps, tickOrigin, origin: performance.timeOrigin }); + } + */ + /* console.log( + 'tick', + phase, + `${begin.toFixed(2)}-${end.toFixed(2)}: ${haps + .map((h) => `${h.value.s}:${(h.whole.begin + 0).toFixed(2)}`) + .join(' ')}`, + ); */ + }, + interval, + interval, + ); + clock.start(); +}; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index cf9bd181..026d8747 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -260,7 +260,7 @@ export function resetGlobalEffects() { analysersData = {}; } -export const superdough = async (value, deadline, hapDuration) => { +export const superdough = async (value, t, hapDuration) => { const ac = getAudioContext(); if (typeof value !== 'object') { throw new Error( @@ -272,7 +272,6 @@ export const superdough = async (value, deadline, hapDuration) => { // duration is passed as value too.. value.duration = hapDuration; // calculate absolute time - let t = ac.currentTime + deadline; // destructure let { s = 'triangle', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44e26d08..61c2f2e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,6 +137,16 @@ importers: specifier: ^5.0.10 version: 5.0.10 + examples/worker-repl: + dependencies: + superdough: + specifier: workspace:* + version: link:../../packages/superdough + devDependencies: + vite: + specifier: ^5.2.0 + version: 5.2.2(@types/node@20.10.6) + packages/codemirror: dependencies: '@codemirror/autocomplete': @@ -244,7 +254,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.11(@types/node@20.10.6) + version: 5.0.11 packages/embed: {} @@ -520,7 +530,7 @@ importers: version: 2.2.1(astro@4.0.8) '@astrojs/react': specifier: ^3.0.9 - version: 3.0.9(@types/react-dom@18.2.18)(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)(vite@5.0.11) + version: 3.0.9(@types/react-dom@18.2.18)(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)(vite@5.2.2) '@astrojs/rss': specifier: ^4.0.2 version: 4.0.2 @@ -668,7 +678,7 @@ importers: version: 0.33.1 vite-plugin-pwa: specifier: ^0.17.4 - version: 0.17.4(vite@5.0.11)(workbox-build@7.0.0)(workbox-window@7.0.0) + version: 0.17.4(vite@5.2.2)(workbox-build@7.0.0)(workbox-window@7.0.0) workbox-window: specifier: ^7.0.0 version: 7.0.0 @@ -929,7 +939,7 @@ packages: dependencies: prismjs: 1.29.0 - /@astrojs/react@3.0.9(@types/react-dom@18.2.18)(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)(vite@5.0.11): + /@astrojs/react@3.0.9(@types/react-dom@18.2.18)(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)(vite@5.2.2): resolution: {integrity: sha512-31J5yF5p9yBFV1axBooLA9PB4B2+MYm7swWhtlezSsJiUNXRFo5Is9qI3teJ7cKgmaCv+ZA593Smskko0NGaDQ==} engines: {node: '>=18.14.1'} peerDependencies: @@ -940,7 +950,7 @@ packages: dependencies: '@types/react': 18.2.46 '@types/react-dom': 18.2.18 - '@vitejs/plugin-react': 4.2.1(vite@5.0.11) + '@vitejs/plugin-react': 4.2.1(vite@5.2.2) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) ultrahtml: 1.5.2 @@ -2415,6 +2425,14 @@ packages: requiresBuild: true optional: true + /@esbuild/aix-ppc64@0.20.2: + resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + optional: true + /@esbuild/android-arm64@0.19.11: resolution: {integrity: sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==} engines: {node: '>=12'} @@ -2432,6 +2450,14 @@ packages: dev: true optional: true + /@esbuild/android-arm64@0.20.2: + resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + optional: true + /@esbuild/android-arm@0.19.11: resolution: {integrity: sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==} engines: {node: '>=12'} @@ -2449,6 +2475,14 @@ packages: dev: true optional: true + /@esbuild/android-arm@0.20.2: + resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + optional: true + /@esbuild/android-x64@0.19.11: resolution: {integrity: sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==} engines: {node: '>=12'} @@ -2466,6 +2500,14 @@ packages: dev: true optional: true + /@esbuild/android-x64@0.20.2: + resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + optional: true + /@esbuild/darwin-arm64@0.19.11: resolution: {integrity: sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==} engines: {node: '>=12'} @@ -2483,6 +2525,14 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64@0.20.2: + resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optional: true + /@esbuild/darwin-x64@0.19.11: resolution: {integrity: sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==} engines: {node: '>=12'} @@ -2500,6 +2550,14 @@ packages: dev: true optional: true + /@esbuild/darwin-x64@0.20.2: + resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + /@esbuild/freebsd-arm64@0.19.11: resolution: {integrity: sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==} engines: {node: '>=12'} @@ -2517,6 +2575,14 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64@0.20.2: + resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + optional: true + /@esbuild/freebsd-x64@0.19.11: resolution: {integrity: sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==} engines: {node: '>=12'} @@ -2534,6 +2600,14 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64@0.20.2: + resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + optional: true + /@esbuild/linux-arm64@0.19.11: resolution: {integrity: sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==} engines: {node: '>=12'} @@ -2551,6 +2625,14 @@ packages: dev: true optional: true + /@esbuild/linux-arm64@0.20.2: + resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-arm@0.19.11: resolution: {integrity: sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==} engines: {node: '>=12'} @@ -2568,6 +2650,14 @@ packages: dev: true optional: true + /@esbuild/linux-arm@0.20.2: + resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-ia32@0.19.11: resolution: {integrity: sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==} engines: {node: '>=12'} @@ -2585,6 +2675,14 @@ packages: dev: true optional: true + /@esbuild/linux-ia32@0.20.2: + resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-loong64@0.19.11: resolution: {integrity: sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==} engines: {node: '>=12'} @@ -2602,6 +2700,14 @@ packages: dev: true optional: true + /@esbuild/linux-loong64@0.20.2: + resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-mips64el@0.19.11: resolution: {integrity: sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==} engines: {node: '>=12'} @@ -2619,6 +2725,14 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el@0.20.2: + resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-ppc64@0.19.11: resolution: {integrity: sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==} engines: {node: '>=12'} @@ -2636,6 +2750,14 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64@0.20.2: + resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-riscv64@0.19.11: resolution: {integrity: sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==} engines: {node: '>=12'} @@ -2653,6 +2775,14 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64@0.20.2: + resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-s390x@0.19.11: resolution: {integrity: sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==} engines: {node: '>=12'} @@ -2670,6 +2800,14 @@ packages: dev: true optional: true + /@esbuild/linux-s390x@0.20.2: + resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-x64@0.19.11: resolution: {integrity: sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==} engines: {node: '>=12'} @@ -2687,6 +2825,14 @@ packages: dev: true optional: true + /@esbuild/linux-x64@0.20.2: + resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/netbsd-x64@0.19.11: resolution: {integrity: sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==} engines: {node: '>=12'} @@ -2704,6 +2850,14 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64@0.20.2: + resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + optional: true + /@esbuild/openbsd-x64@0.19.11: resolution: {integrity: sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==} engines: {node: '>=12'} @@ -2721,6 +2875,14 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64@0.20.2: + resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + optional: true + /@esbuild/sunos-x64@0.19.11: resolution: {integrity: sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==} engines: {node: '>=12'} @@ -2738,6 +2900,14 @@ packages: dev: true optional: true + /@esbuild/sunos-x64@0.20.2: + resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + optional: true + /@esbuild/win32-arm64@0.19.11: resolution: {integrity: sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==} engines: {node: '>=12'} @@ -2755,6 +2925,14 @@ packages: dev: true optional: true + /@esbuild/win32-arm64@0.20.2: + resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + optional: true + /@esbuild/win32-ia32@0.19.11: resolution: {integrity: sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==} engines: {node: '>=12'} @@ -2772,6 +2950,14 @@ packages: dev: true optional: true + /@esbuild/win32-ia32@0.20.2: + resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + optional: true + /@esbuild/win32-x64@0.19.11: resolution: {integrity: sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==} engines: {node: '>=12'} @@ -2789,6 +2975,14 @@ packages: dev: true optional: true + /@esbuild/win32-x64@0.20.2: + resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3770,11 +3964,26 @@ packages: estree-walker: 2.0.2 picomatch: 2.3.1 + /@rollup/rollup-android-arm-eabi@4.13.0: + resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==} + cpu: [arm] + os: [android] + requiresBuild: true + optional: true + /@rollup/rollup-android-arm-eabi@4.9.2: resolution: {integrity: sha512-RKzxFxBHq9ysZ83fn8Iduv3A283K7zPPYuhL/z9CQuyFrjwpErJx0h4aeb/bnJ+q29GRLgJpY66ceQ/Wcsn3wA==} cpu: [arm] os: [android] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.13.0: + resolution: {integrity: sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==} + cpu: [arm64] + os: [android] + requiresBuild: true optional: true /@rollup/rollup-android-arm64@4.9.2: @@ -3782,6 +3991,14 @@ packages: cpu: [arm64] os: [android] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.13.0: + resolution: {integrity: sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==} + cpu: [arm64] + os: [darwin] + requiresBuild: true optional: true /@rollup/rollup-darwin-arm64@4.9.2: @@ -3789,6 +4006,14 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.13.0: + resolution: {integrity: sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==} + cpu: [x64] + os: [darwin] + requiresBuild: true optional: true /@rollup/rollup-darwin-x64@4.9.2: @@ -3796,6 +4021,14 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.13.0: + resolution: {integrity: sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==} + cpu: [arm] + os: [linux] + requiresBuild: true optional: true /@rollup/rollup-linux-arm-gnueabihf@4.9.2: @@ -3803,6 +4036,14 @@ packages: cpu: [arm] os: [linux] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.13.0: + resolution: {integrity: sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==} + cpu: [arm64] + os: [linux] + requiresBuild: true optional: true /@rollup/rollup-linux-arm64-gnu@4.9.2: @@ -3810,6 +4051,14 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.13.0: + resolution: {integrity: sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==} + cpu: [arm64] + os: [linux] + requiresBuild: true optional: true /@rollup/rollup-linux-arm64-musl@4.9.2: @@ -3817,6 +4066,14 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.13.0: + resolution: {integrity: sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==} + cpu: [riscv64] + os: [linux] + requiresBuild: true optional: true /@rollup/rollup-linux-riscv64-gnu@4.9.2: @@ -3824,6 +4081,14 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.13.0: + resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==} + cpu: [x64] + os: [linux] + requiresBuild: true optional: true /@rollup/rollup-linux-x64-gnu@4.9.2: @@ -3831,6 +4096,14 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.13.0: + resolution: {integrity: sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==} + cpu: [x64] + os: [linux] + requiresBuild: true optional: true /@rollup/rollup-linux-x64-musl@4.9.2: @@ -3838,6 +4111,14 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.13.0: + resolution: {integrity: sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==} + cpu: [arm64] + os: [win32] + requiresBuild: true optional: true /@rollup/rollup-win32-arm64-msvc@4.9.2: @@ -3845,6 +4126,14 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.13.0: + resolution: {integrity: sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==} + cpu: [ia32] + os: [win32] + requiresBuild: true optional: true /@rollup/rollup-win32-ia32-msvc@4.9.2: @@ -3852,6 +4141,14 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.13.0: + resolution: {integrity: sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==} + cpu: [x64] + os: [win32] + requiresBuild: true optional: true /@rollup/rollup-win32-x64-msvc@4.9.2: @@ -3859,6 +4156,7 @@ packages: cpu: [x64] os: [win32] requiresBuild: true + dev: true optional: true /@shikijs/core@1.2.0: @@ -4346,6 +4644,9 @@ packages: /@types/estree@1.0.0: resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + /@types/hast@3.0.2: resolution: {integrity: sha512-B5hZHgHsXvfCoO3xgNJvBnX7N8p86TqQeGKXcokW4XXi+qY4vxxPSFYofytvVmpFxzPv7oxDQzjg5Un5m2/xiw==} dependencies: @@ -4900,10 +5201,10 @@ packages: vite-plugin-pwa: '>=0.17.3 <1' dependencies: astro: 4.0.8(@types/node@20.10.6)(typescript@5.3.3) - vite-plugin-pwa: 0.17.4(vite@5.0.11)(workbox-build@7.0.0)(workbox-window@7.0.0) + vite-plugin-pwa: 0.17.4(vite@5.2.2)(workbox-build@7.0.0)(workbox-window@7.0.0) dev: true - /@vitejs/plugin-react@4.2.1(vite@5.0.11): + /@vitejs/plugin-react@4.2.1(vite@5.2.2): resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -4914,7 +5215,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.7) '@types/babel__core': 7.20.5 react-refresh: 0.14.0 - vite: 5.0.11(@types/node@20.10.6) + vite: 5.2.2(@types/node@20.10.6) transitivePeerDependencies: - supports-color dev: false @@ -5393,8 +5694,8 @@ packages: tsconfck: 3.0.0(typescript@5.3.3) unist-util-visit: 5.0.0 vfile: 6.0.1 - vite: 5.0.11(@types/node@20.10.6) - vitefu: 0.2.5(vite@5.0.11) + vite: 5.2.2(@types/node@20.10.6) + vitefu: 0.2.5(vite@5.2.2) which-pm: 2.1.1 yargs-parser: 21.1.1 zod: 3.22.4 @@ -6865,6 +7166,36 @@ packages: '@esbuild/win32-x64': 0.19.5 dev: true + /esbuild@0.20.2: + resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.20.2 + '@esbuild/android-arm': 0.20.2 + '@esbuild/android-arm64': 0.20.2 + '@esbuild/android-x64': 0.20.2 + '@esbuild/darwin-arm64': 0.20.2 + '@esbuild/darwin-x64': 0.20.2 + '@esbuild/freebsd-arm64': 0.20.2 + '@esbuild/freebsd-x64': 0.20.2 + '@esbuild/linux-arm': 0.20.2 + '@esbuild/linux-arm64': 0.20.2 + '@esbuild/linux-ia32': 0.20.2 + '@esbuild/linux-loong64': 0.20.2 + '@esbuild/linux-mips64el': 0.20.2 + '@esbuild/linux-ppc64': 0.20.2 + '@esbuild/linux-riscv64': 0.20.2 + '@esbuild/linux-s390x': 0.20.2 + '@esbuild/linux-x64': 0.20.2 + '@esbuild/netbsd-x64': 0.20.2 + '@esbuild/openbsd-x64': 0.20.2 + '@esbuild/sunos-x64': 0.20.2 + '@esbuild/win32-arm64': 0.20.2 + '@esbuild/win32-ia32': 0.20.2 + '@esbuild/win32-x64': 0.20.2 + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -11244,6 +11575,14 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + /prebuild-install@7.1.1: resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} engines: {node: '>=10'} @@ -12020,6 +12359,28 @@ packages: fsevents: 2.3.3 dev: true + /rollup@4.13.0: + resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.13.0 + '@rollup/rollup-android-arm64': 4.13.0 + '@rollup/rollup-darwin-arm64': 4.13.0 + '@rollup/rollup-darwin-x64': 4.13.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.13.0 + '@rollup/rollup-linux-arm64-gnu': 4.13.0 + '@rollup/rollup-linux-arm64-musl': 4.13.0 + '@rollup/rollup-linux-riscv64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-musl': 4.13.0 + '@rollup/rollup-win32-arm64-msvc': 4.13.0 + '@rollup/rollup-win32-ia32-msvc': 4.13.0 + '@rollup/rollup-win32-x64-msvc': 4.13.0 + fsevents: 2.3.3 + /rollup@4.9.2: resolution: {integrity: sha512-66RB8OtFKUTozmVEh3qyNfH+b+z2RXBVloqO2KCC/pjFaGaHtxP9fVfOQKPSGXg2mElmjmxjW/fZ7iKrEpMH5Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -12039,6 +12400,7 @@ packages: '@rollup/rollup-win32-ia32-msvc': 4.9.2 '@rollup/rollup-win32-x64-msvc': 4.9.2 fsevents: 2.3.3 + dev: true /run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} @@ -12379,6 +12741,10 @@ packages: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: @@ -13556,7 +13922,7 @@ packages: debug: 4.3.4 pathe: 1.1.1 picocolors: 1.0.0 - vite: 5.0.11(@types/node@20.10.6) + vite: 5.2.2(@types/node@20.10.6) transitivePeerDependencies: - '@types/node' - less @@ -13568,7 +13934,7 @@ packages: - terser dev: true - /vite-plugin-pwa@0.17.4(vite@5.0.11)(workbox-build@7.0.0)(workbox-window@7.0.0): + /vite-plugin-pwa@0.17.4(vite@5.2.2)(workbox-build@7.0.0)(workbox-window@7.0.0): resolution: {integrity: sha512-j9iiyinFOYyof4Zk3Q+DtmYyDVBDAi6PuMGNGq6uGI0pw7E+LNm9e+nQ2ep9obMP/kjdWwzilqUrlfVRj9OobA==} engines: {node: '>=16.0.0'} peerDependencies: @@ -13579,7 +13945,7 @@ packages: debug: 4.3.4 fast-glob: 3.3.2 pretty-bytes: 6.1.1 - vite: 5.0.11(@types/node@20.10.6) + vite: 5.2.2(@types/node@20.10.6) workbox-build: 7.0.0 workbox-window: 7.0.0 transitivePeerDependencies: @@ -13621,7 +13987,7 @@ packages: fsevents: 2.3.3 dev: true - /vite@5.0.11(@types/node@20.10.6): + /vite@5.0.11: resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -13649,14 +14015,49 @@ packages: terser: optional: true dependencies: - '@types/node': 20.10.6 esbuild: 0.19.11 postcss: 8.4.32 rollup: 4.9.2 optionalDependencies: fsevents: 2.3.3 + dev: true - /vitefu@0.2.5(vite@5.0.11): + /vite@5.2.2(@types/node@20.10.6): + resolution: {integrity: sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.10.6 + esbuild: 0.20.2 + postcss: 8.4.38 + rollup: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + /vitefu@0.2.5(vite@5.2.2): resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} peerDependencies: vite: ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -13664,7 +14065,7 @@ packages: vite: optional: true dependencies: - vite: 5.0.11(@types/node@20.10.6) + vite: 5.2.2(@types/node@20.10.6) /vitest@1.1.0(@vitest/ui@1.1.0): resolution: {integrity: sha512-oDFiCrw7dd3Jf06HoMtSRARivvyjHJaTxikFxuqJjO76U436PqlVw1uLn7a8OSPrhSfMGVaRakKpA2lePdw79A==} @@ -13710,7 +14111,7 @@ packages: strip-literal: 1.3.0 tinybench: 2.5.1 tinypool: 0.8.1 - vite: 5.0.11(@types/node@20.10.6) + vite: 5.0.11 vite-node: 1.1.0 why-is-node-running: 2.2.2 transitivePeerDependencies: From 10bd6680338cd23b7bfb40df17392943c615be93 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 21 Mar 2024 22:37:15 +0100 Subject: [PATCH 02/13] allow absolute time for superdough with prefix = --- packages/superdough/superdough.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 026d8747..96e335eb 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -272,6 +272,7 @@ export const superdough = async (value, t, hapDuration) => { // duration is passed as value too.. value.duration = hapDuration; // calculate absolute time + t = typeof t === 'string' && t.startsWith('=') ? Number(t.slice(1)) : ac.currentTime + t; // destructure let { s = 'triangle', From dc977b292da3c2ecba2b2be064e0054a60676e62 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 21 Mar 2024 22:38:20 +0100 Subject: [PATCH 03/13] more options for zyklus api: + allow setting custom interval functions + allow disabling phase rounding --- packages/core/zyklus.mjs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/core/zyklus.mjs b/packages/core/zyklus.mjs index d3c48667..0fb4ccc4 100644 --- a/packages/core/zyklus.mjs +++ b/packages/core/zyklus.mjs @@ -7,6 +7,9 @@ function createClock( duration = 0.05, // duration of each cycle interval = 0.1, // interval between callbacks overlap = 0.1, // overlap between callbacks + setInterval = globalThis.setInterval, + clearInterval = globalThis.clearInterval, + round = true, ) { let tick = 0; // counts callbacks let phase = 0; // next callback time @@ -22,7 +25,7 @@ function createClock( } // callback as long as we're inside the lookahead while (phase < lookahead) { - phase = Math.round(phase * precision) / precision; + phase = round ? Math.round(phase * precision) / precision : phase; phase >= t && callback(phase, duration, tick, t); phase < t && console.log('TOO LATE', phase); // what if latency is added from outside? phase += duration; // increment phase by duration @@ -35,7 +38,10 @@ function createClock( onTick(); intervalID = setInterval(onTick, interval * 1000); }; - const clear = () => intervalID !== undefined && clearInterval(intervalID); + const clear = () => { + intervalID !== undefined && clearInterval(intervalID); + intervalID = undefined; + }; const pause = () => clear(); const stop = () => { tick = 0; From 4362cb93170db4b8e567608b00a7fbf6e2e08d4d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 21 Mar 2024 22:39:01 +0100 Subject: [PATCH 04/13] export createClock from core --- packages/core/index.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/index.mjs b/packages/core/index.mjs index a04c85bc..506fa77a 100644 --- a/packages/core/index.mjs +++ b/packages/core/index.mjs @@ -7,8 +7,9 @@ This program is free software: you can redistribute it and/or modify it under th import * as controls from './controls.mjs'; // legacy export * from './euclid.mjs'; import Fraction from './fraction.mjs'; +import createClock from './zyklus.mjs'; import { logger } from './logger.mjs'; -export { Fraction, controls }; +export { Fraction, controls, createClock }; export * from './controls.mjs'; export * from './hap.mjs'; export * from './pattern.mjs'; From ac441b4340d8ab2b34a1c7b74873093b98be2794 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 21 Mar 2024 22:41:45 +0100 Subject: [PATCH 05/13] add working poc for precise cyclist --- examples/worker-repl/main.js | 119 ++++++++---------------------- examples/worker-repl/package.json | 5 +- pnpm-lock.yaml | 45 ++++++++++- 3 files changed, 79 insertions(+), 90 deletions(-) diff --git a/examples/worker-repl/main.js b/examples/worker-repl/main.js index 35454787..059d2065 100644 --- a/examples/worker-repl/main.js +++ b/examples/worker-repl/main.js @@ -1,89 +1,34 @@ -import workerUrl from './worker.mjs?url'; -import { samples, superdough } from 'superdough'; +import { createClock, seq } from '@strudel/core'; +import { getAudioContext, initAudioOnFirstClick, samples, superdough } from 'superdough'; +import { setInterval, clearInterval } from 'worker-timers'; // comment out this line to test with window.setInterval -let ctx; -document.addEventListener('click', async () => { - ctx = new AudioContext(); - await samples('github:tidalcycles/dirt-samples'); - initWorker(); -}); - -function initWorker() { - if (!globalThis.Worker) { - throw new Error('initWorker: Worker not supported in this environment'); - } - const worker = new Worker(workerUrl); - worker.postMessage({ origin: performance.timeOrigin }); - // worker.postMessage(1); - let origin; - let minLatency = 0.05; - - let init = performance.now(); - - // performance.timeOrigin; // unix timestamp when the page loads - // Date.now() // unix timestamp (ms since January 1st 1970) - // performance.timeOrigin // unix timestamp when the page loaded - // performance.now() // precise ms since the page loaded - // performance.timeOrigin + performance.now() ~= Date.now() - - // worker: performance.now() // precise ms since the worker loaded - // worker: performance.timeOrigin // unix timestamp when the worker loaded - - worker.onmessage = (e) => { - const { contextTime, performanceTime } = ctx.getOutputTimestamp(); - console.log('e', e.timeStamp, performance.now()); - /* // clock offsets - const { contextTime: t } = outputTimestamp; // audio clock time in seconds - const { performanceTime: now } = outputTimestamp; // estimated system clock time for contextTime - const { origin: workerOrigin } = e.data; // seconds when the worker was loaded - const { phase } = e.data; // target time in seconds of current tick (relative to system clock) - - let workerOriginDiffMs = workerOrigin - performance.timeOrigin; - // console.log('worker created after seconds:', workerOriginDiffMs); - - let workerOriginDiffSeconds = workerOriginDiffMs / 1000; - - let workerPhaseSeconds = phase + workerOriginDiffSeconds; - let nowSeconds = now / 1000; - - let workerDeadline = workerPhaseSeconds - nowSeconds; */ - - // console.log('workerDeadline', workerDeadline); - - // superdough({ s: 'bd' }, workerDeadline); - - e.data.forEach((event, i) => { - let phase = event.phase * 1000; - //let phase = event.tick * 100 + origin; - let diff = (phase - performanceTime) / 1000; - superdough({ s: 'bd' }, contextTime + diff + 2); - }); - - /* const deadline = phase - now; - - const { tickOrigin } = e.data; // ms when the first tick happened - - const { tick, begin, end, haps, cps } = e.data; - - // console.log('phase', phase); - if (tick === 0) { - console.log('set origin', t); - origin = t; - } - // console.log('tick', tick, begin, end, phase, origin); - haps.forEach((hap) => { - let hapBegin = hap.whole.begin; - hapBegin = (hapBegin.s * hapBegin.n) / hapBegin.d; - const stamp = ctx.getOutputTimestamp(); - - let targetTime = origin + hapBegin * cps; - let deadline = t - targetTime + minLatency; - // console.log('--hap, begin:', begin, 'deadline', deadline); - // console.log('hap', hap.value, begin, deadline); - superdough(hap.value, deadline); - }); */ - - // const stamp = ctx?.getOutputTimestamp(); - // console.log('stamp', stamp); - }; +let loaded = samples('github:tidalcycles/dirt-samples'); +async function run() { + // let pat = seq('hh').s().fast(25.2); + let pat = seq('bd', ['hh', 'hh'], 'jvbass').s().fast(1.92); + await initAudioOnFirstClick(); + await loaded; + const ctx = getAudioContext(); + let last = 0; + const clock = createClock( + () => ctx.currentTime, + (phase, duration, tick, t) => { + const [begin, end] = [last, (last = tick * duration)]; + console.log('q', begin.toFixed(2), end.toFixed(2), phase.toFixed(2)); + let haps = pat.queryArc(begin, end).filter((h) => h.hasOnset()); + // console.log('phase', phase, haps.length); + haps.forEach((hap) => { + superdough(hap.value, '=' + String(hap.whole.begin + 0.1)); + }); + }, + 0.025, // duration of each cycle + 0.1, // interval between callbacks + 0.4, // overlap between callbacks + setInterval, + clearInterval, + false, + ); + clock.start(); } + +run(); diff --git a/examples/worker-repl/package.json b/examples/worker-repl/package.json index 742b0683..9f430b26 100644 --- a/examples/worker-repl/package.json +++ b/examples/worker-repl/package.json @@ -11,5 +11,8 @@ "devDependencies": { "vite": "^5.2.0" }, - "dependencies": {"superdough":"workspace:*"} + "dependencies": { + "superdough": "workspace:*", + "worker-timers": "^7.1.4" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61c2f2e7..5bcd702a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -142,6 +142,9 @@ importers: superdough: specifier: workspace:* version: link:../../packages/superdough + worker-timers: + specifier: ^7.1.4 + version: 7.1.4 devDependencies: vite: specifier: ^5.2.0 @@ -2194,6 +2197,13 @@ packages: regenerator-runtime: 0.14.1 dev: true + /@babel/runtime@7.24.1: + resolution: {integrity: sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + dev: false + /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} @@ -7587,6 +7597,14 @@ packages: /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + /fast-unique-numbers@8.0.13: + resolution: {integrity: sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==} + engines: {node: '>=16.1.0'} + dependencies: + '@babel/runtime': 7.24.1 + tslib: 2.6.2 + dev: false + /fast-xml-parser@4.3.3: resolution: {integrity: sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==} hasBin: true @@ -12008,7 +12026,6 @@ packages: /regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: true /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} @@ -13430,7 +13447,6 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} requiresBuild: true - optional: true /tsutils@3.21.0(typescript@5.3.3): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -14433,6 +14449,31 @@ packages: workbox-core: 7.0.0 dev: true + /worker-timers-broker@6.1.4: + resolution: {integrity: sha512-y3D+Yfj37lrItEMIlcfCm/IRueYtYKgpLlTG2wgTIZ9PSw0n/K4kweilgk3gTC4ahbQNVGT90lU+Rf7W4M5bsw==} + dependencies: + '@babel/runtime': 7.24.1 + fast-unique-numbers: 8.0.13 + tslib: 2.6.2 + worker-timers-worker: 7.0.67 + dev: false + + /worker-timers-worker@7.0.67: + resolution: {integrity: sha512-0ZP2+v2fyiiiGaCEdWxMRUk5YxGFwWdRGB12ZfQy13vw8/27Xd+MW3ua56qlcM30nzjpddXXzLuEpHhGW+Pz7w==} + dependencies: + '@babel/runtime': 7.24.1 + tslib: 2.6.2 + dev: false + + /worker-timers@7.1.4: + resolution: {integrity: sha512-8PRtiPAyeYukrY+iOUL+0tq4Zn5qyCHrTqFTtHxcESfIxGyulxNwyzQkybrYBKhnMWmx0bku3wxRfE1hts5R6Q==} + dependencies: + '@babel/runtime': 7.24.1 + tslib: 2.6.2 + worker-timers-broker: 6.1.4 + worker-timers-worker: 7.0.67 + dev: false + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} From 837dfcaba401236f6f18d4b8a466ea41d7c6d21b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 21 Mar 2024 22:44:50 +0100 Subject: [PATCH 06/13] add readme --- examples/worker-repl/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 examples/worker-repl/README.md diff --git a/examples/worker-repl/README.md b/examples/worker-repl/README.md new file mode 100644 index 00000000..5ff79050 --- /dev/null +++ b/examples/worker-repl/README.md @@ -0,0 +1,7 @@ +# worker-repl + +This is a poc for a more precise version of cyclist, fixing the clock jitter on chrome. +It is also an experiment to use the `worker-timers` lib to test scheduling in inactive windows, +but not the reason why it works. You can comment out the import from `worker-timers` and it still works precisely.. + +I am not 100% sure why it works + the poc also doesn't implement cps yet.. From 41a40c74efcc0a3fc32157a8ee87064337b3f945 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 21 Mar 2024 22:45:11 +0100 Subject: [PATCH 07/13] add run instruction --- examples/worker-repl/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/worker-repl/README.md b/examples/worker-repl/README.md index 5ff79050..41e5fba1 100644 --- a/examples/worker-repl/README.md +++ b/examples/worker-repl/README.md @@ -5,3 +5,10 @@ It is also an experiment to use the `worker-timers` lib to test scheduling in in but not the reason why it works. You can comment out the import from `worker-timers` and it still works precisely.. I am not 100% sure why it works + the poc also doesn't implement cps yet.. + +run it with: + +```sh +pnpm i +pnpm dev +``` From 96a977990a0329bd3b89627b21ef7c6c2227b639 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 21 Mar 2024 22:47:03 +0100 Subject: [PATCH 08/13] add missing dep --- examples/worker-repl/package.json | 1 + pnpm-lock.yaml | 15 +++++---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/examples/worker-repl/package.json b/examples/worker-repl/package.json index 9f430b26..579dcde7 100644 --- a/examples/worker-repl/package.json +++ b/examples/worker-repl/package.json @@ -12,6 +12,7 @@ "vite": "^5.2.0" }, "dependencies": { + "@strudel/core": "workspace:*", "superdough": "workspace:*", "worker-timers": "^7.1.4" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5bcd702a..8da30aad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -139,6 +139,9 @@ importers: examples/worker-repl: dependencies: + '@strudel/core': + specifier: workspace:* + version: link:../../packages/core superdough: specifier: workspace:* version: link:../../packages/superdough @@ -2190,19 +2193,11 @@ packages: regenerator-runtime: 0.13.11 dev: false - /@babel/runtime@7.23.7: - resolution: {integrity: sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 - dev: true - /@babel/runtime@7.24.1: resolution: {integrity: sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 - dev: false /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} @@ -12030,7 +12025,7 @@ packages: /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} dependencies: - '@babel/runtime': 7.23.7 + '@babel/runtime': 7.24.1 dev: true /regexp.prototype.flags@1.5.1: @@ -14323,7 +14318,7 @@ packages: '@apideck/better-ajv-errors': 0.3.6(ajv@8.12.0) '@babel/core': 7.23.7 '@babel/preset-env': 7.23.7(@babel/core@7.23.7) - '@babel/runtime': 7.23.7 + '@babel/runtime': 7.24.1 '@rollup/plugin-babel': 5.3.1(@babel/core@7.23.7)(rollup@2.79.1) '@rollup/plugin-node-resolve': 11.2.1(rollup@2.79.1) '@rollup/plugin-replace': 2.4.2(rollup@2.79.1) From 8d8c843ccf2bfaa3987ef9e64b3cbca79c01e5b8 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 22 Mar 2024 00:29:25 +0100 Subject: [PATCH 09/13] working poc cyclist with cps change --- examples/worker-repl/main.js | 58 +++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/examples/worker-repl/main.js b/examples/worker-repl/main.js index 059d2065..a4e5e1da 100644 --- a/examples/worker-repl/main.js +++ b/examples/worker-repl/main.js @@ -4,22 +4,57 @@ import { setInterval, clearInterval } from 'worker-timers'; // comment out this let loaded = samples('github:tidalcycles/dirt-samples'); async function run() { - // let pat = seq('hh').s().fast(25.2); + //let pat = seq('hh').s().fast(25.2); let pat = seq('bd', ['hh', 'hh'], 'jvbass').s().fast(1.92); await initAudioOnFirstClick(); await loaded; const ctx = getAudioContext(); let last = 0; + let cps = 0.6; + let num_ticks_since_cps_change = 0; + let num_cycles_at_cps_change = 0; + let seconds_at_cps_change; + let latency = 0.1; + + function setCps(_cps) { + if (_cps === cps) { + return; + } + cps = _cps; + num_ticks_since_cps_change = 0; + } + const clock = createClock( () => ctx.currentTime, (phase, duration, tick, t) => { - const [begin, end] = [last, (last = tick * duration)]; - console.log('q', begin.toFixed(2), end.toFixed(2), phase.toFixed(2)); - let haps = pat.queryArc(begin, end).filter((h) => h.hasOnset()); - // console.log('phase', phase, haps.length); - haps.forEach((hap) => { - superdough(hap.value, '=' + String(hap.whole.begin + 0.1)); - }); + if (num_ticks_since_cps_change === 0) { + console.log('changed cps..!!', cps); + num_cycles_at_cps_change = last; + seconds_at_cps_change = phase; + } + num_ticks_since_cps_change++; + const seconds_since_cps_change = num_ticks_since_cps_change * duration; + const num_cycles_since_cps_change = seconds_since_cps_change * cps; + + const [begin, end] = [last, (last = num_cycles_at_cps_change + num_cycles_since_cps_change)]; + + console.log( + 'q', + begin.toFixed(2), + end.toFixed(2), + phase.toFixed(2), + '#', + (end - begin).toFixed(2), + duration.toFixed(2), + ); + + pat + .queryArc(begin, end, { _cps: cps }) + .filter((h) => h.hasOnset()) + .forEach((hap) => { + const deadline = (hap.whole.begin - num_cycles_at_cps_change) / cps + seconds_at_cps_change + latency; + superdough(hap.value, '=' + deadline); + }); }, 0.025, // duration of each cycle 0.1, // interval between callbacks @@ -29,6 +64,13 @@ async function run() { false, ); clock.start(); + setTimeout(() => { + console.log('change cps!!!!!!!!!!!!!!!!!!!!'); + setCps(1.2); + }, 6000); + setTimeout(() => { + clock.stop(); + }, 12000); } run(); From ba8f996103ae3cc096e50f16c3ea1fc5bc0e3cf1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 22 Mar 2024 00:41:30 +0100 Subject: [PATCH 10/13] fix: clock jitter in cyclist --- packages/core/cyclist.mjs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 819c31bc..0280f47d 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -17,40 +17,38 @@ export class Cyclist { 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, tick) => { - if (tick === 0) { - this.origin = phase; - } + (phase, duration) => { 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 time = getTime(); const begin = this.lastEnd; this.lastBegin = begin; - - //convert ticks to cycles, so you can query the pattern for events - const eventLength = duration * this.cps; - const end = this.num_cycles_at_cps_change + this.num_ticks_since_cps_change * eventLength; + const end = this.num_cycles_at_cps_change + num_cycles_since_cps_change; this.lastEnd = end; // query the pattern for events const haps = this.pattern.queryArc(begin, end, { _cps: this.cps }); - const tickdeadline = phase - time; // time left until the phase is a whole number - this.lastTick = time + tickdeadline; + this.lastTick = phase; haps.forEach((hap) => { - if (hap.part.begin.equals(hap.whole.begin)) { - const deadline = (hap.whole.begin - begin) / this.cps + tickdeadline + latency; + if (hap.hasOnset()) { + const deadline = + (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps + this.seconds_at_cps_change + latency; const duration = hap.duration / this.cps; - onTrigger?.(hap, deadline, duration, this.cps); + onTrigger?.(hap, '=' + deadline, duration, this.cps); } }); } catch (e) { From 78890174342947352ad00c2c16acfebdaf9954d2 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 22 Mar 2024 00:46:08 +0100 Subject: [PATCH 11/13] delete now obsolete poc --- examples/worker-repl/.gitignore | 24 ------- examples/worker-repl/README.md | 14 ---- examples/worker-repl/index.html | 13 ---- examples/worker-repl/main.js | 76 -------------------- examples/worker-repl/package.json | 19 ----- examples/worker-repl/worker.mjs | 116 ------------------------------ pnpm-lock.yaml | 52 +------------- 7 files changed, 3 insertions(+), 311 deletions(-) delete mode 100644 examples/worker-repl/.gitignore delete mode 100644 examples/worker-repl/README.md delete mode 100644 examples/worker-repl/index.html delete mode 100644 examples/worker-repl/main.js delete mode 100644 examples/worker-repl/package.json delete mode 100644 examples/worker-repl/worker.mjs diff --git a/examples/worker-repl/.gitignore b/examples/worker-repl/.gitignore deleted file mode 100644 index a547bf36..00000000 --- a/examples/worker-repl/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/examples/worker-repl/README.md b/examples/worker-repl/README.md deleted file mode 100644 index 41e5fba1..00000000 --- a/examples/worker-repl/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# worker-repl - -This is a poc for a more precise version of cyclist, fixing the clock jitter on chrome. -It is also an experiment to use the `worker-timers` lib to test scheduling in inactive windows, -but not the reason why it works. You can comment out the import from `worker-timers` and it still works precisely.. - -I am not 100% sure why it works + the poc also doesn't implement cps yet.. - -run it with: - -```sh -pnpm i -pnpm dev -``` diff --git a/examples/worker-repl/index.html b/examples/worker-repl/index.html deleted file mode 100644 index 97aeb700..00000000 --- a/examples/worker-repl/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - Worker REPL - - -
- -

click somewhere...

- - diff --git a/examples/worker-repl/main.js b/examples/worker-repl/main.js deleted file mode 100644 index a4e5e1da..00000000 --- a/examples/worker-repl/main.js +++ /dev/null @@ -1,76 +0,0 @@ -import { createClock, seq } from '@strudel/core'; -import { getAudioContext, initAudioOnFirstClick, samples, superdough } from 'superdough'; -import { setInterval, clearInterval } from 'worker-timers'; // comment out this line to test with window.setInterval - -let loaded = samples('github:tidalcycles/dirt-samples'); -async function run() { - //let pat = seq('hh').s().fast(25.2); - let pat = seq('bd', ['hh', 'hh'], 'jvbass').s().fast(1.92); - await initAudioOnFirstClick(); - await loaded; - const ctx = getAudioContext(); - let last = 0; - let cps = 0.6; - let num_ticks_since_cps_change = 0; - let num_cycles_at_cps_change = 0; - let seconds_at_cps_change; - let latency = 0.1; - - function setCps(_cps) { - if (_cps === cps) { - return; - } - cps = _cps; - num_ticks_since_cps_change = 0; - } - - const clock = createClock( - () => ctx.currentTime, - (phase, duration, tick, t) => { - if (num_ticks_since_cps_change === 0) { - console.log('changed cps..!!', cps); - num_cycles_at_cps_change = last; - seconds_at_cps_change = phase; - } - num_ticks_since_cps_change++; - const seconds_since_cps_change = num_ticks_since_cps_change * duration; - const num_cycles_since_cps_change = seconds_since_cps_change * cps; - - const [begin, end] = [last, (last = num_cycles_at_cps_change + num_cycles_since_cps_change)]; - - console.log( - 'q', - begin.toFixed(2), - end.toFixed(2), - phase.toFixed(2), - '#', - (end - begin).toFixed(2), - duration.toFixed(2), - ); - - pat - .queryArc(begin, end, { _cps: cps }) - .filter((h) => h.hasOnset()) - .forEach((hap) => { - const deadline = (hap.whole.begin - num_cycles_at_cps_change) / cps + seconds_at_cps_change + latency; - superdough(hap.value, '=' + deadline); - }); - }, - 0.025, // duration of each cycle - 0.1, // interval between callbacks - 0.4, // overlap between callbacks - setInterval, - clearInterval, - false, - ); - clock.start(); - setTimeout(() => { - console.log('change cps!!!!!!!!!!!!!!!!!!!!'); - setCps(1.2); - }, 6000); - setTimeout(() => { - clock.stop(); - }, 12000); -} - -run(); diff --git a/examples/worker-repl/package.json b/examples/worker-repl/package.json deleted file mode 100644 index 579dcde7..00000000 --- a/examples/worker-repl/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "worker-repl", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "devDependencies": { - "vite": "^5.2.0" - }, - "dependencies": { - "@strudel/core": "workspace:*", - "superdough": "workspace:*", - "worker-timers": "^7.1.4" - } -} diff --git a/examples/worker-repl/worker.mjs b/examples/worker-repl/worker.mjs deleted file mode 100644 index f62e8f97..00000000 --- a/examples/worker-repl/worker.mjs +++ /dev/null @@ -1,116 +0,0 @@ -// used to consistently schedule events -const createClock = ( - getTime, - callback, // called slightly before each cycle - duration = 0.05, // duration of each cycle - interval = 0.1, // interval between callbacks - overlap = 0.1, // overlap between callbacks -) => { - let tick = 0; // counts callbacks - let phase = 0; // next callback time - let precision = 10 ** 4; // used to round phase - let minLatency = 0; - const setDuration = (setter) => (duration = setter(duration)); - overlap = overlap || interval / 2; - const onTick = () => { - const t = getTime(); - const lookahead = t + interval + overlap; // the time window for this tick - if (phase === 0) { - phase = t + minLatency; - } - // callback as long as we're inside the lookahead - while (phase < lookahead) { - // phase = Math.round(phase * precision) / precision; - phase >= t && callback(phase, duration, tick, t); - phase < t && console.log('TOO LATE', phase); // what if latency is added from outside? - phase += duration; // increment phase by duration - tick++; - } - }; - let intervalID; - const start = () => { - clear(); // just in case start was called more than once - onTick(); - intervalID = setInterval(onTick, interval * 1000); - }; - const clear = () => intervalID !== undefined && clearInterval(intervalID); - const pause = () => clear(); - const stop = () => { - tick = 0; - phase = 0; - clear(); - }; - const getPhase = () => phase; - // setCallback - return { setDuration, start, stop, pause, duration, interval, getPhase, minLatency }; -}; - -onmessage = async function (e) { - console.log('Worker: Message received from main script', e); - - /* const { seq } = await import('@strudel/core'); - let pat = seq('bd').fast(4).s(); */ - if (!e.data.origin) { - console.log('no origin'); - return; - } - console.log('start!', e.data.origin); - // e.data.origin = unix ms when main document was created - // performance.timeOrigin = unix ms when worker was created - let originDiff = (performance.timeOrigin - e.data.origin) / 1000; - let firstTick; - console.log('originDiff', originDiff); - - let precision = 10 ** 4; - let cps = 0.5; - let interval = 0.05; - let tickOrigin; - let lastEnd = 0; - let bag = []; - let clock = createClock( - () => performance.now() / 1000, - (phase, duration, tick, t) => { - if (tick === 0) { - firstTick = t; - console.log('firstTick', t); - } - // let durationInCycles = duration * cps; - /* phase = lastEnd; */ - // phase = Math.round(phase * precision) / precision; - - bag.push({ tick, duration, phase, cps, origin: performance.timeOrigin }); - //postMessage({ tick, phase, cps, origin: performance.timeOrigin }); - let pack = 4; - if (bag.length === pack) { - console.log('send', performance.now()); - postMessage(bag); - bag = []; - } - - /* phase += duration; - lastEnd = phase; */ - /*if (tick === 0) { - tickOrigin = performance.now(); - } - let begin = lastEnd || 0; - let end = begin + duration * cps; - lastEnd = end; - //console.log('query', begin, phase); - const haps = pat.queryArc(begin, end).filter((hap) => hap.hasOnset()); - if (haps.length) { - postMessage({ tick, phase, begin, end, haps, cps, tickOrigin, origin: performance.timeOrigin }); - } - */ - /* console.log( - 'tick', - phase, - `${begin.toFixed(2)}-${end.toFixed(2)}: ${haps - .map((h) => `${h.value.s}:${(h.whole.begin + 0).toFixed(2)}`) - .join(' ')}`, - ); */ - }, - interval, - interval, - ); - clock.start(); -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8da30aad..c76263d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,22 +137,6 @@ importers: specifier: ^5.0.10 version: 5.0.10 - examples/worker-repl: - dependencies: - '@strudel/core': - specifier: workspace:* - version: link:../../packages/core - superdough: - specifier: workspace:* - version: link:../../packages/superdough - worker-timers: - specifier: ^7.1.4 - version: 7.1.4 - devDependencies: - vite: - specifier: ^5.2.0 - version: 5.2.2(@types/node@20.10.6) - packages/codemirror: dependencies: '@codemirror/autocomplete': @@ -2198,6 +2182,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 + dev: true /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} @@ -7592,14 +7577,6 @@ packages: /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - /fast-unique-numbers@8.0.13: - resolution: {integrity: sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==} - engines: {node: '>=16.1.0'} - dependencies: - '@babel/runtime': 7.24.1 - tslib: 2.6.2 - dev: false - /fast-xml-parser@4.3.3: resolution: {integrity: sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==} hasBin: true @@ -12021,6 +11998,7 @@ packages: /regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + dev: true /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} @@ -13442,6 +13420,7 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} requiresBuild: true + optional: true /tsutils@3.21.0(typescript@5.3.3): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -14444,31 +14423,6 @@ packages: workbox-core: 7.0.0 dev: true - /worker-timers-broker@6.1.4: - resolution: {integrity: sha512-y3D+Yfj37lrItEMIlcfCm/IRueYtYKgpLlTG2wgTIZ9PSw0n/K4kweilgk3gTC4ahbQNVGT90lU+Rf7W4M5bsw==} - dependencies: - '@babel/runtime': 7.24.1 - fast-unique-numbers: 8.0.13 - tslib: 2.6.2 - worker-timers-worker: 7.0.67 - dev: false - - /worker-timers-worker@7.0.67: - resolution: {integrity: sha512-0ZP2+v2fyiiiGaCEdWxMRUk5YxGFwWdRGB12ZfQy13vw8/27Xd+MW3ua56qlcM30nzjpddXXzLuEpHhGW+Pz7w==} - dependencies: - '@babel/runtime': 7.24.1 - tslib: 2.6.2 - dev: false - - /worker-timers@7.1.4: - resolution: {integrity: sha512-8PRtiPAyeYukrY+iOUL+0tq4Zn5qyCHrTqFTtHxcESfIxGyulxNwyzQkybrYBKhnMWmx0bku3wxRfE1hts5R6Q==} - dependencies: - '@babel/runtime': 7.24.1 - tslib: 2.6.2 - worker-timers-broker: 6.1.4 - worker-timers-worker: 7.0.67 - dev: false - /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} From 14a5e7dcb612cbf41b01934cb1547f74e60b1f8e Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 22 Mar 2024 01:01:17 +0100 Subject: [PATCH 12/13] make regular cyclist work in the background + use worker-timers in Repl + repl / cyclist now accept custom interval functions --- packages/core/cyclist.mjs | 6 +++++- packages/core/repl.mjs | 4 ++++ pnpm-lock.yaml | 39 ++++++++++++++++++++++++++++++++++++--- website/package.json | 5 +++-- website/src/repl/Repl.jsx | 3 +++ 5 files changed, 51 insertions(+), 6 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 0280f47d..6a0b27ac 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 }) { + constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1, setInterval, clearInterval }) { this.started = false; this.cps = 0.5; this.num_ticks_since_cps_change = 0; @@ -57,6 +57,10 @@ export class Cyclist { } }, interval, // duration of each cycle + 0.1, + 0.1, + setInterval, + clearInterval, ); } now() { diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index aa8762d8..626a5ae1 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -17,6 +17,8 @@ export function repl({ editPattern, onUpdateState, sync = false, + setInterval, + clearInterval, }) { const state = { schedulerError: undefined, @@ -44,6 +46,8 @@ export function repl({ updateState({ started }); onToggle?.(started); }, + setInterval, + clearInterval, }; // NeoCyclist uses a shared worker to communicate between instances, which is not supported on mobile chrome diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c76263d0..a20e6be4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -656,6 +656,9 @@ importers: tailwindcss: specifier: ^3.4.0 version: 3.4.0 + worker-timers: + specifier: ^7.1.4 + version: 7.1.4 devDependencies: '@vite-pwa/astro': specifier: ^0.2.0 @@ -2182,7 +2185,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 - dev: true /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} @@ -7577,6 +7579,14 @@ packages: /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + /fast-unique-numbers@8.0.13: + resolution: {integrity: sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==} + engines: {node: '>=16.1.0'} + dependencies: + '@babel/runtime': 7.24.1 + tslib: 2.6.2 + dev: false + /fast-xml-parser@4.3.3: resolution: {integrity: sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==} hasBin: true @@ -11998,7 +12008,6 @@ packages: /regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: true /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} @@ -13420,7 +13429,6 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} requiresBuild: true - optional: true /tsutils@3.21.0(typescript@5.3.3): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -14423,6 +14431,31 @@ packages: workbox-core: 7.0.0 dev: true + /worker-timers-broker@6.1.4: + resolution: {integrity: sha512-y3D+Yfj37lrItEMIlcfCm/IRueYtYKgpLlTG2wgTIZ9PSw0n/K4kweilgk3gTC4ahbQNVGT90lU+Rf7W4M5bsw==} + dependencies: + '@babel/runtime': 7.24.1 + fast-unique-numbers: 8.0.13 + tslib: 2.6.2 + worker-timers-worker: 7.0.67 + dev: false + + /worker-timers-worker@7.0.67: + resolution: {integrity: sha512-0ZP2+v2fyiiiGaCEdWxMRUk5YxGFwWdRGB12ZfQy13vw8/27Xd+MW3ua56qlcM30nzjpddXXzLuEpHhGW+Pz7w==} + dependencies: + '@babel/runtime': 7.24.1 + tslib: 2.6.2 + dev: false + + /worker-timers@7.1.4: + resolution: {integrity: sha512-8PRtiPAyeYukrY+iOUL+0tq4Zn5qyCHrTqFTtHxcESfIxGyulxNwyzQkybrYBKhnMWmx0bku3wxRfE1hts5R6Q==} + dependencies: + '@babel/runtime': 7.24.1 + tslib: 2.6.2 + worker-timers-broker: 6.1.4 + worker-timers-worker: 7.0.67 + dev: false + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} diff --git a/website/package.json b/website/package.json index ad0cfbcd..e48c2dda 100644 --- a/website/package.json +++ b/website/package.json @@ -26,9 +26,9 @@ "@nanostores/react": "^0.7.1", "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", - "@strudel/draw": "workspace:*", "@strudel/csound": "workspace:*", "@strudel/desktopbridge": "workspace:*", + "@strudel/draw": "workspace:*", "@strudel/hydra": "workspace:*", "@strudel/midi": "workspace:*", "@strudel/mini": "workspace:*", @@ -60,7 +60,8 @@ "rehype-slug": "^6.0.0", "rehype-urls": "^1.2.0", "remark-toc": "^9.0.0", - "tailwindcss": "^3.4.0" + "tailwindcss": "^3.4.0", + "worker-timers": "^7.1.4" }, "devDependencies": { "@vite-pwa/astro": "^0.2.0", diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 675a1066..f30fc418 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -37,6 +37,7 @@ import { prebake } from './prebake.mjs'; import { getRandomTune, initCode, loadModules, shareCode, ReplContext } from './util.mjs'; import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon'; import './Repl.css'; +import { setInterval, clearInterval } from 'worker-timers'; const { latestCode } = settingsMap.get(); @@ -75,6 +76,8 @@ export function Repl({ embedded = false }) { sync: false, defaultOutput: webaudioOutput, getTime: () => getAudioContext().currentTime, + setInterval, + clearInterval, transpiler, autodraw: false, root: containerRef.current, From e8f6124a07f8d61145e57629cc704ae4c9f5b270 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 22 Mar 2024 01:22:35 +0100 Subject: [PATCH 13/13] + make cyclist trigger backwards compatible for now + add absolute time as new 5th onTrigger param --- packages/core/cyclist.mjs | 9 ++++++--- packages/core/repl.mjs | 7 ++++--- packages/webaudio/webaudio.mjs | 4 +++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 6a0b27ac..b0727ee5 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -23,7 +23,7 @@ export class Cyclist { this.clock = createClock( getTime, // called slightly before each cycle - (phase, duration) => { + (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; @@ -45,10 +45,13 @@ export class Cyclist { haps.forEach((hap) => { if (hap.hasOnset()) { - const deadline = + 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; - onTrigger?.(hap, '=' + deadline, 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); } }); } catch (e) { diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 626a5ae1..d2a61d96 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -166,14 +166,15 @@ export function repl({ export const getTrigger = ({ getTime, defaultOutput }) => - async (hap, deadline, duration, cps) => { + async (hap, deadline, duration, cps, t) => { + // 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); + 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); + await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t); } } catch (err) { logger(`[cyclist] error: ${err.message}`, 'error'); diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 7f28b8b7..f82a436a 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -16,7 +16,9 @@ const hap2value = (hap) => { }; export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps); -export const webaudioOutput = (hap, deadline, hapDuration) => superdough(hap2value(hap), deadline, hapDuration); +// 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); Pattern.prototype.webaudio = function () { return this.onTrigger(webaudioOutputTrigger);