diff --git a/repl/src/tutorial/tutorial.mdx b/repl/src/tutorial/tutorial.mdx index 29363c87..bf9cb6ed 100644 --- a/repl/src/tutorial/tutorial.mdx +++ b/repl/src/tutorial/tutorial.mdx @@ -9,3 +9,7 @@ hello!!!! blablalba + + + + \ No newline at end of file diff --git a/repl/src/useCycle.ts b/repl/src/useCycle.ts index cd9ea2cb..bb855682 100644 --- a/repl/src/useCycle.ts +++ b/repl/src/useCycle.ts @@ -1,8 +1,9 @@ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import type { ToneEventCallback } from 'tone'; import * as Tone from 'tone'; import { TimeSpan } from '../../strudel.mjs'; import type { Hap } from './types'; +import usePostMessage from './usePostMessage'; export declare interface UseCycleProps { onEvent: ToneEventCallback; @@ -61,7 +62,6 @@ function useCycle(props: UseCycleProps) { }, [onEvent, onSchedule, onQuery, ready]); const start = async () => { - console.log('start'); setStarted(true); await Tone.start(); Tone.Transport.start('+0.1'); @@ -72,7 +72,7 @@ function useCycle(props: UseCycleProps) { Tone.Transport.pause(); }; const toggle = () => (started ? stop() : start()); - return { start, stop, onEvent, started, toggle, query, activeCycle }; + return { start, stop, setStarted, onEvent, started, toggle, query, activeCycle }; } export default useCycle; diff --git a/repl/src/usePostMessage.ts b/repl/src/usePostMessage.ts new file mode 100644 index 00000000..aa28d8a9 --- /dev/null +++ b/repl/src/usePostMessage.ts @@ -0,0 +1,11 @@ +import { useEffect } from 'react'; + +function usePostMessage(listener) { + useEffect(() => { + window.addEventListener('message', listener); + return () => window.removeEventListener('message', listener); + }, [listener]); + return (data) => window.postMessage(data, '*'); +} + +export default usePostMessage; diff --git a/repl/src/useRepl.ts b/repl/src/useRepl.ts index 5e29d4a1..330d4484 100644 --- a/repl/src/useRepl.ts +++ b/repl/src/useRepl.ts @@ -1,11 +1,19 @@ -import { useCallback, useLayoutEffect, useRef, useState } from 'react'; +import { useCallback, useLayoutEffect, useState, useMemo, useEffect } from 'react'; import { isNote } from 'tone'; import { evaluate } from './evaluate'; import { useWebMidi } from './midi'; import type { Pattern } from './types'; import useCycle from './useCycle'; +import usePostMessage from './usePostMessage'; + +let s4 = () => { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); +}; function useRepl({ tune, defaultSynth }) { + const id = useMemo(() => s4(), []); const [code, setCode] = useState(tune); const [activeCode, setActiveCode] = useState(); const [log, setLog] = useState(''); @@ -14,6 +22,7 @@ function useRepl({ tune, defaultSynth }) { const dirty = code !== activeCode; const activateCode = (_code = code) => { !cycle.started && cycle.start(); + broadcast({ type: 'start', from: id }); if (activeCode && !dirty) { setError(undefined); return; @@ -76,6 +85,14 @@ function useRepl({ tune, defaultSynth }) { ready: !!pattern, }); + const broadcast = usePostMessage(({ data: { from, type } }) => { + if (type === 'start' && from !== id) { + // console.log('message', from, type); + cycle.setStarted(false); + setActiveCode(undefined); + } + }); + // set active pattern on ctrl+enter useLayoutEffect(() => { const handleKeyPress = (e: any) => {