From 07d534b43c98a4200afdc9ace0133e1b97a5b400 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 5 Feb 2022 13:12:05 +0100 Subject: [PATCH] fix build --- .gitignore | 1 + docs/dist/App.js | 85 +++++++++++++++++++++++++++++++++++++ docs/dist/cx.js | 3 ++ docs/dist/index.js | 10 +++++ docs/dist/logo.svg | 33 ++++++++++++++ docs/dist/logo.svg.proxy.js | 1 + docs/dist/useCycle.js | 51 ++++++++++++++++++++++ docs/index.html | 6 +-- repl/public/index.html | 6 +-- repl/snowpack.config.mjs | 1 + 10 files changed, 191 insertions(+), 6 deletions(-) create mode 100644 docs/dist/App.js create mode 100644 docs/dist/cx.js create mode 100644 docs/dist/index.js create mode 100644 docs/dist/logo.svg create mode 100644 docs/dist/logo.svg.proxy.js create mode 100644 docs/dist/useCycle.js diff --git a/.gitignore b/.gitignore index 79533bf5..97241f88 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ dist +!docs/dist dist-* cabal-dev *.o diff --git a/docs/dist/App.js b/docs/dist/App.js new file mode 100644 index 00000000..57b010da --- /dev/null +++ b/docs/dist/App.js @@ -0,0 +1,85 @@ +import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from "../_snowpack/pkg/react.js"; +import logo from "./logo.svg.proxy.js"; +import * as strudel from "../_snowpack/link/strudel.js"; +import cx from "./cx.js"; +import * as Tone from "../_snowpack/pkg/tone.js"; +import useCycle from "./useCycle.js"; +const {Fraction, TimeSpan} = strudel; +const fr = (v) => new Fraction(v); +const ts = (start, end) => new TimeSpan(fr(start), fr(end)); +const parse = (code) => { + const {sequence, stack, pure, slowcat, slow} = strudel; + return eval(code); +}; +const synth = new Tone.Synth().toDestination(); +function App() { + const [code, setCode] = useState("slow(sequence('c3', 'eb3', sequence('g3', 'f3')), 'g3')"); + const [log, setLog] = useState(""); + const logBox = useRef(); + const [error, setError] = useState(); + const [pattern, setPattern] = useState(); + const logCycle = (_events, cycle2) => { + if (_events.length) { + setLog((log2) => log2 + `${log2 ? "\n\n" : ""}# cycle ${cycle2} +` + _events.map((e) => e.show()).join("\n")); + } + }; + const cycle = useCycle({ + onEvent: useCallback((time, event) => { + synth.triggerAttackRelease(event.value, event.duration, time); + }, []), + onQuery: useCallback((span) => pattern?.query(span) || [], [pattern]), + onSchedule: useCallback((_events, cycle2) => { + logCycle(_events, cycle2); + }, [pattern]), + ready: !!pattern + }); + useEffect(() => { + try { + const _pattern = parse(code); + setPattern(_pattern); + setError(void 0); + } catch (err) { + setError(err); + } + }, [code]); + useLayoutEffect(() => { + logBox.current.scrollTop = logBox.current?.scrollHeight; + }, [log]); + return /* @__PURE__ */ React.createElement("div", { + className: "h-[100vh] bg-slate-900 flex-row" + }, /* @__PURE__ */ React.createElement("header", { + className: "px-2 flex items-center space-x-2 border-b border-gray-200 bg-white" + }, /* @__PURE__ */ React.createElement("img", { + src: logo, + className: "Tidal-logo w-16 h-16", + alt: "logo" + }), /* @__PURE__ */ React.createElement("h1", { + className: "text-2xl" + }, "Strudel REPL")), /* @__PURE__ */ React.createElement("section", { + className: "grow p-2 text-gray-100" + }, /* @__PURE__ */ React.createElement("div", { + className: "relative" + }, /* @__PURE__ */ React.createElement("div", { + className: "absolute right-2 bottom-2 text-red-500" + }, error?.message), /* @__PURE__ */ React.createElement("textarea", { + className: cx("w-full h-32 bg-slate-600", error ? "focus:ring-red-500" : "focus:ring-slate-800"), + value: code, + onChange: (e) => { + setLog((log2) => log2 + `${log2 ? "\n\n" : ""}✏️ edit +${code} +${e.target.value}`); + setCode(e.target.value); + } + })), /* @__PURE__ */ React.createElement("textarea", { + className: "w-full h-64 bg-slate-600", + value: log, + readOnly: true, + ref: logBox, + style: {fontFamily: "monospace"} + }), /* @__PURE__ */ React.createElement("button", { + className: "w-full border border-gray-700 p-2 bg-slate-700 hover:bg-slate-500", + onClick: () => cycle.toggle() + }, cycle.started ? "pause" : "play"))); +} +export default App; diff --git a/docs/dist/cx.js b/docs/dist/cx.js new file mode 100644 index 00000000..1677e96e --- /dev/null +++ b/docs/dist/cx.js @@ -0,0 +1,3 @@ +export default function cx(...classes) { + return classes.filter(Boolean).join(" "); +} diff --git a/docs/dist/index.js b/docs/dist/index.js new file mode 100644 index 00000000..828bd19d --- /dev/null +++ b/docs/dist/index.js @@ -0,0 +1,10 @@ +import * as __SNOWPACK_ENV__ from '../_snowpack/env.js'; +import.meta.env = __SNOWPACK_ENV__; + +import React from "../_snowpack/pkg/react.js"; +import ReactDOM from "../_snowpack/pkg/react-dom.js"; +import App from "./App.js"; +ReactDOM.render(/* @__PURE__ */ React.createElement(React.StrictMode, null, /* @__PURE__ */ React.createElement(App, null)), document.getElementById("root")); +if (undefined /* [snowpack] import.meta.hot */ ) { + undefined /* [snowpack] import.meta.hot */ .accept(); +} diff --git a/docs/dist/logo.svg b/docs/dist/logo.svg new file mode 100644 index 00000000..209658c6 --- /dev/null +++ b/docs/dist/logo.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + Layer 1 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/dist/logo.svg.proxy.js b/docs/dist/logo.svg.proxy.js new file mode 100644 index 00000000..73f6fe7f --- /dev/null +++ b/docs/dist/logo.svg.proxy.js @@ -0,0 +1 @@ +export default "/strudel/dist/logo.svg"; \ No newline at end of file diff --git a/docs/dist/useCycle.js b/docs/dist/useCycle.js new file mode 100644 index 00000000..e0f04819 --- /dev/null +++ b/docs/dist/useCycle.js @@ -0,0 +1,51 @@ +import {useEffect, useState} from "../_snowpack/pkg/react.js"; +import * as Tone from "../_snowpack/pkg/tone.js"; +import {TimeSpan} from "../_snowpack/link/strudel.js"; +function useCycle(props) { + const {onEvent, onQuery, onSchedule, ready = true} = props; + const [started, setStarted] = useState(false); + const cycleDuration = 1; + const activeCycle = () => Math.floor(Tone.Transport.seconds / cycleDuration); + const query = (cycle = activeCycle()) => { + const timespan = new TimeSpan(cycle, cycle + 1); + const _events = onQuery?.(timespan) || []; + onSchedule?.(_events, cycle); + schedule(_events, cycle); + }; + const schedule = (events, cycle = activeCycle()) => { + const timespan = new TimeSpan(cycle, cycle + 1); + const cancelFrom = timespan.begin.valueOf(); + Tone.Transport.cancel(cancelFrom); + const queryNextTime = (cycle + 1) * cycleDuration - 0.1; + Tone.Transport.schedule(() => { + query(cycle + 1); + }, queryNextTime); + events?.forEach((event) => { + Tone.Transport.schedule((time) => { + const toneEvent = { + time: event.part.begin.valueOf(), + duration: event.part.end.valueOf() - event.part.begin.valueOf(), + value: event.value + }; + onEvent(time, toneEvent); + }, event.part.begin.valueOf()); + }); + }; + useEffect(() => { + ready && query(); + }, [onEvent, onSchedule, onQuery]); + const start = async () => { + console.log("start"); + setStarted(true); + await Tone.start(); + Tone.Transport.start("+0.1"); + }; + const stop = () => { + console.log("stop"); + setStarted(false); + Tone.Transport.pause(); + }; + const toggle = () => started ? stop() : start(); + return {start, stop, onEvent, started, toggle, schedule, query, activeCycle}; +} +export default useCycle; diff --git a/docs/index.html b/docs/index.html index 0e7db17b..507724ce 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2,8 +2,8 @@ - - + + Strudel REPL @@ -11,6 +11,6 @@
- + diff --git a/repl/public/index.html b/repl/public/index.html index 0e7db17b..9d566483 100644 --- a/repl/public/index.html +++ b/repl/public/index.html @@ -2,8 +2,8 @@ - - + + Strudel REPL @@ -11,6 +11,6 @@
- + diff --git a/repl/snowpack.config.mjs b/repl/snowpack.config.mjs index 82853e55..f33b19f6 100644 --- a/repl/snowpack.config.mjs +++ b/repl/snowpack.config.mjs @@ -35,5 +35,6 @@ export default { buildOptions: { /* ... */ out: '../docs', + baseUrl: '/strudel', }, };