export type Difficulty = string; export interface RhythmSettings { mode: 'bpm' | 'seconds'; bpm: number; seconds: number; active: boolean; autoAdvance: boolean; sound: boolean; volume: number; } export type PhraseMaterial = 'melody' | 'scale' | 'library' | 'import'; export type PhrasePace = 'step' | 'tempo'; export interface PhraseSettings { material: PhraseMaterial; libraryId: string; /** Imported score id (the parsed score lives in component state, session-only). */ keyTonic: string; keyMode: string; // ModeId bars: number; // 1–8 (melodies) meterNumerator: 3 | 4; rhythmLevel: 1 | 2; scaleCoverage: 'one-octave' | 'two-octave' | 'position'; scaleDirection: 'up' | 'down' | 'updown'; /** Phrase-local fret window (guitar/bass) — overrides the note set. */ fretWindowEnabled: boolean; fretMin: number; fretMax: number; /** Practice range for library/import material (1-based, inclusive). */ startBar: number; barCount: number; pace: PhrasePace; bpm: number; clickSound: boolean; repeat: boolean; inputMode: 'mic' | 'virtual'; } export const DEFAULT_PHRASE_SETTINGS: PhraseSettings = { material: 'melody', libraryId: '', keyTonic: 'C', keyMode: 'major', bars: 2, meterNumerator: 4, rhythmLevel: 1, scaleCoverage: 'one-octave', scaleDirection: 'updown', fretWindowEnabled: false, fretMin: 0, fretMax: 4, startBar: 1, barCount: 2, pace: 'step', bpm: 60, clickSound: false, repeat: false, inputMode: 'mic', }; export function getPhraseSettings(s: AppSettings): PhraseSettings { return { ...DEFAULT_PHRASE_SETTINGS, ...(s.phrase ?? {}) }; } export interface AppSettings { phrase?: PhraseSettings; difficulty: Difficulty; showHint: boolean; showFretboard: boolean; showTuningMeter: boolean; tuningId: string; keySignature: string; instrument: string; rhythm: RhythmSettings; zenMode: boolean; gameMode: 'sight_reading' | 'ear_training' | 'phrase'; customMinFret?: number; customMaxFret?: number; autoPlaySightReading?: boolean; autoPlayVolume?: number; /** Length of the played reference note in seconds */ referenceNoteDuration?: number; /** Hold the next reference note until the microphone detects silence, * so a still-ringing instrument doesn't mask it */ waitForQuiet?: boolean; virtualGuitarVolume?: number; virtualGuitarMute?: boolean; micSensitivity?: number; disableAnimation?: boolean; theme?: 'light' | 'dark' | 'auto'; } export const DEFAULT_SETTINGS: AppSettings = { difficulty: 'first_pos', showHint: false, showFretboard: false, showTuningMeter: false, tuningId: 'standard', keySignature: 'C', instrument: 'guitar', rhythm: { mode: 'bpm', bpm: 60, seconds: 5, active: false, autoAdvance: false, sound: true, volume: 0.5 }, zenMode: false, gameMode: 'sight_reading', phrase: DEFAULT_PHRASE_SETTINGS, customMinFret: 0, customMaxFret: 12, autoPlaySightReading: false, autoPlayVolume: 0.5, referenceNoteDuration: 1.5, waitForQuiet: false, virtualGuitarVolume: 0.5, virtualGuitarMute: false, micSensitivity: 0.5, disableAnimation: false, theme: 'auto' };