@layercode/js-sdk 2.4.0 → 2.6.0

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.
@@ -505,12 +505,37 @@ class WavStreamPlayer {
505
505
  this.sampleRate = sampleRate;
506
506
  this.context = null;
507
507
  this.stream = null;
508
+ this.gainNode = null;
508
509
  this.analyser = null;
509
510
  this.trackSampleOffsets = {};
510
511
  this.interruptedTrackIds = {};
511
512
  this.finishedPlayingCallback = finishedPlayingCallback;
512
513
  this.isPlaying = false;
513
514
  this.amplitudeMonitorRaf = undefined;
515
+ this.muted = false;
516
+ }
517
+
518
+ _ensureGainNode() {
519
+ if (!this.context) {
520
+ return null;
521
+ }
522
+ if (!this.gainNode) {
523
+ this.gainNode = this.context.createGain();
524
+ this._applyGain();
525
+ }
526
+ return this.gainNode;
527
+ }
528
+
529
+ _applyGain() {
530
+ if (!this.gainNode || !this.context) {
531
+ return;
532
+ }
533
+ const target = this.muted ? 0 : 1;
534
+ try {
535
+ this.gainNode.gain.setTargetAtTime(target, this.context.currentTime, 0.01);
536
+ } catch {
537
+ this.gainNode.gain.value = target;
538
+ }
514
539
  }
515
540
 
516
541
  /**
@@ -550,6 +575,7 @@ class WavStreamPlayer {
550
575
  analyser.fftSize = 8192;
551
576
  analyser.smoothingTimeConstant = 0.1;
552
577
  this.analyser = analyser;
578
+ this._ensureGainNode();
553
579
  this.isPlaying = true;
554
580
  return true;
555
581
  }
@@ -619,12 +645,19 @@ class WavStreamPlayer {
619
645
  */
