Skip to content

Commit 77a8355

Browse files
authored
fix: explicitly close AudioResampler instances too free up resources (#1210)
1 parent e399b9b commit 77a8355

15 files changed

Lines changed: 92 additions & 59 deletions

File tree

.changeset/smart-kids-tickle.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@livekit/agents": patch
3+
"@livekit/agents-plugin-google": patch
4+
"@livekit/agents-plugin-phonic": patch
5+
"@livekit/agents-plugin-silero": patch
6+
---
7+
8+
Explicitly close AudioResampler instances too free up resources

agents/src/inference/interruption/interruption_stream.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ export class InterruptionStreamBase {
414414

415415
async close(): Promise<void> {
416416
if (!this.inputStream.closed) await this.inputStream.close();
417+
this.resampler?.close();
417418
this.model.removeStream(this);
418419
}
419420
}

agents/src/ipc/supervised_proc.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ describe('pidusage on dead process', () => {
6666
const exitPromise = new Promise<void>((r) => child.on('exit', r));
6767

6868
child.kill('SIGKILL');
69+
await exitPromise;
6970

7071
const results = await Promise.all([
7172
getChildMemoryUsageMB(pid),
7273
getChildMemoryUsageMB(pid),
7374
getChildMemoryUsageMB(pid),
7475
]);
7576

76-
await exitPromise;
7777
expect(results.every((r) => r === 0)).toBe(true);
7878
});
7979
});

agents/src/stt/stt.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ export abstract class SpeechStream implements AsyncIterableIterator<SpeechEvent>
445445
if (!this.queue.closed) this.queue.close();
446446
if (!this.output.closed) this.output.close();
447447
if (!this.abortController.signal.aborted) this.abortController.abort();
448+
this.resampler?.close();
448449
this.closed = true;
449450
}
450451

agents/src/tts/fallback_adapter.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,8 @@ class FallbackChunkedStream extends ChunkedStream {
324324
this.adapter.markUnAvailable(i);
325325
continue;
326326
}
327+
const resampler = this.adapter.createResamplerForTTS(i);
328+
327329
try {
328330
this._logger.debug({ tts: tts.label }, 'attempting TTS synthesis');
329331
const connOptions: APIConnectOptions = {
@@ -332,7 +334,6 @@ class FallbackChunkedStream extends ChunkedStream {
332334
};
333335
const stream = tts.synthesize(this.inputText, connOptions, this.abortSignal);
334336
let audioReceived = false;
335-
const resampler = this.adapter.createResamplerForTTS(i);
336337
for await (const audio of stream) {
337338
if (this.abortController.signal.aborted) {
338339
stream.close();
@@ -384,6 +385,8 @@ class FallbackChunkedStream extends ChunkedStream {
384385
} else {
385386
throw error;
386387
}
388+
} finally {
389+
resampler?.close();
387390
}
388391
}
389392
const labels = this.adapter.ttsInstances.map((t) => t.label).join(', ');
@@ -443,6 +446,7 @@ class FallbackSynthesizeStream extends SynthesizeStream {
443446
this.adapter.markUnAvailable(i);
444447
continue;
445448
}
449+
const resampler = this.adapter.createResamplerForTTS(i);
446450

447451
try {
448452
this._logger.debug({ tts: originalTts.label }, 'attempting TTS stream');
@@ -453,7 +457,6 @@ class FallbackSynthesizeStream extends SynthesizeStream {
453457
};
454458

455459
const stream = tts.stream({ connOptions });
456-
const resampler = this.adapter.createResamplerForTTS(i);
457460
let bufferIndex = 0;
458461
let streamOutputCompleted = false;
459462
const forwardBufferToTTS = async () => {
@@ -575,6 +578,8 @@ class FallbackSynthesizeStream extends SynthesizeStream {
575578
} else {
576579
throw error;
577580
}
581+
} finally {
582+
resampler?.close();
578583
}
579584
}
580585
await readInputLLMStream.catch(() => {});

agents/src/utils.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ describe('utils', () => {
147147
return 'completed';
148148
});
149149

150-
await delay(39);
150+
await delay(35);
151151
task.cancel();
152152

153153
expect(arr).toEqual([0, 1, 2]);

agents/src/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ export function resampleStream({
723723
for (const frame of resampler.flush()) {
724724
controller.enqueue(frame);
725725
}
726+
resampler.close();
726727
}
727728
},
728729
});

agents/src/voice/generation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,7 @@ async function forwardAudio(
855855

856856
reader?.releaseLock();
857857
audioOutput.flush();
858+
resampler?.close();
858859
}
859860
}
860861

agents/src/voice/recorder_io/recorder_io.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ export class RecorderIO {
352352
} finally {
353353
inReader.releaseLock();
354354
outReader.releaseLock();
355+
this.inResampler?.close();
356+
this.outResampler?.close();
355357

356358
if (!this.closeFuture.done) {
357359
this.closeFuture.resolve();

plugins/google/src/beta/realtime/realtime_api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,7 @@ export class RealtimeSession extends llm.RealtimeSession {
810810
this.#closed = true;
811811

812812
this.sessionShouldClose.set();
813+
this.inputResampler?.close();
813814

814815
await this.closeActiveSession();
815816

@@ -1660,6 +1661,7 @@ export class RealtimeSession extends llm.RealtimeSession {
16601661
if (this.inputResampler) {
16611662
if (frame.sampleRate !== this.inputResamplerInputRate) {
16621663
// input audio changed to a different sample rate
1664+
this.inputResampler.close();
16631665
this.inputResampler = undefined;
16641666
this.inputResamplerInputRate = undefined;
16651667
}

0 commit comments

Comments
 (0)