diff --git a/src/App.tsx b/src/App.tsx
index 6c80290..459cafd 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -69,6 +69,14 @@ function App() {
const currentTuning = TUNINGS[settings.tuningId];
const currentInstrumentDef = INSTRUMENT_DEFINITIONS[settings.instrument];
+ // Resolve overrides
+ const currentRangeDef = useMemo(() => {
+ return currentInstrumentDef.ranges.find(r => r.id === settings.difficulty);
+ }, [currentInstrumentDef, settings.difficulty]);
+
+ const activeClef = currentRangeDef?.clef ?? currentInstrumentDef.clefMode;
+ const activeTranspose = currentRangeDef?.transpose ?? currentInstrumentDef.transpose;
+
// Generate valid notes based on difficulty
const validNotes = useMemo(() => {
// Find range config
@@ -423,10 +431,10 @@ function App() {
targetMidi={targetMidi}
playedMidi={virtualNote ?? pitchData?.midi}
keySignature={settings.keySignature}
- clef={currentInstrumentDef.clefMode}
- transpose={currentInstrumentDef.transpose}
+ clef={activeClef}
+ transpose={activeTranspose}
width={Math.min(window.innerWidth - 40, 500)}
- height={currentInstrumentDef.clefMode === 'grand' ? 260 : 180}
+ height={activeClef === 'grand' ? 260 : 180}
hideTargetNote={settings.gameMode === 'ear_training' && !revealed}
/>
@@ -458,7 +466,7 @@ function App() {
{settings.showHint && (
- {getNoteDetails(targetMidi + currentInstrumentDef.transpose).scientific}
+ {getNoteDetails(targetMidi + activeTranspose).scientific}
)}
{currentInstrumentDef.showTuning && currentTuning && (
diff --git a/src/music/InstrumentConfigs.ts b/src/music/InstrumentConfigs.ts
index 4ee31c7..2563320 100644
--- a/src/music/InstrumentConfigs.ts
+++ b/src/music/InstrumentConfigs.ts
@@ -14,6 +14,8 @@ export interface NoteSetConfig {
defaultMinFret?: number;
defaultMaxFret?: number;
stringIndex?: number; // 0-based index from lowest string (0) to highest
+ transpose?: number; // Override instrument default
+ clef?: ClefMode; // Override instrument default
}
export interface InstrumentDefinition {
@@ -98,17 +100,27 @@ export const INSTRUMENT_DEFINITIONS: Record
= {
voice: {
id: 'voice',
displayName: 'Voice',
- clefMode: 'treble', // Dynamic? Usually vocal music is specific clef OR treble w/ 8va. Let's stick to Treble/Bass per range?
- // Actually, let's keep simple static clef or make App logic handle it.
- // For now, Voice is usually Treble unless Bass/Baritone.
- // Let's use Treble by default and maybe switch if range is low.
+ clefMode: 'treble', // Default
transpose: 0,
showTuning: false,
ranges: [
{ id: 'soprano', label: 'Soprano (C4-A5)', type: 'static', min: 60, max: 81 },
{ id: 'alto', label: 'Alto (G3-E5)', type: 'static', min: 55, max: 76 },
- { id: 'tenor', label: 'Tenor (C3-A4)', type: 'static', min: 48, max: 69 },
- { id: 'bass_voice', label: 'Bass (E2-E4)', type: 'static', min: 40, max: 64 }
+ {
+ id: 'tenor',
+ label: 'Tenor (C3-A4) [8vb]',
+ type: 'static',
+ min: 48, max: 69,
+ transpose: 12 // Reads Treble, Sounds octave lower.
+ },
+ {
+ id: 'bass_voice',
+ label: 'Bass (E2-E4)',
+ type: 'static',
+ min: 40, max: 64,
+ clef: 'bass', // Override to Bass Clef
+ transpose: 0 // Reads actual pitch on Bass Clef
+ }
]
},
whistle: {