fix: feed VAD the raw mic track captured before setProcessor
After setProcessor resolves, track.mediaStreamTrack returns the processed (noise-gated) track. The VAD was seeing gated silence, closing immediately, and deadlocking with both gates closed. Capture the raw MediaStreamTrack before calling setProcessor and pass that to SileroVADGate instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -440,6 +440,7 @@ export class Publisher {
|
|||||||
let transformer: NoiseGateTransformer | null = null;
|
let transformer: NoiseGateTransformer | null = null;
|
||||||
let audioCtx: AudioContext | null = null;
|
let audioCtx: AudioContext | null = null;
|
||||||
let vadGate: SileroVADGate | null = null;
|
let vadGate: SileroVADGate | null = null;
|
||||||
|
let rawMicTrack: MediaStreamTrack | null = null;
|
||||||
|
|
||||||
const currentParams = (): NoiseGateParams => ({
|
const currentParams = (): NoiseGateParams => ({
|
||||||
threshold: noiseGateThreshold.getValue(),
|
threshold: noiseGateThreshold.getValue(),
|
||||||
@@ -460,14 +461,8 @@ export class Publisher {
|
|||||||
transformer?.setVADOpen(true);
|
transformer?.setVADOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const startVAD = (track: LocalAudioTrack, ctx: AudioContext): void => {
|
const startVAD = (rawTrack: MediaStreamTrack, ctx: AudioContext): void => {
|
||||||
stopVAD();
|
stopVAD();
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const rawTrack: MediaStreamTrack | undefined = (track as any).mediaStreamTrack;
|
|
||||||
if (!rawTrack) {
|
|
||||||
this.logger.warn("[VAD] no underlying MediaStreamTrack — skipping VAD");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const stream = new MediaStream([rawTrack]);
|
const stream = new MediaStream([rawTrack]);
|
||||||
vadGate = new SileroVADGate(stream, ctx, {
|
vadGate = new SileroVADGate(stream, ctx, {
|
||||||
positiveThreshold: vadPositiveThreshold.getValue(),
|
positiveThreshold: vadPositiveThreshold.getValue(),
|
||||||
@@ -488,6 +483,9 @@ export class Publisher {
|
|||||||
if (enabled && !audioTrack.getProcessor()) {
|
if (enabled && !audioTrack.getProcessor()) {
|
||||||
const params = currentParams();
|
const params = currentParams();
|
||||||
this.logger.info("[NoiseGate] attaching processor, params:", params);
|
this.logger.info("[NoiseGate] attaching processor, params:", params);
|
||||||
|
// Capture the raw mic track BEFORE setProcessor replaces it
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
rawMicTrack = (audioTrack as any).mediaStreamTrack ?? null;
|
||||||
transformer = new NoiseGateTransformer(params);
|
transformer = new NoiseGateTransformer(params);
|
||||||
audioCtx = new AudioContext();
|
audioCtx = new AudioContext();
|
||||||
this.logger.info("[NoiseGate] AudioContext state before resume:", audioCtx.state);
|
this.logger.info("[NoiseGate] AudioContext state before resume:", audioCtx.state);
|
||||||
@@ -500,7 +498,7 @@ export class Publisher {
|
|||||||
.setProcessor(transformer as any);
|
.setProcessor(transformer as any);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.logger.info("[NoiseGate] setProcessor resolved");
|
this.logger.info("[NoiseGate] setProcessor resolved");
|
||||||
if (vadEnabled.getValue() && audioCtx) startVAD(audioTrack, audioCtx);
|
if (vadEnabled.getValue() && audioCtx && rawMicTrack) startVAD(rawMicTrack, audioCtx);
|
||||||
}).catch((e: unknown) => {
|
}).catch((e: unknown) => {
|
||||||
this.logger.error("[NoiseGate] setProcessor failed", e);
|
this.logger.error("[NoiseGate] setProcessor failed", e);
|
||||||
});
|
});
|
||||||
@@ -511,6 +509,7 @@ export class Publisher {
|
|||||||
void audioCtx?.close();
|
void audioCtx?.close();
|
||||||
audioCtx = null;
|
audioCtx = null;
|
||||||
transformer = null;
|
transformer = null;
|
||||||
|
rawMicTrack = null;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(audioTrack as any).setAudioContext(undefined);
|
(audioTrack as any).setAudioContext(undefined);
|
||||||
} else {
|
} else {
|
||||||
@@ -522,9 +521,9 @@ export class Publisher {
|
|||||||
combineLatest([audioTrack$, vadEnabled.value$])
|
combineLatest([audioTrack$, vadEnabled.value$])
|
||||||
.pipe(scope.bind())
|
.pipe(scope.bind())
|
||||||
.subscribe(([audioTrack, enabled]) => {
|
.subscribe(([audioTrack, enabled]) => {
|
||||||
if (!audioTrack || !audioCtx) return;
|
if (!audioCtx || !rawMicTrack) return;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
startVAD(audioTrack, audioCtx);
|
startVAD(rawMicTrack, audioCtx);
|
||||||
} else {
|
} else {
|
||||||
stopVAD();
|
stopVAD();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user