import React, { useState } from 'react'; import { X, Volume2, VolumeX, ChevronDown, ChevronUp, Mic, MicOff, Sun, Moon, Monitor } from 'lucide-react'; import type { AppSettings } from '../types/SettingsTypes'; import type { MicrophoneDebugInfo } from '../hooks/usePitchDetector'; interface SettingsModalProps { isOpen: boolean; onClose: () => void; settings: AppSettings; onUpdateSettings: (s: AppSettings) => void; audioLevel?: number; debugInfo?: MicrophoneDebugInfo | null; isListening?: boolean; onMicToggle?: () => void; } // Level meter component const LevelMeter: React.FC<{ level: number; isActive: boolean }> = ({ level, isActive }) => { // Convert RMS to dB for display, then normalize to 0-100% // RMS of 0.001 = -60dB, RMS of 1.0 = 0dB const db = level > 0 ? 20 * Math.log10(level) : -100; // Map -60dB to 0dB => 0% to 100% const percentage = Math.max(0, Math.min(100, ((db + 60) / 60) * 100)); return (
80 ? '#ef4444' : percentage > 50 ? '#f59e0b' : '#22c55e', borderRadius: '6px', transition: 'width 0.05s ease-out' }} />
{isActive ? `${Math.round(db)} dB` : '— dB'}
); }; export const SettingsModal: React.FC = ({ isOpen, onClose, settings, onUpdateSettings, audioLevel = 0, debugInfo, isListening = false, onMicToggle }) => { const [showDebugInfo, setShowDebugInfo] = useState(false); if (!isOpen) return null; return (
e.stopPropagation()}>

Settings

{/* Theme Settings */}
{[ { id: 'light', icon: Sun, label: 'Light' }, { id: 'dark', icon: Moon, label: 'Dark' }, { id: 'auto', icon: Monitor, label: 'Auto' } ].map(mode => ( ))}

{/* Animation Settings */}

Removes the "Good!" overlay to allow faster playing.


{settings.virtualGuitarMute ? : } onUpdateSettings({ ...settings, virtualGuitarVolume: parseFloat(e.target.value) })} style={{ flex: 1 }} disabled={settings.virtualGuitarMute} />

{/* Auto Play Settings */}
onUpdateSettings({ ...settings, autoPlayVolume: parseFloat(e.target.value) })} style={{ flex: 1 }} />

{settings.gameMode === 'ear_training' ? "Automatically plays the note audio (Required for Ear Training)." : "Automatically plays the note audio when a new note appears."}


{/* Microphone Toggle */}

{isListening ? 'Microphone is active and listening for notes.' : 'Enable microphone to detect your playing.'}

{/* Mic Sensitivity */}
Low (-20dB) {Math.round(-20 - ((settings.micSensitivity ?? 0.5) * 40))} dB High (-60dB)
onUpdateSettings({ ...settings, micSensitivity: parseFloat(e.target.value) })} style={{ width: '100%' }} />

Adjust if notes are not detected (increase/right) or if background noise triggers notes (decrease/left).

{/* Microphone Level Meter */}
{!isListening && (

Enable microphone to see audio levels

)}
{/* Debug Info Toggle */} {/* Debug Info Panel */} {showDebugInfo && (
Status: {debugInfo?.isCapturing ? '✓ Capturing' : debugInfo?.audioContextState === 'suspended' ? '⚠ Suspended' : isListening ? '✗ Not Capturing' : '○ Inactive'} AudioContext: {debugInfo?.audioContextState || 'N/A'} Sample Rate: {debugInfo?.sampleRate ? `${debugInfo.sampleRate} Hz` : 'N/A'} Input Device: {debugInfo?.inputDeviceLabel || 'N/A'} Permission: {debugInfo?.permissionState || 'unknown'} RMS Level: {debugInfo?.currentRmsLevel !== undefined ? `${debugInfo.currentRmsLevel.toFixed(4)} (${Math.round(debugInfo.currentRmsDb)} dB)` : 'N/A'} Browser: {typeof navigator !== 'undefined' ? navigator.userAgent.slice(0, 60) + '...' : 'N/A'}
{debugInfo?.audioContextState === 'suspended' && (
⚠ AudioContext is suspended. Try tapping the mic button again or interacting with the page.
)} {isListening && !debugInfo?.isCapturing && debugInfo?.audioContextState === 'running' && (
✗ AudioContext is running but no audio is being captured. Check if another app is using the microphone.
)}
)}
); };