strudel/docs/dist/useRepl.js
Felix Roos 4487834591 build
2022-03-21 19:50:47 +01:00

121 lines
3.4 KiB
JavaScript

import {useCallback, useState, useMemo} from "../_snowpack/pkg/react.js";
import {getPlayableNoteValue} from "../_snowpack/link/util.js";
import {evaluate} from "./evaluate.js";
import useCycle from "./useCycle.js";
import usePostMessage from "./usePostMessage.js";
let s4 = () => {
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
};
function useRepl({tune, defaultSynth, autolink = true, onEvent, onDraw}) {
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 = code !== activeCode || error;
const generateHash = () => encodeURIComponent(btoa(code));
const activateCode = async (_code = code) => {
if (activeCode && !dirty) {
setError(void 0);
cycle.start();
return;
}
try {
setPending(true);
const parsed = await evaluate(_code);
cycle.start();
broadcast({type: "start", from: id});
setPattern(() => parsed.pattern);
if (autolink) {
window.location.hash = "#" + encodeURIComponent(btoa(code));
}
setHash(generateHash());
setError(void 0);
setActiveCode(_code);
setPending(false);
} catch (err) {
err.message = "evaluation error: " + err.message;
console.warn(err);
setError(err);
}
};
const pushLog = (message) => setLog((log2) => log2 + `${log2 ? "\n\n" : ""}${message}`);
const logCycle = (_events, cycle2) => {
if (_events.length) {
}
};
onDraw = useMemo(() => {
if (activeCode && !activeCode.includes("strudel disable-highlighting")) {
return onDraw;
}
}, [activeCode]);
const cycle = useCycle({
onDraw,
onEvent: useCallback((time, event) => {
try {
onEvent?.(event);
const {onTrigger, velocity} = event.context;
if (!onTrigger) {
if (defaultSynth) {
const note = getPlayableNoteValue(event);
defaultSynth.triggerAttackRelease(note, event.duration, time, velocity);
} else {
throw new Error("no defaultSynth passed to useRepl.");
}
} else {
onTrigger(time, event);
}
} catch (err) {
console.warn(err);
err.message = "unplayable event: " + err?.message;
pushLog(err.message);
}
}, [onEvent]),
onQuery: useCallback((state) => {
try {
return pattern?.query(state) || [];
} catch (err) {
console.warn(err);
err.message = "query error: " + err.message;
setError(err);
return [];
}
}, [pattern]),
onSchedule: useCallback((_events, cycle2) => logCycle(_events, cycle2), [pattern]),
ready: !!pattern && !!activeCode
});
const broadcast = usePostMessage(({data: {from, type}}) => {
if (type === "start" && from !== id) {
cycle.setStarted(false);
setActiveCode(void 0);
}
});
const togglePlay = () => {
if (!cycle.started) {
activateCode();
} else {
cycle.stop();
}
};
return {
pending,
code,
setCode,
pattern,
error,
cycle,
setPattern,
dirty,
log,
togglePlay,
activateCode,
activeCode,
pushLog,
hash
};
}
export default useRepl;