diff --git a/src/App.tsx b/src/App.tsx
index 7f3f1b2..e26aa9e 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -101,7 +101,7 @@ function App() {
}
}, [pitchData, targetMidi, matchStartTime, generateNewNote]);
- const targetDetails = getNoteDetails(targetMidi);
+
// Hint text construction
const hintPositions = useMemo(() => {
@@ -135,7 +135,9 @@ function App() {
{settings.showHint && (
-
{targetDetails.scientific}
+
+ {getNoteDetails(targetMidi + 12).scientific} (Written)
+
{hintPositions.map((p, i) => (
diff --git a/src/components/SheetMusic.tsx b/src/components/SheetMusic.tsx
index 6d3e0d2..8dbe26e 100644
--- a/src/components/SheetMusic.tsx
+++ b/src/components/SheetMusic.tsx
@@ -8,6 +8,7 @@ interface SheetMusicProps {
clef?: 'treble' | 'bass';
width?: number;
height?: number;
+ transpose?: number; // Transposition in semitones for visualization (e.g., +12 for guitar)
}
export const SheetMusic: React.FC = ({
@@ -15,7 +16,8 @@ export const SheetMusic: React.FC = ({
playedMidi,
clef = 'treble',
width = 300,
- height = 200
+ height = 200,
+ transpose = 12 // Default to +1 octave (Guitar Notation)
}) => {
const containerRef = useRef(null);
@@ -35,37 +37,45 @@ export const SheetMusic: React.FC = ({
stave.addClef(clef);
stave.setContext(context).draw();
+ // Helper to create keys for VexFlow
+ const getVexFlowKey = (midi: number) => {
+ const visualMidi = midi + transpose;
+ const details = getNoteDetails(visualMidi);
+ return {
+ keys: [`${details.name.toLowerCase()}/${details.octave}`],
+ hasAccidental: details.name.includes("#")
+ };
+ };
+
// Create Target Note
- const targetDetails = getNoteDetails(targetMidi);
- const targetKey = `${targetDetails.name.toLowerCase()}/${targetDetails.octave}`;
+ const targetData = getVexFlowKey(targetMidi);
const targetStaveNote = new StaveNote({
- keys: [targetKey],
+ keys: targetData.keys,
duration: "w",
clef: clef
});
- if (targetDetails.name.includes("#")) {
+ if (targetData.hasAccidental) {
targetStaveNote.addModifier(new Accidental("#"));
}
if (playedMidi) {
- const playedDetails = getNoteDetails(playedMidi);
- const playedKey = `${playedDetails.name.toLowerCase()}/${playedDetails.octave}`;
+ const playedData = getVexFlowKey(playedMidi);
const playedStaveNote = new StaveNote({
- keys: [playedKey],
+ keys: playedData.keys,
duration: "h",
- clef: clef,
+ clef: clef
});
// Target note as half note to match measure
const targetNoteHalf = new StaveNote({
- keys: [targetKey],
+ keys: targetData.keys,
duration: "h",
clef: clef
});
- if (targetDetails.name.includes("#")) targetNoteHalf.addModifier(new Accidental("#"));
+ if (targetData.hasAccidental) targetNoteHalf.addModifier(new Accidental("#"));
if (playedMidi === targetMidi) {
@@ -74,10 +84,11 @@ export const SheetMusic: React.FC = ({
playedStaveNote.setStyle({ fillStyle: "var(--color-error)", strokeStyle: "var(--color-error)" });
}
- if (playedDetails.name.includes("#")) {
+ if (playedData.hasAccidental) {
playedStaveNote.addModifier(new Accidental("#"));
}
+ // Use camelCase properties as fixed previously
const voiceCombined = new Voice({ numBeats: 4, beatValue: 4 });
voiceCombined.addTickables([targetNoteHalf, playedStaveNote]);
@@ -91,7 +102,7 @@ export const SheetMusic: React.FC = ({
voice.draw(context, stave);
}
- }, [targetMidi, playedMidi, clef, width, height]);
+ }, [targetMidi, playedMidi, clef, width, height, transpose]);
return ;
};
diff --git a/src/music/NoteUtils.ts b/src/music/NoteUtils.ts
index 1b5cb18..08d3eb9 100644
--- a/src/music/NoteUtils.ts
+++ b/src/music/NoteUtils.ts
@@ -49,3 +49,8 @@ export function getRandomNote(min: number, max: number, validNotes?: number[]):
}
return Math.floor(Math.random() * (max - min + 1)) + min;
}
+
+// Helper to convert Written note to Sounding note for guitar
+// e.g. Input: "C4" (Written) -> Returns C3 (Sounding) MIDI
+// But usually we work with MIDI.
+// This is just a note for logic: Guitar Sounding = Written - 12.