fix: mute audio output during System Audio and Mic capture to prevent echo
Insert a zero-gain GainNode between ScriptProcessorNode and audioContext.destination. The processor stays in the graph (so onaudioprocess fires on all browsers) but zero volume reaches the speakers, eliminating the echo/feedback loop during live capture.
This commit is contained in:
parent
f637ab10a5
commit
80af17a255
|
|
@ -28,6 +28,7 @@ export function useMediaStreamASR({ wsUrl }: UseMediaStreamASRProps): UseMediaSt
|
||||||
const wsRef = useRef<WebSocket | null>(null)
|
const wsRef = useRef<WebSocket | null>(null)
|
||||||
const audioContextRef = useRef<AudioContext | null>(null)
|
const audioContextRef = useRef<AudioContext | null>(null)
|
||||||
const processorRef = useRef<ScriptProcessorNode | null>(null)
|
const processorRef = useRef<ScriptProcessorNode | null>(null)
|
||||||
|
const gainNodeRef = useRef<GainNode | null>(null)
|
||||||
const sourceRef = useRef<MediaStreamAudioSourceNode | null>(null)
|
const sourceRef = useRef<MediaStreamAudioSourceNode | null>(null)
|
||||||
const streamRef = useRef<MediaStream | null>(null)
|
const streamRef = useRef<MediaStream | null>(null)
|
||||||
const isStreamingRef = useRef(false)
|
const isStreamingRef = useRef(false)
|
||||||
|
|
@ -63,8 +64,10 @@ export function useMediaStreamASR({ wsUrl }: UseMediaStreamASRProps): UseMediaSt
|
||||||
}
|
}
|
||||||
|
|
||||||
processorRef.current?.disconnect()
|
processorRef.current?.disconnect()
|
||||||
|
gainNodeRef.current?.disconnect()
|
||||||
sourceRef.current?.disconnect()
|
sourceRef.current?.disconnect()
|
||||||
processorRef.current = null
|
processorRef.current = null
|
||||||
|
gainNodeRef.current = null
|
||||||
sourceRef.current = null
|
sourceRef.current = null
|
||||||
|
|
||||||
if (wsRef.current) {
|
if (wsRef.current) {
|
||||||
|
|
@ -114,18 +117,22 @@ export function useMediaStreamASR({ wsUrl }: UseMediaStreamASRProps): UseMediaSt
|
||||||
const processor = audioContext.createScriptProcessor(4096, 1, 1)
|
const processor = audioContext.createScriptProcessor(4096, 1, 1)
|
||||||
processorRef.current = processor
|
processorRef.current = processor
|
||||||
|
|
||||||
// onaudioprocess — mirrors useVideoASR lines 126-132 exactly
|
// Zero-gain node mutes audio output to prevent echo/feedback. The processor
|
||||||
|
// must remain in the graph (connected to destination) so onaudioprocess fires.
|
||||||
|
const zeroGain = audioContext.createGain()
|
||||||
|
zeroGain.gain.value = 0
|
||||||
|
gainNodeRef.current = zeroGain
|
||||||
|
|
||||||
processor.onaudioprocess = (e) => {
|
processor.onaudioprocess = (e) => {
|
||||||
const float32Data = e.inputBuffer.getChannelData(0)
|
const float32Data = e.inputBuffer.getChannelData(0)
|
||||||
const outputData = e.outputBuffer.getChannelData(0)
|
|
||||||
outputData.set(float32Data)
|
|
||||||
if (!isStreamingRef.current) return
|
if (!isStreamingRef.current) return
|
||||||
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return
|
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return
|
||||||
wsRef.current.send(float32Data.buffer)
|
wsRef.current.send(float32Data.buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
source.connect(processor)
|
source.connect(processor)
|
||||||
processor.connect(audioContext.destination)
|
processor.connect(zeroGain)
|
||||||
|
zeroGain.connect(audioContext.destination)
|
||||||
|
|
||||||
const ws = new WebSocket(wsUrl)
|
const ws = new WebSocket(wsUrl)
|
||||||
wsRef.current = ws
|
wsRef.current = ws
|
||||||
|
|
@ -181,6 +188,7 @@ export function useMediaStreamASR({ wsUrl }: UseMediaStreamASRProps): UseMediaSt
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
processorRef.current?.disconnect()
|
processorRef.current?.disconnect()
|
||||||
|
gainNodeRef.current?.disconnect()
|
||||||
sourceRef.current?.disconnect()
|
sourceRef.current?.disconnect()
|
||||||
wsRef.current?.close()
|
wsRef.current?.close()
|
||||||
audioContextRef.current?.close()
|
audioContextRef.current?.close()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue