mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-24 12:08:28 +00:00
commit
cd30b2af3f
12391
package-lock.json
generated
12391
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -44,11 +44,10 @@
|
|||||||
"c8": "^7.12.0",
|
"c8": "^7.12.0",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"gh-pages": "^4.0.0",
|
"gh-pages": "^4.0.0",
|
||||||
"happy-dom": "^6.0.4",
|
|
||||||
"jsdoc": "^3.6.10",
|
"jsdoc": "^3.6.10",
|
||||||
"jsdoc-json": "^2.0.2",
|
"jsdoc-json": "^2.0.2",
|
||||||
"jsdoc-to-markdown": "^7.1.1",
|
"jsdoc-to-markdown": "^7.1.1",
|
||||||
"lerna": "^4.0.0",
|
"lerna": "^6.0.0",
|
||||||
"rollup-plugin-visualizer": "^5.8.1",
|
"rollup-plugin-visualizer": "^5.8.1",
|
||||||
"vitest": "^0.21.1"
|
"vitest": "^0.21.1"
|
||||||
}
|
}
|
||||||
|
|||||||
44
packages/core/examples/scheduled.html
Normal file
44
packages/core/examples/scheduled.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="text"
|
||||||
|
value="seq('c3','eb3','g3').note().s('sawtooth').out()"
|
||||||
|
style="width: 100%; font-size: 2em; outline: none; margin-bottom: 10px"
|
||||||
|
spellcheck="false"
|
||||||
|
/>
|
||||||
|
<button id="start">play</button>
|
||||||
|
<div id="output"></div>
|
||||||
|
<script type="module">
|
||||||
|
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@latest');
|
||||||
|
|
||||||
|
const controls = await import('https://cdn.skypack.dev/@strudel.cycles/core@latest/controls.mjs');
|
||||||
|
const { getAudioContext, Scheduler } = await import('https://cdn.skypack.dev/@strudel.cycles/webaudio@latest');
|
||||||
|
let scheduler;
|
||||||
|
|
||||||
|
const audioContext = getAudioContext();
|
||||||
|
const latency = 0.2;
|
||||||
|
|
||||||
|
Object.assign(window, strudel);
|
||||||
|
Object.assign(window, controls.default);
|
||||||
|
scheduler = new Scheduler({
|
||||||
|
audioContext,
|
||||||
|
interval: 0.1,
|
||||||
|
latency,
|
||||||
|
onEvent: (hap) => {
|
||||||
|
if (!hap.context.onTrigger) {
|
||||||
|
console.warn('no output chosen. use one of .out() .webdirt() .osc()');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let started;
|
||||||
|
document.getElementById('start').addEventListener('click', async () => {
|
||||||
|
const code = document.getElementById('text').value;
|
||||||
|
const pattern = eval(code);
|
||||||
|
const events = pattern._firstCycleValues;
|
||||||
|
console.log(code, '->', events);
|
||||||
|
scheduler.setPattern(pattern);
|
||||||
|
if (!started) {
|
||||||
|
scheduler.start();
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@ -30,10 +30,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel.cycles/core": "^0.2.0",
|
"@strudel.cycles/core": "^0.2.0",
|
||||||
"estraverse": "^5.3.0",
|
"estraverse": "^5.3.0",
|
||||||
"shift-ast": "^6.1.0",
|
"shift-ast": "^7.0.0",
|
||||||
"shift-codegen": "^7.0.3",
|
"shift-codegen": "^8.1.0",
|
||||||
"shift-parser": "^7.0.3",
|
"shift-parser": "^8.0.0",
|
||||||
"shift-spec": "^2018.0.2",
|
"shift-spec": "^2019.0.0",
|
||||||
"shift-traverser": "^1.0.0"
|
"shift-traverser": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,12 +5,11 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { isNote } from 'tone';
|
import { isNote } from 'tone';
|
||||||
import _WebMidi from 'webmidi';
|
import * as _WebMidi from 'webmidi';
|
||||||
import { Pattern, isPattern } from '@strudel.cycles/core';
|
import { Pattern, isPattern } from '@strudel.cycles/core';
|
||||||
import { Tone } from '@strudel.cycles/tone';
|
import { Tone } from '@strudel.cycles/tone';
|
||||||
|
|
||||||
// if you use WebMidi from outside of this package, make sure to import that instance:
|
// if you use WebMidi from outside of this package, make sure to import that instance:
|
||||||
export const WebMidi = _WebMidi;
|
export const { WebMidi } = _WebMidi;
|
||||||
|
|
||||||
export function enableWebMidi() {
|
export function enableWebMidi() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|||||||
@ -23,6 +23,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel.cycles/tone": "^0.2.0",
|
"@strudel.cycles/tone": "^0.2.0",
|
||||||
"tone": "^14.7.77",
|
"tone": "^14.7.77",
|
||||||
"webmidi": "^2.5.2"
|
"webmidi": "^3.0.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,6 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"osc-js": "^2.3.2"
|
"osc-js": "^2.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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
787
packages/react/dist/index.es.js
vendored
787
packages/react/dist/index.es.js
vendored
@ -1,582 +1,367 @@
|
|||||||
import React, { useCallback, useState, useEffect, useMemo, useRef, useLayoutEffect } from 'react';
|
import d, { useCallback as C, useState as b, useEffect as S, useMemo as L, useRef as X, useLayoutEffect as Y } from "react";
|
||||||
import _CodeMirror from '@uiw/react-codemirror';
|
import Z from "@uiw/react-codemirror";
|
||||||
import { Decoration, EditorView } from '@codemirror/view';
|
import { Decoration as x, EditorView as j } from "@codemirror/view";
|
||||||
import { StateEffect, StateField } from '@codemirror/state';
|
import { StateEffect as K, StateField as Q } from "@codemirror/state";
|
||||||
import { javascript } from '@codemirror/lang-javascript';
|
import { javascript as ee } from "@codemirror/lang-javascript";
|
||||||
import { tags } from '@lezer/highlight';
|
import { tags as i } from "@lezer/highlight";
|
||||||
import { createTheme } from '@uiw/codemirror-themes';
|
import { createTheme as te } from "@uiw/codemirror-themes";
|
||||||
import { useInView } from 'react-hook-inview';
|
import { useInView as oe } from "react-hook-inview";
|
||||||
import { evaluate } from '@strudel.cycles/eval';
|
import { evaluate as ne } from "@strudel.cycles/eval";
|
||||||
import { Tone } from '@strudel.cycles/tone';
|
import { Tone as h } from "@strudel.cycles/tone";
|
||||||
import { TimeSpan, State } from '@strudel.cycles/core';
|
import { TimeSpan as re, State as ae } from "@strudel.cycles/core";
|
||||||
import { webaudioOutputTrigger } from '@strudel.cycles/webaudio';
|
import { webaudioOutputTrigger as se } from "@strudel.cycles/webaudio";
|
||||||
import { WebMidi, enableWebMidi } from '@strudel.cycles/midi';
|
import { WebMidi as k, enableWebMidi as ce } from "@strudel.cycles/midi";
|
||||||
|
const ie = te({
|
||||||
var strudelTheme = createTheme({
|
theme: "dark",
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
settings: {
|
||||||
background: '#222',
|
background: "#222",
|
||||||
foreground: '#75baff', // whats that?
|
foreground: "#75baff",
|
||||||
caret: '#ffcc00',
|
caret: "#ffcc00",
|
||||||
selection: 'rgba(128, 203, 196, 0.5)',
|
selection: "rgba(128, 203, 196, 0.5)",
|
||||||
selectionMatch: '#036dd626',
|
selectionMatch: "#036dd626",
|
||||||
lineHighlight: '#8a91991a',
|
lineHighlight: "#8a91991a",
|
||||||
gutterBackground: 'transparent',
|
gutterBackground: "transparent",
|
||||||
// gutterForeground: '#8a919966',
|
gutterForeground: "#676e95"
|
||||||
gutterForeground: '#676e95',
|
|
||||||
},
|
},
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: tags.keyword, color: '#c792ea' },
|
{ tag: i.keyword, color: "#c792ea" },
|
||||||
{ tag: tags.operator, color: '#89ddff' },
|
{ tag: i.operator, color: "#89ddff" },
|
||||||
{ tag: tags.special(tags.variableName), color: '#eeffff' },
|
{ tag: i.special(i.variableName), color: "#eeffff" },
|
||||||
{ tag: tags.typeName, color: '#f07178' },
|
{ tag: i.typeName, color: "#f07178" },
|
||||||
{ tag: tags.atom, color: '#f78c6c' },
|
{ tag: i.atom, color: "#f78c6c" },
|
||||||
{ tag: tags.number, color: '#ff5370' },
|
{ tag: i.number, color: "#ff5370" },
|
||||||
{ tag: tags.definition(tags.variableName), color: '#82aaff' },
|
{ tag: i.definition(i.variableName), color: "#82aaff" },
|
||||||
{ tag: tags.string, color: '#c3e88d' },
|
{ tag: i.string, color: "#c3e88d" },
|
||||||
{ tag: tags.special(tags.string), color: '#f07178' },
|
{ tag: i.special(i.string), color: "#f07178" },
|
||||||
{ tag: tags.comment, color: '#7d8799' },
|
{ tag: i.comment, color: "#7d8799" },
|
||||||
{ tag: tags.variableName, color: '#f07178' },
|
{ tag: i.variableName, color: "#f07178" },
|
||||||
{ tag: tags.tagName, color: '#ff5370' },
|
{ tag: i.tagName, color: "#ff5370" },
|
||||||
{ tag: tags.bracket, color: '#a2a1a4' },
|
{ tag: i.bracket, color: "#a2a1a4" },
|
||||||
{ tag: tags.meta, color: '#ffcb6b' },
|
{ tag: i.meta, color: "#ffcb6b" },
|
||||||
{ tag: tags.attributeName, color: '#c792ea' },
|
{ tag: i.attributeName, color: "#c792ea" },
|
||||||
{ tag: tags.propertyName, color: '#c792ea' },
|
{ tag: i.propertyName, color: "#c792ea" },
|
||||||
{ tag: tags.className, color: '#decb6b' },
|
{ tag: i.className, color: "#decb6b" },
|
||||||
{ tag: tags.invalid, color: '#ffffff' },
|
{ tag: i.invalid, color: "#ffffff" }
|
||||||
],
|
]
|
||||||
});
|
});
|
||||||
|
const P = K.define(), le = Q.define({
|
||||||
var style = '';
|
|
||||||
|
|
||||||
const setFlash = StateEffect.define();
|
|
||||||
const flashField = StateField.define({
|
|
||||||
create() {
|
create() {
|
||||||
return Decoration.none;
|
return x.none;
|
||||||
},
|
},
|
||||||
update(flash2, tr) {
|
update(e, n) {
|
||||||
try {
|
try {
|
||||||
for (let e of tr.effects) {
|
for (let r of n.effects)
|
||||||
if (e.is(setFlash)) {
|
if (r.is(P))
|
||||||
if (e.value) {
|
if (r.value) {
|
||||||
const mark = Decoration.mark({ attributes: { style: `background-color: #FFCA2880` } });
|
const c = x.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
||||||
flash2 = Decoration.set([mark.range(0, tr.newDoc.length)]);
|
e = x.set([c.range(0, n.newDoc.length)]);
|
||||||
} else {
|
} else
|
||||||
flash2 = Decoration.set([]);
|
e = x.set([]);
|
||||||
}
|
return e;
|
||||||
}
|
} catch (r) {
|
||||||
}
|
return console.warn("flash error", r), e;
|
||||||
return flash2;
|
|
||||||
} catch (err) {
|
|
||||||
console.warn("flash error", err);
|
|
||||||
return flash2;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
provide: (f) => EditorView.decorations.from(f)
|
provide: (e) => j.decorations.from(e)
|
||||||
});
|
}), de = (e) => {
|
||||||
const flash = (view) => {
|
e.dispatch({ effects: P.of(!0) }), setTimeout(() => {
|
||||||
view.dispatch({ effects: setFlash.of(true) });
|
e.dispatch({ effects: P.of(!1) });
|
||||||
setTimeout(() => {
|
|
||||||
view.dispatch({ effects: setFlash.of(false) });
|
|
||||||
}, 200);
|
}, 200);
|
||||||
};
|
}, B = K.define(), ue = Q.define({
|
||||||
const setHighlights = StateEffect.define();
|
|
||||||
const highlightField = StateField.define({
|
|
||||||
create() {
|
create() {
|
||||||
return Decoration.none;
|
return x.none;
|
||||||
},
|
},
|
||||||
update(highlights, tr) {
|
update(e, n) {
|
||||||
try {
|
try {
|
||||||
for (let e of tr.effects) {
|
for (let r of n.effects)
|
||||||
if (e.is(setHighlights)) {
|
if (r.is(B)) {
|
||||||
const marks = e.value.map(
|
const c = r.value.map(
|
||||||
(hap) => (hap.context.locations || []).map(({ start, end }) => {
|
(l) => (l.context.locations || []).map(({ start: a, end: m }) => {
|
||||||
const color = hap.context.color || "#FFCA28";
|
const t = l.context.color || "#FFCA28";
|
||||||
let from = tr.newDoc.line(start.line).from + start.column;
|
let u = n.newDoc.line(a.line).from + a.column, o = n.newDoc.line(m.line).from + m.column;
|
||||||
let to = tr.newDoc.line(end.line).from + end.column;
|
const y = n.newDoc.length;
|
||||||
const l = tr.newDoc.length;
|
return u > y || o > y ? void 0 : x.mark({ attributes: { style: `outline: 1.5px solid ${t};` } }).range(u, o);
|
||||||
if (from > l || to > l) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const mark = Decoration.mark({ attributes: { style: `outline: 1.5px solid ${color};` } });
|
|
||||||
return mark.range(from, to);
|
|
||||||
})
|
})
|
||||||
).flat().filter(Boolean) || [];
|
).flat().filter(Boolean) || [];
|
||||||
highlights = Decoration.set(marks, true);
|
e = x.set(c, !0);
|
||||||
}
|
}
|
||||||
}
|
return e;
|
||||||
return highlights;
|
} catch {
|
||||||
} catch (err) {
|
return x.set([]);
|
||||||
return Decoration.set([]);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
provide: (f) => EditorView.decorations.from(f)
|
provide: (e) => j.decorations.from(e)
|
||||||
});
|
}), fe = [ee(), ie, ue, le];
|
||||||
const extensions = [javascript(), strudelTheme, highlightField, flashField];
|
function me({ value: e, onChange: n, onViewChanged: r, onSelectionChange: c, options: l, editorDidMount: a }) {
|
||||||
function CodeMirror({ value, onChange, onViewChanged, onSelectionChange, options, editorDidMount }) {
|
const m = C(
|
||||||
const handleOnChange = useCallback(
|
(o) => {
|
||||||
(value2) => {
|
n?.(o);
|
||||||
onChange?.(value2);
|
|
||||||
},
|
},
|
||||||
[onChange]
|
[n]
|
||||||
);
|
), t = C(
|
||||||
const handleOnCreateEditor = useCallback(
|
(o) => {
|
||||||
(view) => {
|
r?.(o);
|
||||||
onViewChanged?.(view);
|
|
||||||
},
|
},
|
||||||
[onViewChanged]
|
[r]
|
||||||
);
|
), u = C(
|
||||||
const handleOnUpdate = useCallback(
|
(o) => {
|
||||||
(viewUpdate) => {
|
o.selectionSet && c && c?.(o.state.selection);
|
||||||
if (viewUpdate.selectionSet && onSelectionChange) {
|
|
||||||
onSelectionChange?.(viewUpdate.state.selection);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[onSelectionChange]
|
[c]
|
||||||
);
|
);
|
||||||
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(_CodeMirror, {
|
return /* @__PURE__ */ d.createElement(d.Fragment, null, /* @__PURE__ */ d.createElement(Z, {
|
||||||
value,
|
value: e,
|
||||||
onChange: handleOnChange,
|
onChange: m,
|
||||||
onCreateEditor: handleOnCreateEditor,
|
onCreateEditor: t,
|
||||||
onUpdate: handleOnUpdate,
|
onUpdate: u,
|
||||||
extensions
|
extensions: fe
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
function ge(e) {
|
||||||
/*
|
const { onEvent: n, onQuery: r, onSchedule: c, ready: l = !0, onDraw: a } = e, [m, t] = b(!1), u = 1, o = () => Math.floor(h.getTransport().seconds / u), y = (v = o()) => {
|
||||||
useCycle.mjs - <short description TODO>
|
const O = new re(v, v + 1), M = r?.(new ae(O)) || [];
|
||||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/useCycle.mjs>
|
c?.(M, v);
|
||||||
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/>.
|
const _ = O.begin.valueOf();
|
||||||
*/
|
h.getTransport().cancel(_);
|
||||||
|
const R = (v + 1) * u - 0.5, E = Math.max(h.getTransport().seconds, R) + 0.1;
|
||||||
/* export declare interface UseCycleProps {
|
h.getTransport().schedule(() => {
|
||||||
onEvent: ToneEventCallback<any>;
|
y(v + 1);
|
||||||
onQuery?: (state: State) => Hap[];
|
}, E), M?.filter((g) => g.part.begin.equals(g.whole?.begin)).forEach((g) => {
|
||||||
onSchedule?: (events: Hap[], cycle: number) => void;
|
h.getTransport().schedule((D) => {
|
||||||
onDraw?: ToneEventCallback<any>;
|
n(D, g, h.getContext().currentTime), h.Draw.schedule(() => {
|
||||||
ready?: boolean; // if false, query will not be called on change props
|
a?.(D, g);
|
||||||
} */
|
}, D);
|
||||||
|
}, g.part.begin.valueOf());
|
||||||
// function useCycle(props: UseCycleProps) {
|
});
|
||||||
function useCycle(props) {
|
|
||||||
// onX must use useCallback!
|
|
||||||
const { onEvent, onQuery, onSchedule, ready = true, onDraw } = props;
|
|
||||||
const [started, setStarted] = useState(false);
|
|
||||||
const cycleDuration = 1;
|
|
||||||
const activeCycle = () => Math.floor(Tone.getTransport().seconds / cycleDuration);
|
|
||||||
|
|
||||||
// pull events with onQuery + count up to next cycle
|
|
||||||
const query = (cycle = activeCycle()) => {
|
|
||||||
const timespan = new TimeSpan(cycle, cycle + 1);
|
|
||||||
const events = onQuery?.(new State(timespan)) || [];
|
|
||||||
onSchedule?.(events, cycle);
|
|
||||||
// cancel events after current query. makes sure no old events are player for rescheduled cycles
|
|
||||||
// console.log('schedule', cycle);
|
|
||||||
// query next cycle in the middle of the current
|
|
||||||
const cancelFrom = timespan.begin.valueOf();
|
|
||||||
Tone.getTransport().cancel(cancelFrom);
|
|
||||||
// const queryNextTime = (cycle + 1) * cycleDuration - 0.1;
|
|
||||||
const queryNextTime = (cycle + 1) * cycleDuration - 0.5;
|
|
||||||
|
|
||||||
// if queryNextTime would be before current time, execute directly (+0.1 for safety that it won't miss)
|
|
||||||
const t = Math.max(Tone.getTransport().seconds, queryNextTime) + 0.1;
|
|
||||||
Tone.getTransport().schedule(() => {
|
|
||||||
query(cycle + 1);
|
|
||||||
}, t);
|
|
||||||
|
|
||||||
// schedule events for next cycle
|
|
||||||
events
|
|
||||||
?.filter((event) => event.part.begin.equals(event.whole?.begin))
|
|
||||||
.forEach((event) => {
|
|
||||||
Tone.getTransport().schedule((time) => {
|
|
||||||
onEvent(time, event, Tone.getContext().currentTime);
|
|
||||||
Tone.Draw.schedule(() => {
|
|
||||||
// do drawing or DOM manipulation here
|
|
||||||
onDraw?.(time, event);
|
|
||||||
}, time);
|
|
||||||
}, event.part.begin.valueOf());
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
S(() => {
|
||||||
useEffect(() => {
|
l && y();
|
||||||
ready && query();
|
}, [n, c, r, a, l]);
|
||||||
}, [onEvent, onSchedule, onQuery, onDraw, ready]);
|
const w = async () => {
|
||||||
|
t(!0), await h.start(), h.getTransport().start("+0.1");
|
||||||
const start = async () => {
|
}, p = () => {
|
||||||
setStarted(true);
|
h.getTransport().pause(), t(!1);
|
||||||
await Tone.start();
|
|
||||||
Tone.getTransport().start('+0.1');
|
|
||||||
};
|
};
|
||||||
const stop = () => {
|
|
||||||
Tone.getTransport().pause();
|
|
||||||
setStarted(false);
|
|
||||||
};
|
|
||||||
const toggle = () => (started ? stop() : start());
|
|
||||||
return {
|
return {
|
||||||
start,
|
start: w,
|
||||||
stop,
|
stop: p,
|
||||||
onEvent,
|
onEvent: n,
|
||||||
started,
|
started: m,
|
||||||
setStarted,
|
setStarted: t,
|
||||||
toggle,
|
toggle: () => m ? p() : w(),
|
||||||
query,
|
query: y,
|
||||||
activeCycle,
|
activeCycle: o
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
function pe(e) {
|
||||||
/*
|
return S(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), C((n) => window.postMessage(n, "*"), []);
|
||||||
usePostMessage.mjs - <short description TODO>
|
|
||||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/usePostMessage.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function usePostMessage(listener) {
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener('message', listener);
|
|
||||||
return () => window.removeEventListener('message', listener);
|
|
||||||
}, [listener]);
|
|
||||||
return useCallback((data) => window.postMessage(data, '*'), []);
|
|
||||||
}
|
}
|
||||||
|
let he = () => Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
||||||
|
const be = (e) => encodeURIComponent(btoa(e));
|
||||||
|
function ye({ tune: e, autolink: n = !0, onEvent: r, onDraw: c }) {
|
||||||
|
const l = L(() => he(), []), [a, m] = b(e), [t, u] = b(), [o, y] = b(""), [w, p] = b(), [q, v] = b(!1), [O, M] = b(""), [_, R] = b(), E = L(() => a !== t || w, [a, t, w]), g = C((f) => y((s) => s + `${s ? `
|
||||||
|
|
||||||
/*
|
` : ""}${f}`), []), D = L(() => {
|
||||||
useRepl.mjs - <short description TODO>
|
if (t && !t.includes("strudel disable-highlighting"))
|
||||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/useRepl.mjs>
|
return (f, s) => c?.(f, s, t);
|
||||||
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/>.
|
}, [t, c]), z = L(() => t && t.includes("strudel hide-header"), [t]), N = L(() => t && t.includes("strudel hide-console"), [t]), F = ge({
|
||||||
*/
|
onDraw: D,
|
||||||
|
onEvent: C(
|
||||||
let s4 = () => {
|
(f, s, J) => {
|
||||||
return Math.floor((1 + Math.random()) * 0x10000)
|
|
||||||
.toString(16)
|
|
||||||
.substring(1);
|
|
||||||
};
|
|
||||||
const generateHash = (code) => encodeURIComponent(btoa(code));
|
|
||||||
|
|
||||||
function useRepl({ tune, autolink = true, onEvent, onDraw: onDrawProp }) {
|
|
||||||
const id = useMemo(() => s4(), []);
|
|
||||||
const [code, setCode] = useState(tune);
|
|
||||||
const [activeCode, setActiveCode] = useState();
|
|
||||||
const [log, setLog] = useState('');
|
|
||||||
const [error, setError] = useState();
|
|
||||||
const [pending, setPending] = useState(false);
|
|
||||||
const [hash, setHash] = useState('');
|
|
||||||
const [pattern, setPattern] = useState();
|
|
||||||
const dirty = useMemo(() => code !== activeCode || error, [code, activeCode, error]);
|
|
||||||
const pushLog = useCallback((message) => setLog((log) => log + `${log ? '\n\n' : ''}${message}`), []);
|
|
||||||
|
|
||||||
// below block allows disabling the highlighting by including "strudel disable-highlighting" in the code (as comment)
|
|
||||||
const onDraw = useMemo(() => {
|
|
||||||
if (activeCode && !activeCode.includes('strudel disable-highlighting')) {
|
|
||||||
return (time, event) => onDrawProp?.(time, event, activeCode);
|
|
||||||
}
|
|
||||||
}, [activeCode, onDrawProp]);
|
|
||||||
|
|
||||||
const hideHeader = useMemo(() => activeCode && activeCode.includes('strudel hide-header'), [activeCode]);
|
|
||||||
const hideConsole = useMemo(() => activeCode && activeCode.includes('strudel hide-console'), [activeCode]);
|
|
||||||
// cycle hook to control scheduling
|
|
||||||
const cycle = useCycle({
|
|
||||||
onDraw,
|
|
||||||
onEvent: useCallback(
|
|
||||||
(time, event, currentTime) => {
|
|
||||||
try {
|
try {
|
||||||
onEvent?.(event);
|
r?.(s), s.context.logs?.length && s.context.logs.forEach(g);
|
||||||
if (event.context.logs?.length) {
|
const { onTrigger: A = se } = s.context;
|
||||||
event.context.logs.forEach(pushLog);
|
A(f, s, J, 1);
|
||||||
}
|
} catch (A) {
|
||||||
const { onTrigger = webaudioOutputTrigger } = event.context;
|
console.warn(A), A.message = "unplayable event: " + A?.message, g(A.message);
|
||||||
onTrigger(time, event, currentTime, 1 /* cps */);
|
|
||||||
} catch (err) {
|
|
||||||
console.warn(err);
|
|
||||||
err.message = 'unplayable event: ' + err?.message;
|
|
||||||
pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onEvent, pushLog],
|
[r, g]
|
||||||
),
|
),
|
||||||
onQuery: useCallback(
|
onQuery: C(
|
||||||
(state) => {
|
(f) => {
|
||||||
try {
|
try {
|
||||||
return pattern?.query(state) || [];
|
return _?.query(f) || [];
|
||||||
} catch (err) {
|
} catch (s) {
|
||||||
console.warn(err);
|
return console.warn(s), s.message = "query error: " + s.message, p(s), [];
|
||||||
err.message = 'query error: ' + err.message;
|
|
||||||
setError(err);
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[pattern],
|
[_]
|
||||||
),
|
),
|
||||||
onSchedule: useCallback((_events, cycle) => logCycle(_events), []),
|
onSchedule: C((f, s) => G(f), []),
|
||||||
ready: !!pattern && !!activeCode,
|
ready: !!_ && !!t
|
||||||
});
|
}), V = pe(({ data: { from: f, type: s } }) => {
|
||||||
|
s === "start" && f !== l && (F.setStarted(!1), u(void 0));
|
||||||
const broadcast = usePostMessage(({ data: { from, type } }) => {
|
}), I = C(
|
||||||
if (type === 'start' && from !== id) {
|
async (f = a) => {
|
||||||
// console.log('message', from, type);
|
if (t && !E) {
|
||||||
cycle.setStarted(false);
|
p(void 0), F.start();
|
||||||
setActiveCode(undefined);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const activateCode = useCallback(
|
|
||||||
async (_code = code) => {
|
|
||||||
if (activeCode && !dirty) {
|
|
||||||
setError(undefined);
|
|
||||||
cycle.start();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
setPending(true);
|
v(!0);
|
||||||
const parsed = await evaluate(_code);
|
const s = await ne(f);
|
||||||
cycle.start();
|
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);
|
||||||
broadcast({ type: 'start', from: id });
|
} catch (s) {
|
||||||
setPattern(() => parsed.pattern);
|
s.message = "evaluation error: " + s.message, console.warn(s), p(s);
|
||||||
if (autolink) {
|
|
||||||
window.location.hash = '#' + encodeURIComponent(btoa(code));
|
|
||||||
}
|
|
||||||
setHash(generateHash(code));
|
|
||||||
setError(undefined);
|
|
||||||
setActiveCode(_code);
|
|
||||||
setPending(false);
|
|
||||||
} catch (err) {
|
|
||||||
err.message = 'evaluation error: ' + err.message;
|
|
||||||
console.warn(err);
|
|
||||||
setError(err);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[activeCode, dirty, code, cycle, autolink, id, broadcast],
|
[t, E, a, F, n, l, V]
|
||||||
);
|
), G = (f, s) => {
|
||||||
// logs events of cycle
|
f.length;
|
||||||
const logCycle = (_events, cycle) => {
|
|
||||||
if (_events.length) ;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const togglePlay = () => {
|
|
||||||
if (!cycle.started) {
|
|
||||||
activateCode();
|
|
||||||
} else {
|
|
||||||
cycle.stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hideHeader,
|
hideHeader: z,
|
||||||
hideConsole,
|
hideConsole: N,
|
||||||
pending,
|
pending: q,
|
||||||
code,
|
code: a,
|
||||||
setCode,
|
setCode: m,
|
||||||
pattern,
|
pattern: _,
|
||||||
error,
|
error: w,
|
||||||
cycle,
|
cycle: F,
|
||||||
setPattern,
|
setPattern: R,
|
||||||
dirty,
|
dirty: E,
|
||||||
log,
|
log: o,
|
||||||
togglePlay,
|
togglePlay: () => {
|
||||||
setActiveCode,
|
F.started ? F.stop() : I();
|
||||||
activateCode,
|
},
|
||||||
activeCode,
|
setActiveCode: u,
|
||||||
pushLog,
|
activateCode: I,
|
||||||
hash,
|
activeCode: t,
|
||||||
|
pushLog: g,
|
||||||
|
hash: O
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
function W(...e) {
|
||||||
/*
|
return e.filter(Boolean).join(" ");
|
||||||
cx.js - <short description TODO>
|
|
||||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/cx.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function cx(...classes) {
|
|
||||||
// : Array<string | undefined>
|
|
||||||
return classes.filter(Boolean).join(' ');
|
|
||||||
}
|
}
|
||||||
|
let H = [], U;
|
||||||
let highlights = []; // actively highlighted events
|
function we({ view: e, pattern: n, active: r }) {
|
||||||
let lastEnd;
|
S(() => {
|
||||||
|
if (e)
|
||||||
function useHighlighting({ view, pattern, active }) {
|
if (n && r) {
|
||||||
useEffect(() => {
|
let l = function() {
|
||||||
if (view) {
|
|
||||||
if (pattern && active) {
|
|
||||||
let frame = requestAnimationFrame(updateHighlights);
|
|
||||||
|
|
||||||
function updateHighlights() {
|
|
||||||
try {
|
try {
|
||||||
const audioTime = Tone.getTransport().seconds;
|
const a = h.getTransport().seconds, t = [Math.max(U || a, a - 1 / 10), a + 1 / 60];
|
||||||
// force min framerate of 10 fps => fixes crash on tab refocus, where lastEnd could be far away
|
U = a + 1 / 60, H = H.filter((o) => o.whole.end > a);
|
||||||
// see https://github.com/tidalcycles/strudel/issues/108
|
const u = n.queryArc(...t).filter((o) => o.hasOnset());
|
||||||
const begin = Math.max(lastEnd || audioTime, audioTime - 1 / 10);
|
H = H.concat(u), e.dispatch({ effects: B.of(H) });
|
||||||
const span = [begin, audioTime + 1 / 60];
|
} catch {
|
||||||
lastEnd = audioTime + 1 / 60;
|
e.dispatch({ effects: B.of([]) });
|
||||||
highlights = highlights.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
|
|
||||||
const haps = pattern.queryArc(...span).filter((hap) => hap.hasOnset());
|
|
||||||
highlights = highlights.concat(haps); // add potential new onsets
|
|
||||||
view.dispatch({ effects: setHighlights.of(highlights) }); // highlight all still active + new active haps
|
|
||||||
} catch (err) {
|
|
||||||
// console.log('error in updateHighlights', err);
|
|
||||||
view.dispatch({ effects: setHighlights.of([]) });
|
|
||||||
}
|
}
|
||||||
frame = requestAnimationFrame(updateHighlights);
|
c = requestAnimationFrame(l);
|
||||||
}
|
}, c = requestAnimationFrame(l);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelAnimationFrame(frame);
|
cancelAnimationFrame(c);
|
||||||
};
|
};
|
||||||
} else {
|
} else
|
||||||
highlights = [];
|
H = [], e.dispatch({ effects: B.of([]) });
|
||||||
view.dispatch({ effects: setHighlights.of([]) });
|
}, [n, r, e]);
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [pattern, active, view]);
|
|
||||||
}
|
}
|
||||||
|
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 = {
|
||||||
var tailwind = '';
|
container: ve,
|
||||||
|
header: Ee,
|
||||||
const container = "_container_3i85k_1";
|
buttons: ke,
|
||||||
const header = "_header_3i85k_5";
|
button: Ce,
|
||||||
const buttons = "_buttons_3i85k_9";
|
buttonDisabled: Me,
|
||||||
const button = "_button_3i85k_9";
|
error: _e,
|
||||||
const buttonDisabled = "_buttonDisabled_3i85k_17";
|
body: Ne
|
||||||
const error = "_error_3i85k_21";
|
|
||||||
const body = "_body_3i85k_25";
|
|
||||||
var styles = {
|
|
||||||
container: container,
|
|
||||||
header: header,
|
|
||||||
buttons: buttons,
|
|
||||||
button: button,
|
|
||||||
buttonDisabled: buttonDisabled,
|
|
||||||
error: error,
|
|
||||||
body: body
|
|
||||||
};
|
};
|
||||||
|
function $({ type: e }) {
|
||||||
function Icon({ type }) {
|
return /* @__PURE__ */ d.createElement("svg", {
|
||||||
return /* @__PURE__ */ React.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__ */ React.createElement("path", {
|
refresh: /* @__PURE__ */ d.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__ */ React.createElement("path", {
|
play: /* @__PURE__ */ d.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__ */ React.createElement("path", {
|
pause: /* @__PURE__ */ d.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"
|
||||||
})
|
})
|
||||||
}[type]);
|
}[e]);
|
||||||
}
|
}
|
||||||
|
function Ve({ tune: e, hideOutsideView: n = !1, init: r, onEvent: c, enableKeyboard: l }) {
|
||||||
function MiniRepl({ tune, hideOutsideView = false, init, onEvent, enableKeyboard }) {
|
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, setCode, pattern, activeCode, activateCode, evaluateOnly, error, cycle, dirty, togglePlay, stop } = useRepl({
|
tune: e,
|
||||||
tune,
|
autolink: !1,
|
||||||
autolink: false,
|
onEvent: c
|
||||||
onEvent
|
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
S(() => {
|
||||||
init && evaluateOnly();
|
r && y();
|
||||||
}, [tune, init]);
|
}, [e, r]);
|
||||||
const [view, setView] = useState();
|
const [M, _] = b(), [R, E] = oe({
|
||||||
const [ref, isVisible] = useInView({
|
|
||||||
threshold: 0.01
|
threshold: 0.01
|
||||||
});
|
}), g = X(), D = L(() => ((E || !n) && (g.current = !0), E || g.current), [E, n]);
|
||||||
const wasVisible = useRef();
|
return we({ view: M, pattern: t, active: p.started && !u?.includes("strudel disable-highlighting") }), Y(() => {
|
||||||
const show = useMemo(() => {
|
if (l) {
|
||||||
if (isVisible || !hideOutsideView) {
|
const z = async (N) => {
|
||||||
wasVisible.current = true;
|
(N.ctrlKey || N.altKey) && (N.code === "Enter" ? (N.preventDefault(), de(M), await o()) : N.code === "Period" && (p.stop(), N.preventDefault()));
|
||||||
}
|
|
||||||
return isVisible || wasVisible.current;
|
|
||||||
}, [isVisible, hideOutsideView]);
|
|
||||||
useHighlighting({ view, pattern, active: cycle.started && !activeCode?.includes("strudel disable-highlighting") });
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
if (enableKeyboard) {
|
|
||||||
const handleKeyPress = async (e) => {
|
|
||||||
if (e.ctrlKey || e.altKey) {
|
|
||||||
if (e.code === "Enter") {
|
|
||||||
e.preventDefault();
|
|
||||||
flash(view);
|
|
||||||
await activateCode();
|
|
||||||
} else if (e.code === "Period") {
|
|
||||||
cycle.stop();
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
window.addEventListener("keydown", handleKeyPress, true);
|
return window.addEventListener("keydown", z, !0), () => window.removeEventListener("keydown", z, !0);
|
||||||
return () => window.removeEventListener("keydown", handleKeyPress, true);
|
|
||||||
}
|
}
|
||||||
}, [enableKeyboard, pattern, code, activateCode, cycle, view]);
|
}, [l, t, a, o, p, M]), /* @__PURE__ */ d.createElement("div", {
|
||||||
return /* @__PURE__ */ React.createElement("div", {
|
className: T.container,
|
||||||
className: styles.container,
|
ref: R
|
||||||
ref
|
}, /* @__PURE__ */ d.createElement("div", {
|
||||||
}, /* @__PURE__ */ React.createElement("div", {
|
className: T.header
|
||||||
className: styles.header
|
}, /* @__PURE__ */ d.createElement("div", {
|
||||||
}, /* @__PURE__ */ React.createElement("div", {
|
className: T.buttons
|
||||||
className: styles.buttons
|
}, /* @__PURE__ */ d.createElement("button", {
|
||||||
}, /* @__PURE__ */ React.createElement("button", {
|
className: W(T.button, p.started ? "sc-animate-pulse" : ""),
|
||||||
className: cx(styles.button, cycle.started ? "sc-animate-pulse" : ""),
|
onClick: () => v()
|
||||||
onClick: () => togglePlay()
|
}, /* @__PURE__ */ d.createElement($, {
|
||||||
}, /* @__PURE__ */ React.createElement(Icon, {
|
type: p.started ? "pause" : "play"
|
||||||
type: cycle.started ? "pause" : "play"
|
})), /* @__PURE__ */ d.createElement("button", {
|
||||||
})), /* @__PURE__ */ React.createElement("button", {
|
className: W(q ? T.button : T.buttonDisabled),
|
||||||
className: cx(dirty ? styles.button : styles.buttonDisabled),
|
onClick: () => o()
|
||||||
onClick: () => activateCode()
|
}, /* @__PURE__ */ d.createElement($, {
|
||||||
}, /* @__PURE__ */ React.createElement(Icon, {
|
|
||||||
type: "refresh"
|
type: "refresh"
|
||||||
}))), error && /* @__PURE__ */ React.createElement("div", {
|
}))), w && /* @__PURE__ */ d.createElement("div", {
|
||||||
className: styles.error
|
className: T.error
|
||||||
}, error.message)), /* @__PURE__ */ React.createElement("div", {
|
}, w.message)), /* @__PURE__ */ d.createElement("div", {
|
||||||
className: styles.body
|
className: T.body
|
||||||
}, show && /* @__PURE__ */ React.createElement(CodeMirror, {
|
}, D && /* @__PURE__ */ d.createElement(me, {
|
||||||
value: code,
|
value: a,
|
||||||
onChange: setCode,
|
onChange: m,
|
||||||
onViewChanged: setView
|
onViewChanged: _
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
function Ie(e) {
|
||||||
/*
|
const { ready: n, connected: r, disconnected: c } = e, [l, a] = b(!0), [m, t] = b(k?.outputs || []);
|
||||||
useWebMidi.js - <short description TODO>
|
return S(() => {
|
||||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/useWebMidi.js>
|
ce().then(() => {
|
||||||
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/>.
|
k.addListener("connected", (o) => {
|
||||||
*/
|
t([...k.outputs]), r?.(k, o);
|
||||||
|
}), k.addListener("disconnected", (o) => {
|
||||||
function useWebMidi(props) {
|
t([...k.outputs]), c?.(k, o);
|
||||||
const { ready, connected, disconnected } = props;
|
}), n?.(k), a(!1);
|
||||||
const [loading, setLoading] = useState(true);
|
}).catch((o) => {
|
||||||
const [outputs, setOutputs] = useState(WebMidi?.outputs || []);
|
if (o) {
|
||||||
useEffect(() => {
|
console.error(o), console.warn("Web Midi could not be enabled..");
|
||||||
enableWebMidi()
|
return;
|
||||||
.then(() => {
|
}
|
||||||
// Reacting when a new device becomes available
|
});
|
||||||
WebMidi.addListener('connected', (e) => {
|
}, [n, r, c, m]), { loading: l, outputs: m, outputByName: (o) => k.getOutputByName(o) };
|
||||||
setOutputs([...WebMidi.outputs]);
|
|
||||||
connected?.(WebMidi, e);
|
|
||||||
});
|
|
||||||
// Reacting when a device becomes unavailable
|
|
||||||
WebMidi.addListener('disconnected', (e) => {
|
|
||||||
setOutputs([...WebMidi.outputs]);
|
|
||||||
disconnected?.(WebMidi, e);
|
|
||||||
});
|
|
||||||
ready?.(WebMidi);
|
|
||||||
setLoading(false);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
//throw new Error("Web Midi could not be enabled...");
|
|
||||||
console.warn('Web Midi could not be enabled..');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [ready, connected, disconnected, outputs]);
|
|
||||||
const outputByName = (name) => WebMidi.getOutputByName(name);
|
|
||||||
return { loading, outputs, outputByName };
|
|
||||||
}
|
}
|
||||||
|
export {
|
||||||
export { CodeMirror, MiniRepl, cx, flash, useCycle, useHighlighting, usePostMessage, useRepl, useWebMidi };
|
me as CodeMirror,
|
||||||
|
Ve as MiniRepl,
|
||||||
|
W as cx,
|
||||||
|
de as flash,
|
||||||
|
ge as useCycle,
|
||||||
|
we as useHighlighting,
|
||||||
|
pe as usePostMessage,
|
||||||
|
ye as useRepl,
|
||||||
|
Ie as useWebMidi
|
||||||
|
};
|
||||||
|
|||||||
2
packages/react/dist/style.css
vendored
2
packages/react/dist/style.css
vendored
@ -1 +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{--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: }.sc-h-5{height:1.25rem}.sc-w-5{width:1.25rem}@keyframes sc-pulse{50%{opacity:.5}}.sc-animate-pulse{animation:sc-pulse 2s cubic-bezier(.4,0,.6,1) infinite}._container_3i85k_1{overflow:hidden;border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(34 34 34 / var(--tw-bg-opacity))}._header_3i85k_5{display:flex;justify-content:space-between;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}._buttons_3i85k_9{display:flex}._button_3i85k_9{display:flex;width:4rem;cursor:pointer;align-items:center;justify-content:center;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}._button_3i85k_9:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}._buttonDisabled_3i85k_17{display:flex;width:4rem;cursor:pointer;cursor:not-allowed;align-items:center;justify-content:center;--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}._error_3i85k_21{padding:.25rem;text-align:right;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}._body_3i85k_25{position:relative;overflow:auto}
|
.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{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--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: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--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: }.sc-h-5{height:1.25rem}.sc-w-5{width:1.25rem}@keyframes sc-pulse{50%{opacity:.5}}.sc-animate-pulse{animation:sc-pulse 2s cubic-bezier(.4,0,.6,1) infinite}._container_3i85k_1{overflow:hidden;border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(34 34 34 / var(--tw-bg-opacity))}._header_3i85k_5{display:flex;justify-content:space-between;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}._buttons_3i85k_9{display:flex}._button_3i85k_9{display:flex;width:4rem;cursor:pointer;align-items:center;justify-content:center;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}._button_3i85k_9:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}._buttonDisabled_3i85k_17{display:flex;width:4rem;cursor:pointer;cursor:not-allowed;align-items:center;justify-content:center;--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}._error_3i85k_21{padding:.25rem;text-align:right;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}._body_3i85k_25{position:relative;overflow:auto}
|
||||||
|
|||||||
@ -37,12 +37,12 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-javascript": "^6.0.2",
|
"@codemirror/lang-javascript": "^6.1.1",
|
||||||
"@strudel.cycles/core": "^0.2.0",
|
"@strudel.cycles/core": "^0.2.0",
|
||||||
"@strudel.cycles/eval": "^0.2.0",
|
"@strudel.cycles/eval": "^0.2.0",
|
||||||
"@strudel.cycles/tone": "^0.2.0",
|
"@strudel.cycles/tone": "^0.2.0",
|
||||||
"@uiw/codemirror-themes": "^4.11.4",
|
"@uiw/codemirror-themes": "^4.12.4",
|
||||||
"@uiw/react-codemirror": "^4.11.4",
|
"@uiw/react-codemirror": "^4.12.4",
|
||||||
"react-hook-inview": "^4.5.0"
|
"react-hook-inview": "^4.5.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@ -52,12 +52,12 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^17.0.2",
|
"@types/react": "^17.0.2",
|
||||||
"@types/react-dom": "^17.0.2",
|
"@types/react-dom": "^17.0.2",
|
||||||
"@vitejs/plugin-react": "^1.3.0",
|
"@vitejs/plugin-react": "^2.2.0",
|
||||||
"autoprefixer": "^10.4.7",
|
"autoprefixer": "^10.4.7",
|
||||||
"postcss": "^8.4.13",
|
"postcss": "^8.4.18",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"tailwindcss": "^3.0.24",
|
"tailwindcss": "^3.0.24",
|
||||||
"vite": "^2.9.9"
|
"vite": "^3.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,10 @@ 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 { evalScope } from '@strudel.cycles/eval';
|
||||||
|
import { controls } from '@strudel.cycles/core';
|
||||||
|
|
||||||
evalScope(
|
evalScope(
|
||||||
|
controls,
|
||||||
import('@strudel.cycles/core'),
|
import('@strudel.cycles/core'),
|
||||||
import('@strudel.cycles/tone'),
|
import('@strudel.cycles/tone'),
|
||||||
import('@strudel.cycles/tonal'),
|
import('@strudel.cycles/tonal'),
|
||||||
|
|||||||
@ -27,6 +27,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel.cycles/core": "^0.2.0",
|
"@strudel.cycles/core": "^0.2.0",
|
||||||
"@tonaljs/tonal": "^4.6.5",
|
"@tonaljs/tonal": "^4.6.5",
|
||||||
"webmidi": "^3.0.15"
|
"webmidi": "^3.0.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,6 @@
|
|||||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strudel.cycles/core": "^0.2.0",
|
"@strudel.cycles/core": "^0.2.0",
|
||||||
"@tonejs/piano": "^0.2.1",
|
|
||||||
"chord-voicings": "^0.0.1",
|
"chord-voicings": "^0.0.1",
|
||||||
"tone": "^14.7.77"
|
"tone": "^14.7.77"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,8 +30,6 @@ const {
|
|||||||
getDestination,
|
getDestination,
|
||||||
Players,
|
Players,
|
||||||
} = Tone;
|
} = Tone;
|
||||||
import * as tonePiano from '@tonejs/piano';
|
|
||||||
const { Piano } = tonePiano;
|
|
||||||
import { getPlayableNoteValue } from '@strudel.cycles/core/util.mjs';
|
import { getPlayableNoteValue } from '@strudel.cycles/core/util.mjs';
|
||||||
|
|
||||||
// "balanced" | "interactive" | "playback";
|
// "balanced" | "interactive" | "playback";
|
||||||
@ -61,10 +59,6 @@ Pattern.prototype.tone = function (instrument) {
|
|||||||
instrument.triggerAttack(note, time);
|
instrument.triggerAttack(note, time);
|
||||||
} else if (instrument instanceof NoiseSynth) {
|
} else if (instrument instanceof NoiseSynth) {
|
||||||
instrument.triggerAttackRelease(hap.duration.valueOf(), time); // noise has no value
|
instrument.triggerAttackRelease(hap.duration.valueOf(), time); // noise has no value
|
||||||
} else if (instrument instanceof Piano) {
|
|
||||||
note = getPlayableNoteValue(hap);
|
|
||||||
instrument.keyDown({ note, time, velocity });
|
|
||||||
instrument.keyUp({ note, time: time + hap.duration.valueOf(), velocity });
|
|
||||||
} else if (instrument instanceof Sampler) {
|
} else if (instrument instanceof Sampler) {
|
||||||
note = getPlayableNoteValue(hap);
|
note = getPlayableNoteValue(hap);
|
||||||
instrument.triggerAttackRelease(note, hap.duration.valueOf(), time, velocity);
|
instrument.triggerAttackRelease(note, hap.duration.valueOf(), time, velocity);
|
||||||
@ -110,11 +104,6 @@ export const players = (options, baseUrl = '') => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
export const synth = (options) => new Synth(options);
|
export const synth = (options) => new Synth(options);
|
||||||
export const piano = async (options = { velocities: 1 }) => {
|
|
||||||
const p = new Piano(options);
|
|
||||||
await p.load();
|
|
||||||
return p;
|
|
||||||
};
|
|
||||||
|
|
||||||
// effect helpers
|
// effect helpers
|
||||||
export const vol = (v) => new Gain(v);
|
export const vol = (v) => new Gain(v);
|
||||||
|
|||||||
865
repl/package-lock.json
generated
865
repl/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,6 @@
|
|||||||
"postcss": "^8.4.13",
|
"postcss": "^8.4.13",
|
||||||
"rollup-plugin-visualizer": "^5.8.1",
|
"rollup-plugin-visualizer": "^5.8.1",
|
||||||
"tailwindcss": "^3.0.24",
|
"tailwindcss": "^3.0.24",
|
||||||
"vite": "^2.9.9"
|
"vite": "^3.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user