diff --git a/src/main/services/AudioCaptureService.ts b/src/main/services/AudioCaptureService.ts index bdd7c05..9b6a993 100644 --- a/src/main/services/AudioCaptureService.ts +++ b/src/main/services/AudioCaptureService.ts @@ -227,15 +227,40 @@ class AudioCaptureService extends EventEmitter { } async getDevices(): Promise { - // 현재 Phase에서는 기본 디바이스만 반환. 실제 디바이스 열거는 추후. - logger.debug('Getting audio devices (default only)') - return [ - { - deviceId: 'default', - label: 'Default Microphone', - isDefault: true - } + const devices: AudioDevice[] = [ + { deviceId: 'default', label: '시스템 기본 마이크', isDefault: true }, ] + + try { + // PowerShell로 Windows 오디오 입력 디바이스 열거 + const { execSync } = await import('child_process') + const psCommand = `Get-CimInstance Win32_SoundDevice | Where-Object { $_.StatusInfo -eq 3 } | Select-Object -Property DeviceID, Name | ConvertTo-Json -Compress` + const output = execSync(`powershell -NoProfile -Command "${psCommand}"`, { + encoding: 'utf8', + timeout: 5000, + }).trim() + + if (output) { + const parsed: unknown = JSON.parse(output.startsWith('[') ? output : `[${output}]`) + if (Array.isArray(parsed)) { + for (const dev of parsed) { + const d = dev as { DeviceID?: string; Name?: string } + if (d.DeviceID && d.Name) { + devices.push({ + deviceId: d.DeviceID, + label: d.Name, + isDefault: false, + }) + } + } + } + } + } catch (err) { + logger.warn(`Failed to enumerate audio devices via PowerShell: ${err instanceof Error ? err.message : String(err)}`) + } + + logger.debug(`Found ${devices.length} audio device(s)`) + return devices } getCurrentDevice(): AudioDevice | null { diff --git a/src/renderer/components/SettingsModal.tsx b/src/renderer/components/SettingsModal.tsx index 88e202e..c3d4cd5 100644 --- a/src/renderer/components/SettingsModal.tsx +++ b/src/renderer/components/SettingsModal.tsx @@ -29,7 +29,8 @@ import KeyboardIcon from '@mui/icons-material/Keyboard' import EditIcon from '@mui/icons-material/Edit' import { d3roPalette } from '../theme' import { HotkeyRecordModal } from './HotkeyRecordModal' -import type { ThemeMode, AppConfig, HotkeyBinding } from '@shared/types' +import MicIcon from '@mui/icons-material/Mic' +import type { ThemeMode, AppConfig, HotkeyBinding, AudioDevice } from '@shared/types' interface SettingsModalProps { open: boolean @@ -183,6 +184,10 @@ export function SettingsModal({ open, onClose }: SettingsModalProps): React.Reac const [hotkeyModalOpen, setHotkeyModalOpen] = useState(false) const [hotkeyModalTarget, setHotkeyModalTarget] = useState<'dictation' | 'handsFree'>('dictation') + // 오디오 디바이스 + const [audioDevices, setAudioDevices] = useState([]) + const [selectedDeviceId, setSelectedDeviceId] = useState('default') + // 설정 로드 useEffect(() => { if (!open) return @@ -193,8 +198,10 @@ export function SettingsModal({ open, onClose }: SettingsModalProps): React.Reac window.electronAPI.hotkey.getDictationShortcut(), window.electronAPI.hotkey.getHandsFreeShortcut(), window.electronAPI.hotkey.isEnabled(), + window.electronAPI.audio.getDevices(), + window.electronAPI.audio.getSelectedDevice(), ]) - .then(([configResult, dictResult, hfResult, enabledResult]) => { + .then(([configResult, dictResult, hfResult, enabledResult, devicesResult, selectedResult]) => { if (configResult.success) setConfig(configResult.data) if (dictResult.success && dictResult.data) setDictationBinding(dictResult.data) if (hfResult.success && hfResult.data) setHandsFreeBinding(hfResult.data) @@ -202,6 +209,8 @@ export function SettingsModal({ open, onClose }: SettingsModalProps): React.Reac setHotkeyGlobalEnabled(enabledResult.data) setDictationEnabled(enabledResult.data) } + if (devicesResult.success) setAudioDevices(devicesResult.data) + if (selectedResult.success && selectedResult.data) setSelectedDeviceId(selectedResult.data) }) .finally(() => setLoading(false)) }, [open]) @@ -412,9 +421,34 @@ export function SettingsModal({ open, onClose }: SettingsModalProps): React.Reac {/* ── 오디오 탭 ────────────────────────────── */} - - 현재 시스템 기본 마이크를 사용합니다. - 마이크 선택 및 테스트 기능은 추후 업데이트에서 제공됩니다. + + 마이크 + + + + 입력 장치 + + + + + + + 텍스트 삽입