Skip to content

Commit 44a0471

Browse files
authored
Add support for noiseCancellation frameProcessors (#966)
1 parent 989f16a commit 44a0471

File tree

7 files changed

+430
-46
lines changed

7 files changed

+430
-46
lines changed

.changeset/large-cars-pull.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@livekit/agents": patch
3+
---
4+
5+
Add support for noiseCancellation frameProcessors

agents/src/voice/room_io/_input.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-FileCopyrightText: 2025 LiveKit, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
4-
import type { AudioFrame } from '@livekit/rtc-node';
4+
import { type AudioFrame, FrameProcessor } from '@livekit/rtc-node';
55
import {
66
AudioStream,
77
type NoiseCancellationOptions,
@@ -22,6 +22,7 @@ export class ParticipantAudioInputStream extends AudioInput {
2222
private sampleRate: number;
2323
private numChannels: number;
2424
private noiseCancellation?: NoiseCancellationOptions;
25+
private frameProcessor?: FrameProcessor<AudioFrame>;
2526
private publication: RemoteTrackPublication | null = null;
2627
private participantIdentity: string | null = null;
2728
private logger = log();
@@ -34,16 +35,21 @@ export class ParticipantAudioInputStream extends AudioInput {
3435
room: Room;
3536
sampleRate: number;
3637
numChannels: number;
37-
noiseCancellation?: NoiseCancellationOptions;
38+
noiseCancellation?: NoiseCancellationOptions | FrameProcessor<AudioFrame>;
3839
}) {
3940
super();
4041
this.room = room;
4142
this.sampleRate = sampleRate;
4243
this.numChannels = numChannels;
43-
this.noiseCancellation = noiseCancellation;
44+
if (noiseCancellation instanceof FrameProcessor) {
45+
this.frameProcessor = noiseCancellation;
46+
} else {
47+
this.noiseCancellation = noiseCancellation;
48+
}
4449

4550
this.room.on(RoomEvent.TrackSubscribed, this.onTrackSubscribed);
4651
this.room.on(RoomEvent.TrackUnpublished, this.onTrackUnpublished);
52+
this.room.on(RoomEvent.TokenRefreshed, this.onTokenRefreshed);
4753
}
4854

4955
setParticipant(participant: RemoteParticipant | string | null) {
@@ -116,6 +122,9 @@ export class ParticipantAudioInputStream extends AudioInput {
116122
if (this.deferredStream.isSourceSet) {
117123
this.deferredStream.detachSource();
118124
}
125+
126+
this.frameProcessor?.close();
127+
119128
this.publication = null;
120129
}
121130

@@ -140,21 +149,40 @@ export class ParticipantAudioInputStream extends AudioInput {
140149
outputRate: this.sampleRate,
141150
}),
142151
);
152+
this.frameProcessor?.onStreamInfoUpdated({
153+
participantIdentity: participant.identity,
154+
roomName: this.room.name!,
155+
publicationSid: publication.sid!,
156+
});
157+
this.frameProcessor?.onCredentialsUpdated({
158+
token: this.room.token!,
159+
url: this.room.serverUrl!,
160+
});
143161
return true;
144162
};
145163

164+
private onTokenRefreshed = () => {
165+
if (this.room.token && this.room.serverUrl) {
166+
this.frameProcessor?.onCredentialsUpdated({
167+
token: this.room.token,
168+
url: this.room.serverUrl,
169+
});
170+
}
171+
};
172+
146173
private createStream(track: RemoteTrack): ReadableStream<AudioFrame> {
147174
return new AudioStream(track, {
148175
sampleRate: this.sampleRate,
149176
numChannels: this.numChannels,
150-
noiseCancellation: this.noiseCancellation,
177+
noiseCancellation: this.frameProcessor || this.noiseCancellation,
151178
// TODO(AJS-269): resolve compatibility issue with node-sdk to remove the forced type casting
152179
}) as unknown as ReadableStream<AudioFrame>;
153180
}
154181

155182
async close() {
156183
this.room.off(RoomEvent.TrackSubscribed, this.onTrackSubscribed);
157184
this.room.off(RoomEvent.TrackUnpublished, this.onTrackUnpublished);
185+
this.room.off(RoomEvent.TokenRefreshed, this.onTokenRefreshed);
158186
this.closeStream();
159187
// Ignore errors - stream may be locked by RecorderIO or already cancelled
160188
await this.deferredStream.stream.cancel().catch(() => {});

agents/src/voice/room_io/room_io.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
import {
5+
type AudioFrame,
56
ConnectionState,
67
DisconnectReason,
8+
type FrameProcessor,
79
type NoiseCancellationOptions,
810
type Participant,
911
ParticipantKind,
@@ -75,7 +77,7 @@ export interface RoomInputOptions {
7577
Can be overridden by the `participant` argument of RoomIO constructor or `set_participant`.
7678
*/
7779
participantIdentity?: string;
78-
noiseCancellation?: NoiseCancellationOptions;
80+
noiseCancellation?: NoiseCancellationOptions | FrameProcessor<AudioFrame>;
7981
textInputCallback?: TextInputCallback;
8082
/** Participant kinds accepted for auto subscription. If not provided,
8183
accept `DEFAULT_PARTICIPANT_KINDS`

examples/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"@livekit/agents-plugin-silero": "workspace:*",
4141
"@livekit/agents-plugin-xai": "workspace:*",
4242
"@livekit/noise-cancellation-node": "^0.1.9",
43+
"@livekit/plugins-ai-coustics": "0.1.7",
4344
"@livekit/rtc-node": "catalog:",
4445
"@opentelemetry/api": "^1.9.0",
4546
"@opentelemetry/api-logs": "^0.54.0",

examples/src/basic_agent.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
} from '@livekit/agents';
1414
import * as livekit from '@livekit/agents-plugin-livekit';
1515
import * as silero from '@livekit/agents-plugin-silero';
16-
import { BackgroundVoiceCancellation } from '@livekit/noise-cancellation-node';
16+
// import { BackgroundVoiceCancellation } from '@livekit/noise-cancellation-node';
17+
import * as aic from '@livekit/plugins-ai-coustics';
1718
import { fileURLToPath } from 'node:url';
1819
import { z } from 'zod';
1920

@@ -82,7 +83,9 @@ export default defineAgent({
8283
agent,
8384
room: ctx.room,
8485
inputOptions: {
85-
noiseCancellation: BackgroundVoiceCancellation(),
86+
noiseCancellation: aic.audioEnhancement(),
87+
// or for krisp use
88+
// noiseCancellation: BackgroundVoiceCancellation(),
8689
},
8790
});
8891

0 commit comments

Comments
 (0)