/* eslint-disable react/prop-types */ import React /*, { useRef, useEffect, useState }*/ from "react"; const NOTE_NAMES = [ "C", "C♯", "D", "D♯", "E", "F", "F♯", "G", "G♯", "A", "A♯", "B", ]; const WHITE_KEYS = [0, 2, 4, 5, 7, 9, 11]; // C2 = 36, C5 = 72 const LOWEST_MIDI = 24; const HIGHEST_MIDI = 84; function getNoteName(midi) { const octave = Math.floor(midi / 12) - 1; const note = NOTE_NAMES[midi % 12]; return `${note}${octave}`; } function isWhiteKey(midi) { return WHITE_KEYS.includes(midi % 12); } export default function PianoKeyboard({ heldNotes }) { const heldMap = {}; heldNotes.forEach((n) => (heldMap[n.midi] = n.voice)); const keys = []; for (let midi = LOWEST_MIDI; midi <= HIGHEST_MIDI; midi++) { const white = isWhiteKey(midi); const held = heldMap[midi] !== undefined; keys.push({ midi, white, held, voice: heldMap[midi], noteName: getNoteName(midi), }); } const whiteKeys = keys.filter((k) => k.white); const blackKeys = keys.filter((k) => !k.white); // For responsive black key positioning const numWhite = whiteKeys.length; // Map midi to white key index for black key positioning const midiToWhiteIndex = {}; let whiteIdx = 0; for (let midi = LOWEST_MIDI; midi <= HIGHEST_MIDI; midi++) { if (isWhiteKey(midi)) { midiToWhiteIndex[midi] = whiteIdx++; } } // For each black key, find its position between white keys function getBlackKeyPercent(midi) { // Black keys are always after a white key except for the first key // For example, C# is between C and D // So, find the previous white key index, then add ~0.65 of a white key width const prevWhite = midi - 1; const idx = midiToWhiteIndex[prevWhite]; if (idx === undefined) return 0; // Offset: (idx + 0.65) / numWhite * 100% return ((idx + 1) / numWhite) * 100; } return (
{/* White keys */}
{whiteKeys.map((k, i) => (
{k.noteName} {k.held && ( {k.voice} )}
))}
{/* Black keys */}
{blackKeys.map((k) => (
{k.held && ( {k.voice} )}
))}
); }