// Canonical pitch representation: sharps only. // Flats are derived via ENHARMONIC for display purposes only. export const CHROMATIC = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']; export const ENHARMONIC = { 'C#': 'Db', 'D#': 'Eb', 'F#': 'Gb', 'G#': 'Ab', 'A#': 'Bb' }; export const NATURAL = ['C', 'D', 'E', 'F', 'G', 'A', 'B']; // Standard tuning, index 0 = lowest string. // 6-string: strings 6→1 (low E → high e) // 7-string: strings 7→1 (low B → high e) export const OPEN_NOTES = { 6: ['E', 'A', 'D', 'G', 'B', 'E'], 7: ['B', 'E', 'A', 'D', 'G', 'B', 'E'], }; export function getNoteAt(stringIdx, fret, numStrings) { const openIdx = CHROMATIC.indexOf(OPEN_NOTES[numStrings][stringIdx]); return CHROMATIC[(openIdx + fret) % 12]; } // Returns "D#/Eb" for accidentals unless sharpsOnly is set. export function noteDisplay(note, sharpsOnly) { if (sharpsOnly || !ENHARMONIC[note]) return note; return `${note}/${ENHARMONIC[note]}`; } // Returns 9 answer choices including the correct note (all in sharp form). export function getAnswerChoices(correct, naturalOnly) { const pool = naturalOnly ? NATURAL : CHROMATIC; const others = pool.filter(n => n !== correct).sort(() => Math.random() - 0.5).slice(0, 8); return [...others, correct].sort(() => Math.random() - 0.5); }