"use client"; export const DEFAULT_AUDIO_INPUT_DEVICE_ID = "default"; export interface VoiceOverInputDevice { deviceId: string; label: string; isDefault: boolean; } export function buildVoiceOverAudioConstraints({ deviceId, }: { deviceId?: string | null; }): MediaStreamConstraints { const audio: MediaTrackConstraints = { echoCancellation: true, noiseSuppression: true, autoGainControl: true, }; if (deviceId && deviceId !== DEFAULT_AUDIO_INPUT_DEVICE_ID) { audio.deviceId = { exact: deviceId }; } return { audio }; } export function normalizeSelectedDeviceId({ deviceId, devices, }: { deviceId: string | null; devices: VoiceOverInputDevice[]; }): string | null { if (!deviceId) { return null; } if (deviceId === DEFAULT_AUDIO_INPUT_DEVICE_ID) { return deviceId; } return devices.some((device) => device.deviceId === deviceId) ? deviceId : null; } export async function listVoiceOverInputDevices({ requestPermission = false, }: { requestPermission?: boolean; } = {}): Promise { const mediaDevices = navigator.mediaDevices; if (!mediaDevices?.enumerateDevices) { throw new Error("Microphone selection is not supported in this browser"); } let permissionStream: MediaStream | null = null; if (requestPermission) { permissionStream = await mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true, }, }); } try { const devices = await mediaDevices.enumerateDevices(); let inputIndex = 1; return devices .filter((device) => device.kind === "audioinput") .map((device) => { const isDefault = device.deviceId === DEFAULT_AUDIO_INPUT_DEVICE_ID; const fallbackLabel = isDefault ? "System default microphone" : `Microphone ${inputIndex++}`; return { deviceId: device.deviceId, label: device.label || fallbackLabel, isDefault, }; }); } finally { if (permissionStream) { for (const track of permissionStream.getTracks()) { track.stop(); } } } }