Skip to content

Commit e511665

Browse files
authored
Merge pull request #88 from anam-org/alpha
feat: disable input audio option
2 parents 6d2eef3 + b298e0f commit e511665

File tree

5 files changed

+83
-42
lines changed

5 files changed

+83
-42
lines changed

src/AnamClient.ts

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ export default class AnamClient {
4545
options,
4646
);
4747
if (configError) {
48-
throw new Error(configError);
48+
throw new ClientError(
49+
configError,
50+
ErrorCode.CLIENT_ERROR_CODE_CONFIGURATION_ERROR,
51+
400,
52+
);
4953
}
5054

5155
this.personaConfig = personaConfig;
@@ -114,6 +118,9 @@ export default class AnamClient {
114118

115119
// Validate voice detection configuration
116120
if (options?.voiceDetection) {
121+
if (options.disableInputAudio) {
122+
return 'Voice detection is disabled because input audio is disabled. Please set disableInputAudio to false to enable voice detection.';
123+
}
117124
// End of speech sensitivity must be a number between 0 and 1
118125
if (options.voiceDetection.endOfSpeechSensitivity !== undefined) {
119126
if (typeof options.voiceDetection.endOfSpeechSensitivity !== 'number') {
@@ -183,8 +190,11 @@ export default class AnamClient {
183190
iceServers,
184191
inputAudio: {
185192
inputAudioState: this.inputAudioState,
186-
userProvidedMediaStream: userProvidedAudioStream,
193+
userProvidedMediaStream: this.clientOptions?.disableInputAudio
194+
? undefined
195+
: userProvidedAudioStream,
187196
audioDeviceId: this.clientOptions?.audioDeviceId,
197+
disableInputAudio: this.clientOptions?.disableInputAudio,
188198
},
189199
},
190200
this.publicEventEmitter,
@@ -203,9 +213,9 @@ export default class AnamClient {
203213
return sessionId;
204214
}
205215

206-
private async startSessionIfNeeded(userProvidedMediaStream?: MediaStream) {
216+
private async startSessionIfNeeded(userProvidedAudioStream?: MediaStream) {
207217
if (!this.sessionId || !this.streamingClient) {
208-
await this.startSession(userProvidedMediaStream);
218+
await this.startSession(userProvidedAudioStream);
209219

210220
if (!this.sessionId || !this.streamingClient) {
211221
throw new ClientError(
@@ -223,6 +233,11 @@ export default class AnamClient {
223233
public async stream(
224234
userProvidedAudioStream?: MediaStream,
225235
): Promise<MediaStream[]> {
236+
if (this.clientOptions?.disableInputAudio && userProvidedAudioStream) {
237+
console.warn(
238+
'AnamClient:Input audio is disabled. User provided audio stream will be ignored.',
239+
);
240+
}
226241
await this.startSessionIfNeeded(userProvidedAudioStream);
227242
if (this._isStreaming) {
228243
throw new Error('Already streaming');
@@ -261,10 +276,15 @@ export default class AnamClient {
261276
public async streamToVideoAndAudioElements(
262277
videoElementId: string,
263278
audioElementId: string,
264-
userProvidedMediaStream?: MediaStream,
279+
userProvidedAudioStream?: MediaStream,
265280
): Promise<void> {
281+
if (this.clientOptions?.disableInputAudio && userProvidedAudioStream) {
282+
console.warn(
283+
'AnamClient:Input audio is disabled. User provided audio stream will be ignored.',
284+
);
285+
}
266286
try {
267-
await this.startSessionIfNeeded(userProvidedMediaStream);
287+
await this.startSessionIfNeeded(userProvidedAudioStream);
268288
} catch (error) {
269289
if (error instanceof ClientError) {
270290
throw error;
@@ -340,14 +360,24 @@ export default class AnamClient {
340360
}
341361

342362
public getInputAudioState(): InputAudioState {
363+
if (this.clientOptions?.disableInputAudio) {
364+
console.warn(
365+
'AnamClient: Audio state will not be used because input audio is disabled.',
366+
);
367+
}
343368
// if streaming client is available, make sure our state is up to date
344369
if (this.streamingClient) {
345370
this.inputAudioState = this.streamingClient.getInputAudioState();
346371
}
347372
return this.inputAudioState;
348373
}
349374
public muteInputAudio(): InputAudioState {
350-
if (this.streamingClient) {
375+
if (this.clientOptions?.disableInputAudio) {
376+
console.warn(
377+
'AnamClient: Input audio is disabled. Muting input audio will have no effect.',
378+
);
379+
}
380+
if (this.streamingClient && !this.clientOptions?.disableInputAudio) {
351381
this.inputAudioState = this.streamingClient.muteInputAudio();
352382
} else {
353383
this.inputAudioState = {
@@ -359,7 +389,12 @@ export default class AnamClient {
359389
}
360390

361391
public unmuteInputAudio(): InputAudioState {
362-
if (this.streamingClient) {
392+
if (this.clientOptions?.disableInputAudio) {
393+
console.warn(
394+
'AnamClient: Input audio is disabled. Unmuting input audio will have no effect.',
395+
);
396+
}
397+
if (this.streamingClient && !this.clientOptions?.disableInputAudio) {
363398
this.inputAudioState = this.streamingClient.unmuteInputAudio();
364399
} else {
365400
this.inputAudioState = {

src/lib/ClientError.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export enum ErrorCode {
77
CLIENT_ERROR_CODE_SERVICE_BUSY = 'CLIENT_ERROR_CODE_SERVICE_BUSY',
88
CLIENT_ERROR_CODE_NO_PLAN_FOUND = 'CLIENT_ERROR_CODE_NO_PLAN_FOUND',
99
CLIENT_ERROR_CODE_UNKNOWN_ERROR = 'CLIENT_ERROR_CODE_UNKNOWN_ERROR',
10+
CLIENT_ERROR_CODE_CONFIGURATION_ERROR = 'CLIENT_ERROR_CODE_CONFIGURATION_ERROR',
1011
}
1112

1213
// TODO: Move to CoreApiRestClient if we have a pattern for not exposing this

src/modules/StreamingClient.ts

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export class StreamingClient {
3838
private audioStream: MediaStream | null = null;
3939
private inputAudioState: InputAudioState = { isMuted: false };
4040
private audioDeviceId: string | undefined;
41+
private disableInputAudio: boolean;
4142

4243
constructor(
4344
sessionId: string,
@@ -53,6 +54,7 @@ export class StreamingClient {
5354
if (options.inputAudio.userProvidedMediaStream) {
5455
this.inputAudioStream = options.inputAudio.userProvidedMediaStream;
5556
}
57+
this.disableInputAudio = options.inputAudio.disableInputAudio === true;
5658
// register event handlers
5759
this.internalEventEmitter.addListener(
5860
InternalEvent.WEB_SOCKET_OPEN,
@@ -413,42 +415,45 @@ export class StreamingClient {
413415
* Audio
414416
*
415417
* If the user hasn't provided an audio stream, capture the audio stream from the user's microphone and send it to the peer connection
418+
* If input audio is disabled we don't send any audio to the peer connection
416419
*/
417-
if (this.inputAudioStream) {
418-
// verify the user provided stream has audio tracks
419-
if (!this.inputAudioStream.getAudioTracks().length) {
420-
throw new Error(
421-
'StreamingClient - setupDataChannels: user provided stream does not have audio tracks',
422-
);
423-
}
424-
} else {
425-
const audioConstraints: MediaTrackConstraints = {
426-
echoCancellation: true,
427-
};
428-
429-
// If an audio device ID is provided in the options, use it
430-
if (this.audioDeviceId) {
431-
audioConstraints.deviceId = {
432-
exact: this.audioDeviceId,
420+
if (!this.disableInputAudio) {
421+
if (this.inputAudioStream) {
422+
// verify the user provided stream has audio tracks
423+
if (!this.inputAudioStream.getAudioTracks().length) {
424+
throw new Error(
425+
'StreamingClient - setupDataChannels: user provided stream does not have audio tracks',
426+
);
427+
}
428+
} else {
429+
const audioConstraints: MediaTrackConstraints = {
430+
echoCancellation: true,
433431
};
434-
}
435432

436-
this.inputAudioStream = await navigator.mediaDevices.getUserMedia({
437-
audio: audioConstraints,
438-
});
439-
}
433+
// If an audio device ID is provided in the options, use it
434+
if (this.audioDeviceId) {
435+
audioConstraints.deviceId = {
436+
exact: this.audioDeviceId,
437+
};
438+
}
440439

441-
// mute the audio tracks if the user has muted the microphone
442-
if (this.inputAudioState.isMuted) {
443-
this.muteAllAudioTracks();
440+
this.inputAudioStream = await navigator.mediaDevices.getUserMedia({
441+
audio: audioConstraints,
442+
});
443+
}
444+
445+
// mute the audio tracks if the user has muted the microphone
446+
if (this.inputAudioState.isMuted) {
447+
this.muteAllAudioTracks();
448+
}
449+
const audioTrack = this.inputAudioStream.getAudioTracks()[0];
450+
this.peerConnection.addTrack(audioTrack, this.inputAudioStream);
451+
// pass the stream to the callback if it exists
452+
this.publicEventEmitter.emit(
453+
AnamEvent.INPUT_AUDIO_STREAM_STARTED,
454+
this.inputAudioStream,
455+
);
444456
}
445-
const audioTrack = this.inputAudioStream.getAudioTracks()[0];
446-
this.peerConnection.addTrack(audioTrack, this.inputAudioStream);
447-
// pass the stream to the callback if it exists
448-
this.publicEventEmitter.emit(
449-
AnamEvent.INPUT_AUDIO_STREAM_STARTED,
450-
this.inputAudioStream,
451-
);
452457

453458
/**
454459
* Text
@@ -462,9 +467,7 @@ export class StreamingClient {
462467
dataChannel.onopen = () => {
463468
this.dataChannel = dataChannel ?? null;
464469
};
465-
dataChannel.onclose = () => {
466-
// TODO: should we set the data channel to null here?
467-
};
470+
dataChannel.onclose = () => {};
468471
// pass text message to the message history client
469472
dataChannel.onmessage = (event) => {
470473
const messageEvent = JSON.parse(event.data) as WebRtcTextMessageEvent;

src/types/AnamPublicClientOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export interface AnamPublicClientOptions {
55
api?: CoreApiRestClientOptions;
66
voiceDetection?: VoiceDetectionOptions;
77
audioDeviceId?: string;
8+
disableInputAudio?: boolean;
89
}

src/types/streaming/InputAudioOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export interface InputAudioOptions {
44
inputAudioState: InputAudioState;
55
userProvidedMediaStream?: MediaStream;
66
audioDeviceId?: string;
7+
disableInputAudio?: boolean;
78
}

0 commit comments

Comments
 (0)