in src/components/CoconetPlayer.tsx [113:252]
export default function CoconetPlayer() {
const {
state,
setState,
startDigit,
setStartDigit,
instrument,
scale,
bpm,
shouldPlay,
loopPlay,
useCoconet,
} = useContext(CoconetContext);
const inView = useContext(InViewContext);
const [melody, setMelody] = useState<mm.INoteSequence>();
const [nextMelody, setNextMelody] = useState<mm.INoteSequence>();
const [harmony, setHarmony] = useState<mm.INoteSequence>();
const [nextHarmony, setNextHarmony] = useState<mm.INoteSequence>();
const [needsPrefetch, setNeedsPrefetch] = useState(false);
const model = useCoconetModel(state !== CoconetState.Initialized);
const {
start,
pause,
stop,
activeNote,
state: playerState,
atEnd: playerAtEnd,
} = useMagentaPlayer({
load: state !== CoconetState.Initialized,
notes: useCoconet ? harmony : melody,
loop: false,
bpm,
});
// Start loading once visible.
useEffect(() => {
if (state === CoconetState.Initialized && inView)
setState(CoconetState.LoadRequested);
}, [inView, setState, state]);
// Play if ready.
useEffect(() => {
if (shouldPlay && state === CoconetState.Harmonized && melody && harmony) {
start();
} else if (!shouldPlay && playerState === PlayerState.Started) {
pause();
}
}, [harmony, melody, pause, playerState, shouldPlay, start, state]);
// State machine...
useEffect(() => {
switch (state) {
case CoconetState.LoadRequested:
setState(CoconetState.Loading);
stop();
createMelody({
startDigit,
instrument,
scale,
length: chunkLength,
}).then((m) => {
setMelody(m);
setState(CoconetState.Loaded);
});
break;
case CoconetState.Loaded:
setState(CoconetState.HarmonizeRequested);
break;
case CoconetState.HarmonizeRequested:
if (!model || !melody) return;
setState(CoconetState.Harmonizing);
stop();
createHarmony({ model, melody }).then((h) => {
setHarmony(h);
setState(CoconetState.Harmonized);
setNeedsPrefetch(true);
});
break;
}
}, [instrument, melody, model, scale, setState, startDigit, state, stop]);
// Prefetch next.
useEffect(() => {
if (state !== CoconetState.Harmonized || !model) return;
if (needsPrefetch) {
setNeedsPrefetch(false);
createMelody({
startDigit: startDigit + chunkLength,
instrument,
scale,
length: chunkLength,
})
.then((m) => {
setNextMelody(m);
return createHarmony({ model, melody: m });
})
.then((h) => {
setNextHarmony(h);
});
}
}, [instrument, model, needsPrefetch, nextMelody, scale, startDigit, state]);
// Move to the next section.
useEffect(() => {
if (playerAtEnd) {
if (!loopPlay && nextMelody && nextHarmony) {
setStartDigit((d) => (d + chunkLength) % (pi.length - chunkLength + 1));
setMelody(nextMelody);
setNextMelody(undefined);
setHarmony(nextHarmony);
setNextHarmony(undefined);
setNeedsPrefetch(true);
}
}
}, [loopPlay, nextHarmony, nextMelody, playerAtEnd, setStartDigit]);
return (
<Box
height={270}
minWidth={480}
border={1}
borderRadius={2}
padding={1}
bgcolor="#fff"
>
<MagentaVisualizer
notes={useCoconet ? harmony : melody}
height={270}
width={480}
activeNote={activeNote}
noteColor="#90a4ae"
activeNoteColor="#e53935"
/>
</Box>
);
}