export default function GeniePiano()

in src/components/GeniePiano.tsx [322:442]


export default function GeniePiano(props: Props) {
  const { activeButton, inView, onReady } = props;

  const [piano, setPiano] = useState<Piano>();
  const [floatyNotes, setFloatyNotes] = useState<FloatyNotes>();
  const [activeNote, setActiveNote] = useState<number>();
  const [noteToPaint, setNoteToPaint] = useState<Note>();
  const [octaves, setOctaves] = useState(7);
  const genie = usePianoGenie(inView);
  const {
    playNoteDown,
    playNoteUp,
    ready: playerReady,
  } = useMagentaPlayer({
    load: inView,
  });
  const availableNotes = useMemo(() => getAvailableNotes(octaves), [octaves]);
  const ref = useRef<HTMLDivElement>(null);
  const { width, height } = useResizeDetector({
    targetRef: ref,
    refreshMode: "throttle",
    refreshRate: 5,
  });

  useEffect(() => {
    if (!ref.current) return;
    const floatyNotes = new FloatyNotes(ref.current);
    floatyNotes.startDrawLoop();
    setFloatyNotes(floatyNotes);
    const piano = new Piano(ref.current);
    setPiano(piano);

    return () => {
      setPiano(undefined);
      floatyNotes.detached = true;
      setFloatyNotes(undefined);
    };
  }, []);

  useEffect(() => {
    if (!width || !piano) return;
    const octaves = width > 700 ? 7 : 3;
    setOctaves(octaves);
    piano.config.octaves = octaves;
    const bonusNotes = 4; // starts on an A, ends on a C.
    const totalWhiteNotes =
      CONSTANTS.WHITE_NOTES_PER_OCTAVE * octaves + (bonusNotes - 1);
    piano.resize(octaves, totalWhiteNotes);
    piano.draw();
    floatyNotes?.resize(piano.config.whiteNoteHeight);
  }, [floatyNotes, piano, width]);

  useEffect(() => {
    floatyNotes?.resize(piano?.config.whiteNoteHeight);
  }, [floatyNotes, piano, height]);

  useEffect(() => {
    if (!genie || !piano || !floatyNotes) return;
    let note: number;
    let noteToPaint: Note;
    if (activeButton !== undefined) {
      if (octaves > 6) {
        note = genie.next(activeButton);
      } else {
        note = genie.nextFromKeyList(
          activeButton,
          availableNotes,
          defaultTemperature
        );
      }
      const midi = note + CONSTANTS.LOWEST_PIANO_KEY_MIDI_NOTE;
      playNoteDown({ pitch: midi });
      const rect = piano.highlightNote(note, activeButton);
      if (rect) {
        noteToPaint = floatyNotes.addNote(
          activeButton,
          rect.getAttribute("x"),
          rect.getAttribute("width")
        );
      }
    }
    setActiveNote((prev) => {
      if (prev) {
        const midi = prev + CONSTANTS.LOWEST_PIANO_KEY_MIDI_NOTE;
        playNoteUp({ pitch: midi });
        piano?.clearNote(prev);
      }
      return note;
    });
    setNoteToPaint((prev) => {
      if (prev) {
        prev.on = false;
      }
      return noteToPaint;
    });
  }, [
    activeButton,
    availableNotes,
    floatyNotes,
    genie,
    octaves,
    piano,
    playNoteDown,
    playNoteUp,
  ]);

  useEffect(() => {
    if (onReady && genie && playerReady) onReady();
  }, [genie, onReady, playerReady]);

  return (
    <>
      <div ref={ref} className="genie-piano">
        <div className="background"></div>
        <canvas></canvas>
        <svg></svg>
      </div>
      <GenieButtons active={activeButton}></GenieButtons>
    </>
  );
}