export default function CoconetPlayer()

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>
  );
}