From af7d5529e7e6a019811a26dd745dfd7cf639c985 Mon Sep 17 00:00:00 2001 From: crosswk Date: Wed, 29 Jul 2026 20:34:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=BA=A6=E5=85=8B=E9=A3=8E?= =?UTF-8?q?=E5=88=97=E8=A1=A8=EF=BC=9A=E6=9E=9A=E4=B8=BE=E5=89=8D=E5=85=88?= =?UTF-8?q?=E7=94=B3=E8=AF=B7=E6=9D=83=E9=99=90=EF=BC=8C=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=B3=BB=E7=BB=9F=E5=85=A8=E9=83=A8=E9=BA=A6?= =?UTF-8?q?=E5=85=8B=E9=A3=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/services/audio.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/client/src/services/audio.ts b/client/src/services/audio.ts index 111e659..6a95e35 100644 --- a/client/src/services/audio.ts +++ b/client/src/services/audio.ts @@ -44,7 +44,7 @@ if ((import.meta as unknown as Record).hot) { mediaStream = null } if (audioCtx && audioCtx.state !== 'closed') { - audioCtx.close().catch(() => {}) + audioCtx.close().catch(() => { }) } audioCtx = null onAudioData = null @@ -132,8 +132,24 @@ registerProcessor('pcm-processor', PCMProcessor); ` export async function listMicrophones(): Promise { - const devices = await navigator.mediaDevices.enumerateDevices() - return devices.filter((d) => d.kind === 'audioinput') + const audioInputs = (list: MediaDeviceInfo[]) => list.filter((d) => d.kind === 'audioinput') + + let devices = audioInputs(await navigator.mediaDevices.enumerateDevices()) + + // 未授予麦克风权限时,enumerateDevices() 只会返回一个无名字的通用设备。 + // 先申请一次权限(用完立刻关闭),再重新枚举,才能拿到系统里所有麦克风及其名称。 + const needPermission = devices.length === 0 || devices.every((d) => !d.label) + if (needPermission) { + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }) + stream.getTracks().forEach((t) => t.stop()) + devices = audioInputs(await navigator.mediaDevices.enumerateDevices()) + } catch { + // 权限被拒或设备不存在:保留已有结果(可能只有通用项) + } + } + + return devices } function createAudioContext() { @@ -178,7 +194,7 @@ async function teardownCapture() { } if (audioCtx && audioCtx.state !== 'closed') { - await audioCtx.close().catch(() => {}) + await audioCtx.close().catch(() => { }) } audioCtx = null actualSampleRate = TARGET_SAMPLE_RATE