@vapi-ai/web 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api.ts +445 -3
- package/dist/api.d.ts +394 -3
- package/dist/api.js +36 -0
- package/dist/vapi.d.ts +23 -2
- package/dist/vapi.js +111 -92
- package/package.json +1 -1
- package/vapi.ts +141 -102
package/dist/vapi.js
CHANGED
|
@@ -138,6 +138,7 @@ class Vapi extends VapiEventEmitter {
|
|
|
138
138
|
dailyCallObject = {};
|
|
139
139
|
microphoneTrack = null;
|
|
140
140
|
cancelMicrophonePreflight = null;
|
|
141
|
+
clientSideNoiseCancellationEnabled = true;
|
|
141
142
|
async microphoneAcquire() {
|
|
142
143
|
const source = this.dailyCallObject.audioSource;
|
|
143
144
|
if (source && typeof source === 'object') {
|
|
@@ -280,6 +281,7 @@ class Vapi extends VapiEventEmitter {
|
|
|
280
281
|
}
|
|
281
282
|
});
|
|
282
283
|
this.started = true;
|
|
284
|
+
this.clientSideNoiseCancellationEnabled = options?.clientSideNoiseCancellationEnabled ?? true;
|
|
283
285
|
let failureStage = 'microphone-preflight';
|
|
284
286
|
try {
|
|
285
287
|
if (this.call) {
|
|
@@ -653,59 +655,66 @@ class Vapi extends VapiEventEmitter {
|
|
|
653
655
|
});
|
|
654
656
|
}
|
|
655
657
|
});
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
stage: 'audio-processing-setup',
|
|
659
|
-
status: 'started',
|
|
660
|
-
timestamp: new Date().toISOString()
|
|
661
|
-
});
|
|
662
|
-
const audioProcessingStartTime = Date.now();
|
|
663
|
-
try {
|
|
664
|
-
const audioProcessingUpdate = this.call.updateInputSettings({
|
|
665
|
-
audio: {
|
|
666
|
-
processor: {
|
|
667
|
-
type: 'noise-cancellation',
|
|
668
|
-
},
|
|
669
|
-
},
|
|
670
|
-
});
|
|
671
|
-
audioProcessingUpdate.catch((error) => {
|
|
672
|
-
this.emitAudioProcessingError('audio-processing-setup', error);
|
|
673
|
-
});
|
|
674
|
-
// The observer waits for this update rather than starting alongside the
|
|
675
|
-
// remote one above, so that Krisp has already swapped the microphone
|
|
676
|
-
// track. See maybeStartLocalAudioLevelObserver().
|
|
677
|
-
//
|
|
678
|
-
// A separate chain, not another link on the one above: EventEmitter
|
|
679
|
-
// rethrows out of emit('error') when a consumer registered no 'error'
|
|
680
|
-
// listener, and whether the observer starts must not hinge on that.
|
|
681
|
-
audioProcessingUpdate
|
|
682
|
-
.catch(() => { })
|
|
683
|
-
.then(() => this.maybeStartLocalAudioLevelObserver());
|
|
684
|
-
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
658
|
+
if (this.clientSideNoiseCancellationEnabled) {
|
|
659
|
+
// Stage 7: Audio processing setup
|
|
685
660
|
this.emit('call-start-progress', {
|
|
686
661
|
stage: 'audio-processing-setup',
|
|
687
|
-
status: '
|
|
688
|
-
duration: audioProcessingDuration,
|
|
662
|
+
status: 'started',
|
|
689
663
|
timestamp: new Date().toISOString()
|
|
690
664
|
});
|
|
665
|
+
const audioProcessingStartTime = Date.now();
|
|
666
|
+
try {
|
|
667
|
+
const audioProcessingUpdate = this.call.updateInputSettings({
|
|
668
|
+
audio: {
|
|
669
|
+
processor: {
|
|
670
|
+
type: 'noise-cancellation',
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
});
|
|
674
|
+
audioProcessingUpdate.catch((error) => {
|
|
675
|
+
this.emitAudioProcessingError('audio-processing-setup', error);
|
|
676
|
+
});
|
|
677
|
+
// The observer waits for this update rather than starting alongside the
|
|
678
|
+
// remote one above, so that Krisp has already swapped the microphone
|
|
679
|
+
// track. See maybeStartLocalAudioLevelObserver().
|
|
680
|
+
//
|
|
681
|
+
// A separate chain, not another link on the one above: EventEmitter
|
|
682
|
+
// rethrows out of emit('error') when a consumer registered no 'error'
|
|
683
|
+
// listener, and whether the observer starts must not hinge on that.
|
|
684
|
+
audioProcessingUpdate
|
|
685
|
+
.catch(() => { })
|
|
686
|
+
.then(() => this.maybeStartLocalAudioLevelObserver());
|
|
687
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
688
|
+
this.emit('call-start-progress', {
|
|
689
|
+
stage: 'audio-processing-setup',
|
|
690
|
+
status: 'completed',
|
|
691
|
+
duration: audioProcessingDuration,
|
|
692
|
+
timestamp: new Date().toISOString()
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
catch (error) {
|
|
696
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
697
|
+
const serializedError = serializeError(error);
|
|
698
|
+
this.emit('call-start-progress', {
|
|
699
|
+
stage: 'audio-processing-setup',
|
|
700
|
+
status: 'failed',
|
|
701
|
+
duration: audioProcessingDuration,
|
|
702
|
+
timestamp: new Date().toISOString(),
|
|
703
|
+
metadata: { error: serializedError.message }
|
|
704
|
+
});
|
|
705
|
+
this.emit('error', {
|
|
706
|
+
type: 'audio-processing-setup-error',
|
|
707
|
+
stage: 'audio-processing-setup',
|
|
708
|
+
error: serializedError,
|
|
709
|
+
timestamp: new Date().toISOString()
|
|
710
|
+
});
|
|
711
|
+
// Don't throw here, this is non-critical
|
|
712
|
+
}
|
|
691
713
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
this.
|
|
696
|
-
stage: 'audio-processing-setup',
|
|
697
|
-
status: 'failed',
|
|
698
|
-
duration: audioProcessingDuration,
|
|
699
|
-
timestamp: new Date().toISOString(),
|
|
700
|
-
metadata: { error: serializedError.message }
|
|
701
|
-
});
|
|
702
|
-
this.emit('error', {
|
|
703
|
-
type: 'audio-processing-setup-error',
|
|
704
|
-
stage: 'audio-processing-setup',
|
|
705
|
-
error: serializedError,
|
|
706
|
-
timestamp: new Date().toISOString()
|
|
707
|
-
});
|
|
708
|
-
// Don't throw here, this is non-critical
|
|
714
|
+
else {
|
|
715
|
+
// Daily's default processor is 'none', so the microphone track is never
|
|
716
|
+
// swapped and the observer has nothing to wait for.
|
|
717
|
+
void this.maybeStartLocalAudioLevelObserver();
|
|
709
718
|
}
|
|
710
719
|
const totalDuration = Date.now() - startTime;
|
|
711
720
|
this.emit('call-start-success', {
|
|
@@ -1163,10 +1172,10 @@ class Vapi extends VapiEventEmitter {
|
|
|
1163
1172
|
/**
|
|
1164
1173
|
* Reconnects to an active call.
|
|
1165
1174
|
*
|
|
1166
|
-
*
|
|
1167
1175
|
* @param webCall
|
|
1176
|
+
* @param options
|
|
1168
1177
|
*/
|
|
1169
|
-
async reconnect(webCall) {
|
|
1178
|
+
async reconnect(webCall, options) {
|
|
1170
1179
|
const startTime = Date.now();
|
|
1171
1180
|
if (this.started) {
|
|
1172
1181
|
throw new Error('Cannot reconnect while a call is already in progress. Call stop() first.');
|
|
@@ -1174,6 +1183,9 @@ class Vapi extends VapiEventEmitter {
|
|
|
1174
1183
|
if (!webCall.webCallUrl) {
|
|
1175
1184
|
throw new Error('webCallUrl is required for reconnection.');
|
|
1176
1185
|
}
|
|
1186
|
+
if (options?.clientSideNoiseCancellationEnabled !== undefined) {
|
|
1187
|
+
this.clientSideNoiseCancellationEnabled = options.clientSideNoiseCancellationEnabled;
|
|
1188
|
+
}
|
|
1177
1189
|
this.emit('call-start-progress', {
|
|
1178
1190
|
stage: 'reconnect-initialization',
|
|
1179
1191
|
status: 'started',
|
|
@@ -1477,53 +1489,60 @@ class Vapi extends VapiEventEmitter {
|
|
|
1477
1489
|
});
|
|
1478
1490
|
// Don't throw here, this is non-critical
|
|
1479
1491
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
stage: 'audio-processing-setup',
|
|
1483
|
-
status: 'started',
|
|
1484
|
-
timestamp: new Date().toISOString()
|
|
1485
|
-
});
|
|
1486
|
-
const audioProcessingStartTime = Date.now();
|
|
1487
|
-
try {
|
|
1488
|
-
const audioProcessingUpdate = this.call.updateInputSettings({
|
|
1489
|
-
audio: {
|
|
1490
|
-
processor: {
|
|
1491
|
-
type: 'noise-cancellation',
|
|
1492
|
-
},
|
|
1493
|
-
},
|
|
1494
|
-
});
|
|
1495
|
-
audioProcessingUpdate.catch((error) => {
|
|
1496
|
-
this.emitAudioProcessingError('audio-processing-setup', error);
|
|
1497
|
-
});
|
|
1498
|
-
// The observer waits for this update rather than starting alongside the
|
|
1499
|
-
// remote one above, so that Krisp has already swapped the microphone
|
|
1500
|
-
// track. See maybeStartLocalAudioLevelObserver().
|
|
1501
|
-
//
|
|
1502
|
-
// A separate chain, not another link on the one above: EventEmitter
|
|
1503
|
-
// rethrows out of emit('error') when a consumer registered no 'error'
|
|
1504
|
-
// listener, and whether the observer starts must not hinge on that.
|
|
1505
|
-
audioProcessingUpdate
|
|
1506
|
-
.catch(() => { })
|
|
1507
|
-
.then(() => this.maybeStartLocalAudioLevelObserver());
|
|
1508
|
-
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
1492
|
+
if (this.clientSideNoiseCancellationEnabled) {
|
|
1493
|
+
// Stage 6: Audio processing setup
|
|
1509
1494
|
this.emit('call-start-progress', {
|
|
1510
1495
|
stage: 'audio-processing-setup',
|
|
1511
|
-
status: '
|
|
1512
|
-
duration: audioProcessingDuration,
|
|
1496
|
+
status: 'started',
|
|
1513
1497
|
timestamp: new Date().toISOString()
|
|
1514
1498
|
});
|
|
1499
|
+
const audioProcessingStartTime = Date.now();
|
|
1500
|
+
try {
|
|
1501
|
+
const audioProcessingUpdate = this.call.updateInputSettings({
|
|
1502
|
+
audio: {
|
|
1503
|
+
processor: {
|
|
1504
|
+
type: 'noise-cancellation',
|
|
1505
|
+
},
|
|
1506
|
+
},
|
|
1507
|
+
});
|
|
1508
|
+
audioProcessingUpdate.catch((error) => {
|
|
1509
|
+
this.emitAudioProcessingError('audio-processing-setup', error);
|
|
1510
|
+
});
|
|
1511
|
+
// The observer waits for this update rather than starting alongside the
|
|
1512
|
+
// remote one above, so that Krisp has already swapped the microphone
|
|
1513
|
+
// track. See maybeStartLocalAudioLevelObserver().
|
|
1514
|
+
//
|
|
1515
|
+
// A separate chain, not another link on the one above: EventEmitter
|
|
1516
|
+
// rethrows out of emit('error') when a consumer registered no 'error'
|
|
1517
|
+
// listener, and whether the observer starts must not hinge on that.
|
|
1518
|
+
audioProcessingUpdate
|
|
1519
|
+
.catch(() => { })
|
|
1520
|
+
.then(() => this.maybeStartLocalAudioLevelObserver());
|
|
1521
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
1522
|
+
this.emit('call-start-progress', {
|
|
1523
|
+
stage: 'audio-processing-setup',
|
|
1524
|
+
status: 'completed',
|
|
1525
|
+
duration: audioProcessingDuration,
|
|
1526
|
+
timestamp: new Date().toISOString()
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
catch (error) {
|
|
1530
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
1531
|
+
const serializedError = serializeError(error);
|
|
1532
|
+
this.emit('call-start-progress', {
|
|
1533
|
+
stage: 'audio-processing-setup',
|
|
1534
|
+
status: 'failed',
|
|
1535
|
+
duration: audioProcessingDuration,
|
|
1536
|
+
timestamp: new Date().toISOString(),
|
|
1537
|
+
metadata: { error: serializedError.message }
|
|
1538
|
+
});
|
|
1539
|
+
// Don't throw here, this is non-critical
|
|
1540
|
+
}
|
|
1515
1541
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
this.
|
|
1520
|
-
stage: 'audio-processing-setup',
|
|
1521
|
-
status: 'failed',
|
|
1522
|
-
duration: audioProcessingDuration,
|
|
1523
|
-
timestamp: new Date().toISOString(),
|
|
1524
|
-
metadata: { error: serializedError.message }
|
|
1525
|
-
});
|
|
1526
|
-
// Don't throw here, this is non-critical
|
|
1542
|
+
else {
|
|
1543
|
+
// Daily's default processor is 'none', so the microphone track is never
|
|
1544
|
+
// swapped and the observer has nothing to wait for.
|
|
1545
|
+
void this.maybeStartLocalAudioLevelObserver();
|
|
1527
1546
|
}
|
|
1528
1547
|
const totalDuration = Date.now() - startTime;
|
|
1529
1548
|
this.emit('call-start-success', {
|
package/package.json
CHANGED
package/vapi.ts
CHANGED
|
@@ -224,6 +224,15 @@ type VideoRecordingStartFailedMessage = {
|
|
|
224
224
|
type StartCallOptions = {
|
|
225
225
|
/** Acquire the microphone before creating a backend call. Defaults to true. */
|
|
226
226
|
microphonePreflightEnabled?: boolean;
|
|
227
|
+
/**
|
|
228
|
+
* Run Daily's Krisp noise cancellation on the microphone
|
|
229
|
+
* in the browser, before the audio leaves the device. Set to `false` to send
|
|
230
|
+
* the raw microphone audio. Defaults to true.
|
|
231
|
+
*
|
|
232
|
+
* Separate from the assistant's server-side `backgroundSpeechDenoisingPlan`,
|
|
233
|
+
* which this option does not change.
|
|
234
|
+
*/
|
|
235
|
+
clientSideNoiseCancellationEnabled?: boolean;
|
|
227
236
|
/**
|
|
228
237
|
* This determines whether the daily room will be deleted and all participants will be kicked once the user leaves the room.
|
|
229
238
|
* If set to `false`, the room will be kept alive even after the user leaves, allowing clients to reconnect to the same room.
|
|
@@ -254,6 +263,18 @@ type StartCallOptions = {
|
|
|
254
263
|
videoRecordingStartMode?: VideoRecordingStartMode;
|
|
255
264
|
}
|
|
256
265
|
|
|
266
|
+
type ReconnectOptions = {
|
|
267
|
+
/**
|
|
268
|
+
* Run Daily's Krisp noise cancellation on the microphone
|
|
269
|
+
* in the browser. Defaults to the value used by the last `start()` or
|
|
270
|
+
* `reconnect()`, or true if neither set it.
|
|
271
|
+
*
|
|
272
|
+
* Separate from the assistant's server-side `backgroundSpeechDenoisingPlan`,
|
|
273
|
+
* which this option does not change.
|
|
274
|
+
*/
|
|
275
|
+
clientSideNoiseCancellationEnabled?: boolean;
|
|
276
|
+
}
|
|
277
|
+
|
|
257
278
|
type WebCall = {
|
|
258
279
|
/**
|
|
259
280
|
* The Vapi WebCall URL. This is the URL that the call will be joined on.
|
|
@@ -404,6 +425,7 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
404
425
|
private dailyCallObject: DailyFactoryOptions = {};
|
|
405
426
|
private microphoneTrack: MediaStreamTrack | null = null;
|
|
406
427
|
private cancelMicrophonePreflight: (() => void) | null = null;
|
|
428
|
+
private clientSideNoiseCancellationEnabled: boolean = true;
|
|
407
429
|
|
|
408
430
|
private async microphoneAcquire(): Promise<MediaStreamTrack> {
|
|
409
431
|
const source = this.dailyCallObject.audioSource;
|
|
@@ -579,6 +601,7 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
579
601
|
});
|
|
580
602
|
|
|
581
603
|
this.started = true;
|
|
604
|
+
this.clientSideNoiseCancellationEnabled = options?.clientSideNoiseCancellationEnabled ?? true;
|
|
582
605
|
|
|
583
606
|
let failureStage = 'microphone-preflight';
|
|
584
607
|
try {
|
|
@@ -985,63 +1008,69 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
985
1008
|
}
|
|
986
1009
|
});
|
|
987
1010
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1011
|
+
if (this.clientSideNoiseCancellationEnabled) {
|
|
1012
|
+
// Stage 7: Audio processing setup
|
|
1013
|
+
this.emit('call-start-progress', {
|
|
1014
|
+
stage: 'audio-processing-setup',
|
|
1015
|
+
status: 'started',
|
|
1016
|
+
timestamp: new Date().toISOString()
|
|
1017
|
+
});
|
|
994
1018
|
|
|
995
|
-
|
|
1019
|
+
const audioProcessingStartTime = Date.now();
|
|
996
1020
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1021
|
+
try {
|
|
1022
|
+
const audioProcessingUpdate = this.call.updateInputSettings({
|
|
1023
|
+
audio: {
|
|
1024
|
+
processor: {
|
|
1025
|
+
type: 'noise-cancellation',
|
|
1026
|
+
},
|
|
1002
1027
|
},
|
|
1003
|
-
}
|
|
1004
|
-
});
|
|
1028
|
+
});
|
|
1005
1029
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1030
|
+
audioProcessingUpdate.catch((error) => {
|
|
1031
|
+
this.emitAudioProcessingError('audio-processing-setup', error);
|
|
1032
|
+
});
|
|
1009
1033
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1034
|
+
// The observer waits for this update rather than starting alongside the
|
|
1035
|
+
// remote one above, so that Krisp has already swapped the microphone
|
|
1036
|
+
// track. See maybeStartLocalAudioLevelObserver().
|
|
1037
|
+
//
|
|
1038
|
+
// A separate chain, not another link on the one above: EventEmitter
|
|
1039
|
+
// rethrows out of emit('error') when a consumer registered no 'error'
|
|
1040
|
+
// listener, and whether the observer starts must not hinge on that.
|
|
1041
|
+
audioProcessingUpdate
|
|
1042
|
+
.catch(() => {})
|
|
1043
|
+
.then(() => this.maybeStartLocalAudioLevelObserver());
|
|
1044
|
+
|
|
1045
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
1046
|
+
this.emit('call-start-progress', {
|
|
1047
|
+
stage: 'audio-processing-setup',
|
|
1048
|
+
status: 'completed',
|
|
1049
|
+
duration: audioProcessingDuration,
|
|
1050
|
+
timestamp: new Date().toISOString()
|
|
1051
|
+
});
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
1054
|
+
const serializedError = serializeError(error);
|
|
1055
|
+
this.emit('call-start-progress', {
|
|
1056
|
+
stage: 'audio-processing-setup',
|
|
1057
|
+
status: 'failed',
|
|
1058
|
+
duration: audioProcessingDuration,
|
|
1059
|
+
timestamp: new Date().toISOString(),
|
|
1060
|
+
metadata: { error: serializedError.message }
|
|
1061
|
+
});
|
|
1062
|
+
this.emit('error', {
|
|
1063
|
+
type: 'audio-processing-setup-error',
|
|
1064
|
+
stage: 'audio-processing-setup',
|
|
1065
|
+
error: serializedError,
|
|
1066
|
+
timestamp: new Date().toISOString()
|
|
1067
|
+
});
|
|
1068
|
+
// Don't throw here, this is non-critical
|
|
1069
|
+
}
|
|
1070
|
+
} else {
|
|
1071
|
+
// Daily's default processor is 'none', so the microphone track is never
|
|
1072
|
+
// swapped and the observer has nothing to wait for.
|
|
1073
|
+
void this.maybeStartLocalAudioLevelObserver();
|
|
1045
1074
|
}
|
|
1046
1075
|
|
|
1047
1076
|
const totalDuration = Date.now() - startTime;
|
|
@@ -1567,11 +1596,11 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
1567
1596
|
|
|
1568
1597
|
/**
|
|
1569
1598
|
* Reconnects to an active call.
|
|
1570
|
-
*
|
|
1571
|
-
*
|
|
1572
|
-
* @param
|
|
1599
|
+
*
|
|
1600
|
+
* @param webCall
|
|
1601
|
+
* @param options
|
|
1573
1602
|
*/
|
|
1574
|
-
async reconnect(webCall: WebCall): Promise<void> {
|
|
1603
|
+
async reconnect(webCall: WebCall, options?: ReconnectOptions): Promise<void> {
|
|
1575
1604
|
const startTime = Date.now();
|
|
1576
1605
|
|
|
1577
1606
|
if (this.started) {
|
|
@@ -1582,6 +1611,10 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
1582
1611
|
throw new Error('webCallUrl is required for reconnection.');
|
|
1583
1612
|
}
|
|
1584
1613
|
|
|
1614
|
+
if (options?.clientSideNoiseCancellationEnabled !== undefined) {
|
|
1615
|
+
this.clientSideNoiseCancellationEnabled = options.clientSideNoiseCancellationEnabled;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1585
1618
|
this.emit('call-start-progress', {
|
|
1586
1619
|
stage: 'reconnect-initialization',
|
|
1587
1620
|
status: 'started',
|
|
@@ -1912,57 +1945,63 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
1912
1945
|
// Don't throw here, this is non-critical
|
|
1913
1946
|
}
|
|
1914
1947
|
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1948
|
+
if (this.clientSideNoiseCancellationEnabled) {
|
|
1949
|
+
// Stage 6: Audio processing setup
|
|
1950
|
+
this.emit('call-start-progress', {
|
|
1951
|
+
stage: 'audio-processing-setup',
|
|
1952
|
+
status: 'started',
|
|
1953
|
+
timestamp: new Date().toISOString()
|
|
1954
|
+
});
|
|
1921
1955
|
|
|
1922
|
-
|
|
1956
|
+
const audioProcessingStartTime = Date.now();
|
|
1923
1957
|
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1958
|
+
try {
|
|
1959
|
+
const audioProcessingUpdate = this.call.updateInputSettings({
|
|
1960
|
+
audio: {
|
|
1961
|
+
processor: {
|
|
1962
|
+
type: 'noise-cancellation',
|
|
1963
|
+
},
|
|
1929
1964
|
},
|
|
1930
|
-
}
|
|
1931
|
-
});
|
|
1965
|
+
});
|
|
1932
1966
|
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1967
|
+
audioProcessingUpdate.catch((error) => {
|
|
1968
|
+
this.emitAudioProcessingError('audio-processing-setup', error);
|
|
1969
|
+
});
|
|
1936
1970
|
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1971
|
+
// The observer waits for this update rather than starting alongside the
|
|
1972
|
+
// remote one above, so that Krisp has already swapped the microphone
|
|
1973
|
+
// track. See maybeStartLocalAudioLevelObserver().
|
|
1974
|
+
//
|
|
1975
|
+
// A separate chain, not another link on the one above: EventEmitter
|
|
1976
|
+
// rethrows out of emit('error') when a consumer registered no 'error'
|
|
1977
|
+
// listener, and whether the observer starts must not hinge on that.
|
|
1978
|
+
audioProcessingUpdate
|
|
1979
|
+
.catch(() => {})
|
|
1980
|
+
.then(() => this.maybeStartLocalAudioLevelObserver());
|
|
1981
|
+
|
|
1982
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
1983
|
+
this.emit('call-start-progress', {
|
|
1984
|
+
stage: 'audio-processing-setup',
|
|
1985
|
+
status: 'completed',
|
|
1986
|
+
duration: audioProcessingDuration,
|
|
1987
|
+
timestamp: new Date().toISOString()
|
|
1988
|
+
});
|
|
1989
|
+
} catch (error) {
|
|
1990
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
1991
|
+
const serializedError = serializeError(error);
|
|
1992
|
+
this.emit('call-start-progress', {
|
|
1993
|
+
stage: 'audio-processing-setup',
|
|
1994
|
+
status: 'failed',
|
|
1995
|
+
duration: audioProcessingDuration,
|
|
1996
|
+
timestamp: new Date().toISOString(),
|
|
1997
|
+
metadata: { error: serializedError.message }
|
|
1998
|
+
});
|
|
1999
|
+
// Don't throw here, this is non-critical
|
|
2000
|
+
}
|
|
2001
|
+
} else {
|
|
2002
|
+
// Daily's default processor is 'none', so the microphone track is never
|
|
2003
|
+
// swapped and the observer has nothing to wait for.
|
|
2004
|
+
void this.maybeStartLocalAudioLevelObserver();
|
|
1966
2005
|
}
|
|
1967
2006
|
|
|
1968
2007
|
const totalDuration = Date.now() - startTime;
|