import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import logo from './logo.svg'; import * as strudel from '../../strudel.mjs'; import cx from './cx'; import * as Tone from 'tone'; import useCycle from './useCycle'; import type { Hap, Pattern } from './types'; import * as tunes from './tunes'; import * as krill from './parse'; import CodeMirror from './CodeMirror'; const { tetris, tetrisMini, tetrisHaskell } = tunes; const { sequence, pure, reify, slowcat, fastcat, cat, stack, silence } = strudel; // make available to eval const { mini, h } = krill; // for eval (direct import wont work somehow) const parse = (code: string): Pattern => eval(code); const synth = new Tone.PolySynth().toDestination(); synth.set({ oscillator: { type: 'triangle' }, envelope: { release: 0.01, }, }); function App() { const [mode, setMode] = useState('javascript'); const [code, setCode] = useState(tetrisHaskell); const [log, setLog] = useState(''); const logBox = useRef(); const [error, setError] = useState(); const [pattern, setPattern] = useState(); // logs events of cycle const logCycle = (_events: any, cycle: any) => { if (_events.length) { setLog((log) => log + `${log ? '\n\n' : ''}# cycle ${cycle}\n` + _events.map((e: any) => e.show()).join('\n')); } }; // cycle hook to control scheduling const cycle = useCycle({ onEvent: useCallback((time, event) => { // console.log('event', event, time); synth.triggerAttackRelease(event.value, event.duration, time); }, []), onQuery: useCallback( (span) => { try { return pattern?.query(span) || []; } catch (err: any) { setError(err); return []; } }, [pattern] ), onSchedule: useCallback( (_events, cycle) => { // console.log('schedule', _events, cycle); logCycle(_events, cycle); }, [pattern] ), ready: !!pattern, }); // parse pattern when code changes useEffect(() => { try { let _pattern; try { _pattern = h(code); setMode('pegjs'); // haskell mode does not recognize quotes, pegjs looks ok by accident.. // console.log('h _pattern', _pattern); } catch (err) { setMode('javascript'); // code is not haskell like _pattern = parse(code); // console.log('not haskell..', _pattern); } setPattern(_pattern); // cycle.query(cycle.activeCycle()); // reschedule active cycle setError(undefined); } catch (err: any) { console.warn(err); setError(err); } }, [code]); // scroll log box to bottom when log changes useLayoutEffect(() => { logBox.current.scrollTop = logBox.current?.scrollHeight; }, [log]); return (
logo

Strudel REPL

{ setLog((log) => log + `${log ? '\n\n' : ''}✏️ edit\n${code}\n${value}`); setCode(value); }} />
{error &&
{error?.message || 'unknown error'}
} {/*