diff --git a/web/app.js b/web/app.js index c1ecae7..e8bd0d7 100644 --- a/web/app.js +++ b/web/app.js @@ -36,6 +36,8 @@ import audioProcessorUrl from "./audio-processor.js?worker&url"; ws: null, connected: false, recording: false, + pendingStart: false, + startCancelled: false, audioCtx: null, workletNode: null, stream: null, @@ -185,16 +187,25 @@ import audioProcessorUrl from "./audio-processor.js?worker&url"; state.audioCtx = audioCtx; } async function startRecording() { - if (state.recording) return; + if (state.recording || state.pendingStart) return; + state.pendingStart = true; + state.startCancelled = false; try { await initAudio(); + if (state.startCancelled) { state.pendingStart = false; return; } // Ensure AudioContext is running (may suspend between recordings) if (state.audioCtx.state === "suspended") { await state.audioCtx.resume(); } + if (state.startCancelled) { state.pendingStart = false; return; } const stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, channelCount: 1 }, }); + if (state.startCancelled) { + stream.getTracks().forEach((t) => t.stop()); + state.pendingStart = false; + return; + } state.stream = stream; const source = state.audioCtx.createMediaStreamSource(stream); const worklet = new AudioWorkletNode(state.audioCtx, "audio-processor"); @@ -208,15 +219,23 @@ import audioProcessorUrl from "./audio-processor.js?worker&url"; worklet.port.postMessage({ command: "start" }); // Don't connect worklet to destination (no playback) state.workletNode = worklet; + state.pendingStart = false; state.recording = true; sendJSON({ type: "start" }); micBtn.classList.add("recording"); setPreview("", false); } catch (err) { + state.pendingStart = false; showToast(`麦克风错误: ${err.message}`); } } function stopRecording() { + // Cancel pending async start if still initializing + if (state.pendingStart) { + state.startCancelled = true; + micBtn.classList.remove("recording"); + return; + } if (!state.recording) return; state.recording = false; // Stop worklet