620
646
  _start() {
621
647
  const streamNode = new AudioWorkletNode(this.context, "stream_processor");
622
- streamNode.connect(this.context.destination);
648
+ const gainNode = this._ensureGainNode();
649
+ if (!gainNode) {
650
+ throw new Error("GainNode not initialized");
651
+ }
623
652
  streamNode.port.onmessage = (e) => {
624
653
  const { event } = e.data;
625
654
  if (event === "stop") {
626
655
  streamNode.disconnect();
656
+ gainNode.disconnect();
627
657
  this.stream = null;
658
+ if (this.analyser) {
659
+ this.analyser.disconnect();
660
+ }
628
661
  this.isPlaying = false;
629
662
  this.finishedPlayingCallback();
630
663
  } else if (event === "offset") {
@@ -633,8 +666,15 @@ class WavStreamPlayer {
633
666
  this.trackSampleOffsets[requestId] = { trackId, offset, currentTime };
634
667
  }
635
668
  };
636
- this.analyser.disconnect();
637
- streamNode.connect(this.analyser);
669
+ if (this.analyser) {
670
+ this.analyser.disconnect();
671
+ streamNode.connect(gainNode);
672
+ gainNode.connect(this.analyser);
673
+ this.analyser.connect(this.context.destination);
674
+ } else {
675
+ streamNode.connect(gainNode);
676
+ gainNode.connect(this.context.destination);
677
+ }
638
678
  this.stream = streamNode;
639
679
  this.isPlaying = true;
640
680
  return true;
@@ -764,6 +804,16 @@ class WavStreamPlayer {
764
804
  }
765
805
  }
766
806
 
807
+ mute() {
808
+ this.muted = true;
809
+ this._applyGain();
810
+ }
811
+
812
+ unmute() {
813
+ this.muted = false;
814
+ this._applyGain();
815
+ }
816
+
767
817
  /**
768
818
  * Disconnects the audio context and cleans up resources
769
819
  * @returns {void}
@@ -775,6 +825,11 @@ class WavStreamPlayer {
775
825
  this.stream = null;
776
826
  }
777
827
 
828
+ if (this.gainNode) {
829
+ this.gainNode.disconnect();
830
+ this.gainNode = null;
831
+ }
832
+
778
833
  if (this.analyser) {
779
834
  this.analyser.disconnect();
780
835
  }
@@ -3625,7 +3680,7 @@ class LayercodeClient {
3625
3680
  * @param {Object} options - Configuration options
3626
3681
  */
3627
3682
  constructor(options) {
3628
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
3683
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
3629
3684
  this.deviceId = null;
3630
3685
  this.options = {
3631
3686
  agentId: options.agentId,
@@ -3636,22 +3691,25 @@ class LayercodeClient {
3636
3691
  vadResumeDelay: (_c = options.vadResumeDelay) !== null && _c !== void 0 ? _c : 500,
3637
3692
  audioInput: (_d = options.audioInput) !== null && _d !== void 0 ? _d : true,
3638
3693
  audioInputChanged: (_e = options.audioInputChanged) !== null && _e !== void 0 ? _e : NOOP,
3639
- onConnect: (_f = options.onConnect) !== null && _f !== void 0 ? _f : NOOP,
3640
- onDisconnect: (_g = options.onDisconnect) !== null && _g !== void 0 ? _g : NOOP,
3641
- onError: (_h = options.onError) !== null && _h !== void 0 ? _h : NOOP,
3642
- onDeviceSwitched: (_j = options.onDeviceSwitched) !== null && _j !== void 0 ? _j : NOOP,
3643
- onDevicesChanged: (_k = options.onDevicesChanged) !== null && _k !== void 0 ? _k : NOOP,
3644
- onDataMessage: (_l = options.onDataMessage) !== null && _l !== void 0 ? _l : NOOP,
3645
- onMessage: (_m = options.onMessage) !== null && _m !== void 0 ? _m : NOOP,
3646
- onUserAmplitudeChange: (_o = options.onUserAmplitudeChange) !== null && _o !== void 0 ? _o : NOOP,
3647
- onAgentAmplitudeChange: (_p = options.onAgentAmplitudeChange) !== null && _p !== void 0 ? _p : NOOP,
3648
- onStatusChange: (_q = options.onStatusChange) !== null && _q !== void 0 ? _q : NOOP,
3649
- onUserIsSpeakingChange: (_r = options.onUserIsSpeakingChange) !== null && _r !== void 0 ? _r : NOOP,
3650
- onAgentSpeakingChange: (_s = options.onAgentSpeakingChange) !== null && _s !== void 0 ? _s : NOOP,
3651
- onMuteStateChange: (_t = options.onMuteStateChange) !== null && _t !== void 0 ? _t : NOOP,
3652
- enableAmplitudeMonitoring: (_u = options.enableAmplitudeMonitoring) !== null && _u !== void 0 ? _u : true,
3694
+ audioOutput: (_f = options.audioOutput) !== null && _f !== void 0 ? _f : true,
3695
+ audioOutputChanged: (_g = options.audioOutputChanged) !== null && _g !== void 0 ? _g : NOOP,
3696
+ onConnect: (_h = options.onConnect) !== null && _h !== void 0 ? _h : NOOP,
3697
+ onDisconnect: (_j = options.onDisconnect) !== null && _j !== void 0 ? _j : NOOP,
3698
+ onError: (_k = options.onError) !== null && _k !== void 0 ? _k : NOOP,
3699
+ onDeviceSwitched: (_l = options.onDeviceSwitched) !== null && _l !== void 0 ? _l : NOOP,
3700
+ onDevicesChanged: (_m = options.onDevicesChanged) !== null && _m !== void 0 ? _m : NOOP,
3701
+ onDataMessage: (_o = options.onDataMessage) !== null && _o !== void 0 ? _o : NOOP,
3702
+ onMessage: (_p = options.onMessage) !== null && _p !== void 0 ? _p : NOOP,
3703
+ onUserAmplitudeChange: (_q = options.onUserAmplitudeChange) !== null && _q !== void 0 ? _q : NOOP,
3704
+ onAgentAmplitudeChange: (_r = options.onAgentAmplitudeChange) !== null && _r !== void 0 ? _r : NOOP,
3705
+ onStatusChange: (_s = options.onStatusChange) !== null && _s !== void 0 ? _s : NOOP,
3706
+ onUserIsSpeakingChange: (_t = options.onUserIsSpeakingChange) !== null && _t !== void 0 ? _t : NOOP,
3707
+ onAgentSpeakingChange: (_u = options.onAgentSpeakingChange) !== null && _u !== void 0 ? _u : NOOP,
3708
+ onMuteStateChange: (_v = options.onMuteStateChange) !== null && _v !== void 0 ? _v : NOOP,
3709
+ enableAmplitudeMonitoring: (_w = options.enableAmplitudeMonitoring) !== null && _w !== void 0 ? _w : true,
3653
3710
  };
3654
- this.audioInput = (_v = options.audioInput) !== null && _v !== void 0 ? _v : true;
3711
+ this.audioInput = (_x = options.audioInput) !== null && _x !== void 0 ? _x : true;
3712
+ this.audioOutput = (_y = options.audioOutput) !== null && _y !== void 0 ? _y : true;
3655
3713
  this._emitAudioInput();
3656
3714
  this.AMPLITUDE_MONITORING_SAMPLE_RATE = 2;
3657
3715
  this._websocketUrl = DEFAULT_WS_URL;
@@ -4037,11 +4095,30 @@ class LayercodeClient {
4037
4095
  }
4038
4096
  }
4039
4097
  _stopAmplitudeMonitoring() {
4040
- var _a, _b;
4041
- (_a = this.stopPlayerAmplitude) === null || _a === void 0 ? void 0 : _a.call(this);
4042
- (_b = this.stopRecorderAmplitude) === null || _b === void 0 ? void 0 : _b.call(this);
4043
- this.stopPlayerAmplitude = undefined;
4044
- this.stopRecorderAmplitude = undefined;
4098
+ this._stopPlayerAmplitudeMonitoring();
4099
+ this._stopRecorderAmplitudeMonitoring();
4100
+ }
4101
+ _stopPlayerAmplitudeMonitoring() {
4102
+ var _a;
4103
+ this.agentAudioAmplitude = 0;
4104
+ if (this.options.enableAmplitudeMonitoring && this.options.onAgentAmplitudeChange !== NOOP) {
4105
+ this.options.onAgentAmplitudeChange(0);
4106
+ }
4107
+ if (this.stopPlayerAmplitude) {
4108
+ (_a = this.stopPlayerAmplitude) === null || _a === void 0 ? void 0 : _a.call(this);
4109
+ this.stopPlayerAmplitude = undefined;
4110
+ }
4111
+ }
4112
+ _stopRecorderAmplitudeMonitoring() {
4113
+ var _a;
4114
+ this.userAudioAmplitude = 0;
4115
+ if (this.options.enableAmplitudeMonitoring && this.options.onUserAmplitudeChange !== NOOP) {
4116
+ this.options.onUserAmplitudeChange(0);
4117
+ }
4118
+ if (this.stopRecorderAmplitude) {
4119
+ (_a = this.stopRecorderAmplitude) === null || _a === void 0 ? void 0 : _a.call(this);
4120
+ this.stopRecorderAmplitude = undefined;
4121
+ }
4045
4122
  }
4046
4123
  async audioInputConnect() {
4047
4124
  // Turn mic ON
@@ -4050,10 +4127,9 @@ class LayercodeClient {
4050
4127
  this._setupDeviceChangeListener();
4051
4128
  }
4052
4129
  async audioInputDisconnect() {
4053
- var _a;
4054
4130
  try {
4055
4131
  // stop amplitude monitoring tied to the recorder
4056
- (_a = this.stopRecorderAmplitude) === null || _a === void 0 ? void 0 : _a.call(this);
4132
+ this._stopRecorderAmplitudeMonitoring();
4057
4133
  // Try a graceful stop; end() already stops tracks and closes the AudioContext
4058
4134
  await this.wavRecorder.end();
4059
4135
  this.stopVad();
@@ -4061,7 +4137,7 @@ class LayercodeClient {
4061
4137
  this._teardownDeviceListeners();
4062
4138
  this.recorderStarted = false;
4063
4139
  }
4064
- catch (_b) {
4140
+ catch (_a) {
4065
4141
  // If there wasn't an active session, just release any stray tracks
4066
4142
  const stream = this.wavRecorder.getStream();
4067
4143
  stream === null || stream === void 0 ? void 0 : stream.getTracks().forEach((t) => t.stop());
@@ -4079,10 +4155,25 @@ class LayercodeClient {
4079
4155
  }
4080
4156
  }
4081
4157
  }
4158
+ async setAudioOutput(state) {
4159
+ if (this.audioOutput !== state) {
4160
+ this.audioOutput = state;
4161
+ this._emitAudioOutput();
4162
+ if (state) {
4163
+ this.wavPlayer.unmute();
4164
+ }
4165
+ else {
4166
+ this.wavPlayer.mute();
4167
+ }
4168
+ }
4169
+ }
4082
4170
  /** Emitters for audio flags */
4083
4171
  _emitAudioInput() {
4084
4172
  this.options.audioInputChanged(this.audioInput);
4085
4173
  }
4174
+ _emitAudioOutput() {
4175
+ this.options.audioOutputChanged(this.audioOutput);
4176
+ }
4086
4177
  get audioInputEnabled() {
4087
4178
  return this.audioInput;
4088
4179
  }
@@ -4208,6 +4299,12 @@ class LayercodeClient {
4208
4299
  if (!this.options.enableAmplitudeMonitoring) {
4209
4300
  this.agentAudioAmplitude = 0;
4210
4301
  }
4302
+ if (this.audioOutput) {
4303
+ this.wavPlayer.unmute();
4304
+ }
4305
+ else {
4306
+ this.wavPlayer.mute();
4307
+ }
4211
4308
  }
4212
4309
  async connectToAudioInput() {
4213
4310
  if (!this.audioInput) {
@@ -4281,16 +4378,15 @@ class LayercodeClient {
4281
4378
  * Restarts audio recording after a device switch to ensure audio is captured from the new device
4282
4379
  */
4283
4380
  async _restartAudioRecording() {
4284
- var _a, _b, _c;
4381
+ var _a, _b;
4285
4382
  try {
4286
4383
  console.debug('Restarting audio recording after device switch...');
4287
4384
  // Stop amplitude monitoring tied to the previous recording session before tearing it down
4288
- (_a = this.stopRecorderAmplitude) === null || _a === void 0 ? void 0 : _a.call(this);
4289
- this.stopRecorderAmplitude = undefined;
4385
+ this._stopRecorderAmplitudeMonitoring();
4290
4386
  try {
4291
4387
  await this.wavRecorder.end();
4292
4388
  }
4293
- catch (_d) {
4389
+ catch (_c) {
4294
4390
  // Ignore cleanup errors
4295
4391
  }
4296
4392
  // Start with new device
@@ -4312,7 +4408,7 @@ class LayercodeClient {
4312
4408
  this.recorderStarted = true;
4313
4409
  this._sendReadyIfNeeded();
4314
4410
  }
4315
- const reportedDeviceId = (_b = this.activeDeviceId) !== null && _b !== void 0 ? _b : (this.useSystemDefaultDevice ? 'default' : (_c = this.deviceId) !== null && _c !== void 0 ? _c : 'default');
4411
+ const reportedDeviceId = (_a = this.activeDeviceId) !== null && _a !== void 0 ? _a : (this.useSystemDefaultDevice ? 'default' : (_b = this.deviceId) !== null && _b !== void 0 ? _b : 'default');
4316
4412
  if (reportedDeviceId !== previousReportedDeviceId) {
4317
4413
  this.lastReportedDeviceId = reportedDeviceId;
4318
4414
  if (this.options.onDeviceSwitched) {
@@ -4456,6 +4552,7 @@ class LayercodeClient {
4456
4552
  console.log('Microphone muted');
4457
4553
  this.options.onMuteStateChange(true);
4458
4554
  this.stopVad();
4555
+ this._stopRecorderAmplitudeMonitoring();
4459
4556
  }
4460
4557
  }
4461
4558
  /**
@@ -4467,6 +4564,9 @@ class LayercodeClient {
4467
4564
  console.log('Microphone unmuted');
4468
4565
  this.options.onMuteStateChange(false);
4469
4566
  this._initializeVAD();
4567
+ if (this.stopRecorderAmplitude === undefined) {
4568
+ this._setupAmplitudeMonitoring(this.wavRecorder, this.options.onUserAmplitudeChange, (amp) => (this.userAudioAmplitude = amp));
4569
+ }
4470
4570
  }
4471
4571
  }
4472
4572
  }