mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-14 15:18:30 +00:00
Merge pull request #248 from tidalcycles/general-purpose-scheduler
General purpose scheduler
This commit is contained in:
commit
ef5cd36a37
66
package-lock.json
generated
66
package-lock.json
generated
@ -12463,7 +12463,40 @@
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"bjork": "^0.0.1",
|
||||
"fraction.js": "^4.2.0"
|
||||
"fraction.js": "^4.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"packages/core/node_modules/react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"packages/core/node_modules/react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"packages/core/node_modules/scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"packages/embed": {
|
||||
@ -14584,7 +14617,36 @@
|
||||
"version": "file:packages/core",
|
||||
"requires": {
|
||||
"bjork": "^0.0.1",
|
||||
"fraction.js": "^4.2.0"
|
||||
"fraction.js": "^4.2.0",
|
||||
"react-dom": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
}
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@strudel.cycles/embed": {
|
||||
|
||||
81
packages/core/cyclist.mjs
Normal file
81
packages/core/cyclist.mjs
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
cyclist.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/cyclist.mjs>
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import createClock from './zyklus.mjs';
|
||||
|
||||
export class Cyclist {
|
||||
worker;
|
||||
pattern;
|
||||
started = false;
|
||||
cps = 1; // TODO
|
||||
getTime;
|
||||
phase = 0;
|
||||
constructor({ interval, onTrigger, onError, getTime, latency = 0.1 }) {
|
||||
this.getTime = getTime;
|
||||
const round = (x) => Math.round(x * 1000) / 1000;
|
||||
this.clock = createClock(
|
||||
getTime,
|
||||
(phase, duration, tick) => {
|
||||
if (tick === 0) {
|
||||
this.origin = phase;
|
||||
}
|
||||
const begin = round(phase - this.origin);
|
||||
this.phase = begin - latency;
|
||||
const end = round(begin + duration);
|
||||
const time = getTime();
|
||||
try {
|
||||
const haps = this.pattern.queryArc(begin, end); // get Haps
|
||||
// console.log('haps', haps.map((hap) => hap.value.n).join(' '));
|
||||
haps.forEach((hap) => {
|
||||
// console.log('hap', hap.value.n, hap.part.begin);
|
||||
if (hap.part.begin.equals(hap.whole.begin)) {
|
||||
const deadline = hap.whole.begin + this.origin - time + latency;
|
||||
const duration = hap.duration * 1;
|
||||
onTrigger?.(hap, deadline, duration);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('scheduler error', e);
|
||||
onError?.(e);
|
||||
}
|
||||
}, // called slightly before each cycle
|
||||
interval, // duration of each cycle
|
||||
);
|
||||
}
|
||||
getPhase() {
|
||||
return this.phase;
|
||||
}
|
||||
start() {
|
||||
if (!this.pattern) {
|
||||
throw new Error('Scheduler: no pattern set! call .setPattern first.');
|
||||
}
|
||||
this.clock.start();
|
||||
this.started = true;
|
||||
}
|
||||
pause() {
|
||||
this.clock.stop();
|
||||
delete this.origin;
|
||||
this.started = false;
|
||||
}
|
||||
stop() {
|
||||
delete this.origin;
|
||||
this.clock.stop();
|
||||
this.started = false;
|
||||
}
|
||||
setPattern(pat, autostart = false) {
|
||||
this.pattern = pat;
|
||||
if (autostart && !this.started) {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
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('')}`);
|
||||
}
|
||||
}
|
||||
45
packages/core/evaluate.mjs
Normal file
45
packages/core/evaluate.mjs
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
evaluate.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/eval/evaluate.mjs>
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
|
||||
const { isPattern, Pattern } = strudel;
|
||||
|
||||
let scoped = false;
|
||||
export const evalScope = async (...args) => {
|
||||
if (scoped) {
|
||||
console.warn('evalScope was called more than once.');
|
||||
}
|
||||
scoped = true;
|
||||
const results = await Promise.allSettled(args);
|
||||
const modules = results.filter((result) => result.status === 'fulfilled').map((r) => r.value);
|
||||
results.forEach((result, i) => {
|
||||
if (result.status === 'rejected') {
|
||||
console.warn(`evalScope: module with index ${i} could not be loaded:`, result.reason);
|
||||
}
|
||||
});
|
||||
Object.assign(globalThis, ...modules, Pattern.prototype.bootstrap());
|
||||
};
|
||||
|
||||
function safeEval(str) {
|
||||
return Function('"use strict";return (' + str + ')')();
|
||||
}
|
||||
|
||||
export const evaluate = async (code, transpiler) => {
|
||||
if (!scoped) {
|
||||
await evalScope(); // at least scope Pattern.prototype.boostrap
|
||||
}
|
||||
if (transpiler) {
|
||||
code = transpiler(code); // transform syntactically correct js code to semantically usable code
|
||||
}
|
||||
let evaluated = await safeEval(code);
|
||||
if (!isPattern(evaluated)) {
|
||||
console.log('evaluated', evaluated);
|
||||
const message = `got "${typeof evaluated}" instead of pattern`;
|
||||
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
|
||||
}
|
||||
return { mode: 'javascript', pattern: evaluated };
|
||||
};
|
||||
90
packages/core/examples/repl.html
Normal file
90
packages/core/examples/repl.html
Normal file
@ -0,0 +1,90 @@
|
||||
<div style="position: absolute; bottom: 0; right: 0; padding: 4px; width: 100vw; display: flex">
|
||||
<button id="start" style="font-size: 2em">start</button>
|
||||
<button id="stop" style="font-size: 2em">stop</button>
|
||||
<button id="slower" style="font-size: 2em">slower</button>
|
||||
<button id="faster" style="font-size: 2em">faster</button>
|
||||
</div>
|
||||
<textarea
|
||||
style="font-size: 2em; background: #052b49; color: #fff; height: 100%; width: 100%; outline: none; border: 0"
|
||||
id="text"
|
||||
spellcheck="false"
|
||||
>
|
||||
Loading...</textarea
|
||||
>
|
||||
<script type="module">
|
||||
document.body.style = 'margin: 0';
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
|
||||
const { cat, State, TimeSpan, Scheduler, getPlayableNoteValue, getFreq } = strudel;
|
||||
Object.assign(window, strudel); // add strudel to eval scope
|
||||
|
||||
const ctx = new AudioContext();
|
||||
const scheduler = new Scheduler({
|
||||
// audioContext: getAudioContext(),
|
||||
interval: 0.1,
|
||||
onTrigger: (hap, time, duration) => {
|
||||
const freq = getFrequency(hap);
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = 0.2;
|
||||
osc.frequency.value = freq;
|
||||
osc.type = 'triangle';
|
||||
const onset = ctx.currentTime + time;
|
||||
const offset = onset + duration;
|
||||
const attack = 0.05;
|
||||
const release = 0.05;
|
||||
osc.start(onset);
|
||||
osc.stop(offset + release);
|
||||
|
||||
const g = ctx.createGain();
|
||||
g.gain.setValueAtTime(gain, onset);
|
||||
g.gain.linearRampToValueAtTime(gain, onset + attack);
|
||||
g.gain.setValueAtTime(gain, offset - release);
|
||||
g.gain.linearRampToValueAtTime(0, offset);
|
||||
osc.connect(g);
|
||||
g.connect(ctx.destination);
|
||||
},
|
||||
});
|
||||
|
||||
let initialCode = `stack('c4','e4',cat('g4','a4','b4','a4'))
|
||||
.add(cat(0,1,2,3,4,3,2,1).slow(8))
|
||||
.fast(2)
|
||||
.cps(tri.range(1,8).slow(32))`;
|
||||
|
||||
try {
|
||||
const base64 = decodeURIComponent(window.location.href.split('#')[1]);
|
||||
initialCode = atob(base64);
|
||||
} catch (err) {
|
||||
console.warn('failed to decode', err);
|
||||
}
|
||||
const input = document.getElementById('text');
|
||||
input.value = initialCode;
|
||||
const evaluate = () => {
|
||||
try {
|
||||
const pattern = eval(input.value);
|
||||
scheduler.setPattern(pattern);
|
||||
window.location.hash = '#' + encodeURIComponent(btoa(input.value)); // update url hash
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
}
|
||||
};
|
||||
evaluate();
|
||||
input.addEventListener('input', () => evaluate());
|
||||
document.getElementById('start').addEventListener('click', async () => {
|
||||
await ctx.resume();
|
||||
scheduler.start();
|
||||
});
|
||||
document.getElementById('stop').addEventListener('click', () => scheduler.stop());
|
||||
document.getElementById('slower').addEventListener('click', () => scheduler.setCps(scheduler.cps - 0.1));
|
||||
document.getElementById('faster').addEventListener('click', () => scheduler.setCps(scheduler.cps + 0.1));
|
||||
</script>
|
||||
<!--
|
||||
sequence(1,2).mul(55/2) // frequencies
|
||||
.mul(slowcat(1,2))
|
||||
.mul(slowcat(1,3/2,4/3,5/3).slow(8))
|
||||
.fast(3)
|
||||
.freq()
|
||||
.velocity(.5)
|
||||
.s('sawtooth')
|
||||
.cutoff(800)
|
||||
.out()
|
||||
-->
|
||||
1
packages/core/examples/vite-vanilla-repl/.gitignore
vendored
Normal file
1
packages/core/examples/vite-vanilla-repl/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
!dist
|
||||
10
packages/core/examples/vite-vanilla-repl/README.md
Normal file
10
packages/core/examples/vite-vanilla-repl/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# vite-vanilla-repl
|
||||
|
||||
This folder demonstrates how to set up a strudel repl using vite and vanilla JS. Run it using:
|
||||
|
||||
```sh
|
||||
npm i
|
||||
npm run dev
|
||||
```
|
||||
|
||||
or view it [live on githack](https://rawcdn.githack.com/tidalcycles/strudel/5fb36acb046ead7cd6ad3cd10f532e7f585f536a/packages/core/examples/vite-vanilla-repl/dist/index.html)
|
||||
1
packages/core/examples/vite-vanilla-repl/dist/assets/index.66252a18.js
vendored
Normal file
1
packages/core/examples/vite-vanilla-repl/dist/assets/index.66252a18.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/core/examples/vite-vanilla-repl/dist/assets/index.b66fb637.js
vendored
Normal file
1
packages/core/examples/vite-vanilla-repl/dist/assets/index.b66fb637.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{g as s,b as d,e as t,j as u,l as o,f as r,p as f,i as p,d as g,h as i,w as l,a as m}from"./index.f88145a1.js";export{s as getAudioContext,d as getCachedBuffer,t as getLoadedBuffer,u as getLoadedSamples,o as loadBuffer,r as loadGithubSamples,f as panic,p as resetLoadedSamples,g as reverseBuffer,i as samples,l as webaudioOutput,m as webaudioOutputTrigger};
|
||||
8
packages/core/examples/vite-vanilla-repl/dist/assets/index.d9f8bbbd.js
vendored
Normal file
8
packages/core/examples/vite-vanilla-repl/dist/assets/index.d9f8bbbd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
56
packages/core/examples/vite-vanilla-repl/dist/assets/index.f88145a1.js
vendored
Normal file
56
packages/core/examples/vite-vanilla-repl/dist/assets/index.f88145a1.js
vendored
Normal file
File diff suppressed because one or more lines are too long
36
packages/core/examples/vite-vanilla-repl/dist/index.html
vendored
Normal file
36
packages/core/examples/vite-vanilla-repl/dist/index.html
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite Vanilla Strudel REPL</title>
|
||||
<script type="module" crossorigin src="/tidalcycles/strudel/general-purpose-scheduler/packages/core/examples/vite-vanilla-repl/dist/assets/index.f88145a1.js"></script>
|
||||
</head>
|
||||
<body style="margin: 0; background: #222">
|
||||
<div style="display: grid; height: 100vh">
|
||||
<textarea
|
||||
id="text"
|
||||
style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
</div>
|
||||
<button
|
||||
id="start"
|
||||
style="
|
||||
position: absolute;
|
||||
border-radius: 10px;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 20px;
|
||||
border: 2px solid white;
|
||||
background: transparent;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
"
|
||||
>
|
||||
evaluate
|
||||
</button>
|
||||
<div id="output"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
35
packages/core/examples/vite-vanilla-repl/index.html
Normal file
35
packages/core/examples/vite-vanilla-repl/index.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite Vanilla Strudel REPL</title>
|
||||
</head>
|
||||
<body style="margin: 0; background: #222">
|
||||
<div style="display: grid; height: 100vh">
|
||||
<textarea
|
||||
id="text"
|
||||
style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
</div>
|
||||
<button
|
||||
id="start"
|
||||
style="
|
||||
position: absolute;
|
||||
border-radius: 10px;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 20px;
|
||||
border: 2px solid white;
|
||||
background: transparent;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
"
|
||||
>
|
||||
evaluate
|
||||
</button>
|
||||
<div id="output"></div>
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
26
packages/core/examples/vite-vanilla-repl/main.js
Normal file
26
packages/core/examples/vite-vanilla-repl/main.js
Normal file
@ -0,0 +1,26 @@
|
||||
import { controls, repl, evalScope } from '@strudel.cycles/core';
|
||||
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
|
||||
import shapeshifter from '@strudel.cycles/eval/shapeshifter.mjs';
|
||||
import tune from './tune.mjs';
|
||||
|
||||
const ctx = getAudioContext();
|
||||
const input = document.getElementById('text');
|
||||
input.innerHTML = tune;
|
||||
|
||||
evalScope(
|
||||
controls,
|
||||
import('@strudel.cycles/core'),
|
||||
import('@strudel.cycles/mini'),
|
||||
import('@strudel.cycles/webaudio'),
|
||||
import('@strudel.cycles/tonal'),
|
||||
);
|
||||
|
||||
const { evaluate } = repl({
|
||||
defaultOutput: webaudioOutput,
|
||||
getTime: () => ctx.currentTime,
|
||||
transpiler: shapeshifter,
|
||||
});
|
||||
document.getElementById('start').addEventListener('click', () => {
|
||||
ctx.resume();
|
||||
evaluate(input.value);
|
||||
});
|
||||
885
packages/core/examples/vite-vanilla-repl/package-lock.json
generated
Normal file
885
packages/core/examples/vite-vanilla-repl/package-lock.json
generated
Normal file
@ -0,0 +1,885 @@
|
||||
{
|
||||
"name": "vite-vanilla-repl",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "vite-vanilla-repl",
|
||||
"version": "0.0.0",
|
||||
"devDependencies": {
|
||||
"vite": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
|
||||
"integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz",
|
||||
"integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz",
|
||||
"integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/android-arm": "0.15.13",
|
||||
"@esbuild/linux-loong64": "0.15.13",
|
||||
"esbuild-android-64": "0.15.13",
|
||||
"esbuild-android-arm64": "0.15.13",
|
||||
"esbuild-darwin-64": "0.15.13",
|
||||
"esbuild-darwin-arm64": "0.15.13",
|
||||
"esbuild-freebsd-64": "0.15.13",
|
||||
"esbuild-freebsd-arm64": "0.15.13",
|
||||
"esbuild-linux-32": "0.15.13",
|
||||
"esbuild-linux-64": "0.15.13",
|
||||
"esbuild-linux-arm": "0.15.13",
|
||||
"esbuild-linux-arm64": "0.15.13",
|
||||
"esbuild-linux-mips64le": "0.15.13",
|
||||
"esbuild-linux-ppc64le": "0.15.13",
|
||||
"esbuild-linux-riscv64": "0.15.13",
|
||||
"esbuild-linux-s390x": "0.15.13",
|
||||
"esbuild-netbsd-64": "0.15.13",
|
||||
"esbuild-openbsd-64": "0.15.13",
|
||||
"esbuild-sunos-64": "0.15.13",
|
||||
"esbuild-windows-32": "0.15.13",
|
||||
"esbuild-windows-64": "0.15.13",
|
||||
"esbuild-windows-arm64": "0.15.13"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-android-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz",
|
||||
"integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-android-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-darwin-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz",
|
||||
"integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-darwin-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-freebsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-freebsd-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-32": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz",
|
||||
"integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz",
|
||||
"integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-arm": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz",
|
||||
"integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-mips64le": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz",
|
||||
"integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-ppc64le": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz",
|
||||
"integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-riscv64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz",
|
||||
"integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-s390x": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz",
|
||||
"integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-netbsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-openbsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-sunos-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz",
|
||||
"integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-windows-32": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz",
|
||||
"integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-windows-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz",
|
||||
"integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-windows-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has": "^1.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.18",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz",
|
||||
"integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.4",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.9.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "2.79.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
||||
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.2.tgz",
|
||||
"integrity": "sha512-pLrhatFFOWO9kS19bQ658CnRYzv0WLbsPih6R+iFeEEhDOuYgYCX2rztUViMz/uy/V8cLCJvLFeiOK7RJEzHcw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.15.9",
|
||||
"postcss": "^8.4.18",
|
||||
"resolve": "^1.22.1",
|
||||
"rollup": "^2.79.1"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"less": "*",
|
||||
"sass": "*",
|
||||
"stylus": "*",
|
||||
"sugarss": "*",
|
||||
"terser": "^5.4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"less": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
},
|
||||
"stylus": {
|
||||
"optional": true
|
||||
},
|
||||
"sugarss": {
|
||||
"optional": true
|
||||
},
|
||||
"terser": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@esbuild/android-arm": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
|
||||
"integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-loong64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz",
|
||||
"integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz",
|
||||
"integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@esbuild/android-arm": "0.15.13",
|
||||
"@esbuild/linux-loong64": "0.15.13",
|
||||
"esbuild-android-64": "0.15.13",
|
||||
"esbuild-android-arm64": "0.15.13",
|
||||
"esbuild-darwin-64": "0.15.13",
|
||||
"esbuild-darwin-arm64": "0.15.13",
|
||||
"esbuild-freebsd-64": "0.15.13",
|
||||
"esbuild-freebsd-arm64": "0.15.13",
|
||||
"esbuild-linux-32": "0.15.13",
|
||||
"esbuild-linux-64": "0.15.13",
|
||||
"esbuild-linux-arm": "0.15.13",
|
||||
"esbuild-linux-arm64": "0.15.13",
|
||||
"esbuild-linux-mips64le": "0.15.13",
|
||||
"esbuild-linux-ppc64le": "0.15.13",
|
||||
"esbuild-linux-riscv64": "0.15.13",
|
||||
"esbuild-linux-s390x": "0.15.13",
|
||||
"esbuild-netbsd-64": "0.15.13",
|
||||
"esbuild-openbsd-64": "0.15.13",
|
||||
"esbuild-sunos-64": "0.15.13",
|
||||
"esbuild-windows-32": "0.15.13",
|
||||
"esbuild-windows-64": "0.15.13",
|
||||
"esbuild-windows-arm64": "0.15.13"
|
||||
}
|
||||
},
|
||||
"esbuild-android-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz",
|
||||
"integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-android-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-darwin-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz",
|
||||
"integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-darwin-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-freebsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-freebsd-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-32": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz",
|
||||
"integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz",
|
||||
"integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-arm": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz",
|
||||
"integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-mips64le": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz",
|
||||
"integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-ppc64le": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz",
|
||||
"integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-riscv64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz",
|
||||
"integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-s390x": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz",
|
||||
"integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-netbsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-openbsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-sunos-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz",
|
||||
"integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-windows-32": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz",
|
||||
"integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-windows-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz",
|
||||
"integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-windows-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.18",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz",
|
||||
"integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.4",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.9.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.79.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
||||
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true
|
||||
},
|
||||
"vite": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.2.tgz",
|
||||
"integrity": "sha512-pLrhatFFOWO9kS19bQ658CnRYzv0WLbsPih6R+iFeEEhDOuYgYCX2rztUViMz/uy/V8cLCJvLFeiOK7RJEzHcw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esbuild": "^0.15.9",
|
||||
"fsevents": "~2.3.2",
|
||||
"postcss": "^8.4.18",
|
||||
"resolve": "^1.22.1",
|
||||
"rollup": "^2.79.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
packages/core/examples/vite-vanilla-repl/package.json
Normal file
14
packages/core/examples/vite-vanilla-repl/package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "vite-vanilla-repl",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build --base /tidalcycles/strudel/general-purpose-scheduler/packages/core/examples/vite-vanilla-repl/dist/",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^3.2.0"
|
||||
}
|
||||
}
|
||||
31
packages/core/examples/vite-vanilla-repl/tune.mjs
Normal file
31
packages/core/examples/vite-vanilla-repl/tune.mjs
Normal file
@ -0,0 +1,31 @@
|
||||
export default `await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
|
||||
stack(
|
||||
// amen
|
||||
n("0 1 2 3 4 5 6 7")
|
||||
.sometimes(x=>x.ply(2))
|
||||
.rarely(x=>x.speed("2 | -2"))
|
||||
.sometimesBy(.4, x=>x.delay(".5"))
|
||||
.s("amencutup")
|
||||
.slow(2)
|
||||
.room(.5)
|
||||
,
|
||||
// bass
|
||||
sine.add(saw.slow(4)).range(0,7).segment(8)
|
||||
.superimpose(x=>x.add(.1))
|
||||
.scale('G0 minor').note()
|
||||
.s("sawtooth").decay(.1).sustain(0)
|
||||
.gain(.4).cutoff(perlin.range(300,3000).slow(8)).resonance(10)
|
||||
.degradeBy("0 0.1 .5 .1")
|
||||
.rarely(add(note("12")))
|
||||
,
|
||||
// chord
|
||||
note("Bb3,D4".superimpose(x=>x.add(.2)))
|
||||
.s('sawtooth').cutoff(1000).struct("<~@3 [~ x]>")
|
||||
.decay(.05).sustain(.0).delay(.8).delaytime(.125).room(.8)
|
||||
,
|
||||
// alien
|
||||
s("breath").room(1).shape(.6).chop(16).rev().mask("<x ~@7>")
|
||||
,
|
||||
n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5))
|
||||
).reset("<x@7 x(5,8)>")`;
|
||||
@ -15,6 +15,8 @@ export * from './state.mjs';
|
||||
export * from './timespan.mjs';
|
||||
export * from './util.mjs';
|
||||
export * from './speak.mjs';
|
||||
export * from './evaluate.mjs';
|
||||
export * from './repl.mjs';
|
||||
export { default as drawLine } from './drawLine.mjs';
|
||||
export { default as gist } from './gist.js';
|
||||
// below won't work with runtime.mjs (json import fails)
|
||||
|
||||
23
packages/core/repl.mjs
Normal file
23
packages/core/repl.mjs
Normal file
@ -0,0 +1,23 @@
|
||||
import { Cyclist } from './cyclist.mjs';
|
||||
import { evaluate as _evaluate } from './evaluate.mjs';
|
||||
|
||||
export function repl({ interval, defaultOutput, onSchedulerError, onEvalError, onEval, getTime, transpiler }) {
|
||||
const scheduler = new Cyclist({ interval, onTrigger: defaultOutput, onError: onSchedulerError, getTime });
|
||||
const evaluate = async (code) => {
|
||||
if (!code) {
|
||||
throw new Error('no code to evaluate');
|
||||
}
|
||||
try {
|
||||
const { pattern } = await _evaluate(code, transpiler);
|
||||
scheduler.setPattern(pattern, true);
|
||||
onEval?.({
|
||||
pattern,
|
||||
code,
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn(`eval error: ${err.message}`);
|
||||
onEvalError?.(err);
|
||||
}
|
||||
};
|
||||
return { scheduler, evaluate };
|
||||
}
|
||||
@ -73,8 +73,11 @@ export const getPlayableNoteValue = (hap) => {
|
||||
export const getFrequency = (hap) => {
|
||||
let { value, context } = hap;
|
||||
// if value is number => interpret as midi number as long as its not marked as frequency
|
||||
if (typeof value === 'object' && value.freq) {
|
||||
return value.freq;
|
||||
if (typeof value === 'object') {
|
||||
if (value.freq) {
|
||||
return value.freq;
|
||||
}
|
||||
return getFreq(value.note || value.n || value.value);
|
||||
}
|
||||
if (typeof value === 'number' && context.type !== 'frequency') {
|
||||
value = fromMidi(hap.value);
|
||||
|
||||
48
packages/core/zyklus.mjs
Normal file
48
packages/core/zyklus.mjs
Normal file
@ -0,0 +1,48 @@
|
||||
// will move to https://github.com/felixroos/zyklus
|
||||
// TODO: started flag
|
||||
|
||||
function 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.01;
|
||||
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);
|
||||
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 = () => {
|
||||
onTick();
|
||||
intervalID = setInterval(onTick, interval * 1000);
|
||||
};
|
||||
const clear = () => clearInterval(intervalID);
|
||||
const pause = () => clear();
|
||||
const stop = () => {
|
||||
tick = 0;
|
||||
phase = 0;
|
||||
clear();
|
||||
};
|
||||
const getPhase = () => phase;
|
||||
// setCallback
|
||||
return { setDuration, start, stop, pause, duration, getPhase };
|
||||
}
|
||||
export default createClock;
|
||||
@ -11,10 +11,9 @@ npm i @strudel.cycles/eval --save
|
||||
|
||||
## Example
|
||||
|
||||
<!-- TODO: -extend +evalScope -->
|
||||
|
||||
```js
|
||||
import { evaluate, extend } from '@strudel.cycles/eval';
|
||||
import { evalScope } from '@strudel.cycles/core';
|
||||
import { evaluate } from '@strudel.cycles/eval';
|
||||
|
||||
evalScope(
|
||||
import('@strudel.cycles/core'),
|
||||
|
||||
@ -4,46 +4,9 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { evaluate as _evaluate } from '@strudel.cycles/core';
|
||||
import shapeshifter from './shapeshifter.mjs';
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
|
||||
const { isPattern, Pattern } = strudel;
|
||||
|
||||
export const extend = (...args) => {
|
||||
console.warn('@strudel.cycles/eval extend is deprecated, please use evalScope instead');
|
||||
Object.assign(globalThis, ...args);
|
||||
};
|
||||
|
||||
let scoped = false;
|
||||
export const evalScope = async (...args) => {
|
||||
if (scoped) {
|
||||
console.warn('@strudel.cycles/eval evalScope was called more than once.');
|
||||
}
|
||||
scoped = true;
|
||||
const results = await Promise.allSettled(args);
|
||||
const modules = results.filter((result) => result.status === 'fulfilled').map((r) => r.value);
|
||||
results.forEach((result, i) => {
|
||||
if (result.status === 'rejected') {
|
||||
console.warn(`evalScope: module with index ${i} could not be loaded:`, result.reason);
|
||||
}
|
||||
});
|
||||
Object.assign(globalThis, ...modules, Pattern.prototype.bootstrap());
|
||||
};
|
||||
|
||||
function safeEval(str) {
|
||||
return Function('"use strict";return (' + str + ')')();
|
||||
}
|
||||
|
||||
export const evaluate = async (code) => {
|
||||
if (!scoped) {
|
||||
await evalScope(); // at least scope Pattern.prototype.boostrap
|
||||
}
|
||||
const shapeshifted = shapeshifter(code); // transform syntactically correct js code to semantically usable code
|
||||
let evaluated = await safeEval(shapeshifted);
|
||||
if (!isPattern(evaluated)) {
|
||||
console.log('evaluated', evaluated);
|
||||
const message = `got "${typeof evaluated}" instead of pattern`;
|
||||
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
|
||||
}
|
||||
return { mode: 'javascript', pattern: evaluated };
|
||||
return _evaluate(code, shapeshifter);
|
||||
};
|
||||
|
||||
@ -6,10 +6,10 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
|
||||
import { expect, describe, it } from 'vitest';
|
||||
|
||||
import { evaluate, evalScope } from '../evaluate.mjs';
|
||||
import { evaluate } from '../evaluate.mjs';
|
||||
import { mini } from '@strudel.cycles/mini';
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
const { fastcat } = strudel;
|
||||
const { fastcat, evalScope } = strudel;
|
||||
|
||||
describe('evaluate', async () => {
|
||||
await evalScope({ mini }, strudel);
|
||||
|
||||
@ -13,9 +13,8 @@ npm i @strudel.cycles/react
|
||||
Here is a minimal example of how to set up a MiniRepl:
|
||||
|
||||
```jsx
|
||||
import { evalScope } from '@strudel.cycles/eval';
|
||||
import { evalScope, controls } from '@strudel.cycles/core';
|
||||
import { MiniRepl } from '@strudel.cycles/react';
|
||||
import controls from '@strudel.cycles/core/controls.mjs';
|
||||
import { prebake } from '../repl/src/prebake.mjs';
|
||||
|
||||
evalScope(
|
||||
|
||||
4
packages/react/dist/index.cjs.js
vendored
4
packages/react/dist/index.cjs.js
vendored
File diff suppressed because one or more lines are too long
593
packages/react/dist/index.es.js
vendored
593
packages/react/dist/index.es.js
vendored
@ -1,17 +1,17 @@
|
||||
import d, { useCallback as C, useState as b, useEffect as S, useMemo as L, useRef as X, useLayoutEffect as Y } from "react";
|
||||
import Z from "@uiw/react-codemirror";
|
||||
import { Decoration as x, EditorView as j } from "@codemirror/view";
|
||||
import { StateEffect as K, StateField as Q } from "@codemirror/state";
|
||||
import { javascript as ee } from "@codemirror/lang-javascript";
|
||||
import { tags as i } from "@lezer/highlight";
|
||||
import { createTheme as te } from "@uiw/codemirror-themes";
|
||||
import { useInView as oe } from "react-hook-inview";
|
||||
import { evaluate as ne } from "@strudel.cycles/eval";
|
||||
import { Tone as h } from "@strudel.cycles/tone";
|
||||
import { TimeSpan as re, State as ae } from "@strudel.cycles/core";
|
||||
import { webaudioOutputTrigger as se } from "@strudel.cycles/webaudio";
|
||||
import { WebMidi as k, enableWebMidi as ce } from "@strudel.cycles/midi";
|
||||
const ie = te({
|
||||
import y, { useCallback as T, useState as w, useEffect as q, useMemo as R, useRef as I, useLayoutEffect as j } from "react";
|
||||
import Y from "@uiw/react-codemirror";
|
||||
import { Decoration as A, EditorView as K } from "@codemirror/view";
|
||||
import { StateEffect as U, StateField as Q } from "@codemirror/state";
|
||||
import { javascript as Z } from "@codemirror/lang-javascript";
|
||||
import { tags as m } from "@lezer/highlight";
|
||||
import { createTheme as ee } from "@uiw/codemirror-themes";
|
||||
import { useInView as te } from "react-hook-inview";
|
||||
import { evaluate as G } from "@strudel.cycles/eval";
|
||||
import { Tone as M } from "@strudel.cycles/tone";
|
||||
import { TimeSpan as oe, State as re } from "@strudel.cycles/core";
|
||||
import { webaudioOutputTrigger as ne } from "@strudel.cycles/webaudio";
|
||||
import { WebMidi as N, enableWebMidi as se } from "@strudel.cycles/midi";
|
||||
const ae = ee({
|
||||
theme: "dark",
|
||||
settings: {
|
||||
background: "#222",
|
||||
@ -24,344 +24,445 @@ const ie = te({
|
||||
gutterForeground: "#676e95"
|
||||
},
|
||||
styles: [
|
||||
{ tag: i.keyword, color: "#c792ea" },
|
||||
{ tag: i.operator, color: "#89ddff" },
|
||||
{ tag: i.special(i.variableName), color: "#eeffff" },
|
||||
{ tag: i.typeName, color: "#f07178" },
|
||||
{ tag: i.atom, color: "#f78c6c" },
|
||||
{ tag: i.number, color: "#ff5370" },
|
||||
{ tag: i.definition(i.variableName), color: "#82aaff" },
|
||||
{ tag: i.string, color: "#c3e88d" },
|
||||
{ tag: i.special(i.string), color: "#f07178" },
|
||||
{ tag: i.comment, color: "#7d8799" },
|
||||
{ tag: i.variableName, color: "#f07178" },
|
||||
{ tag: i.tagName, color: "#ff5370" },
|
||||
{ tag: i.bracket, color: "#a2a1a4" },
|
||||
{ tag: i.meta, color: "#ffcb6b" },
|
||||
{ tag: i.attributeName, color: "#c792ea" },
|
||||
{ tag: i.propertyName, color: "#c792ea" },
|
||||
{ tag: i.className, color: "#decb6b" },
|
||||
{ tag: i.invalid, color: "#ffffff" }
|
||||
{ tag: m.keyword, color: "#c792ea" },
|
||||
{ tag: m.operator, color: "#89ddff" },
|
||||
{ tag: m.special(m.variableName), color: "#eeffff" },
|
||||
{ tag: m.typeName, color: "#f07178" },
|
||||
{ tag: m.atom, color: "#f78c6c" },
|
||||
{ tag: m.number, color: "#ff5370" },
|
||||
{ tag: m.definition(m.variableName), color: "#82aaff" },
|
||||
{ tag: m.string, color: "#c3e88d" },
|
||||
{ tag: m.special(m.string), color: "#f07178" },
|
||||
{ tag: m.comment, color: "#7d8799" },
|
||||
{ tag: m.variableName, color: "#f07178" },
|
||||
{ tag: m.tagName, color: "#ff5370" },
|
||||
{ tag: m.bracket, color: "#a2a1a4" },
|
||||
{ tag: m.meta, color: "#ffcb6b" },
|
||||
{ tag: m.attributeName, color: "#c792ea" },
|
||||
{ tag: m.propertyName, color: "#c792ea" },
|
||||
{ tag: m.className, color: "#decb6b" },
|
||||
{ tag: m.invalid, color: "#ffffff" }
|
||||
]
|
||||
});
|
||||
const P = K.define(), le = Q.define({
|
||||
const z = U.define(), ce = Q.define({
|
||||
create() {
|
||||
return x.none;
|
||||
return A.none;
|
||||
},
|
||||
update(e, n) {
|
||||
update(e, o) {
|
||||
try {
|
||||
for (let r of n.effects)
|
||||
if (r.is(P))
|
||||
for (let r of o.effects)
|
||||
if (r.is(z))
|
||||
if (r.value) {
|
||||
const c = x.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
||||
e = x.set([c.range(0, n.newDoc.length)]);
|
||||
const n = A.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
||||
e = A.set([n.range(0, o.newDoc.length)]);
|
||||
} else
|
||||
e = x.set([]);
|
||||
e = A.set([]);
|
||||
return e;
|
||||
} catch (r) {
|
||||
return console.warn("flash error", r), e;
|
||||
}
|
||||
},
|
||||
provide: (e) => j.decorations.from(e)
|
||||
}), de = (e) => {
|
||||
e.dispatch({ effects: P.of(!0) }), setTimeout(() => {
|
||||
e.dispatch({ effects: P.of(!1) });
|
||||
provide: (e) => K.decorations.from(e)
|
||||
}), ie = (e) => {
|
||||
e.dispatch({ effects: z.of(!0) }), setTimeout(() => {
|
||||
e.dispatch({ effects: z.of(!1) });
|
||||
}, 200);
|
||||
}, B = K.define(), ue = Q.define({
|
||||
}, O = U.define(), le = Q.define({
|
||||
create() {
|
||||
return x.none;
|
||||
return A.none;
|
||||
},
|
||||
update(e, n) {
|
||||
update(e, o) {
|
||||
try {
|
||||
for (let r of n.effects)
|
||||
if (r.is(B)) {
|
||||
const c = r.value.map(
|
||||
(l) => (l.context.locations || []).map(({ start: a, end: m }) => {
|
||||
const t = l.context.color || "#FFCA28";
|
||||
let u = n.newDoc.line(a.line).from + a.column, o = n.newDoc.line(m.line).from + m.column;
|
||||
const y = n.newDoc.length;
|
||||
return u > y || o > y ? void 0 : x.mark({ attributes: { style: `outline: 1.5px solid ${t};` } }).range(u, o);
|
||||
for (let r of o.effects)
|
||||
if (r.is(O)) {
|
||||
const n = r.value.map(
|
||||
(s) => (s.context.locations || []).map(({ start: i, end: a }) => {
|
||||
const t = s.context.color || "#FFCA28";
|
||||
let l = o.newDoc.line(i.line).from + i.column, c = o.newDoc.line(a.line).from + a.column;
|
||||
const f = o.newDoc.length;
|
||||
return l > f || c > f ? void 0 : A.mark({ attributes: { style: `outline: 1.5px solid ${t};` } }).range(l, c);
|
||||
})
|
||||
).flat().filter(Boolean) || [];
|
||||
e = x.set(c, !0);
|
||||
e = A.set(n, !0);
|
||||
}
|
||||
return e;
|
||||
} catch {
|
||||
return x.set([]);
|
||||
return A.set([]);
|
||||
}
|
||||
},
|
||||
provide: (e) => j.decorations.from(e)
|
||||
}), fe = [ee(), ie, ue, le];
|
||||
function me({ value: e, onChange: n, onViewChanged: r, onSelectionChange: c, options: l, editorDidMount: a }) {
|
||||
const m = C(
|
||||
(o) => {
|
||||
n?.(o);
|
||||
provide: (e) => K.decorations.from(e)
|
||||
}), ue = [Z(), ae, le, ce];
|
||||
function de({ value: e, onChange: o, onViewChanged: r, onSelectionChange: n, options: s, editorDidMount: i }) {
|
||||
const a = T(
|
||||
(c) => {
|
||||
o?.(c);
|
||||
},
|
||||
[n]
|
||||
), t = C(
|
||||
(o) => {
|
||||
r?.(o);
|
||||
[o]
|
||||
), t = T(
|
||||
(c) => {
|
||||
r?.(c);
|
||||
},
|
||||
[r]
|
||||
), u = C(
|
||||
(o) => {
|
||||
o.selectionSet && c && c?.(o.state.selection);
|
||||
), l = T(
|
||||
(c) => {
|
||||
c.selectionSet && n && n?.(c.state.selection);
|
||||
},
|
||||
[c]
|
||||
[n]
|
||||
);
|
||||
return /* @__PURE__ */ d.createElement(d.Fragment, null, /* @__PURE__ */ d.createElement(Z, {
|
||||
return /* @__PURE__ */ y.createElement(y.Fragment, null, /* @__PURE__ */ y.createElement(Y, {
|
||||
value: e,
|
||||
onChange: m,
|
||||
onChange: a,
|
||||
onCreateEditor: t,
|
||||
onUpdate: u,
|
||||
extensions: fe
|
||||
onUpdate: l,
|
||||
extensions: ue
|
||||
}));
|
||||
}
|
||||
function ge(e) {
|
||||
const { onEvent: n, onQuery: r, onSchedule: c, ready: l = !0, onDraw: a } = e, [m, t] = b(!1), u = 1, o = () => Math.floor(h.getTransport().seconds / u), y = (v = o()) => {
|
||||
const O = new re(v, v + 1), M = r?.(new ae(O)) || [];
|
||||
c?.(M, v);
|
||||
const _ = O.begin.valueOf();
|
||||
h.getTransport().cancel(_);
|
||||
const R = (v + 1) * u - 0.5, E = Math.max(h.getTransport().seconds, R) + 0.1;
|
||||
h.getTransport().schedule(() => {
|
||||
y(v + 1);
|
||||
}, E), M?.filter((g) => g.part.begin.equals(g.whole?.begin)).forEach((g) => {
|
||||
h.getTransport().schedule((D) => {
|
||||
n(D, g, h.getContext().currentTime), h.Draw.schedule(() => {
|
||||
a?.(D, g);
|
||||
}, D);
|
||||
}, g.part.begin.valueOf());
|
||||
function fe(e) {
|
||||
const { onEvent: o, onQuery: r, onSchedule: n, ready: s = !0, onDraw: i } = e, [a, t] = w(!1), l = 1, c = () => Math.floor(M.getTransport().seconds / l), f = (g = c()) => {
|
||||
const C = new oe(g, g + 1), v = r?.(new re(C)) || [];
|
||||
n?.(v, g);
|
||||
const h = C.begin.valueOf();
|
||||
M.getTransport().cancel(h);
|
||||
const D = (g + 1) * l - 0.5, _ = Math.max(M.getTransport().seconds, D) + 0.1;
|
||||
M.getTransport().schedule(() => {
|
||||
f(g + 1);
|
||||
}, _), v?.filter((E) => E.part.begin.equals(E.whole?.begin)).forEach((E) => {
|
||||
M.getTransport().schedule((L) => {
|
||||
o(L, E, M.getContext().currentTime), M.Draw.schedule(() => {
|
||||
i?.(L, E);
|
||||
}, L);
|
||||
}, E.part.begin.valueOf());
|
||||
});
|
||||
};
|
||||
S(() => {
|
||||
l && y();
|
||||
}, [n, c, r, a, l]);
|
||||
const w = async () => {
|
||||
t(!0), await h.start(), h.getTransport().start("+0.1");
|
||||
}, p = () => {
|
||||
h.getTransport().pause(), t(!1);
|
||||
q(() => {
|
||||
s && f();
|
||||
}, [o, n, r, i, s]);
|
||||
const p = async () => {
|
||||
t(!0), await M.start(), M.getTransport().start("+0.1");
|
||||
}, d = () => {
|
||||
M.getTransport().pause(), t(!1);
|
||||
};
|
||||
return {
|
||||
start: w,
|
||||
stop: p,
|
||||
onEvent: n,
|
||||
started: m,
|
||||
start: p,
|
||||
stop: d,
|
||||
onEvent: o,
|
||||
started: a,
|
||||
setStarted: t,
|
||||
toggle: () => m ? p() : w(),
|
||||
query: y,
|
||||
activeCycle: o
|
||||
toggle: () => a ? d() : p(),
|
||||
query: f,
|
||||
activeCycle: c
|
||||
};
|
||||
}
|
||||
function pe(e) {
|
||||
return S(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), C((n) => window.postMessage(n, "*"), []);
|
||||
function ge(e) {
|
||||
return q(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), T((o) => window.postMessage(o, "*"), []);
|
||||
}
|
||||
let he = () => Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
||||
const be = (e) => encodeURIComponent(btoa(e));
|
||||
function ye({ tune: e, autolink: n = !0, onEvent: r, onDraw: c }) {
|
||||
const l = L(() => he(), []), [a, m] = b(e), [t, u] = b(), [o, y] = b(""), [w, p] = b(), [q, v] = b(!1), [O, M] = b(""), [_, R] = b(), E = L(() => a !== t || w, [a, t, w]), g = C((f) => y((s) => s + `${s ? `
|
||||
const me = (e) => encodeURIComponent(btoa(e));
|
||||
function pe({ tune: e, autolink: o = !0, onEvent: r, onDraw: n }) {
|
||||
const s = R(() => he(), []), [i, a] = w(e), [t, l] = w(), [c, f] = w(""), [p, d] = w(), [k, g] = w(!1), [C, v] = w(""), [h, D] = w(), _ = R(() => i !== t || p, [i, t, p]), E = T((b) => f((u) => u + `${u ? `
|
||||
|
||||
` : ""}${f}`), []), D = L(() => {
|
||||
` : ""}${b}`), []), L = R(() => {
|
||||
if (t && !t.includes("strudel disable-highlighting"))
|
||||
return (f, s) => c?.(f, s, t);
|
||||
}, [t, c]), z = L(() => t && t.includes("strudel hide-header"), [t]), N = L(() => t && t.includes("strudel hide-console"), [t]), F = ge({
|
||||
onDraw: D,
|
||||
onEvent: C(
|
||||
(f, s, J) => {
|
||||
return (b, u) => n?.(b, u, t);
|
||||
}, [t, n]), H = R(() => t && t.includes("strudel hide-header"), [t]), x = R(() => t && t.includes("strudel hide-console"), [t]), P = fe({
|
||||
onDraw: L,
|
||||
onEvent: T(
|
||||
(b, u, X) => {
|
||||
try {
|
||||
r?.(s), s.context.logs?.length && s.context.logs.forEach(g);
|
||||
const { onTrigger: A = se } = s.context;
|
||||
A(f, s, J, 1);
|
||||
} catch (A) {
|
||||
console.warn(A), A.message = "unplayable event: " + A?.message, g(A.message);
|
||||
r?.(u), u.context.logs?.length && u.context.logs.forEach(E);
|
||||
const { onTrigger: S = ne } = u.context;
|
||||
S(b, u, X, 1);
|
||||
} catch (S) {
|
||||
console.warn(S), S.message = "unplayable event: " + S?.message, E(S.message);
|
||||
}
|
||||
},
|
||||
[r, g]
|
||||
[r, E]
|
||||
),
|
||||
onQuery: C(
|
||||
(f) => {
|
||||
onQuery: T(
|
||||
(b) => {
|
||||
try {
|
||||
return _?.query(f) || [];
|
||||
} catch (s) {
|
||||
return console.warn(s), s.message = "query error: " + s.message, p(s), [];
|
||||
return h?.query(b) || [];
|
||||
} catch (u) {
|
||||
return console.warn(u), u.message = "query error: " + u.message, d(u), [];
|
||||
}
|
||||
},
|
||||
[_]
|
||||
[h]
|
||||
),
|
||||
onSchedule: C((f, s) => G(f), []),
|
||||
ready: !!_ && !!t
|
||||
}), V = pe(({ data: { from: f, type: s } }) => {
|
||||
s === "start" && f !== l && (F.setStarted(!1), u(void 0));
|
||||
}), I = C(
|
||||
async (f = a) => {
|
||||
if (t && !E) {
|
||||
p(void 0), F.start();
|
||||
onSchedule: T((b, u) => J(b), []),
|
||||
ready: !!h && !!t
|
||||
}), B = ge(({ data: { from: b, type: u } }) => {
|
||||
u === "start" && b !== s && (P.setStarted(!1), l(void 0));
|
||||
}), V = T(
|
||||
async (b = i) => {
|
||||
if (t && !_) {
|
||||
d(void 0), P.start();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
v(!0);
|
||||
const s = await ne(f);
|
||||
F.start(), V({ type: "start", from: l }), R(() => s.pattern), n && (window.location.hash = "#" + encodeURIComponent(btoa(a))), M(be(a)), p(void 0), u(f), v(!1);
|
||||
} catch (s) {
|
||||
s.message = "evaluation error: " + s.message, console.warn(s), p(s);
|
||||
g(!0);
|
||||
const u = await G(b);
|
||||
P.start(), B({ type: "start", from: s }), D(() => u.pattern), o && (window.location.hash = "#" + encodeURIComponent(btoa(i))), v(me(i)), d(void 0), l(b), g(!1);
|
||||
} catch (u) {
|
||||
u.message = "evaluation error: " + u.message, console.warn(u), d(u);
|
||||
}
|
||||
},
|
||||
[t, E, a, F, n, l, V]
|
||||
), G = (f, s) => {
|
||||
f.length;
|
||||
[t, _, i, P, o, s, B]
|
||||
), J = (b, u) => {
|
||||
b.length;
|
||||
};
|
||||
return {
|
||||
hideHeader: z,
|
||||
hideConsole: N,
|
||||
pending: q,
|
||||
code: a,
|
||||
setCode: m,
|
||||
pattern: _,
|
||||
error: w,
|
||||
cycle: F,
|
||||
setPattern: R,
|
||||
dirty: E,
|
||||
log: o,
|
||||
hideHeader: H,
|
||||
hideConsole: x,
|
||||
pending: k,
|
||||
code: i,
|
||||
setCode: a,
|
||||
pattern: h,
|
||||
error: p,
|
||||
cycle: P,
|
||||
setPattern: D,
|
||||
dirty: _,
|
||||
log: c,
|
||||
togglePlay: () => {
|
||||
F.started ? F.stop() : I();
|
||||
P.started ? P.stop() : V();
|
||||
},
|
||||
setActiveCode: u,
|
||||
activateCode: I,
|
||||
setActiveCode: l,
|
||||
activateCode: V,
|
||||
activeCode: t,
|
||||
pushLog: g,
|
||||
hash: O
|
||||
pushLog: E,
|
||||
hash: C
|
||||
};
|
||||
}
|
||||
function W(...e) {
|
||||
function $(...e) {
|
||||
return e.filter(Boolean).join(" ");
|
||||
}
|
||||
let H = [], U;
|
||||
function we({ view: e, pattern: n, active: r }) {
|
||||
S(() => {
|
||||
function ye({ view: e, pattern: o, active: r, getTime: n }) {
|
||||
const s = I([]), i = I();
|
||||
q(() => {
|
||||
if (e)
|
||||
if (n && r) {
|
||||
let l = function() {
|
||||
if (o && r) {
|
||||
let t = function() {
|
||||
try {
|
||||
const a = h.getTransport().seconds, t = [Math.max(U || a, a - 1 / 10), a + 1 / 60];
|
||||
U = a + 1 / 60, H = H.filter((o) => o.whole.end > a);
|
||||
const u = n.queryArc(...t).filter((o) => o.hasOnset());
|
||||
H = H.concat(u), e.dispatch({ effects: B.of(H) });
|
||||
const l = n(), f = [Math.max(i.current || l, l - 1 / 10), l + 1 / 60];
|
||||
i.current = l + 1 / 60, s.current = s.current.filter((d) => d.whole.end > l);
|
||||
const p = o.queryArc(...f).filter((d) => d.hasOnset());
|
||||
s.current = s.current.concat(p), e.dispatch({ effects: O.of(s.current) });
|
||||
} catch {
|
||||
e.dispatch({ effects: B.of([]) });
|
||||
e.dispatch({ effects: O.of([]) });
|
||||
}
|
||||
c = requestAnimationFrame(l);
|
||||
}, c = requestAnimationFrame(l);
|
||||
a = requestAnimationFrame(t);
|
||||
}, a = requestAnimationFrame(t);
|
||||
return () => {
|
||||
cancelAnimationFrame(c);
|
||||
cancelAnimationFrame(a);
|
||||
};
|
||||
} else
|
||||
H = [], e.dispatch({ effects: B.of([]) });
|
||||
}, [n, r, e]);
|
||||
s.current = [], e.dispatch({ effects: O.of([]) });
|
||||
}, [o, r, e]);
|
||||
}
|
||||
const ve = "_container_3i85k_1", Ee = "_header_3i85k_5", ke = "_buttons_3i85k_9", Ce = "_button_3i85k_9", Me = "_buttonDisabled_3i85k_17", _e = "_error_3i85k_21", Ne = "_body_3i85k_25", T = {
|
||||
container: ve,
|
||||
header: Ee,
|
||||
buttons: ke,
|
||||
button: Ce,
|
||||
buttonDisabled: Me,
|
||||
error: _e,
|
||||
body: Ne
|
||||
const be = "_container_3i85k_1", we = "_header_3i85k_5", ve = "_buttons_3i85k_9", Ee = "_button_3i85k_9", ke = "_buttonDisabled_3i85k_17", Ce = "_error_3i85k_21", Me = "_body_3i85k_25", F = {
|
||||
container: be,
|
||||
header: we,
|
||||
buttons: ve,
|
||||
button: Ee,
|
||||
buttonDisabled: ke,
|
||||
error: Ce,
|
||||
body: Me
|
||||
};
|
||||
function $({ type: e }) {
|
||||
return /* @__PURE__ */ d.createElement("svg", {
|
||||
function W({ type: e }) {
|
||||
return /* @__PURE__ */ y.createElement("svg", {
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
className: "sc-h-5 sc-w-5",
|
||||
viewBox: "0 0 20 20",
|
||||
fill: "currentColor"
|
||||
}, {
|
||||
refresh: /* @__PURE__ */ d.createElement("path", {
|
||||
refresh: /* @__PURE__ */ y.createElement("path", {
|
||||
fillRule: "evenodd",
|
||||
d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z",
|
||||
clipRule: "evenodd"
|
||||
}),
|
||||
play: /* @__PURE__ */ d.createElement("path", {
|
||||
play: /* @__PURE__ */ y.createElement("path", {
|
||||
fillRule: "evenodd",
|
||||
d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z",
|
||||
clipRule: "evenodd"
|
||||
}),
|
||||
pause: /* @__PURE__ */ d.createElement("path", {
|
||||
pause: /* @__PURE__ */ y.createElement("path", {
|
||||
fillRule: "evenodd",
|
||||
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z",
|
||||
clipRule: "evenodd"
|
||||
})
|
||||
}[e]);
|
||||
}
|
||||
function Ve({ tune: e, hideOutsideView: n = !1, init: r, onEvent: c, enableKeyboard: l }) {
|
||||
const { code: a, setCode: m, pattern: t, activeCode: u, activateCode: o, evaluateOnly: y, error: w, cycle: p, dirty: q, togglePlay: v, stop: O } = ye({
|
||||
function Be({ tune: e, hideOutsideView: o = !1, init: r, onEvent: n, enableKeyboard: s }) {
|
||||
const { code: i, setCode: a, pattern: t, activeCode: l, activateCode: c, evaluateOnly: f, error: p, cycle: d, dirty: k, togglePlay: g, stop: C } = pe({
|
||||
tune: e,
|
||||
autolink: !1,
|
||||
onEvent: c
|
||||
onEvent: n
|
||||
});
|
||||
S(() => {
|
||||
r && y();
|
||||
q(() => {
|
||||
r && f();
|
||||
}, [e, r]);
|
||||
const [M, _] = b(), [R, E] = oe({
|
||||
const [v, h] = w(), [D, _] = te({
|
||||
threshold: 0.01
|
||||
}), g = X(), D = L(() => ((E || !n) && (g.current = !0), E || g.current), [E, n]);
|
||||
return we({ view: M, pattern: t, active: p.started && !u?.includes("strudel disable-highlighting") }), Y(() => {
|
||||
if (l) {
|
||||
const z = async (N) => {
|
||||
(N.ctrlKey || N.altKey) && (N.code === "Enter" ? (N.preventDefault(), de(M), await o()) : N.code === "Period" && (p.stop(), N.preventDefault()));
|
||||
}), E = I(), L = R(() => ((_ || !o) && (E.current = !0), _ || E.current), [_, o]);
|
||||
return ye({
|
||||
view: v,
|
||||
pattern: t,
|
||||
active: d.started && !l?.includes("strudel disable-highlighting"),
|
||||
getTime: () => M.getTransport().seconds
|
||||
}), j(() => {
|
||||
if (s) {
|
||||
const H = async (x) => {
|
||||
(x.ctrlKey || x.altKey) && (x.code === "Enter" ? (x.preventDefault(), ie(v), await c()) : x.code === "Period" && (d.stop(), x.preventDefault()));
|
||||
};
|
||||
return window.addEventListener("keydown", z, !0), () => window.removeEventListener("keydown", z, !0);
|
||||
return window.addEventListener("keydown", H, !0), () => window.removeEventListener("keydown", H, !0);
|
||||
}
|
||||
}, [l, t, a, o, p, M]), /* @__PURE__ */ d.createElement("div", {
|
||||
className: T.container,
|
||||
ref: R
|
||||
}, /* @__PURE__ */ d.createElement("div", {
|
||||
className: T.header
|
||||
}, /* @__PURE__ */ d.createElement("div", {
|
||||
className: T.buttons
|
||||
}, /* @__PURE__ */ d.createElement("button", {
|
||||
className: W(T.button, p.started ? "sc-animate-pulse" : ""),
|
||||
onClick: () => v()
|
||||
}, /* @__PURE__ */ d.createElement($, {
|
||||
type: p.started ? "pause" : "play"
|
||||
})), /* @__PURE__ */ d.createElement("button", {
|
||||
className: W(q ? T.button : T.buttonDisabled),
|
||||
onClick: () => o()
|
||||
}, /* @__PURE__ */ d.createElement($, {
|
||||
}, [s, t, i, c, d, v]), /* @__PURE__ */ y.createElement("div", {
|
||||
className: F.container,
|
||||
ref: D
|
||||
}, /* @__PURE__ */ y.createElement("div", {
|
||||
className: F.header
|
||||
}, /* @__PURE__ */ y.createElement("div", {
|
||||
className: F.buttons
|
||||
}, /* @__PURE__ */ y.createElement("button", {
|
||||
className: $(F.button, d.started ? "sc-animate-pulse" : ""),
|
||||
onClick: () => g()
|
||||
}, /* @__PURE__ */ y.createElement(W, {
|
||||
type: d.started ? "pause" : "play"
|
||||
})), /* @__PURE__ */ y.createElement("button", {
|
||||
className: $(k ? F.button : F.buttonDisabled),
|
||||
onClick: () => c()
|
||||
}, /* @__PURE__ */ y.createElement(W, {
|
||||
type: "refresh"
|
||||
}))), w && /* @__PURE__ */ d.createElement("div", {
|
||||
className: T.error
|
||||
}, w.message)), /* @__PURE__ */ d.createElement("div", {
|
||||
className: T.body
|
||||
}, D && /* @__PURE__ */ d.createElement(me, {
|
||||
value: a,
|
||||
onChange: m,
|
||||
onViewChanged: _
|
||||
}))), p && /* @__PURE__ */ y.createElement("div", {
|
||||
className: F.error
|
||||
}, p.message)), /* @__PURE__ */ y.createElement("div", {
|
||||
className: F.body
|
||||
}, L && /* @__PURE__ */ y.createElement(de, {
|
||||
value: i,
|
||||
onChange: a,
|
||||
onViewChanged: h
|
||||
})));
|
||||
}
|
||||
function Ie(e) {
|
||||
const { ready: n, connected: r, disconnected: c } = e, [l, a] = b(!0), [m, t] = b(k?.outputs || []);
|
||||
return S(() => {
|
||||
ce().then(() => {
|
||||
k.addListener("connected", (o) => {
|
||||
t([...k.outputs]), r?.(k, o);
|
||||
}), k.addListener("disconnected", (o) => {
|
||||
t([...k.outputs]), c?.(k, o);
|
||||
}), n?.(k), a(!1);
|
||||
}).catch((o) => {
|
||||
if (o) {
|
||||
console.error(o), console.warn("Web Midi could not be enabled..");
|
||||
function Te(e, o, r = 0.05, n = 0.1, s = 0.1) {
|
||||
let i = 0, a = 0, t = 10 ** 4, l = 0.01;
|
||||
const c = (h) => r = h(r);
|
||||
s = s || n / 2;
|
||||
const f = () => {
|
||||
const h = e(), D = h + n + s;
|
||||
for (a === 0 && (a = h + l); a < D; )
|
||||
a = Math.round(a * t) / t, a >= h && o(a, r, i), a < h && console.log("TOO LATE", a), a += r, i++;
|
||||
};
|
||||
let p;
|
||||
const d = () => {
|
||||
f(), p = setInterval(f, n * 1e3);
|
||||
}, k = () => clearInterval(p);
|
||||
return { setDuration: c, start: d, stop: () => {
|
||||
i = 0, a = 0, k();
|
||||
}, pause: () => k(), duration: r, getPhase: () => a };
|
||||
}
|
||||
class _e {
|
||||
worker;
|
||||
pattern;
|
||||
started = !1;
|
||||
cps = 1;
|
||||
getTime;
|
||||
phase = 0;
|
||||
constructor({ interval: o, onTrigger: r, onError: n, getTime: s, latency: i = 0.1 }) {
|
||||
this.getTime = s;
|
||||
const a = (t) => Math.round(t * 1e3) / 1e3;
|
||||
this.clock = Te(
|
||||
s,
|
||||
(t, l, c) => {
|
||||
c === 0 && (this.origin = t);
|
||||
const f = a(t - this.origin);
|
||||
this.phase = f - i;
|
||||
const p = a(f + l), d = s();
|
||||
try {
|
||||
this.pattern.queryArc(f, p).forEach((g) => {
|
||||
if (g.part.begin.equals(g.whole.begin)) {
|
||||
const C = g.whole.begin + this.origin - d + i, v = g.duration * 1;
|
||||
r?.(g, C, v);
|
||||
}
|
||||
});
|
||||
} catch (k) {
|
||||
console.warn("scheduler error", k), n?.(k);
|
||||
}
|
||||
},
|
||||
o
|
||||
);
|
||||
}
|
||||
getPhase() {
|
||||
return this.phase;
|
||||
}
|
||||
start() {
|
||||
if (!this.pattern)
|
||||
throw new Error("Scheduler: no pattern set! call .setPattern first.");
|
||||
this.clock.start(), this.started = !0;
|
||||
}
|
||||
pause() {
|
||||
this.clock.stop(), delete this.origin, this.started = !1;
|
||||
}
|
||||
stop() {
|
||||
delete this.origin, this.clock.stop(), this.started = !1;
|
||||
}
|
||||
setPattern(o) {
|
||||
this.pattern = o;
|
||||
}
|
||||
setCps(o = 1) {
|
||||
this.cps = o;
|
||||
}
|
||||
log(o, r, n) {
|
||||
const s = n.filter((i) => i.hasOnset());
|
||||
console.log(`${o.toFixed(4)} - ${r.toFixed(4)} ${Array(s.length).fill("I").join("")}`);
|
||||
}
|
||||
}
|
||||
function Ve({ defaultOutput: e, interval: o, getTime: r, code: n, evalOnMount: s = !1 }) {
|
||||
const [i, a] = w(), [t, l] = w(), [c, f] = w(n), [p, d] = w(), k = n !== c, g = R(
|
||||
() => new _e({ interval: o, onTrigger: e, onError: a, getTime: r }),
|
||||
[e, o]
|
||||
), C = T(async () => {
|
||||
if (!n) {
|
||||
console.log("no code..");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { pattern: h } = await G(n);
|
||||
f(n), g?.setPattern(h), d(h), l();
|
||||
} catch (h) {
|
||||
l(h), console.warn("eval error", h);
|
||||
}
|
||||
}, [n, g]), v = I();
|
||||
return q(() => {
|
||||
!v.current && s && (v.current = !0, C());
|
||||
}, [C, s]), { schedulerError: i, scheduler: g, evalError: t, evaluate: C, activeCode: c, isDirty: k, pattern: p };
|
||||
}
|
||||
const $e = (e) => j(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
|
||||
function We(e) {
|
||||
const { ready: o, connected: r, disconnected: n } = e, [s, i] = w(!0), [a, t] = w(N?.outputs || []);
|
||||
return q(() => {
|
||||
se().then(() => {
|
||||
N.addListener("connected", (c) => {
|
||||
t([...N.outputs]), r?.(N, c);
|
||||
}), N.addListener("disconnected", (c) => {
|
||||
t([...N.outputs]), n?.(N, c);
|
||||
}), o?.(N), i(!1);
|
||||
}).catch((c) => {
|
||||
if (c) {
|
||||
console.error(c), console.warn("Web Midi could not be enabled..");
|
||||
return;
|
||||
}
|
||||
});
|
||||
}, [n, r, c, m]), { loading: l, outputs: m, outputByName: (o) => k.getOutputByName(o) };
|
||||
}, [o, r, n, a]), { loading: s, outputs: a, outputByName: (c) => N.getOutputByName(c) };
|
||||
}
|
||||
export {
|
||||
me as CodeMirror,
|
||||
Ve as MiniRepl,
|
||||
W as cx,
|
||||
de as flash,
|
||||
ge as useCycle,
|
||||
we as useHighlighting,
|
||||
pe as usePostMessage,
|
||||
ye as useRepl,
|
||||
Ie as useWebMidi
|
||||
de as CodeMirror,
|
||||
Be as MiniRepl,
|
||||
$ as cx,
|
||||
ie as flash,
|
||||
fe as useCycle,
|
||||
ye as useHighlighting,
|
||||
$e as useKeydown,
|
||||
ge as usePostMessage,
|
||||
pe as useRepl,
|
||||
Ve as useStrudel,
|
||||
We as useWebMidi
|
||||
};
|
||||
|
||||
24
packages/react/examples/nano-repl/.gitignore
vendored
Normal file
24
packages/react/examples/nano-repl/.gitignore
vendored
Normal file
@ -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?
|
||||
1
packages/react/examples/nano-repl/dist/assets/index.0dabc914.js
vendored
Normal file
1
packages/react/examples/nano-repl/dist/assets/index.0dabc914.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/react/examples/nano-repl/dist/assets/index.5b4bbb94.js
vendored
Normal file
1
packages/react/examples/nano-repl/dist/assets/index.5b4bbb94.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/react/examples/nano-repl/dist/assets/index.75f8960b.css
vendored
Normal file
1
packages/react/examples/nano-repl/dist/assets/index.75f8960b.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.cm-editor{background-color:transparent!important;height:100%;z-index:11;font-size:16px}.cm-theme-light{width:100%}.cm-line>*{background:#00000095}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.fixed{position:fixed}.absolute{position:absolute}.bottom-0{bottom:0px}.z-\[12\]{z-index:12}.flex{display:flex}.w-full{width:100%}.justify-center{justify-content:center}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.bg-slate-500{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}.px-2{padding-left:.5rem;padding-right:.5rem}body{background:#123}
|
||||
8
packages/react/examples/nano-repl/dist/assets/index.bb9bb2c4.js
vendored
Normal file
8
packages/react/examples/nano-repl/dist/assets/index.bb9bb2c4.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/react/examples/nano-repl/dist/assets/index.cc0b04cf.js
vendored
Normal file
1
packages/react/examples/nano-repl/dist/assets/index.cc0b04cf.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/react/examples/nano-repl/dist/assets/index.e7ac54e9.js
vendored
Normal file
1
packages/react/examples/nano-repl/dist/assets/index.e7ac54e9.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{f as s,i as t,h as o,j as d,q as f,l as p,k as u,p as i,o as l,n as r,w as g}from"./index.ec9f9930.js";export{s as getAudioContext,t as getCachedBuffer,o as getDestination,d as getLoadedBuffer,f as getLoadedSamples,p as loadBuffer,u as loadGithubSamples,i as panic,l as resetLoadedSamples,r as samples,g as webaudioOutput};
|
||||
124
packages/react/examples/nano-repl/dist/assets/index.ec9f9930.js
vendored
Normal file
124
packages/react/examples/nano-repl/dist/assets/index.ec9f9930.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/react/examples/nano-repl/dist/assets/index.fbde1114.js
vendored
Normal file
1
packages/react/examples/nano-repl/dist/assets/index.fbde1114.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/react/examples/nano-repl/dist/assets/osc.c09823d8.js
vendored
Normal file
1
packages/react/examples/nano-repl/dist/assets/osc.c09823d8.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/react/examples/nano-repl/dist/assets/serial.b42b098c.js
vendored
Normal file
1
packages/react/examples/nano-repl/dist/assets/serial.b42b098c.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{P as w}from"./index.ec9f9930.js";var i,f=!1;async function b(a=38400){if(!f){if(f=!0,i)return i;if("serial"in navigator){const r=await navigator.serial.requestPort();await r.open({baudRate:a});const o=new TextEncoderStream;o.readable.pipeTo(r.writable);const s=o.writable.getWriter();i=function(e){s.write(e)}}else throw"Webserial is not available in this browser."}}const g=.1;w.prototype.serial=function(...a){return this._withHap(r=>{i||b(...a);const o=(s,e,u)=>{var t="";if(typeof e.value=="object")if("action"in e.value){t+=e.value.action+"(";var c=!0;for(const[n,l]of Object.entries(e.value))n!=="action"&&(c?c=!1:t+=",",t+=`${n}:${l}`);t+=")"}else for(const[n,l]of Object.entries(e.value))t+=`${n}:${l};`;else t=e.value;const v=(s-u+g)*1e3;window.setTimeout(i,v,t)};return r.setContext({...r.context,onTrigger:o})})};export{b as getWriter};
|
||||
14
packages/react/examples/nano-repl/dist/index.html
vendored
Normal file
14
packages/react/examples/nano-repl/dist/index.html
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Strudel Nano REPL</title>
|
||||
<script type="module" crossorigin src="./assets/index.ec9f9930.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.75f8960b.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
12
packages/react/examples/nano-repl/index.html
Normal file
12
packages/react/examples/nano-repl/index.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Strudel Nano REPL</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
3520
packages/react/examples/nano-repl/package-lock.json
generated
Normal file
3520
packages/react/examples/nano-repl/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
packages/react/examples/nano-repl/package.json
Normal file
24
packages/react/examples/nano-repl/package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "nano-repl",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@vitejs/plugin-react": "^2.0.1",
|
||||
"autoprefixer": "^10.4.8",
|
||||
"postcss": "^8.4.16",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"vite": "^3.0.7"
|
||||
}
|
||||
}
|
||||
139
packages/react/examples/nano-repl/src/App.jsx
Normal file
139
packages/react/examples/nano-repl/src/App.jsx
Normal file
@ -0,0 +1,139 @@
|
||||
import { evalScope, controls } from '@strudel.cycles/core';
|
||||
import { getAudioContext, panic, webaudioOutput } from '@strudel.cycles/webaudio';
|
||||
import { useCallback, useState } from 'react';
|
||||
import CodeMirror, { flash } from '../../../src/components/CodeMirror6';
|
||||
import useKeydown from '../../../src/hooks/useKeydown.mjs';
|
||||
import useStrudel from '../../../src/hooks/useStrudel';
|
||||
import useHighlighting from '../../../src/hooks/useHighlighting';
|
||||
import './style.css';
|
||||
// import { prebake } from '../../../../../repl/src/prebake.mjs';
|
||||
|
||||
// TODO: only import stuff when play is pressed?
|
||||
evalScope(
|
||||
controls,
|
||||
import('@strudel.cycles/core'),
|
||||
// import('@strudel.cycles/tone'),
|
||||
// import('@strudel.cycles/midi'), // TODO: find out why midi loads tone.js
|
||||
import('@strudel.cycles/tonal'),
|
||||
import('@strudel.cycles/mini'),
|
||||
import('@strudel.cycles/xen'),
|
||||
import('@strudel.cycles/webaudio'),
|
||||
import('@strudel.cycles/osc'),
|
||||
import('@strudel.cycles/webdirt'),
|
||||
import('@strudel.cycles/serial'),
|
||||
import('@strudel.cycles/soundfonts'),
|
||||
);
|
||||
|
||||
const defaultTune = `samples({
|
||||
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
|
||||
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
stack(
|
||||
s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
|
||||
.speed(perlin.range(.7,.9)) // random sample speed variation
|
||||
//.hush()
|
||||
,"<a1 b1*2 a1(3,8) e2>" // bassline
|
||||
.off(1/8,x=>x.add(12).degradeBy(.5)) // random octave jumps
|
||||
.add(perlin.range(0,.5)) // random pitch variation
|
||||
.superimpose(add(.05)) // add second, slightly detuned voice
|
||||
.n() // wrap in "n"
|
||||
.decay(.15).sustain(0) // make each note of equal length
|
||||
.s('sawtooth') // waveform
|
||||
.gain(.4) // turn down
|
||||
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
|
||||
//.hush()
|
||||
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings() // chords
|
||||
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
||||
.add(perlin.range(0,.5)) // random pitch variation
|
||||
.n() // wrap in "n"
|
||||
.s('square') // waveform
|
||||
.gain(.16) // turn down
|
||||
.cutoff(500) // fixed cutoff
|
||||
.attack(1) // slowly fade in
|
||||
//.hush()
|
||||
,"a4 c5 <e6 a6>".struct("x(5,8)")
|
||||
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
||||
.add(perlin.range(0,.5)) // random pitch variation
|
||||
.n() // wrap in "n"
|
||||
.decay(.1).sustain(0) // make notes short
|
||||
.s('triangle') // waveform
|
||||
.degradeBy(perlin.range(0,.5)) // randomly controlled random removal :)
|
||||
.echoWith(4,.125,(x,n)=>x.gain(.15*1/(n+1))) // echo notes
|
||||
//.hush()
|
||||
)
|
||||
.fast(2/3)`;
|
||||
|
||||
// await prebake();
|
||||
|
||||
const ctx = getAudioContext();
|
||||
const getTime = () => ctx.currentTime;
|
||||
function App() {
|
||||
const [code, setCode] = useState(defaultTune);
|
||||
const [view, setView] = useState();
|
||||
// const [code, setCode] = useState(`"c3".note().slow(2)`);
|
||||
const { scheduler, evaluate, schedulerError, evalError, isDirty, activeCode, pattern } = useStrudel({
|
||||
code,
|
||||
defaultOutput: webaudioOutput,
|
||||
getTime,
|
||||
});
|
||||
|
||||
useHighlighting({
|
||||
view,
|
||||
pattern,
|
||||
active: !activeCode?.includes('strudel disable-highlighting'),
|
||||
getTime: () => scheduler.phase,
|
||||
});
|
||||
|
||||
const error = evalError || schedulerError;
|
||||
useKeydown(
|
||||
useCallback(
|
||||
async (e) => {
|
||||
if (e.ctrlKey || e.altKey) {
|
||||
if (e.code === 'Enter') {
|
||||
e.preventDefault();
|
||||
flash(view);
|
||||
await evaluate(code);
|
||||
if (e.shiftKey) {
|
||||
panic();
|
||||
scheduler.stop();
|
||||
scheduler.start();
|
||||
}
|
||||
if (!scheduler.started) {
|
||||
scheduler.start();
|
||||
}
|
||||
} else if (e.code === 'Period') {
|
||||
scheduler.pause();
|
||||
panic();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
},
|
||||
[scheduler, evaluate, view],
|
||||
),
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
{/* <textarea value={code} onChange={(e) => setCode(e.target.value)} cols="64" rows="30" /> */}
|
||||
<nav className="z-[12] w-full flex justify-center absolute bottom-0">
|
||||
<div className="bg-slate-500 space-x-2 px-2 rounded-t-md">
|
||||
<button
|
||||
onClick={async () => {
|
||||
await evaluate(code);
|
||||
await getAudioContext().resume();
|
||||
scheduler.start();
|
||||
}}
|
||||
>
|
||||
start
|
||||
</button>
|
||||
<button onClick={() => scheduler.stop()}>stop</button>
|
||||
{isDirty && <button onClick={() => evaluate(code)}>eval</button>}
|
||||
</div>
|
||||
{error && <p>error {error.message}</p>}
|
||||
</nav>
|
||||
<CodeMirror value={code} onChange={setCode} onViewChanged={setView} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
9
packages/react/examples/nano-repl/src/main.jsx
Normal file
9
packages/react/examples/nano-repl/src/main.jsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
);
|
||||
7
packages/react/examples/nano-repl/src/style.css
Normal file
7
packages/react/examples/nano-repl/src/style.css
Normal file
@ -0,0 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
background: #123;
|
||||
}
|
||||
14
packages/react/examples/nano-repl/tailwind.config.cjs
Normal file
14
packages/react/examples/nano-repl/tailwind.config.cjs
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
tailwind.config.js - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/tailwind.config.js>
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
// TODO: find out if leaving out tutorial path works now
|
||||
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
7
packages/react/examples/nano-repl/vite.config.js
Normal file
7
packages/react/examples/nano-repl/vite.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()]
|
||||
})
|
||||
@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { MiniRepl } from './components/MiniRepl';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import { evalScope } from '@strudel.cycles/eval';
|
||||
import { controls } from '@strudel.cycles/core';
|
||||
import { controls, evalScope } from '@strudel.cycles/core';
|
||||
|
||||
evalScope(
|
||||
controls,
|
||||
|
||||
@ -8,6 +8,7 @@ import 'tailwindcss/tailwind.css';
|
||||
import './style.css';
|
||||
import styles from './MiniRepl.module.css';
|
||||
import { Icon } from './Icon';
|
||||
import { Tone } from '@strudel.cycles/tone';
|
||||
|
||||
export function MiniRepl({ tune, hideOutsideView = false, init, onEvent, enableKeyboard }) {
|
||||
const { code, setCode, pattern, activeCode, activateCode, evaluateOnly, error, cycle, dirty, togglePlay, stop } =
|
||||
@ -30,7 +31,12 @@ export function MiniRepl({ tune, hideOutsideView = false, init, onEvent, enableK
|
||||
}
|
||||
return isVisible || wasVisible.current;
|
||||
}, [isVisible, hideOutsideView]);
|
||||
useHighlighting({ view, pattern, active: cycle.started && !activeCode?.includes('strudel disable-highlighting') });
|
||||
useHighlighting({
|
||||
view,
|
||||
pattern,
|
||||
active: cycle.started && !activeCode?.includes('strudel disable-highlighting'),
|
||||
getTime: () => Tone.getTransport().seconds,
|
||||
});
|
||||
|
||||
// set active pattern on ctrl+enter
|
||||
useLayoutEffect(() => {
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { setHighlights } from '../components/CodeMirror6';
|
||||
import { Tone } from '@strudel.cycles/tone';
|
||||
|
||||
let highlights = []; // actively highlighted events
|
||||
let lastEnd;
|
||||
|
||||
function useHighlighting({ view, pattern, active }) {
|
||||
function useHighlighting({ view, pattern, active, getTime }) {
|
||||
const highlights = useRef([]);
|
||||
const lastEnd = useRef();
|
||||
useEffect(() => {
|
||||
if (view) {
|
||||
if (pattern && active) {
|
||||
@ -13,16 +11,16 @@ function useHighlighting({ view, pattern, active }) {
|
||||
|
||||
function updateHighlights() {
|
||||
try {
|
||||
const audioTime = Tone.getTransport().seconds;
|
||||
const audioTime = getTime();
|
||||
// force min framerate of 10 fps => fixes crash on tab refocus, where lastEnd could be far away
|
||||
// see https://github.com/tidalcycles/strudel/issues/108
|
||||
const begin = Math.max(lastEnd || audioTime, audioTime - 1 / 10);
|
||||
const begin = Math.max(lastEnd.current || audioTime, audioTime - 1 / 10);
|
||||
const span = [begin, audioTime + 1 / 60];
|
||||
lastEnd = audioTime + 1 / 60;
|
||||
highlights = highlights.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
|
||||
lastEnd.current = audioTime + 1 / 60;
|
||||
highlights.current = highlights.current.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
|
||||
const haps = pattern.queryArc(...span).filter((hap) => hap.hasOnset());
|
||||
highlights = highlights.concat(haps); // add potential new onsets
|
||||
view.dispatch({ effects: setHighlights.of(highlights) }); // highlight all still active + new active haps
|
||||
highlights.current = highlights.current.concat(haps); // add potential new onsets
|
||||
view.dispatch({ effects: setHighlights.of(highlights.current) }); // highlight all still active + new active haps
|
||||
} catch (err) {
|
||||
// console.log('error in updateHighlights', err);
|
||||
view.dispatch({ effects: setHighlights.of([]) });
|
||||
@ -34,7 +32,7 @@ function useHighlighting({ view, pattern, active }) {
|
||||
cancelAnimationFrame(frame);
|
||||
};
|
||||
} else {
|
||||
highlights = [];
|
||||
highlights.current = [];
|
||||
view.dispatch({ effects: setHighlights.of([]) });
|
||||
}
|
||||
}
|
||||
|
||||
10
packages/react/src/hooks/useKeydown.mjs
Normal file
10
packages/react/src/hooks/useKeydown.mjs
Normal file
@ -0,0 +1,10 @@
|
||||
import { useLayoutEffect } from 'react';
|
||||
|
||||
// set active pattern on ctrl+enter
|
||||
const useKeydown = (callback) =>
|
||||
useLayoutEffect(() => {
|
||||
window.addEventListener('keydown', callback, true);
|
||||
return () => window.removeEventListener('keydown', callback, true);
|
||||
}, [callback]);
|
||||
|
||||
export default useKeydown;
|
||||
44
packages/react/src/hooks/useStrudel.mjs
Normal file
44
packages/react/src/hooks/useStrudel.mjs
Normal file
@ -0,0 +1,44 @@
|
||||
// import { Scheduler } from '@strudel.cycles/core';
|
||||
import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import shapeshifter from '@strudel.cycles/eval/shapeshifter.mjs';
|
||||
import { repl } from '@strudel.cycles/core/repl.mjs';
|
||||
|
||||
function useStrudel({ defaultOutput, interval, getTime, code, evalOnMount = false }) {
|
||||
// scheduler
|
||||
const [schedulerError, setSchedulerError] = useState();
|
||||
const [evalError, setEvalError] = useState();
|
||||
const [activeCode, setActiveCode] = useState(code);
|
||||
const [pattern, setPattern] = useState();
|
||||
const isDirty = code !== activeCode;
|
||||
const { scheduler, evaluate: _evaluate } = useMemo(
|
||||
() =>
|
||||
repl({
|
||||
interval,
|
||||
defaultOutput,
|
||||
onSchedulerError: setSchedulerError,
|
||||
onEvalError: setEvalError,
|
||||
getTime,
|
||||
transpiler: shapeshifter,
|
||||
onEval: ({ pattern: _pattern, code }) => {
|
||||
setActiveCode(code);
|
||||
setPattern(_pattern);
|
||||
setEvalError();
|
||||
},
|
||||
onEvalError: setEvalError,
|
||||
}),
|
||||
[defaultOutput, interval, getTime],
|
||||
);
|
||||
const evaluate = useCallback(() => _evaluate(code), [_evaluate, code]);
|
||||
|
||||
const inited = useRef();
|
||||
useEffect(() => {
|
||||
if (!inited.current && evalOnMount && code) {
|
||||
inited.current = true;
|
||||
evaluate();
|
||||
}
|
||||
}, [evaluate, evalOnMount, code]);
|
||||
|
||||
return { schedulerError, scheduler, evalError, evaluate, activeCode, isDirty, pattern };
|
||||
}
|
||||
|
||||
export default useStrudel;
|
||||
@ -6,5 +6,7 @@ export { default as useCycle } from './hooks/useCycle';
|
||||
export { default as useHighlighting } from './hooks/useHighlighting';
|
||||
export { default as usePostMessage } from './hooks/usePostMessage';
|
||||
export { default as useRepl } from './hooks/useRepl';
|
||||
export { default as useStrudel } from './hooks/useStrudel';
|
||||
export { default as useKeydown } from './hooks/useKeydown';
|
||||
export { default as cx } from './cx';
|
||||
export { useWebMidi } from './hooks/useWebMidi';
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
/*
|
||||
clockworker.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/webaudio/clockworker.mjs>
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// 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 = 0.1; // query span
|
||||
tick = 0;
|
||||
constructor(callback, interval = this.interval) {
|
||||
this.interval = 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.worker.postMessage({ 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 });
|
||||
}
|
||||
}
|
||||
@ -1,67 +0,0 @@
|
||||
<div style="position: absolute; top: 0; right: 0; padding: 4px">
|
||||
<button id="start" style="margin-bottom: 4px; font-size: 2em">start</button><br />
|
||||
<button id="stop" style="margin-bottom: 4px; font-size: 2em">stop</button><br />
|
||||
<button id="slower" style="font-size: 2em">-</button>
|
||||
<button id="faster" style="font-size: 2em">+</button>
|
||||
</div>
|
||||
<textarea
|
||||
style="font-size: 2em; background: #052b49; color: #fff; height: 100%; width: 100%; outline: none; border: 0"
|
||||
id="text"
|
||||
spellcheck="false"
|
||||
>
|
||||
Loading...</textarea
|
||||
>
|
||||
<script type="module">
|
||||
document.body.style = 'margin: 0';
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
import * as util from '@strudel.cycles/core/util.mjs';
|
||||
import '@strudel.cycles/core/euclid.mjs';
|
||||
import { Scheduler, getAudioContext } from '../index.mjs';
|
||||
|
||||
const { cat, State, TimeSpan } = strudel;
|
||||
Object.assign(window, strudel); // add strudel to eval scope
|
||||
|
||||
const scheduler = new Scheduler({
|
||||
audioContext: getAudioContext(),
|
||||
interval: 0.1,
|
||||
onEvent: (e) => {
|
||||
e.context?.createAudioNode?.(e);
|
||||
},
|
||||
});
|
||||
|
||||
let initialCode = `sequence(1,2).mul(55/2) // frequencies
|
||||
.mul(slowcat(1,2))
|
||||
.mul(slowcat(1,3/2,4/3,5/3).slow(8))
|
||||
.fast(3)
|
||||
.freq()
|
||||
.velocity(.5)
|
||||
.s('sawtooth')
|
||||
.cutoff(800)
|
||||
.out()
|
||||
`;
|
||||
|
||||
try {
|
||||
const base64 = decodeURIComponent(window.location.href.split('#')[1]);
|
||||
initialCode = atob(base64);
|
||||
} catch (err) {
|
||||
console.warn('failed to decode', err);
|
||||
}
|
||||
const input = document.getElementById('text');
|
||||
input.value = initialCode;
|
||||
const evaluate = () => {
|
||||
try {
|
||||
const pattern = eval(input.value);
|
||||
scheduler.setPattern(pattern);
|
||||
window.location.hash = '#' + encodeURIComponent(btoa(input.value)); // update url hash
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
}
|
||||
};
|
||||
evaluate();
|
||||
input.addEventListener('input', () => evaluate());
|
||||
|
||||
document.getElementById('start').addEventListener('click', () => scheduler.start());
|
||||
document.getElementById('stop').addEventListener('click', () => scheduler.stop());
|
||||
document.getElementById('slower').addEventListener('click', () => scheduler.setCps(scheduler.cps - 0.1));
|
||||
document.getElementById('faster').addEventListener('click', () => scheduler.setCps(scheduler.cps + 0.1));
|
||||
</script>
|
||||
@ -4,7 +4,5 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export * from './clockworker.mjs';
|
||||
export * from './scheduler.mjs';
|
||||
export * from './webaudio.mjs';
|
||||
export * from './sampler.mjs';
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
/*
|
||||
scheduler.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/webaudio/scheduler.mjs>
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ClockWorker } from './clockworker.mjs';
|
||||
|
||||
export class Scheduler {
|
||||
worker;
|
||||
pattern;
|
||||
phase;
|
||||
audioContext;
|
||||
cps = 1;
|
||||
constructor({ audioContext, interval = 0.1, onEvent, latency = 0.1 }) {
|
||||
this.audioContext = audioContext;
|
||||
this.worker = new ClockWorker((tick, interval) => {
|
||||
const begin = this.phase;
|
||||
const end = this.phase + interval * this.cps;
|
||||
this.phase = end;
|
||||
const haps = this.pattern.queryArc(begin, end);
|
||||
haps.forEach((e) => {
|
||||
if (typeof e.value?.cps === 'number') {
|
||||
this.setCps(e.value?.cps);
|
||||
}
|
||||
if (!e.part.begin.equals(e.whole.begin)) {
|
||||
return;
|
||||
}
|
||||
if (e.context.onTrigger) {
|
||||
const ctxTime = (e.whole.begin - begin) / this.cps + this.audioContext.currentTime + latency;
|
||||
e.context.onTrigger(ctxTime, e, this.audioContext.currentTime, this.cps);
|
||||
}
|
||||
if (onEvent) {
|
||||
onEvent?.(e);
|
||||
}
|
||||
});
|
||||
}, interval);
|
||||
}
|
||||
start() {
|
||||
if (!this.pattern) {
|
||||
throw new Error('Scheduler: no pattern set! call .setPattern first.');
|
||||
}
|
||||
this.audioContext.resume();
|
||||
this.phase = 0;
|
||||
this.worker.start();
|
||||
}
|
||||
stop() {
|
||||
this.worker.stop();
|
||||
}
|
||||
setPattern(pat) {
|
||||
this.pattern = pat;
|
||||
}
|
||||
setCps(cps = 1) {
|
||||
this.cps = cps;
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@
|
||||
"name": "@strudel.cycles/repl",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"start": "vite",
|
||||
|
||||
@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { evalScope, evaluate } from '@strudel.cycles/eval';
|
||||
import { evaluate } from '@strudel.cycles/eval';
|
||||
import { CodeMirror, cx, flash, useHighlighting, useRepl, useWebMidi } from '@strudel.cycles/react';
|
||||
import { cleanupDraw, cleanupUi, Tone } from '@strudel.cycles/tone';
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
@ -14,7 +14,7 @@ import * as tunes from './tunes.mjs';
|
||||
import { prebake } from './prebake.mjs';
|
||||
import * as WebDirt from 'WebDirt';
|
||||
import { resetLoadedSamples, getAudioContext } from '@strudel.cycles/webaudio';
|
||||
import { controls } from '@strudel.cycles/core';
|
||||
import { controls, evalScope } from '@strudel.cycles/core';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
@ -135,7 +135,12 @@ function App() {
|
||||
return () => window.removeEventListener('keydown', handleKeyPress, true);
|
||||
}, [pattern, code, activateCode, cycle, view]);
|
||||
|
||||
useHighlighting({ view, pattern, active: cycle.started && !activeCode?.includes('strudel disable-highlighting') });
|
||||
useHighlighting({
|
||||
view,
|
||||
pattern,
|
||||
active: cycle.started && !activeCode?.includes('strudel disable-highlighting'),
|
||||
getTime: () => Tone.getTransport().seconds,
|
||||
});
|
||||
|
||||
useWebMidi({
|
||||
ready: useCallback(
|
||||
@ -174,8 +179,8 @@ function App() {
|
||||
</div>
|
||||
<div className="flex">
|
||||
<button
|
||||
onClick={() => {
|
||||
getAudioContext().resume(); // fixes no sound in ios webkit
|
||||
onClick={async () => {
|
||||
await getAudioContext().resume(); // fixes no sound in ios webkit
|
||||
togglePlay();
|
||||
}}
|
||||
className={cx('hover:bg-gray-300', !isEmbedded ? 'p-2' : 'px-2')}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
// import * as tunes from './tunes.mjs';
|
||||
import { evaluate } from '@strudel.cycles/eval';
|
||||
import { evalScope } from '@strudel.cycles/eval';
|
||||
import { evalScope } from '@strudel.cycles/core';
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
import * as webaudio from '@strudel.cycles/webaudio';
|
||||
import controls from '@strudel.cycles/core/controls.mjs';
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { evalScope } from '@strudel.cycles/eval';
|
||||
import { evalScope, controls } from '@strudel.cycles/core';
|
||||
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
|
||||
import controls from '@strudel.cycles/core/controls.mjs';
|
||||
import { samples } from '@strudel.cycles/webaudio';
|
||||
import { prebake } from '../repl/src/prebake.mjs';
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user