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