Merge pull request #248 from tidalcycles/general-purpose-scheduler
General purpose scheduler
This commit is contained in:
commit
ef5cd36a37
60 changed files with 5815 additions and 519 deletions
66
package-lock.json
generated
66
package-lock.json
generated
|
|
@ -12463,7 +12463,40 @@
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bjork": "^0.0.1",
|
"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": {
|
"packages/embed": {
|
||||||
|
|
@ -14584,7 +14617,36 @@
|
||||||
"version": "file:packages/core",
|
"version": "file:packages/core",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bjork": "^0.0.1",
|
"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": {
|
"@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 './timespan.mjs';
|
||||||
export * from './util.mjs';
|
export * from './util.mjs';
|
||||||
export * from './speak.mjs';
|
export * from './speak.mjs';
|
||||||
|
export * from './evaluate.mjs';
|
||||||
|
export * from './repl.mjs';
|
||||||
export { default as drawLine } from './drawLine.mjs';
|
export { default as drawLine } from './drawLine.mjs';
|
||||||
export { default as gist } from './gist.js';
|
export { default as gist } from './gist.js';
|
||||||
// below won't work with runtime.mjs (json import fails)
|
// 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) => {
|
export const getFrequency = (hap) => {
|
||||||
let { value, context } = hap;
|
let { value, context } = hap;
|
||||||
// if value is number => interpret as midi number as long as its not marked as frequency
|
// if value is number => interpret as midi number as long as its not marked as frequency
|
||||||
if (typeof value === 'object' && value.freq) {
|
if (typeof value === 'object') {
|
||||||
return value.freq;
|
if (value.freq) {
|
||||||
|
return value.freq;
|
||||||
|
}
|
||||||
|
return getFreq(value.note || value.n || value.value);
|
||||||
}
|
}
|
||||||
if (typeof value === 'number' && context.type !== 'frequency') {
|
if (typeof value === 'number' && context.type !== 'frequency') {
|
||||||
value = fromMidi(hap.value);
|
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
|
## Example
|
||||||
|
|
||||||
<!-- TODO: -extend +evalScope -->
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { evaluate, extend } from '@strudel.cycles/eval';
|
import { evalScope } from '@strudel.cycles/core';
|
||||||
|
import { evaluate } from '@strudel.cycles/eval';
|
||||||
|
|
||||||
evalScope(
|
evalScope(
|
||||||
import('@strudel.cycles/core'),
|
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/>.
|
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 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) => {
|
export const evaluate = async (code) => {
|
||||||
if (!scoped) {
|
return _evaluate(code, shapeshifter);
|
||||||
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 };
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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 { expect, describe, it } from 'vitest';
|
||||||
|
|
||||||
import { evaluate, evalScope } from '../evaluate.mjs';
|
import { evaluate } from '../evaluate.mjs';
|
||||||
import { mini } from '@strudel.cycles/mini';
|
import { mini } from '@strudel.cycles/mini';
|
||||||
import * as strudel from '@strudel.cycles/core';
|
import * as strudel from '@strudel.cycles/core';
|
||||||
const { fastcat } = strudel;
|
const { fastcat, evalScope } = strudel;
|
||||||
|
|
||||||
describe('evaluate', async () => {
|
describe('evaluate', async () => {
|
||||||
await evalScope({ mini }, strudel);
|
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:
|
Here is a minimal example of how to set up a MiniRepl:
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { evalScope } from '@strudel.cycles/eval';
|
import { evalScope, controls } from '@strudel.cycles/core';
|
||||||
import { MiniRepl } from '@strudel.cycles/react';
|
import { MiniRepl } from '@strudel.cycles/react';
|
||||||
import controls from '@strudel.cycles/core/controls.mjs';
|
|
||||||
import { prebake } from '../repl/src/prebake.mjs';
|
import { prebake } from '../repl/src/prebake.mjs';
|
||||||
|
|
||||||
evalScope(
|
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 y, { useCallback as T, useState as w, useEffect as q, useMemo as R, useRef as I, useLayoutEffect as j } from "react";
|
||||||
import Z from "@uiw/react-codemirror";
|
import Y from "@uiw/react-codemirror";
|
||||||
import { Decoration as x, EditorView as j } from "@codemirror/view";
|
import { Decoration as A, EditorView as K } from "@codemirror/view";
|
||||||
import { StateEffect as K, StateField as Q } from "@codemirror/state";
|
import { StateEffect as U, StateField as Q } from "@codemirror/state";
|
||||||
import { javascript as ee } from "@codemirror/lang-javascript";
|
import { javascript as Z } from "@codemirror/lang-javascript";
|
||||||
import { tags as i } from "@lezer/highlight";
|
import { tags as m } from "@lezer/highlight";
|
||||||
import { createTheme as te } from "@uiw/codemirror-themes";
|
import { createTheme as ee } from "@uiw/codemirror-themes";
|
||||||
import { useInView as oe } from "react-hook-inview";
|
import { useInView as te } from "react-hook-inview";
|
||||||
import { evaluate as ne } from "@strudel.cycles/eval";
|
import { evaluate as G } from "@strudel.cycles/eval";
|
||||||
import { Tone as h } from "@strudel.cycles/tone";
|
import { Tone as M } from "@strudel.cycles/tone";
|
||||||
import { TimeSpan as re, State as ae } from "@strudel.cycles/core";
|
import { TimeSpan as oe, State as re } from "@strudel.cycles/core";
|
||||||
import { webaudioOutputTrigger as se } from "@strudel.cycles/webaudio";
|
import { webaudioOutputTrigger as ne } from "@strudel.cycles/webaudio";
|
||||||
import { WebMidi as k, enableWebMidi as ce } from "@strudel.cycles/midi";
|
import { WebMidi as N, enableWebMidi as se } from "@strudel.cycles/midi";
|
||||||
const ie = te({
|
const ae = ee({
|
||||||
theme: "dark",
|
theme: "dark",
|
||||||
settings: {
|
settings: {
|
||||||
background: "#222",
|
background: "#222",
|
||||||
|
|
@ -24,344 +24,445 @@ const ie = te({
|
||||||
gutterForeground: "#676e95"
|
gutterForeground: "#676e95"
|
||||||
},
|
},
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: i.keyword, color: "#c792ea" },
|
{ tag: m.keyword, color: "#c792ea" },
|
||||||
{ tag: i.operator, color: "#89ddff" },
|
{ tag: m.operator, color: "#89ddff" },
|
||||||
{ tag: i.special(i.variableName), color: "#eeffff" },
|
{ tag: m.special(m.variableName), color: "#eeffff" },
|
||||||
{ tag: i.typeName, color: "#f07178" },
|
{ tag: m.typeName, color: "#f07178" },
|
||||||
{ tag: i.atom, color: "#f78c6c" },
|
{ tag: m.atom, color: "#f78c6c" },
|
||||||
{ tag: i.number, color: "#ff5370" },
|
{ tag: m.number, color: "#ff5370" },
|
||||||
{ tag: i.definition(i.variableName), color: "#82aaff" },
|
{ tag: m.definition(m.variableName), color: "#82aaff" },
|
||||||
{ tag: i.string, color: "#c3e88d" },
|
{ tag: m.string, color: "#c3e88d" },
|
||||||
{ tag: i.special(i.string), color: "#f07178" },
|
{ tag: m.special(m.string), color: "#f07178" },
|
||||||
{ tag: i.comment, color: "#7d8799" },
|
{ tag: m.comment, color: "#7d8799" },
|
||||||
{ tag: i.variableName, color: "#f07178" },
|
{ tag: m.variableName, color: "#f07178" },
|
||||||
{ tag: i.tagName, color: "#ff5370" },
|
{ tag: m.tagName, color: "#ff5370" },
|
||||||
{ tag: i.bracket, color: "#a2a1a4" },
|
{ tag: m.bracket, color: "#a2a1a4" },
|
||||||
{ tag: i.meta, color: "#ffcb6b" },
|
{ tag: m.meta, color: "#ffcb6b" },
|
||||||
{ tag: i.attributeName, color: "#c792ea" },
|
{ tag: m.attributeName, color: "#c792ea" },
|
||||||
{ tag: i.propertyName, color: "#c792ea" },
|
{ tag: m.propertyName, color: "#c792ea" },
|
||||||
{ tag: i.className, color: "#decb6b" },
|
{ tag: m.className, color: "#decb6b" },
|
||||||
{ tag: i.invalid, color: "#ffffff" }
|
{ tag: m.invalid, color: "#ffffff" }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
const P = K.define(), le = Q.define({
|
const z = U.define(), ce = Q.define({
|
||||||
create() {
|
create() {
|
||||||
return x.none;
|
return A.none;
|
||||||
},
|
},
|
||||||
update(e, n) {
|
update(e, o) {
|
||||||
try {
|
try {
|
||||||
for (let r of n.effects)
|
for (let r of o.effects)
|
||||||
if (r.is(P))
|
if (r.is(z))
|
||||||
if (r.value) {
|
if (r.value) {
|
||||||
const c = x.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
const n = A.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
||||||
e = x.set([c.range(0, n.newDoc.length)]);
|
e = A.set([n.range(0, o.newDoc.length)]);
|
||||||
} else
|
} else
|
||||||
e = x.set([]);
|
e = A.set([]);
|
||||||
return e;
|
return e;
|
||||||
} catch (r) {
|
} catch (r) {
|
||||||
return console.warn("flash error", r), e;
|
return console.warn("flash error", r), e;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
provide: (e) => j.decorations.from(e)
|
provide: (e) => K.decorations.from(e)
|
||||||
}), de = (e) => {
|
}), ie = (e) => {
|
||||||
e.dispatch({ effects: P.of(!0) }), setTimeout(() => {
|
e.dispatch({ effects: z.of(!0) }), setTimeout(() => {
|
||||||
e.dispatch({ effects: P.of(!1) });
|
e.dispatch({ effects: z.of(!1) });
|
||||||
}, 200);
|
}, 200);
|
||||||
}, B = K.define(), ue = Q.define({
|
}, O = U.define(), le = Q.define({
|
||||||
create() {
|
create() {
|
||||||
return x.none;
|
return A.none;
|
||||||
},
|
},
|
||||||
update(e, n) {
|
update(e, o) {
|
||||||
try {
|
try {
|
||||||
for (let r of n.effects)
|
for (let r of o.effects)
|
||||||
if (r.is(B)) {
|
if (r.is(O)) {
|
||||||
const c = r.value.map(
|
const n = r.value.map(
|
||||||
(l) => (l.context.locations || []).map(({ start: a, end: m }) => {
|
(s) => (s.context.locations || []).map(({ start: i, end: a }) => {
|
||||||
const t = l.context.color || "#FFCA28";
|
const t = s.context.color || "#FFCA28";
|
||||||
let u = n.newDoc.line(a.line).from + a.column, o = n.newDoc.line(m.line).from + m.column;
|
let l = o.newDoc.line(i.line).from + i.column, c = o.newDoc.line(a.line).from + a.column;
|
||||||
const y = n.newDoc.length;
|
const f = o.newDoc.length;
|
||||||
return u > y || o > y ? void 0 : x.mark({ attributes: { style: `outline: 1.5px solid ${t};` } }).range(u, o);
|
return l > f || c > f ? void 0 : A.mark({ attributes: { style: `outline: 1.5px solid ${t};` } }).range(l, c);
|
||||||
})
|
})
|
||||||
).flat().filter(Boolean) || [];
|
).flat().filter(Boolean) || [];
|
||||||
e = x.set(c, !0);
|
e = A.set(n, !0);
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
} catch {
|
} catch {
|
||||||
return x.set([]);
|
return A.set([]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
provide: (e) => j.decorations.from(e)
|
provide: (e) => K.decorations.from(e)
|
||||||
}), fe = [ee(), ie, ue, le];
|
}), ue = [Z(), ae, le, ce];
|
||||||
function me({ value: e, onChange: n, onViewChanged: r, onSelectionChange: c, options: l, editorDidMount: a }) {
|
function de({ value: e, onChange: o, onViewChanged: r, onSelectionChange: n, options: s, editorDidMount: i }) {
|
||||||
const m = C(
|
const a = T(
|
||||||
(o) => {
|
(c) => {
|
||||||
n?.(o);
|
o?.(c);
|
||||||
},
|
},
|
||||||
[n]
|
[o]
|
||||||
), t = C(
|
), t = T(
|
||||||
(o) => {
|
(c) => {
|
||||||
r?.(o);
|
r?.(c);
|
||||||
},
|
},
|
||||||
[r]
|
[r]
|
||||||
), u = C(
|
), l = T(
|
||||||
(o) => {
|
(c) => {
|
||||||
o.selectionSet && c && c?.(o.state.selection);
|
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,
|
value: e,
|
||||||
onChange: m,
|
onChange: a,
|
||||||
onCreateEditor: t,
|
onCreateEditor: t,
|
||||||
onUpdate: u,
|
onUpdate: l,
|
||||||
extensions: fe
|
extensions: ue
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
function ge(e) {
|
function fe(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 { 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 O = new re(v, v + 1), M = r?.(new ae(O)) || [];
|
const C = new oe(g, g + 1), v = r?.(new re(C)) || [];
|
||||||
c?.(M, v);
|
n?.(v, g);
|
||||||
const _ = O.begin.valueOf();
|
const h = C.begin.valueOf();
|
||||||
h.getTransport().cancel(_);
|
M.getTransport().cancel(h);
|
||||||
const R = (v + 1) * u - 0.5, E = Math.max(h.getTransport().seconds, R) + 0.1;
|
const D = (g + 1) * l - 0.5, _ = Math.max(M.getTransport().seconds, D) + 0.1;
|
||||||
h.getTransport().schedule(() => {
|
M.getTransport().schedule(() => {
|
||||||
y(v + 1);
|
f(g + 1);
|
||||||
}, E), M?.filter((g) => g.part.begin.equals(g.whole?.begin)).forEach((g) => {
|
}, _), v?.filter((E) => E.part.begin.equals(E.whole?.begin)).forEach((E) => {
|
||||||
h.getTransport().schedule((D) => {
|
M.getTransport().schedule((L) => {
|
||||||
n(D, g, h.getContext().currentTime), h.Draw.schedule(() => {
|
o(L, E, M.getContext().currentTime), M.Draw.schedule(() => {
|
||||||
a?.(D, g);
|
i?.(L, E);
|
||||||
}, D);
|
}, L);
|
||||||
}, g.part.begin.valueOf());
|
}, E.part.begin.valueOf());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
S(() => {
|
q(() => {
|
||||||
l && y();
|
s && f();
|
||||||
}, [n, c, r, a, l]);
|
}, [o, n, r, i, s]);
|
||||||
const w = async () => {
|
const p = async () => {
|
||||||
t(!0), await h.start(), h.getTransport().start("+0.1");
|
t(!0), await M.start(), M.getTransport().start("+0.1");
|
||||||
}, p = () => {
|
}, d = () => {
|
||||||
h.getTransport().pause(), t(!1);
|
M.getTransport().pause(), t(!1);
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
start: w,
|
start: p,
|
||||||
stop: p,
|
stop: d,
|
||||||
onEvent: n,
|
onEvent: o,
|
||||||
started: m,
|
started: a,
|
||||||
setStarted: t,
|
setStarted: t,
|
||||||
toggle: () => m ? p() : w(),
|
toggle: () => a ? d() : p(),
|
||||||
query: y,
|
query: f,
|
||||||
activeCycle: o
|
activeCycle: c
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function pe(e) {
|
function ge(e) {
|
||||||
return S(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), C((n) => window.postMessage(n, "*"), []);
|
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);
|
let he = () => Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
||||||
const be = (e) => encodeURIComponent(btoa(e));
|
const me = (e) => encodeURIComponent(btoa(e));
|
||||||
function ye({ tune: e, autolink: n = !0, onEvent: r, onDraw: c }) {
|
function pe({ tune: e, autolink: o = !0, onEvent: r, onDraw: n }) {
|
||||||
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 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"))
|
if (t && !t.includes("strudel disable-highlighting"))
|
||||||
return (f, s) => c?.(f, s, t);
|
return (b, u) => n?.(b, u, t);
|
||||||
}, [t, c]), z = L(() => t && t.includes("strudel hide-header"), [t]), N = L(() => t && t.includes("strudel hide-console"), [t]), F = ge({
|
}, [t, n]), H = R(() => t && t.includes("strudel hide-header"), [t]), x = R(() => t && t.includes("strudel hide-console"), [t]), P = fe({
|
||||||
onDraw: D,
|
onDraw: L,
|
||||||
onEvent: C(
|
onEvent: T(
|
||||||
(f, s, J) => {
|
(b, u, X) => {
|
||||||
try {
|
try {
|
||||||
r?.(s), s.context.logs?.length && s.context.logs.forEach(g);
|
r?.(u), u.context.logs?.length && u.context.logs.forEach(E);
|
||||||
const { onTrigger: A = se } = s.context;
|
const { onTrigger: S = ne } = u.context;
|
||||||
A(f, s, J, 1);
|
S(b, u, X, 1);
|
||||||
} catch (A) {
|
} catch (S) {
|
||||||
console.warn(A), A.message = "unplayable event: " + A?.message, g(A.message);
|
console.warn(S), S.message = "unplayable event: " + S?.message, E(S.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[r, g]
|
[r, E]
|
||||||
),
|
),
|
||||||
onQuery: C(
|
onQuery: T(
|
||||||
(f) => {
|
(b) => {
|
||||||
try {
|
try {
|
||||||
return _?.query(f) || [];
|
return h?.query(b) || [];
|
||||||
} catch (s) {
|
} catch (u) {
|
||||||
return console.warn(s), s.message = "query error: " + s.message, p(s), [];
|
return console.warn(u), u.message = "query error: " + u.message, d(u), [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[_]
|
[h]
|
||||||
),
|
),
|
||||||
onSchedule: C((f, s) => G(f), []),
|
onSchedule: T((b, u) => J(b), []),
|
||||||
ready: !!_ && !!t
|
ready: !!h && !!t
|
||||||
}), V = pe(({ data: { from: f, type: s } }) => {
|
}), B = ge(({ data: { from: b, type: u } }) => {
|
||||||
s === "start" && f !== l && (F.setStarted(!1), u(void 0));
|
u === "start" && b !== s && (P.setStarted(!1), l(void 0));
|
||||||
}), I = C(
|
}), V = T(
|
||||||
async (f = a) => {
|
async (b = i) => {
|
||||||
if (t && !E) {
|
if (t && !_) {
|
||||||
p(void 0), F.start();
|
d(void 0), P.start();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
v(!0);
|
g(!0);
|
||||||
const s = await ne(f);
|
const u = await G(b);
|
||||||
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);
|
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 (s) {
|
} catch (u) {
|
||||||
s.message = "evaluation error: " + s.message, console.warn(s), p(s);
|
u.message = "evaluation error: " + u.message, console.warn(u), d(u);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[t, E, a, F, n, l, V]
|
[t, _, i, P, o, s, B]
|
||||||
), G = (f, s) => {
|
), J = (b, u) => {
|
||||||
f.length;
|
b.length;
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
hideHeader: z,
|
hideHeader: H,
|
||||||
hideConsole: N,
|
hideConsole: x,
|
||||||
pending: q,
|
pending: k,
|
||||||
code: a,
|
code: i,
|
||||||
setCode: m,
|
setCode: a,
|
||||||
pattern: _,
|
pattern: h,
|
||||||
error: w,
|
error: p,
|
||||||
cycle: F,
|
cycle: P,
|
||||||
setPattern: R,
|
setPattern: D,
|
||||||
dirty: E,
|
dirty: _,
|
||||||
log: o,
|
log: c,
|
||||||
togglePlay: () => {
|
togglePlay: () => {
|
||||||
F.started ? F.stop() : I();
|
P.started ? P.stop() : V();
|
||||||
},
|
},
|
||||||
setActiveCode: u,
|
setActiveCode: l,
|
||||||
activateCode: I,
|
activateCode: V,
|
||||||
activeCode: t,
|
activeCode: t,
|
||||||
pushLog: g,
|
pushLog: E,
|
||||||
hash: O
|
hash: C
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function W(...e) {
|
function $(...e) {
|
||||||
return e.filter(Boolean).join(" ");
|
return e.filter(Boolean).join(" ");
|
||||||
}
|
}
|
||||||
let H = [], U;
|
function ye({ view: e, pattern: o, active: r, getTime: n }) {
|
||||||
function we({ view: e, pattern: n, active: r }) {
|
const s = I([]), i = I();
|
||||||
S(() => {
|
q(() => {
|
||||||
if (e)
|
if (e)
|
||||||
if (n && r) {
|
if (o && r) {
|
||||||
let l = function() {
|
let t = function() {
|
||||||
try {
|
try {
|
||||||
const a = h.getTransport().seconds, t = [Math.max(U || a, a - 1 / 10), a + 1 / 60];
|
const l = n(), f = [Math.max(i.current || l, l - 1 / 10), l + 1 / 60];
|
||||||
U = a + 1 / 60, H = H.filter((o) => o.whole.end > a);
|
i.current = l + 1 / 60, s.current = s.current.filter((d) => d.whole.end > l);
|
||||||
const u = n.queryArc(...t).filter((o) => o.hasOnset());
|
const p = o.queryArc(...f).filter((d) => d.hasOnset());
|
||||||
H = H.concat(u), e.dispatch({ effects: B.of(H) });
|
s.current = s.current.concat(p), e.dispatch({ effects: O.of(s.current) });
|
||||||
} catch {
|
} catch {
|
||||||
e.dispatch({ effects: B.of([]) });
|
e.dispatch({ effects: O.of([]) });
|
||||||
}
|
}
|
||||||
c = requestAnimationFrame(l);
|
a = requestAnimationFrame(t);
|
||||||
}, c = requestAnimationFrame(l);
|
}, a = requestAnimationFrame(t);
|
||||||
return () => {
|
return () => {
|
||||||
cancelAnimationFrame(c);
|
cancelAnimationFrame(a);
|
||||||
};
|
};
|
||||||
} else
|
} else
|
||||||
H = [], e.dispatch({ effects: B.of([]) });
|
s.current = [], e.dispatch({ effects: O.of([]) });
|
||||||
}, [n, r, e]);
|
}, [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 = {
|
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: ve,
|
container: be,
|
||||||
header: Ee,
|
header: we,
|
||||||
buttons: ke,
|
buttons: ve,
|
||||||
button: Ce,
|
button: Ee,
|
||||||
buttonDisabled: Me,
|
buttonDisabled: ke,
|
||||||
error: _e,
|
error: Ce,
|
||||||
body: Ne
|
body: Me
|
||||||
};
|
};
|
||||||
function $({ type: e }) {
|
function W({ type: e }) {
|
||||||
return /* @__PURE__ */ d.createElement("svg", {
|
return /* @__PURE__ */ y.createElement("svg", {
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
xmlns: "http://www.w3.org/2000/svg",
|
||||||
className: "sc-h-5 sc-w-5",
|
className: "sc-h-5 sc-w-5",
|
||||||
viewBox: "0 0 20 20",
|
viewBox: "0 0 20 20",
|
||||||
fill: "currentColor"
|
fill: "currentColor"
|
||||||
}, {
|
}, {
|
||||||
refresh: /* @__PURE__ */ d.createElement("path", {
|
refresh: /* @__PURE__ */ y.createElement("path", {
|
||||||
fillRule: "evenodd",
|
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",
|
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"
|
clipRule: "evenodd"
|
||||||
}),
|
}),
|
||||||
play: /* @__PURE__ */ d.createElement("path", {
|
play: /* @__PURE__ */ y.createElement("path", {
|
||||||
fillRule: "evenodd",
|
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",
|
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"
|
clipRule: "evenodd"
|
||||||
}),
|
}),
|
||||||
pause: /* @__PURE__ */ d.createElement("path", {
|
pause: /* @__PURE__ */ y.createElement("path", {
|
||||||
fillRule: "evenodd",
|
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",
|
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"
|
clipRule: "evenodd"
|
||||||
})
|
})
|
||||||
}[e]);
|
}[e]);
|
||||||
}
|
}
|
||||||
function Ve({ tune: e, hideOutsideView: n = !1, init: r, onEvent: c, enableKeyboard: l }) {
|
function Be({ tune: e, hideOutsideView: o = !1, init: r, onEvent: n, enableKeyboard: s }) {
|
||||||
const { code: a, setCode: m, pattern: t, activeCode: u, activateCode: o, evaluateOnly: y, error: w, cycle: p, dirty: q, togglePlay: v, stop: O } = ye({
|
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,
|
tune: e,
|
||||||
autolink: !1,
|
autolink: !1,
|
||||||
onEvent: c
|
onEvent: n
|
||||||
});
|
});
|
||||||
S(() => {
|
q(() => {
|
||||||
r && y();
|
r && f();
|
||||||
}, [e, r]);
|
}, [e, r]);
|
||||||
const [M, _] = b(), [R, E] = oe({
|
const [v, h] = w(), [D, _] = te({
|
||||||
threshold: 0.01
|
threshold: 0.01
|
||||||
}), g = X(), D = L(() => ((E || !n) && (g.current = !0), E || g.current), [E, n]);
|
}), E = I(), L = R(() => ((_ || !o) && (E.current = !0), _ || E.current), [_, o]);
|
||||||
return we({ view: M, pattern: t, active: p.started && !u?.includes("strudel disable-highlighting") }), Y(() => {
|
return ye({
|
||||||
if (l) {
|
view: v,
|
||||||
const z = async (N) => {
|
pattern: t,
|
||||||
(N.ctrlKey || N.altKey) && (N.code === "Enter" ? (N.preventDefault(), de(M), await o()) : N.code === "Period" && (p.stop(), N.preventDefault()));
|
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", {
|
}, [s, t, i, c, d, v]), /* @__PURE__ */ y.createElement("div", {
|
||||||
className: T.container,
|
className: F.container,
|
||||||
ref: R
|
ref: D
|
||||||
}, /* @__PURE__ */ d.createElement("div", {
|
}, /* @__PURE__ */ y.createElement("div", {
|
||||||
className: T.header
|
className: F.header
|
||||||
}, /* @__PURE__ */ d.createElement("div", {
|
}, /* @__PURE__ */ y.createElement("div", {
|
||||||
className: T.buttons
|
className: F.buttons
|
||||||
}, /* @__PURE__ */ d.createElement("button", {
|
}, /* @__PURE__ */ y.createElement("button", {
|
||||||
className: W(T.button, p.started ? "sc-animate-pulse" : ""),
|
className: $(F.button, d.started ? "sc-animate-pulse" : ""),
|
||||||
onClick: () => v()
|
onClick: () => g()
|
||||||
}, /* @__PURE__ */ d.createElement($, {
|
}, /* @__PURE__ */ y.createElement(W, {
|
||||||
type: p.started ? "pause" : "play"
|
type: d.started ? "pause" : "play"
|
||||||
})), /* @__PURE__ */ d.createElement("button", {
|
})), /* @__PURE__ */ y.createElement("button", {
|
||||||
className: W(q ? T.button : T.buttonDisabled),
|
className: $(k ? F.button : F.buttonDisabled),
|
||||||
onClick: () => o()
|
onClick: () => c()
|
||||||
}, /* @__PURE__ */ d.createElement($, {
|
}, /* @__PURE__ */ y.createElement(W, {
|
||||||
type: "refresh"
|
type: "refresh"
|
||||||
}))), w && /* @__PURE__ */ d.createElement("div", {
|
}))), p && /* @__PURE__ */ y.createElement("div", {
|
||||||
className: T.error
|
className: F.error
|
||||||
}, w.message)), /* @__PURE__ */ d.createElement("div", {
|
}, p.message)), /* @__PURE__ */ y.createElement("div", {
|
||||||
className: T.body
|
className: F.body
|
||||||
}, D && /* @__PURE__ */ d.createElement(me, {
|
}, L && /* @__PURE__ */ y.createElement(de, {
|
||||||
value: a,
|
value: i,
|
||||||
onChange: m,
|
onChange: a,
|
||||||
onViewChanged: _
|
onViewChanged: h
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
function Ie(e) {
|
function Te(e, o, r = 0.05, n = 0.1, s = 0.1) {
|
||||||
const { ready: n, connected: r, disconnected: c } = e, [l, a] = b(!0), [m, t] = b(k?.outputs || []);
|
let i = 0, a = 0, t = 10 ** 4, l = 0.01;
|
||||||
return S(() => {
|
const c = (h) => r = h(r);
|
||||||
ce().then(() => {
|
s = s || n / 2;
|
||||||
k.addListener("connected", (o) => {
|
const f = () => {
|
||||||
t([...k.outputs]), r?.(k, o);
|
const h = e(), D = h + n + s;
|
||||||
}), k.addListener("disconnected", (o) => {
|
for (a === 0 && (a = h + l); a < D; )
|
||||||
t([...k.outputs]), c?.(k, o);
|
a = Math.round(a * t) / t, a >= h && o(a, r, i), a < h && console.log("TOO LATE", a), a += r, i++;
|
||||||
}), n?.(k), a(!1);
|
};
|
||||||
}).catch((o) => {
|
let p;
|
||||||
if (o) {
|
const d = () => {
|
||||||
console.error(o), console.warn("Web Midi could not be enabled..");
|
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;
|
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 {
|
export {
|
||||||
me as CodeMirror,
|
de as CodeMirror,
|
||||||
Ve as MiniRepl,
|
Be as MiniRepl,
|
||||||
W as cx,
|
$ as cx,
|
||||||
de as flash,
|
ie as flash,
|
||||||
ge as useCycle,
|
fe as useCycle,
|
||||||
we as useHighlighting,
|
ye as useHighlighting,
|
||||||
pe as usePostMessage,
|
$e as useKeydown,
|
||||||
ye as useRepl,
|
ge as usePostMessage,
|
||||||
Ie as useWebMidi
|
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 React from 'react';
|
||||||
import { MiniRepl } from './components/MiniRepl';
|
import { MiniRepl } from './components/MiniRepl';
|
||||||
import 'tailwindcss/tailwind.css';
|
import 'tailwindcss/tailwind.css';
|
||||||
import { evalScope } from '@strudel.cycles/eval';
|
import { controls, evalScope } from '@strudel.cycles/core';
|
||||||
import { controls } from '@strudel.cycles/core';
|
|
||||||
|
|
||||||
evalScope(
|
evalScope(
|
||||||
controls,
|
controls,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import 'tailwindcss/tailwind.css';
|
||||||
import './style.css';
|
import './style.css';
|
||||||
import styles from './MiniRepl.module.css';
|
import styles from './MiniRepl.module.css';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
|
import { Tone } from '@strudel.cycles/tone';
|
||||||
|
|
||||||
export function MiniRepl({ tune, hideOutsideView = false, init, onEvent, enableKeyboard }) {
|
export function MiniRepl({ tune, hideOutsideView = false, init, onEvent, enableKeyboard }) {
|
||||||
const { code, setCode, pattern, activeCode, activateCode, evaluateOnly, error, cycle, dirty, togglePlay, stop } =
|
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;
|
return isVisible || wasVisible.current;
|
||||||
}, [isVisible, hideOutsideView]);
|
}, [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
|
// set active pattern on ctrl+enter
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { setHighlights } from '../components/CodeMirror6';
|
import { setHighlights } from '../components/CodeMirror6';
|
||||||
import { Tone } from '@strudel.cycles/tone';
|
|
||||||
|
|
||||||
let highlights = []; // actively highlighted events
|
function useHighlighting({ view, pattern, active, getTime }) {
|
||||||
let lastEnd;
|
const highlights = useRef([]);
|
||||||
|
const lastEnd = useRef();
|
||||||
function useHighlighting({ view, pattern, active }) {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (view) {
|
if (view) {
|
||||||
if (pattern && active) {
|
if (pattern && active) {
|
||||||
|
|
@ -13,16 +11,16 @@ function useHighlighting({ view, pattern, active }) {
|
||||||
|
|
||||||
function updateHighlights() {
|
function updateHighlights() {
|
||||||
try {
|
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
|
// 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
|
// 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];
|
const span = [begin, audioTime + 1 / 60];
|
||||||
lastEnd = audioTime + 1 / 60;
|
lastEnd.current = audioTime + 1 / 60;
|
||||||
highlights = highlights.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
|
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());
|
const haps = pattern.queryArc(...span).filter((hap) => hap.hasOnset());
|
||||||
highlights = highlights.concat(haps); // add potential new onsets
|
highlights.current = highlights.current.concat(haps); // add potential new onsets
|
||||||
view.dispatch({ effects: setHighlights.of(highlights) }); // highlight all still active + new active haps
|
view.dispatch({ effects: setHighlights.of(highlights.current) }); // highlight all still active + new active haps
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// console.log('error in updateHighlights', err);
|
// console.log('error in updateHighlights', err);
|
||||||
view.dispatch({ effects: setHighlights.of([]) });
|
view.dispatch({ effects: setHighlights.of([]) });
|
||||||
|
|
@ -34,7 +32,7 @@ function useHighlighting({ view, pattern, active }) {
|
||||||
cancelAnimationFrame(frame);
|
cancelAnimationFrame(frame);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
highlights = [];
|
highlights.current = [];
|
||||||
view.dispatch({ effects: setHighlights.of([]) });
|
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 useHighlighting } from './hooks/useHighlighting';
|
||||||
export { default as usePostMessage } from './hooks/usePostMessage';
|
export { default as usePostMessage } from './hooks/usePostMessage';
|
||||||
export { default as useRepl } from './hooks/useRepl';
|
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 { default as cx } from './cx';
|
||||||
export { useWebMidi } from './hooks/useWebMidi';
|
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/>.
|
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 './webaudio.mjs';
|
||||||
export * from './sampler.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",
|
"name": "@strudel.cycles/repl",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
"start": "vite",
|
"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/>.
|
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 { CodeMirror, cx, flash, useHighlighting, useRepl, useWebMidi } from '@strudel.cycles/react';
|
||||||
import { cleanupDraw, cleanupUi, Tone } from '@strudel.cycles/tone';
|
import { cleanupDraw, cleanupUi, Tone } from '@strudel.cycles/tone';
|
||||||
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
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 { prebake } from './prebake.mjs';
|
||||||
import * as WebDirt from 'WebDirt';
|
import * as WebDirt from 'WebDirt';
|
||||||
import { resetLoadedSamples, getAudioContext } from '@strudel.cycles/webaudio';
|
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 { createClient } from '@supabase/supabase-js';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
|
|
@ -135,7 +135,12 @@ function App() {
|
||||||
return () => window.removeEventListener('keydown', handleKeyPress, true);
|
return () => window.removeEventListener('keydown', handleKeyPress, true);
|
||||||
}, [pattern, code, activateCode, cycle, view]);
|
}, [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({
|
useWebMidi({
|
||||||
ready: useCallback(
|
ready: useCallback(
|
||||||
|
|
@ -174,8 +179,8 @@ function App() {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
getAudioContext().resume(); // fixes no sound in ios webkit
|
await getAudioContext().resume(); // fixes no sound in ios webkit
|
||||||
togglePlay();
|
togglePlay();
|
||||||
}}
|
}}
|
||||||
className={cx('hover:bg-gray-300', !isEmbedded ? 'p-2' : 'px-2')}
|
className={cx('hover:bg-gray-300', !isEmbedded ? 'p-2' : 'px-2')}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
// import * as tunes from './tunes.mjs';
|
// import * as tunes from './tunes.mjs';
|
||||||
import { evaluate } from '@strudel.cycles/eval';
|
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 strudel from '@strudel.cycles/core';
|
||||||
import * as webaudio from '@strudel.cycles/webaudio';
|
import * as webaudio from '@strudel.cycles/webaudio';
|
||||||
import controls from '@strudel.cycles/core/controls.mjs';
|
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 { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
|
||||||
import controls from '@strudel.cycles/core/controls.mjs';
|
|
||||||
import { samples } from '@strudel.cycles/webaudio';
|
import { samples } from '@strudel.cycles/webaudio';
|
||||||
import { prebake } from '../repl/src/prebake.mjs';
|
import { prebake } from '../repl/src/prebake.mjs';
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue