ttp-agent-sdk 2.48.18 → 2.48.20
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/dist/agent-widget.dev.js +643 -148
- package/dist/agent-widget.esm.js +1 -1
- package/dist/agent-widget.js +1 -1
- package/package.json +2 -1
- package/dist/examples/pet-store.html +0 -494
- package/dist/examples/seabreeze-hotel.html +0 -403
- package/examples/pet-store.html +0 -494
- package/examples/seabreeze-hotel.html +0 -403
package/dist/agent-widget.dev.js
CHANGED
|
@@ -12129,7 +12129,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
|
|
|
12129
12129
|
|
|
12130
12130
|
// SDK build time for debugging
|
|
12131
12131
|
if (true) {
|
|
12132
|
-
helloMessage.lastBuildTime = "2026-08-
|
|
12132
|
+
helloMessage.lastBuildTime = "2026-08-27T16:45:39.666Z";
|
|
12133
12133
|
}
|
|
12134
12134
|
try {
|
|
12135
12135
|
this.ws.send(JSON.stringify(helloMessage));
|
|
@@ -21439,8 +21439,8 @@ var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
|
|
|
21439
21439
|
|
|
21440
21440
|
|
|
21441
21441
|
// Version - injected at build time from package.json via webpack DefinePlugin
|
|
21442
|
-
var VERSION = "2.48.
|
|
21443
|
-
var BUILD_TIME = "2026-08-
|
|
21442
|
+
var VERSION = "2.48.20";
|
|
21443
|
+
var BUILD_TIME = "2026-08-27T16:45:39.666Z";
|
|
21444
21444
|
console.log("%c TTP Agent SDK v".concat(VERSION, " (").concat(BUILD_TIME, ") "), 'background: #4f46e5; color: white; font-size: 12px; font-weight: bold; padding: 2px 6px; border-radius: 4px;');
|
|
21445
21445
|
|
|
21446
21446
|
// Named exports
|
|
@@ -25616,6 +25616,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
25616
25616
|
/* harmony import */ var _codecs_PCMUCodec_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./codecs/PCMUCodec.js */ "./src/v2/codecs/PCMUCodec.js");
|
|
25617
25617
|
/* harmony import */ var _codecs_PCMACodec_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./codecs/PCMACodec.js */ "./src/v2/codecs/PCMACodec.js");
|
|
25618
25618
|
/* harmony import */ var _hopPlatform_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./hopPlatform.js */ "./src/v2/hopPlatform.js");
|
|
25619
|
+
/* harmony import */ var _loopbackAec_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./loopbackAec.js */ "./src/v2/loopbackAec.js");
|
|
25619
25620
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
25620
25621
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
25621
25622
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -25648,6 +25649,7 @@ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf
|
|
|
25648
25649
|
|
|
25649
25650
|
|
|
25650
25651
|
|
|
25652
|
+
|
|
25651
25653
|
// iOS FIX: Shared AudioContext for playback that persists across AudioPlayer instances.
|
|
25652
25654
|
// iOS WebKit doesn't release audio hardware synchronously when AudioContext.close() is called,
|
|
25653
25655
|
// so creating a new AudioContext immediately after closing the old one can fail silently.
|
|
@@ -25749,6 +25751,18 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25749
25751
|
_this._htmlAudioPlayOk = false;
|
|
25750
25752
|
_this._htmlAudioPlayWatchdog = null;
|
|
25751
25753
|
_this._loggedHopResample = false;
|
|
25754
|
+
// Loopback AEC route (opt-in): the same MediaStream, but pushed through a
|
|
25755
|
+
// pair of local RTCPeerConnections first, because Chrome's echo canceller
|
|
25756
|
+
// only cancels peer-connection audio — a hidden <audio> is invisible to it.
|
|
25757
|
+
// See loopbackAec.js. Off unless asked for; the hop above is unchanged.
|
|
25758
|
+
_this._loopbackDest = null;
|
|
25759
|
+
_this._loopbackEl = null;
|
|
25760
|
+
_this._loopbackClose = null;
|
|
25761
|
+
_this._loopbackReady = false;
|
|
25762
|
+
_this._loopbackPending = false;
|
|
25763
|
+
_this._loopbackFailed = false;
|
|
25764
|
+
_this._loopbackPlayOk = false;
|
|
25765
|
+
_this._loopbackWanted = undefined; // memoized by wantsLoopbackAec()
|
|
25752
25766
|
// Greeting (and other bursts) fire many playChunk()s without await.
|
|
25753
25767
|
// Serialize context creation so they cannot open two AudioContexts
|
|
25754
25768
|
// at different rates in the middle of a sentence.
|
|
@@ -27022,6 +27036,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27022
27036
|
this.gainNode = null;
|
|
27023
27037
|
}
|
|
27024
27038
|
this._teardownHtmlAudioRoute();
|
|
27039
|
+
this._teardownLoopbackRoute();
|
|
27025
27040
|
if (this.audioContext === _sharedPlayerContext) {
|
|
27026
27041
|
_sharedPlayerContext = null;
|
|
27027
27042
|
_sharedPlayerSampleRate = null;
|
|
@@ -27221,7 +27236,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27221
27236
|
}, {
|
|
27222
27237
|
key: "_htmlHopNeedsHardwareRate",
|
|
27223
27238
|
value: function _htmlHopNeedsHardwareRate() {
|
|
27224
|
-
|
|
27239
|
+
// The loopback ends in the same live-MediaStream <audio> the hop does, so
|
|
27240
|
+
// it inherits the same device-rate requirement: a 24 kHz context makes
|
|
27241
|
+
// Chrome clock that stream wrong and play speech ~2x.
|
|
27242
|
+
return this.wantsHtmlAudioPlayback() || this.wantsLoopbackAec();
|
|
27225
27243
|
}
|
|
27226
27244
|
}, {
|
|
27227
27245
|
key: "_isHardwarePlaybackRate",
|
|
@@ -27262,7 +27280,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27262
27280
|
return {
|
|
27263
27281
|
htmlAudioPlayback: this.wantsHtmlAudioPlayback(),
|
|
27264
27282
|
htmlAudioRouteActive: this._htmlAudioRouteActive,
|
|
27265
|
-
htmlAudioPlayOk: this._htmlAudioPlayOk
|
|
27283
|
+
htmlAudioPlayOk: this._htmlAudioPlayOk,
|
|
27284
|
+
loopbackAec: this.wantsLoopbackAec(),
|
|
27285
|
+
loopbackRouteActive: this._loopbackReady,
|
|
27286
|
+
loopbackPlayOk: this._loopbackPlayOk
|
|
27266
27287
|
};
|
|
27267
27288
|
}
|
|
27268
27289
|
|
|
@@ -27279,6 +27300,18 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27279
27300
|
try {
|
|
27280
27301
|
this.gainNode.disconnect();
|
|
27281
27302
|
} catch (_) {/* ignore */}
|
|
27303
|
+
|
|
27304
|
+
// Loopback first when asked for. It returns null until the peer connections
|
|
27305
|
+
// are up (negotiation is async), and we deliberately fall through to a
|
|
27306
|
+
// normal audible route in the meantime rather than holding audio: the
|
|
27307
|
+
// establishment is re-entrant and calls back here when it completes.
|
|
27308
|
+
if (this.wantsLoopbackAec()) {
|
|
27309
|
+
var loopbackDest = this._ensureLoopbackRoute();
|
|
27310
|
+
if (loopbackDest) {
|
|
27311
|
+
this.gainNode.connect(loopbackDest);
|
|
27312
|
+
return;
|
|
27313
|
+
}
|
|
27314
|
+
}
|
|
27282
27315
|
if (this.wantsHtmlAudioPlayback() && this._ensureHtmlAudioRoute()) {
|
|
27283
27316
|
this.gainNode.connect(this._mediaStreamDest);
|
|
27284
27317
|
return;
|
|
@@ -27303,16 +27336,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27303
27336
|
}
|
|
27304
27337
|
try {
|
|
27305
27338
|
this._mediaStreamDest = this.audioContext.createMediaStreamDestination();
|
|
27306
|
-
var el =
|
|
27307
|
-
el.setAttribute('playsinline', '');
|
|
27308
|
-
el.setAttribute('webkit-playsinline', '');
|
|
27309
|
-
el.autoplay = true;
|
|
27310
|
-
el.controls = false;
|
|
27311
|
-
el.preload = 'auto';
|
|
27312
|
-
el.playbackRate = 1;
|
|
27313
|
-
el.style.display = 'none';
|
|
27314
|
-
el.srcObject = this._mediaStreamDest.stream;
|
|
27315
|
-
document.body.appendChild(el);
|
|
27339
|
+
var el = this._createHiddenAudioEl(this._mediaStreamDest.stream);
|
|
27316
27340
|
this._htmlAudioEl = el;
|
|
27317
27341
|
this._htmlAudioRouteActive = true;
|
|
27318
27342
|
this._ensureHtmlAudioPlaying();
|
|
@@ -27393,6 +27417,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27393
27417
|
this.gainNode.disconnect();
|
|
27394
27418
|
} catch (_) {/* ignore */}
|
|
27395
27419
|
this._teardownHtmlAudioRoute();
|
|
27420
|
+
this._teardownLoopbackRoute();
|
|
27396
27421
|
this.gainNode.connect(this.audioContext.destination);
|
|
27397
27422
|
console.warn("\u26A0\uFE0F AudioPlayer: Fell back to AudioContext.destination (".concat(reason, ")"));
|
|
27398
27423
|
this.emit('htmlAudioRoute', {
|
|
@@ -27420,29 +27445,216 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27420
27445
|
this._htmlAudioRouteActive = false;
|
|
27421
27446
|
}
|
|
27422
27447
|
|
|
27448
|
+
/**
|
|
27449
|
+
* Shared factory for the hidden playback element. Both last-hop routes need
|
|
27450
|
+
* exactly this: autoplay, inline on iOS, invisible, rate pinned to 1 so a
|
|
27451
|
+
* page-level playbackRate can't stretch TTS under us.
|
|
27452
|
+
*/
|
|
27453
|
+
}, {
|
|
27454
|
+
key: "_createHiddenAudioEl",
|
|
27455
|
+
value: function _createHiddenAudioEl(stream) {
|
|
27456
|
+
var el = document.createElement('audio');
|
|
27457
|
+
el.setAttribute('playsinline', '');
|
|
27458
|
+
el.setAttribute('webkit-playsinline', '');
|
|
27459
|
+
el.autoplay = true;
|
|
27460
|
+
el.controls = false;
|
|
27461
|
+
el.preload = 'auto';
|
|
27462
|
+
el.playbackRate = 1;
|
|
27463
|
+
el.style.display = 'none';
|
|
27464
|
+
el.srcObject = stream;
|
|
27465
|
+
document.body.appendChild(el);
|
|
27466
|
+
return el;
|
|
27467
|
+
}
|
|
27468
|
+
|
|
27469
|
+
/**
|
|
27470
|
+
* Opt-in loopback route. Default OFF: this is an unproven replacement for the
|
|
27471
|
+
* hop running on live customer sites, so it ships dark and is turned on per
|
|
27472
|
+
* machine (?ttp_loopback=1 / localStorage) or per integration (config).
|
|
27473
|
+
*/
|
|
27474
|
+
}, {
|
|
27475
|
+
key: "wantsLoopbackAec",
|
|
27476
|
+
value: function wantsLoopbackAec() {
|
|
27477
|
+
// Memoized per player: _htmlHopNeedsHardwareRate() consults this from
|
|
27478
|
+
// _honestRateSamples, i.e. once per audio chunk, and reading it live would
|
|
27479
|
+
// put a synchronous storage hit on the hot path for every machine where
|
|
27480
|
+
// the hop is off (there the || cannot short-circuit). Changing the flag
|
|
27481
|
+
// requires a reload anyway — that is how it is documented and tested.
|
|
27482
|
+
if (this._loopbackWanted === undefined) {
|
|
27483
|
+
var override = (0,_loopbackAec_js__WEBPACK_IMPORTED_MODULE_5__.readTtpLoopbackOverride)(typeof window !== 'undefined' ? window : null);
|
|
27484
|
+
this._loopbackWanted = override !== null ? override : typeof this.config.loopbackAec === 'boolean' ? this.config.loopbackAec : false;
|
|
27485
|
+
if (this._loopbackWanted) {
|
|
27486
|
+
console.log('🔊 AudioPlayer: loopback AEC requested — TTS will play via a local peer connection');
|
|
27487
|
+
}
|
|
27488
|
+
}
|
|
27489
|
+
return this._loopbackWanted;
|
|
27490
|
+
}
|
|
27491
|
+
|
|
27492
|
+
/**
|
|
27493
|
+
* Returns the MediaStreamDestination to play into once the loopback is up,
|
|
27494
|
+
* or null while it is not. Never blocks: the caller routes audio somewhere
|
|
27495
|
+
* audible in the meantime and we re-route from _establishLoopback.
|
|
27496
|
+
*/
|
|
27497
|
+
}, {
|
|
27498
|
+
key: "_ensureLoopbackRoute",
|
|
27499
|
+
value: function _ensureLoopbackRoute() {
|
|
27500
|
+
var _this9 = this;
|
|
27501
|
+
if (this._loopbackReady && this._loopbackDest) {
|
|
27502
|
+
this._ensureLoopbackPlaying();
|
|
27503
|
+
return this._loopbackDest;
|
|
27504
|
+
}
|
|
27505
|
+
if (this._loopbackFailed || this._loopbackPending) return null;
|
|
27506
|
+
if (!this.audioContext || typeof this.audioContext.createMediaStreamDestination !== 'function') {
|
|
27507
|
+
this._failLoopback('no_media_stream_destination');
|
|
27508
|
+
return null;
|
|
27509
|
+
}
|
|
27510
|
+
if (!(0,_loopbackAec_js__WEBPACK_IMPORTED_MODULE_5__.isLoopbackAecSupported)(typeof window !== 'undefined' ? window : null)) {
|
|
27511
|
+
this._failLoopback('no_peer_connection');
|
|
27512
|
+
return null;
|
|
27513
|
+
}
|
|
27514
|
+
this._loopbackPending = true;
|
|
27515
|
+
this._establishLoopback().catch(function (e) {
|
|
27516
|
+
console.warn('⚠️ AudioPlayer: loopback AEC route unavailable, keeping existing playback route:', e && e.message);
|
|
27517
|
+
_this9._failLoopback('negotiation_failed');
|
|
27518
|
+
});
|
|
27519
|
+
return null;
|
|
27520
|
+
}
|
|
27521
|
+
}, {
|
|
27522
|
+
key: "_establishLoopback",
|
|
27523
|
+
value: function () {
|
|
27524
|
+
var _establishLoopback2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
|
|
27525
|
+
var gen, dest, _yield$createAecLoopb, remoteStream, close;
|
|
27526
|
+
return _regenerator().w(function (_context8) {
|
|
27527
|
+
while (1) switch (_context8.n) {
|
|
27528
|
+
case 0:
|
|
27529
|
+
// Generation token: a teardown (call end, context rebuild) while we are
|
|
27530
|
+
// negotiating must not resurrect a route into a dead AudioContext.
|
|
27531
|
+
gen = this._loopbackGen = (this._loopbackGen || 0) + 1;
|
|
27532
|
+
dest = this.audioContext.createMediaStreamDestination();
|
|
27533
|
+
_context8.n = 1;
|
|
27534
|
+
return (0,_loopbackAec_js__WEBPACK_IMPORTED_MODULE_5__.createAecLoopback)({
|
|
27535
|
+
stream: dest.stream
|
|
27536
|
+
});
|
|
27537
|
+
case 1:
|
|
27538
|
+
_yield$createAecLoopb = _context8.v;
|
|
27539
|
+
remoteStream = _yield$createAecLoopb.remoteStream;
|
|
27540
|
+
close = _yield$createAecLoopb.close;
|
|
27541
|
+
if (!(gen !== this._loopbackGen || !this.audioContext)) {
|
|
27542
|
+
_context8.n = 2;
|
|
27543
|
+
break;
|
|
27544
|
+
}
|
|
27545
|
+
try {
|
|
27546
|
+
close();
|
|
27547
|
+
} catch (_) {/* ignore */}
|
|
27548
|
+
return _context8.a(2);
|
|
27549
|
+
case 2:
|
|
27550
|
+
// Drop the transitional hop element if one was built while we negotiated,
|
|
27551
|
+
// or it keeps playing a destination nothing is connected to any more.
|
|
27552
|
+
this._teardownHtmlAudioRoute();
|
|
27553
|
+
this._loopbackDest = dest;
|
|
27554
|
+
this._loopbackClose = close;
|
|
27555
|
+
this._loopbackEl = this._createHiddenAudioEl(remoteStream);
|
|
27556
|
+
this._loopbackReady = true;
|
|
27557
|
+
this._loopbackPending = false;
|
|
27558
|
+
this._ensureLoopbackPlaying();
|
|
27559
|
+
this._connectGainToOutput();
|
|
27560
|
+
console.log('🔊 AudioPlayer: Playback last-hop is a local WebRTC loopback (Chrome-cancellable AEC far-end)');
|
|
27561
|
+
this.emit('htmlAudioRoute', {
|
|
27562
|
+
state: 'loopback_established'
|
|
27563
|
+
});
|
|
27564
|
+
case 3:
|
|
27565
|
+
return _context8.a(2);
|
|
27566
|
+
}
|
|
27567
|
+
}, _callee7, this);
|
|
27568
|
+
}));
|
|
27569
|
+
function _establishLoopback() {
|
|
27570
|
+
return _establishLoopback2.apply(this, arguments);
|
|
27571
|
+
}
|
|
27572
|
+
return _establishLoopback;
|
|
27573
|
+
}()
|
|
27574
|
+
}, {
|
|
27575
|
+
key: "_ensureLoopbackPlaying",
|
|
27576
|
+
value: function _ensureLoopbackPlaying() {
|
|
27577
|
+
var _this0 = this;
|
|
27578
|
+
var el = this._loopbackEl;
|
|
27579
|
+
if (!el) return;
|
|
27580
|
+
if (!el.paused && this._loopbackPlayOk) return;
|
|
27581
|
+
var playResult = el.play();
|
|
27582
|
+
if (playResult && typeof playResult.then === 'function') {
|
|
27583
|
+
playResult.then(function () {
|
|
27584
|
+
_this0._loopbackPlayOk = true;
|
|
27585
|
+
}).catch(function (err) {
|
|
27586
|
+
if (_this0._loopbackPlayOk || !el.paused) return;
|
|
27587
|
+
console.warn('⚠️ AudioPlayer: loopback element play() rejected — reverting to the previous route:', err);
|
|
27588
|
+
_this0._failLoopback('play_rejected');
|
|
27589
|
+
_this0._connectGainToOutput();
|
|
27590
|
+
});
|
|
27591
|
+
}
|
|
27592
|
+
}
|
|
27593
|
+
|
|
27594
|
+
/** One-way latch: once the loopback is off for this player it stays off,
|
|
27595
|
+
* rather than retrying every reconnect and stuttering the route. */
|
|
27596
|
+
}, {
|
|
27597
|
+
key: "_failLoopback",
|
|
27598
|
+
value: function _failLoopback(reason) {
|
|
27599
|
+
this._loopbackFailed = true;
|
|
27600
|
+
this._loopbackPending = false;
|
|
27601
|
+
this._teardownLoopbackRoute();
|
|
27602
|
+
this.emit('htmlAudioRoute', {
|
|
27603
|
+
state: 'loopback_fallback',
|
|
27604
|
+
reason: reason
|
|
27605
|
+
});
|
|
27606
|
+
}
|
|
27607
|
+
}, {
|
|
27608
|
+
key: "_teardownLoopbackRoute",
|
|
27609
|
+
value: function _teardownLoopbackRoute() {
|
|
27610
|
+
this._loopbackGen = (this._loopbackGen || 0) + 1;
|
|
27611
|
+
if (this._loopbackEl) {
|
|
27612
|
+
try {
|
|
27613
|
+
this._loopbackEl.pause();
|
|
27614
|
+
} catch (_) {/* ignore */}
|
|
27615
|
+
try {
|
|
27616
|
+
this._loopbackEl.srcObject = null;
|
|
27617
|
+
} catch (_) {/* ignore */}
|
|
27618
|
+
if (this._loopbackEl.parentNode) {
|
|
27619
|
+
this._loopbackEl.parentNode.removeChild(this._loopbackEl);
|
|
27620
|
+
}
|
|
27621
|
+
this._loopbackEl = null;
|
|
27622
|
+
}
|
|
27623
|
+
if (this._loopbackClose) {
|
|
27624
|
+
try {
|
|
27625
|
+
this._loopbackClose();
|
|
27626
|
+
} catch (_) {/* ignore */}
|
|
27627
|
+
this._loopbackClose = null;
|
|
27628
|
+
}
|
|
27629
|
+
this._loopbackDest = null;
|
|
27630
|
+
this._loopbackReady = false;
|
|
27631
|
+
this._loopbackPending = false;
|
|
27632
|
+
this._loopbackPlayOk = false;
|
|
27633
|
+
}
|
|
27634
|
+
|
|
27423
27635
|
/**
|
|
27424
27636
|
* Initialize audio context with correct sample rate
|
|
27425
27637
|
*/
|
|
27426
27638
|
}, {
|
|
27427
27639
|
key: "initializeAudioContext",
|
|
27428
27640
|
value: (function () {
|
|
27429
|
-
var _initializeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
27430
|
-
var
|
|
27431
|
-
return _regenerator().w(function (
|
|
27432
|
-
while (1) switch (
|
|
27641
|
+
var _initializeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
|
|
27642
|
+
var _this1 = this;
|
|
27643
|
+
return _regenerator().w(function (_context9) {
|
|
27644
|
+
while (1) switch (_context9.n) {
|
|
27433
27645
|
case 0:
|
|
27434
27646
|
if (!this._audioContextInitPromise) {
|
|
27435
|
-
|
|
27647
|
+
_context9.n = 1;
|
|
27436
27648
|
break;
|
|
27437
27649
|
}
|
|
27438
|
-
return
|
|
27650
|
+
return _context9.a(2, this._audioContextInitPromise);
|
|
27439
27651
|
case 1:
|
|
27440
27652
|
this._audioContextInitPromise = this._initializeAudioContextImpl().finally(function () {
|
|
27441
|
-
|
|
27653
|
+
_this1._audioContextInitPromise = null;
|
|
27442
27654
|
});
|
|
27443
|
-
return
|
|
27655
|
+
return _context9.a(2, this._audioContextInitPromise);
|
|
27444
27656
|
}
|
|
27445
|
-
},
|
|
27657
|
+
}, _callee8, this);
|
|
27446
27658
|
}));
|
|
27447
27659
|
function initializeAudioContext() {
|
|
27448
27660
|
return _initializeAudioContext.apply(this, arguments);
|
|
@@ -27452,11 +27664,11 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27452
27664
|
}, {
|
|
27453
27665
|
key: "_initializeAudioContextImpl",
|
|
27454
27666
|
value: function () {
|
|
27455
|
-
var _initializeAudioContextImpl2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
27456
|
-
var
|
|
27667
|
+
var _initializeAudioContextImpl2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
|
|
27668
|
+
var _this10 = this;
|
|
27457
27669
|
var ttsSampleRate, useHardwareRate, desiredSampleRate, contextFits, canReuseShared, setupAfterResume, ctxOpts, _t4;
|
|
27458
|
-
return _regenerator().w(function (
|
|
27459
|
-
while (1) switch (
|
|
27670
|
+
return _regenerator().w(function (_context0) {
|
|
27671
|
+
while (1) switch (_context0.p = _context0.n) {
|
|
27460
27672
|
case 0:
|
|
27461
27673
|
ttsSampleRate = this._ttsSampleRate();
|
|
27462
27674
|
useHardwareRate = this._htmlHopNeedsHardwareRate(); // Destination path: match TTS (24 kHz). HTML last-hop: request the standard
|
|
@@ -27474,27 +27686,27 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27474
27686
|
_sharedPlayerSampleRate = null;
|
|
27475
27687
|
}
|
|
27476
27688
|
contextFits = function contextFits(rate) {
|
|
27477
|
-
if (useHardwareRate) return
|
|
27689
|
+
if (useHardwareRate) return _this10._isHardwarePlaybackRate(rate);
|
|
27478
27690
|
return Math.abs(rate - desiredSampleRate) <= 100;
|
|
27479
27691
|
}; // Check if current instance AudioContext exists and matches
|
|
27480
27692
|
if (!this.audioContext) {
|
|
27481
|
-
|
|
27693
|
+
_context0.n = 3;
|
|
27482
27694
|
break;
|
|
27483
27695
|
}
|
|
27484
27696
|
if (!(this.audioContext.state === 'closed')) {
|
|
27485
|
-
|
|
27697
|
+
_context0.n = 1;
|
|
27486
27698
|
break;
|
|
27487
27699
|
}
|
|
27488
27700
|
this._invalidateClosedAudioContext();
|
|
27489
|
-
|
|
27701
|
+
_context0.n = 3;
|
|
27490
27702
|
break;
|
|
27491
27703
|
case 1:
|
|
27492
27704
|
if (!contextFits(this.audioContext.sampleRate)) {
|
|
27493
|
-
|
|
27705
|
+
_context0.n = 2;
|
|
27494
27706
|
break;
|
|
27495
27707
|
}
|
|
27496
27708
|
this._ensureHtmlAudioPlaying();
|
|
27497
|
-
return
|
|
27709
|
+
return _context0.a(2);
|
|
27498
27710
|
case 2:
|
|
27499
27711
|
console.warn("\u26A0\uFE0F AudioPlayer: AudioContext sample rate (".concat(this.audioContext.sampleRate, "Hz) doesn't match playback hop, recreating..."));
|
|
27500
27712
|
this.stopImmediate();
|
|
@@ -27505,44 +27717,44 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27505
27717
|
// causing newly created AudioContexts to fail silently.
|
|
27506
27718
|
canReuseShared = _sharedPlayerContext && _sharedPlayerContext.state !== 'closed' && contextFits(_sharedPlayerContext.sampleRate);
|
|
27507
27719
|
if (!canReuseShared) {
|
|
27508
|
-
|
|
27720
|
+
_context0.n = 8;
|
|
27509
27721
|
break;
|
|
27510
27722
|
}
|
|
27511
27723
|
console.log("\u267B\uFE0F AudioPlayer: Reusing shared AudioContext at ".concat(_sharedPlayerContext.sampleRate, "Hz (iOS-safe)"));
|
|
27512
27724
|
this.audioContext = _sharedPlayerContext;
|
|
27513
27725
|
setupAfterResume = function setupAfterResume() {
|
|
27514
|
-
|
|
27515
|
-
if (
|
|
27726
|
+
_this10.setupAudioContextStateMonitoring();
|
|
27727
|
+
if (_this10.gainNode) {
|
|
27516
27728
|
try {
|
|
27517
|
-
|
|
27729
|
+
_this10.gainNode.disconnect();
|
|
27518
27730
|
} catch (e) {
|
|
27519
27731
|
console.warn('⚠️ AudioPlayer: Error disconnecting old GainNode:', e);
|
|
27520
27732
|
}
|
|
27521
27733
|
}
|
|
27522
|
-
|
|
27523
|
-
|
|
27524
|
-
|
|
27525
|
-
if (!
|
|
27526
|
-
|
|
27734
|
+
_this10.gainNode = _this10.audioContext.createGain();
|
|
27735
|
+
_this10.gainNode.gain.value = 1.0;
|
|
27736
|
+
_this10._connectGainToOutput();
|
|
27737
|
+
if (!_this10._audioContextPrimed) {
|
|
27738
|
+
_this10._primeAudioContext();
|
|
27527
27739
|
}
|
|
27528
27740
|
};
|
|
27529
27741
|
if (!(this.audioContext.state === 'suspended')) {
|
|
27530
|
-
|
|
27742
|
+
_context0.n = 7;
|
|
27531
27743
|
break;
|
|
27532
27744
|
}
|
|
27533
|
-
|
|
27534
|
-
|
|
27745
|
+
_context0.p = 4;
|
|
27746
|
+
_context0.n = 5;
|
|
27535
27747
|
return this.audioContext.resume();
|
|
27536
27748
|
case 5:
|
|
27537
|
-
|
|
27749
|
+
_context0.n = 7;
|
|
27538
27750
|
break;
|
|
27539
27751
|
case 6:
|
|
27540
|
-
|
|
27541
|
-
_t4 =
|
|
27752
|
+
_context0.p = 6;
|
|
27753
|
+
_t4 = _context0.v;
|
|
27542
27754
|
console.warn('⚠️ AudioPlayer: Error resuming shared context:', _t4);
|
|
27543
27755
|
case 7:
|
|
27544
27756
|
setupAfterResume();
|
|
27545
|
-
return
|
|
27757
|
+
return _context0.a(2);
|
|
27546
27758
|
case 8:
|
|
27547
27759
|
console.log("\uD83C\uDFB5 AudioPlayer: Creating AudioContext (tts=".concat(ttsSampleRate, "Hz, htmlHop=").concat(useHardwareRate, ", requested=").concat(desiredSampleRate, "Hz)"));
|
|
27548
27760
|
|
|
@@ -27595,9 +27807,9 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27595
27807
|
this._primeAudioContext();
|
|
27596
27808
|
}
|
|
27597
27809
|
case 9:
|
|
27598
|
-
return
|
|
27810
|
+
return _context0.a(2);
|
|
27599
27811
|
}
|
|
27600
|
-
},
|
|
27812
|
+
}, _callee9, this, [[4, 6]]);
|
|
27601
27813
|
}));
|
|
27602
27814
|
function _initializeAudioContextImpl() {
|
|
27603
27815
|
return _initializeAudioContextImpl2.apply(this, arguments);
|
|
@@ -27611,17 +27823,17 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27611
27823
|
}, {
|
|
27612
27824
|
key: "_primeAudioContext",
|
|
27613
27825
|
value: (function () {
|
|
27614
|
-
var _primeAudioContext2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
27826
|
+
var _primeAudioContext2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
|
|
27615
27827
|
var sampleRate, silentBuffer, source, _t5;
|
|
27616
|
-
return _regenerator().w(function (
|
|
27617
|
-
while (1) switch (
|
|
27828
|
+
return _regenerator().w(function (_context1) {
|
|
27829
|
+
while (1) switch (_context1.p = _context1.n) {
|
|
27618
27830
|
case 0:
|
|
27619
|
-
|
|
27831
|
+
_context1.p = 0;
|
|
27620
27832
|
if (!(!this.audioContext || this._audioContextPrimed)) {
|
|
27621
|
-
|
|
27833
|
+
_context1.n = 1;
|
|
27622
27834
|
break;
|
|
27623
27835
|
}
|
|
27624
|
-
return
|
|
27836
|
+
return _context1.a(2);
|
|
27625
27837
|
case 1:
|
|
27626
27838
|
sampleRate = this.audioContext.sampleRate;
|
|
27627
27839
|
silentBuffer = this.audioContext.createBuffer(1, sampleRate * 0.15, sampleRate);
|
|
@@ -27632,23 +27844,23 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27632
27844
|
source.connect(this.gainNode || this.audioContext.destination);
|
|
27633
27845
|
source.start();
|
|
27634
27846
|
this._ensureHtmlAudioPlaying();
|
|
27635
|
-
|
|
27847
|
+
_context1.n = 2;
|
|
27636
27848
|
return new Promise(function (resolve) {
|
|
27637
27849
|
return setTimeout(resolve, 150);
|
|
27638
27850
|
});
|
|
27639
27851
|
case 2:
|
|
27640
27852
|
this._audioContextPrimed = true;
|
|
27641
27853
|
console.log('✅ AudioPlayer: Audio hardware primed with silent buffer');
|
|
27642
|
-
|
|
27854
|
+
_context1.n = 4;
|
|
27643
27855
|
break;
|
|
27644
27856
|
case 3:
|
|
27645
|
-
|
|
27646
|
-
_t5 =
|
|
27857
|
+
_context1.p = 3;
|
|
27858
|
+
_t5 = _context1.v;
|
|
27647
27859
|
console.error('❌ AudioPlayer: Audio priming failed:', _t5);
|
|
27648
27860
|
case 4:
|
|
27649
|
-
return
|
|
27861
|
+
return _context1.a(2);
|
|
27650
27862
|
}
|
|
27651
|
-
},
|
|
27863
|
+
}, _callee0, this, [[0, 3]]);
|
|
27652
27864
|
}));
|
|
27653
27865
|
function _primeAudioContext() {
|
|
27654
27866
|
return _primeAudioContext2.apply(this, arguments);
|
|
@@ -27675,6 +27887,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27675
27887
|
this.gainNode = null;
|
|
27676
27888
|
}
|
|
27677
27889
|
this._teardownHtmlAudioRoute();
|
|
27890
|
+
this._teardownLoopbackRoute();
|
|
27678
27891
|
|
|
27679
27892
|
// Remove state change listener first
|
|
27680
27893
|
if (this._audioContextStateChangeHandler && this.audioContext) {
|
|
@@ -27715,7 +27928,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27715
27928
|
* Handles mic permission grants, tab switching, browser suspension, etc.
|
|
27716
27929
|
*/
|
|
27717
27930
|
function setupAudioContextStateMonitoring() {
|
|
27718
|
-
var
|
|
27931
|
+
var _this11 = this;
|
|
27719
27932
|
if (!this.audioContext) {
|
|
27720
27933
|
return;
|
|
27721
27934
|
}
|
|
@@ -27728,38 +27941,38 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27728
27941
|
// Create handler that references this.audioContext dynamically
|
|
27729
27942
|
this._audioContextStateChangeHandler = function () {
|
|
27730
27943
|
// Null check required because audioContext may be cleaned up while handler is queued
|
|
27731
|
-
if (!
|
|
27944
|
+
if (!_this11.audioContext) {
|
|
27732
27945
|
console.warn('⚠️ AudioPlayer: State change handler fired but AudioContext is null');
|
|
27733
27946
|
return;
|
|
27734
27947
|
}
|
|
27735
|
-
console.log("\uD83C\uDFB5 AudioPlayer: AudioContext state changed to: ".concat(
|
|
27736
|
-
if (
|
|
27737
|
-
|
|
27948
|
+
console.log("\uD83C\uDFB5 AudioPlayer: AudioContext state changed to: ".concat(_this11.audioContext.state));
|
|
27949
|
+
if (_this11.audioContext.state === 'running') {
|
|
27950
|
+
_this11._ensureHtmlAudioPlaying();
|
|
27738
27951
|
}
|
|
27739
|
-
if (
|
|
27952
|
+
if (_this11.audioContext.state === 'suspended' && _this11.isPlaying) {
|
|
27740
27953
|
// AudioContext was suspended during playback (tab switch, mic permission, etc.)
|
|
27741
27954
|
console.warn('⚠️ AudioPlayer: AudioContext suspended during playback');
|
|
27742
27955
|
// Note: Playback will pause automatically, but we should handle queue processing
|
|
27743
27956
|
// The state change will be handled when we try to process next frame
|
|
27744
|
-
} else if (
|
|
27957
|
+
} else if (_this11.audioContext.state === 'running' && !_this11.isPlaying && (_this11.audioQueue.length > 0 || _this11.pcmChunkQueue.length > 0 || _this11.preparedBuffer.length > 0)) {
|
|
27745
27958
|
// AudioContext resumed and we have queued frames
|
|
27746
27959
|
// This handles: mic permission grant, tab switching back, browser resume, etc.
|
|
27747
27960
|
console.log('✅ AudioPlayer: AudioContext resumed - resuming queue processing');
|
|
27748
27961
|
|
|
27749
27962
|
// Resume queue processing if we have frames
|
|
27750
|
-
if (
|
|
27963
|
+
if (_this11.audioQueue.length > 0 && !_this11.isProcessingQueue) {
|
|
27751
27964
|
setTimeout(function () {
|
|
27752
|
-
return
|
|
27965
|
+
return _this11.processQueue();
|
|
27753
27966
|
}, 50);
|
|
27754
27967
|
}
|
|
27755
|
-
if (
|
|
27968
|
+
if (_this11.pcmChunkQueue.length > 0 && !_this11.isProcessingPcmQueue) {
|
|
27756
27969
|
setTimeout(function () {
|
|
27757
|
-
return
|
|
27970
|
+
return _this11.processPcmQueue();
|
|
27758
27971
|
}, 50);
|
|
27759
27972
|
}
|
|
27760
|
-
if (
|
|
27973
|
+
if (_this11.preparedBuffer.length > 0 && !_this11.isSchedulingFrames) {
|
|
27761
27974
|
setTimeout(function () {
|
|
27762
|
-
return
|
|
27975
|
+
return _this11.scheduleFrames();
|
|
27763
27976
|
}, 50);
|
|
27764
27977
|
}
|
|
27765
27978
|
}
|
|
@@ -27777,49 +27990,49 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27777
27990
|
}, {
|
|
27778
27991
|
key: "processQueue",
|
|
27779
27992
|
value: (function () {
|
|
27780
|
-
var _processQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
27781
|
-
var
|
|
27993
|
+
var _processQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
|
|
27994
|
+
var _this12 = this;
|
|
27782
27995
|
var audioBlob, wasFirstPlay, audioContext, arrayBuffer, audioBuffer, shouldEmitStart, source, _t6;
|
|
27783
|
-
return _regenerator().w(function (
|
|
27784
|
-
while (1) switch (
|
|
27996
|
+
return _regenerator().w(function (_context10) {
|
|
27997
|
+
while (1) switch (_context10.p = _context10.n) {
|
|
27785
27998
|
case 0:
|
|
27786
27999
|
if (!this._isStopped) {
|
|
27787
|
-
|
|
28000
|
+
_context10.n = 1;
|
|
27788
28001
|
break;
|
|
27789
28002
|
}
|
|
27790
28003
|
console.log('🛑 AudioPlayer: Not processing queue - playback was stopped (barge-in)');
|
|
27791
|
-
return
|
|
28004
|
+
return _context10.a(2);
|
|
27792
28005
|
case 1:
|
|
27793
28006
|
if (!(this.isProcessingQueue || this.audioQueue.length === 0)) {
|
|
27794
|
-
|
|
28007
|
+
_context10.n = 2;
|
|
27795
28008
|
break;
|
|
27796
28009
|
}
|
|
27797
|
-
return
|
|
28010
|
+
return _context10.a(2);
|
|
27798
28011
|
case 2:
|
|
27799
28012
|
this.isProcessingQueue = true;
|
|
27800
28013
|
this._ensureHtmlAudioPlaying();
|
|
27801
28014
|
audioBlob = this.audioQueue.shift();
|
|
27802
28015
|
if (audioBlob) {
|
|
27803
|
-
|
|
28016
|
+
_context10.n = 3;
|
|
27804
28017
|
break;
|
|
27805
28018
|
}
|
|
27806
28019
|
this.isProcessingQueue = false;
|
|
27807
|
-
return
|
|
28020
|
+
return _context10.a(2);
|
|
27808
28021
|
case 3:
|
|
27809
|
-
|
|
28022
|
+
_context10.p = 3;
|
|
27810
28023
|
wasFirstPlay = !this.isPlaying && this.currentSource === null; // Ensure context exists and is running (handles lazy init + suspended shared context)
|
|
27811
|
-
|
|
28024
|
+
_context10.n = 4;
|
|
27812
28025
|
return this.waitForAudioContextReady();
|
|
27813
28026
|
case 4:
|
|
27814
28027
|
audioContext = this.audioContext; // Decode audio
|
|
27815
|
-
|
|
28028
|
+
_context10.n = 5;
|
|
27816
28029
|
return audioBlob.arrayBuffer();
|
|
27817
28030
|
case 5:
|
|
27818
|
-
arrayBuffer =
|
|
27819
|
-
|
|
28031
|
+
arrayBuffer = _context10.v;
|
|
28032
|
+
_context10.n = 6;
|
|
27820
28033
|
return audioContext.decodeAudioData(arrayBuffer);
|
|
27821
28034
|
case 6:
|
|
27822
|
-
audioBuffer =
|
|
28035
|
+
audioBuffer = _context10.v;
|
|
27823
28036
|
shouldEmitStart = wasFirstPlay && !this.isPlaying && this.currentSource === null; // Create source
|
|
27824
28037
|
source = audioContext.createBufferSource();
|
|
27825
28038
|
source.buffer = audioBuffer;
|
|
@@ -27838,22 +28051,22 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27838
28051
|
// Handle end
|
|
27839
28052
|
|
|
27840
28053
|
source.onended = function () {
|
|
27841
|
-
|
|
27842
|
-
|
|
28054
|
+
_this12.currentSource = null;
|
|
28055
|
+
_this12.isProcessingQueue = false;
|
|
27843
28056
|
|
|
27844
28057
|
// Process next chunk
|
|
27845
28058
|
|
|
27846
|
-
if (
|
|
28059
|
+
if (_this12.audioQueue.length > 0) {
|
|
27847
28060
|
setTimeout(function () {
|
|
27848
|
-
return
|
|
28061
|
+
return _this12.processQueue();
|
|
27849
28062
|
}, 50);
|
|
27850
28063
|
} else {
|
|
27851
28064
|
// No more chunks - stop after delay
|
|
27852
28065
|
|
|
27853
28066
|
setTimeout(function () {
|
|
27854
|
-
if (
|
|
27855
|
-
|
|
27856
|
-
|
|
28067
|
+
if (_this12.audioQueue.length === 0 && !_this12.currentSource) {
|
|
28068
|
+
_this12.isPlaying = false;
|
|
28069
|
+
_this12.emit('playbackStopped');
|
|
27857
28070
|
}
|
|
27858
28071
|
}, 100);
|
|
27859
28072
|
}
|
|
@@ -27862,11 +28075,11 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27862
28075
|
// Start playback - AudioContext should now be fully ready
|
|
27863
28076
|
|
|
27864
28077
|
source.start();
|
|
27865
|
-
|
|
28078
|
+
_context10.n = 8;
|
|
27866
28079
|
break;
|
|
27867
28080
|
case 7:
|
|
27868
|
-
|
|
27869
|
-
_t6 =
|
|
28081
|
+
_context10.p = 7;
|
|
28082
|
+
_t6 = _context10.v;
|
|
27870
28083
|
console.error('❌ AudioPlayer v2: Error processing queue:', _t6);
|
|
27871
28084
|
this.currentSource = null;
|
|
27872
28085
|
this.emit('playbackError', _t6);
|
|
@@ -27876,7 +28089,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27876
28089
|
if (this.audioQueue.length > 0) {
|
|
27877
28090
|
this.isProcessingQueue = false;
|
|
27878
28091
|
setTimeout(function () {
|
|
27879
|
-
return
|
|
28092
|
+
return _this12.processQueue();
|
|
27880
28093
|
}, 100);
|
|
27881
28094
|
} else {
|
|
27882
28095
|
this.isPlaying = false;
|
|
@@ -27884,9 +28097,9 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27884
28097
|
this.emit('playbackStopped');
|
|
27885
28098
|
}
|
|
27886
28099
|
case 8:
|
|
27887
|
-
return
|
|
28100
|
+
return _context10.a(2);
|
|
27888
28101
|
}
|
|
27889
|
-
},
|
|
28102
|
+
}, _callee1, this, [[3, 7]]);
|
|
27890
28103
|
}));
|
|
27891
28104
|
function processQueue() {
|
|
27892
28105
|
return _processQueue.apply(this, arguments);
|
|
@@ -28099,7 +28312,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28099
28312
|
}, {
|
|
28100
28313
|
key: "markNewSentence",
|
|
28101
28314
|
value: function markNewSentence(text, synced, segmentId) {
|
|
28102
|
-
var
|
|
28315
|
+
var _this13 = this;
|
|
28103
28316
|
var wasStopped = this._isStopped;
|
|
28104
28317
|
var isCurrentlyPlaying = this.isPlaying || this.scheduledSources.size > 0;
|
|
28105
28318
|
|
|
@@ -28158,34 +28371,34 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28158
28371
|
var sentenceText = text; // Capture for timeout callback
|
|
28159
28372
|
this._emptySentenceTimeout = setTimeout(function () {
|
|
28160
28373
|
// Check if this sentence still has no chunks after timeout
|
|
28161
|
-
if (
|
|
28374
|
+
if (_this13.pendingSentenceText === sentenceText && _this13.scheduledBuffers === 0 && _this13.preparedBuffer.length === 0 && _this13.pcmChunkQueue.length === 0 && !_this13._isStopped) {
|
|
28162
28375
|
console.warn("\u26A0\uFE0F AudioPlayer: Empty sentence detected after 5s timeout - no chunks received for: \"".concat(sentenceText.substring(0, 40), "...\""));
|
|
28163
28376
|
// If this empty sentence carried a segment id, report it done (nothing was heard) and
|
|
28164
28377
|
// adopt it as current so the coarse stop below matches the backend's last-sent id.
|
|
28165
|
-
if (
|
|
28166
|
-
var emptySegId =
|
|
28167
|
-
|
|
28168
|
-
|
|
28169
|
-
|
|
28378
|
+
if (_this13.pendingSegmentId != null) {
|
|
28379
|
+
var emptySegId = _this13.pendingSegmentId;
|
|
28380
|
+
_this13.currentSegmentId = emptySegId;
|
|
28381
|
+
_this13.pendingSegmentId = null;
|
|
28382
|
+
_this13.emit('segmentDone', {
|
|
28170
28383
|
segmentId: emptySegId,
|
|
28171
28384
|
status: 'finished',
|
|
28172
28385
|
playedMs: 0
|
|
28173
28386
|
});
|
|
28174
28387
|
}
|
|
28175
28388
|
// Clear pending sentence to unblock next sentence
|
|
28176
|
-
if (
|
|
28177
|
-
|
|
28389
|
+
if (_this13.pendingSentenceText === sentenceText) {
|
|
28390
|
+
_this13.pendingSentenceText = null;
|
|
28178
28391
|
}
|
|
28179
28392
|
// Emit playbackStopped to allow next sentence to start
|
|
28180
28393
|
// Only if we're not currently playing (to avoid interrupting real playback)
|
|
28181
|
-
if (!
|
|
28394
|
+
if (!_this13.isPlaying && _this13.scheduledSources.size === 0) {
|
|
28182
28395
|
console.log('🛑 AudioPlayer: Emitting playbackStopped for empty sentence timeout');
|
|
28183
|
-
|
|
28184
|
-
segmentId:
|
|
28396
|
+
_this13.emit('playbackStopped', {
|
|
28397
|
+
segmentId: _this13.currentSegmentId
|
|
28185
28398
|
});
|
|
28186
28399
|
}
|
|
28187
28400
|
}
|
|
28188
|
-
|
|
28401
|
+
_this13._emptySentenceTimeout = null;
|
|
28189
28402
|
}, 5000); // 5 second timeout - adjust based on expected chunk arrival rate
|
|
28190
28403
|
}
|
|
28191
28404
|
|
|
@@ -28195,14 +28408,14 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28195
28408
|
}, {
|
|
28196
28409
|
key: "startTranscriptChecker",
|
|
28197
28410
|
value: function startTranscriptChecker() {
|
|
28198
|
-
var
|
|
28411
|
+
var _this14 = this;
|
|
28199
28412
|
if (this.isCheckingTranscripts) return;
|
|
28200
28413
|
this.isCheckingTranscripts = true;
|
|
28201
28414
|
console.log('📝 AudioPlayer: Transcript checker started');
|
|
28202
28415
|
var _checkLoop = function checkLoop() {
|
|
28203
|
-
if (!
|
|
28204
|
-
var currentTime =
|
|
28205
|
-
var _iterator2 = _createForOfIteratorHelper(
|
|
28416
|
+
if (!_this14.isCheckingTranscripts || !_this14.audioContext) return;
|
|
28417
|
+
var currentTime = _this14.audioContext.currentTime;
|
|
28418
|
+
var _iterator2 = _createForOfIteratorHelper(_this14.sentenceTimings),
|
|
28206
28419
|
_step2;
|
|
28207
28420
|
try {
|
|
28208
28421
|
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
@@ -28216,13 +28429,13 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28216
28429
|
eventData.synced = _timing.synced;
|
|
28217
28430
|
}
|
|
28218
28431
|
console.log("\uD83D\uDCDD AudioPlayer: Display transcript at ".concat(currentTime.toFixed(3), "s: \"").concat(_timing.text.substring(0, 40), "...\" (synced: ").concat(_timing.synced ? _timing.synced.length : 0, ")"));
|
|
28219
|
-
|
|
28432
|
+
_this14.emit('transcriptDisplay', eventData);
|
|
28220
28433
|
}
|
|
28221
28434
|
// Per-segment natural finish: this segment's audio window has fully elapsed.
|
|
28222
28435
|
if (!_timing.doneReported && _timing.endTime != null && currentTime >= _timing.endTime) {
|
|
28223
28436
|
_timing.doneReported = true;
|
|
28224
28437
|
var _playedMs = Math.round((_timing.endTime - _timing.startTime) * 1000);
|
|
28225
|
-
|
|
28438
|
+
_this14.emit('segmentDone', {
|
|
28226
28439
|
segmentId: _timing.segmentId,
|
|
28227
28440
|
status: 'finished',
|
|
28228
28441
|
playedMs: _playedMs
|
|
@@ -28234,12 +28447,12 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28234
28447
|
} finally {
|
|
28235
28448
|
_iterator2.f();
|
|
28236
28449
|
}
|
|
28237
|
-
if (
|
|
28450
|
+
if (_this14.isPlaying || _this14.scheduledBuffers > 0) {
|
|
28238
28451
|
requestAnimationFrame(_checkLoop);
|
|
28239
28452
|
} else {
|
|
28240
28453
|
// Playback drained naturally — flush any segment whose finish tick we may have missed
|
|
28241
28454
|
// (the last buffer's onended can flip isPlaying=false before this loop's next tick).
|
|
28242
|
-
var _iterator3 = _createForOfIteratorHelper(
|
|
28455
|
+
var _iterator3 = _createForOfIteratorHelper(_this14.sentenceTimings),
|
|
28243
28456
|
_step3;
|
|
28244
28457
|
try {
|
|
28245
28458
|
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
@@ -28248,7 +28461,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28248
28461
|
timing.doneReported = true;
|
|
28249
28462
|
var end = timing.endTime != null ? timing.endTime : timing.startTime;
|
|
28250
28463
|
var playedMs = Math.round((end - timing.startTime) * 1000);
|
|
28251
|
-
|
|
28464
|
+
_this14.emit('segmentDone', {
|
|
28252
28465
|
segmentId: timing.segmentId,
|
|
28253
28466
|
status: 'finished',
|
|
28254
28467
|
playedMs: playedMs
|
|
@@ -28260,7 +28473,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28260
28473
|
} finally {
|
|
28261
28474
|
_iterator3.f();
|
|
28262
28475
|
}
|
|
28263
|
-
|
|
28476
|
+
_this14.isCheckingTranscripts = false;
|
|
28264
28477
|
console.log('📝 AudioPlayer: Transcript checker stopped');
|
|
28265
28478
|
}
|
|
28266
28479
|
};
|
|
@@ -28291,43 +28504,43 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28291
28504
|
}, {
|
|
28292
28505
|
key: "resumeAudioContext",
|
|
28293
28506
|
value: (function () {
|
|
28294
|
-
var _resumeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
28507
|
+
var _resumeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10() {
|
|
28295
28508
|
var _t7;
|
|
28296
|
-
return _regenerator().w(function (
|
|
28297
|
-
while (1) switch (
|
|
28509
|
+
return _regenerator().w(function (_context11) {
|
|
28510
|
+
while (1) switch (_context11.p = _context11.n) {
|
|
28298
28511
|
case 0:
|
|
28299
28512
|
if (!(!this.audioContext || this.audioContext.state === 'closed')) {
|
|
28300
|
-
|
|
28513
|
+
_context11.n = 2;
|
|
28301
28514
|
break;
|
|
28302
28515
|
}
|
|
28303
|
-
|
|
28516
|
+
_context11.n = 1;
|
|
28304
28517
|
return this.initializeAudioContext();
|
|
28305
28518
|
case 1:
|
|
28306
|
-
|
|
28519
|
+
_context11.n = 6;
|
|
28307
28520
|
break;
|
|
28308
28521
|
case 2:
|
|
28309
28522
|
if (!(this.audioContext.state === 'suspended')) {
|
|
28310
|
-
|
|
28523
|
+
_context11.n = 6;
|
|
28311
28524
|
break;
|
|
28312
28525
|
}
|
|
28313
|
-
|
|
28314
|
-
|
|
28526
|
+
_context11.p = 3;
|
|
28527
|
+
_context11.n = 4;
|
|
28315
28528
|
return this.audioContext.resume();
|
|
28316
28529
|
case 4:
|
|
28317
28530
|
console.log('✅ AudioPlayer v2: AudioContext resumed after mic permission');
|
|
28318
|
-
|
|
28531
|
+
_context11.n = 6;
|
|
28319
28532
|
break;
|
|
28320
28533
|
case 5:
|
|
28321
|
-
|
|
28322
|
-
_t7 =
|
|
28534
|
+
_context11.p = 5;
|
|
28535
|
+
_t7 = _context11.v;
|
|
28323
28536
|
console.warn('⚠️ AudioPlayer v2: Failed to resume AudioContext:', _t7);
|
|
28324
28537
|
case 6:
|
|
28325
|
-
|
|
28538
|
+
_context11.n = 7;
|
|
28326
28539
|
return this.waitForAudioContextReady();
|
|
28327
28540
|
case 7:
|
|
28328
|
-
return
|
|
28541
|
+
return _context11.a(2);
|
|
28329
28542
|
}
|
|
28330
|
-
},
|
|
28543
|
+
}, _callee10, this, [[3, 5]]);
|
|
28331
28544
|
}));
|
|
28332
28545
|
function resumeAudioContext() {
|
|
28333
28546
|
return _resumeAudioContext.apply(this, arguments);
|
|
@@ -28578,6 +28791,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28578
28791
|
// Pass through undefined so AudioPlayer applies the platform default
|
|
28579
28792
|
return config.htmlAudioPlayback;
|
|
28580
28793
|
}(),
|
|
28794
|
+
// Opt-in: play TTS through a pair of local RTCPeerConnections so Chrome's
|
|
28795
|
+
// echo canceller can actually see it as a far-end reference (it ignores
|
|
28796
|
+
// <audio> elements). Default off — AudioPlayer also honours
|
|
28797
|
+
// ?ttp_loopback=1 / localStorage 'ttp_loopback' on its own.
|
|
28798
|
+
loopbackAec: config.loopbackAec,
|
|
28581
28799
|
// Protocol version
|
|
28582
28800
|
|
|
28583
28801
|
protocolVersion: config.protocolVersion || 2,
|
|
@@ -29478,7 +29696,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
29478
29696
|
// iOS in-app webviews lack "Safari" in the UA (real Safari always has it).
|
|
29479
29697
|
var webview = isAndroid && (/\bwv\b/.test(ua) || /Version\/[\d.]+.*Chrome/.test(ua)) || isIos && !/Safari/i.test(ua) || false;
|
|
29480
29698
|
var env = {
|
|
29481
|
-
sdkVersion: true ? "2.48.
|
|
29699
|
+
sdkVersion: true ? "2.48.20" : 0,
|
|
29482
29700
|
ua: ua,
|
|
29483
29701
|
platform: (uaData === null || uaData === void 0 ? void 0 : uaData.platform) || navigator.platform || '',
|
|
29484
29702
|
mobile: (_uaData$mobile = uaData === null || uaData === void 0 ? void 0 : uaData.mobile) !== null && _uaData$mobile !== void 0 ? _uaData$mobile : isAndroid || isIos,
|
|
@@ -29493,7 +29711,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
29493
29711
|
} catch (e) {
|
|
29494
29712
|
console.warn('⚠️ VoiceSDK v2: Failed to build client env:', e);
|
|
29495
29713
|
return {
|
|
29496
|
-
sdkVersion: true ? "2.48.
|
|
29714
|
+
sdkVersion: true ? "2.48.20" : 0
|
|
29497
29715
|
};
|
|
29498
29716
|
}
|
|
29499
29717
|
}
|
|
@@ -29641,7 +29859,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
29641
29859
|
|
|
29642
29860
|
// Include SDK build time for debugging
|
|
29643
29861
|
if (true) {
|
|
29644
|
-
helloMessage.lastBuildTime = "2026-08-
|
|
29862
|
+
helloMessage.lastBuildTime = "2026-08-27T16:45:39.666Z";
|
|
29645
29863
|
}
|
|
29646
29864
|
|
|
29647
29865
|
// Client environment (device/browser/webview) for backend logs + Langfuse metadata
|
|
@@ -32359,6 +32577,283 @@ function readTtpHopOverride(win) {
|
|
|
32359
32577
|
|
|
32360
32578
|
/***/ }),
|
|
32361
32579
|
|
|
32580
|
+
/***/ "./src/v2/loopbackAec.js":
|
|
32581
|
+
/*!*******************************!*\
|
|
32582
|
+
!*** ./src/v2/loopbackAec.js ***!
|
|
32583
|
+
\*******************************/
|
|
32584
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
32585
|
+
|
|
32586
|
+
"use strict";
|
|
32587
|
+
__webpack_require__.r(__webpack_exports__);
|
|
32588
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
32589
|
+
/* harmony export */ TTP_LOOPBACK_SESSION_KEY: () => (/* binding */ TTP_LOOPBACK_SESSION_KEY),
|
|
32590
|
+
/* harmony export */ createAecLoopback: () => (/* binding */ createAecLoopback),
|
|
32591
|
+
/* harmony export */ isLoopbackAecSupported: () => (/* binding */ isLoopbackAecSupported),
|
|
32592
|
+
/* harmony export */ preferHighQualityOpus: () => (/* binding */ preferHighQualityOpus),
|
|
32593
|
+
/* harmony export */ readTtpLoopbackOverride: () => (/* binding */ readTtpLoopbackOverride)
|
|
32594
|
+
/* harmony export */ });
|
|
32595
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
32596
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
32597
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
32598
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
32599
|
+
/**
|
|
32600
|
+
* Local WebRTC loopback for echo cancellation.
|
|
32601
|
+
*
|
|
32602
|
+
* WHY THIS EXISTS. Chrome's echo canceller only cancels audio that arrived
|
|
32603
|
+
* through a WebRTC peer connection — not <audio> elements, not Web Audio.
|
|
32604
|
+
* (Firefox/Safari/Edge consider all browser audio; Chrome does not.) That is
|
|
32605
|
+
* why the HTML hop — AudioContext → MediaStreamDestination → hidden <audio> —
|
|
32606
|
+
* never reliably fixed speaker echo on Chrome: where it appeared to work, what
|
|
32607
|
+
* actually helped was Chrome-wide echo cancellation, a separate system-level
|
|
32608
|
+
* feature that is not always available. Two people at the same customer, same
|
|
32609
|
+
* OS class, both on speakers, got opposite results from the same build.
|
|
32610
|
+
*
|
|
32611
|
+
* The fix is to make TTS *actually be* peer-connection audio: wire two
|
|
32612
|
+
* RTCPeerConnections together inside this tab and push playback through them.
|
|
32613
|
+
* The remote track is then far-end reference material Chrome's AEC will
|
|
32614
|
+
* cancel. No server, no SFU, no TURN, no ports — both ends are local and ICE
|
|
32615
|
+
* never leaves the host.
|
|
32616
|
+
*
|
|
32617
|
+
* Cost is one Opus encode/decode (~20-40 ms added latency, and lossy
|
|
32618
|
+
* compression of TTS that was already lossy over the wire). Bitrate is munged
|
|
32619
|
+
* up because the "link" is a memcpy; see preferHighQualityOpus.
|
|
32620
|
+
*
|
|
32621
|
+
* Kept free of AudioPlayer state and injectable so the peer-connection dance
|
|
32622
|
+
* is unit-testable without a browser: see test/loopback-aec.test.mjs.
|
|
32623
|
+
*/
|
|
32624
|
+
|
|
32625
|
+
/** sessionStorage key mirroring an explicit ?ttp_loopback= URL value. */
|
|
32626
|
+
var TTP_LOOPBACK_SESSION_KEY = 'ttp_loopback_pref';
|
|
32627
|
+
|
|
32628
|
+
/**
|
|
32629
|
+
* QA/rollout override for the loopback route. true (force on), false (force
|
|
32630
|
+
* off), or null (no override — apply config, which defaults off).
|
|
32631
|
+
*
|
|
32632
|
+
* Precedence and the sessionStorage mirror match readTtpHopOverride for the
|
|
32633
|
+
* same reason: Borusan's navigate_to_form does window.location.href = '/path',
|
|
32634
|
+
* a same-origin full load that drops the query string mid-call (call
|
|
32635
|
+
* 84b5af7d). Without the mirror the route would silently revert on the very
|
|
32636
|
+
* navigation the tester is exercising.
|
|
32637
|
+
*/
|
|
32638
|
+
function readTtpLoopbackOverride(win) {
|
|
32639
|
+
var w = win || (typeof window !== 'undefined' ? window : null);
|
|
32640
|
+
if (!w) return null;
|
|
32641
|
+
var flag = null;
|
|
32642
|
+
try {
|
|
32643
|
+
flag = new URLSearchParams(w.location.search).get('ttp_loopback');
|
|
32644
|
+
} catch (e) {/* no location in this env */}
|
|
32645
|
+
if (flag === '0' || flag === '1') {
|
|
32646
|
+
try {
|
|
32647
|
+
w.sessionStorage.setItem(TTP_LOOPBACK_SESSION_KEY, flag);
|
|
32648
|
+
} catch (e) {/* storage blocked */}
|
|
32649
|
+
return flag === '1';
|
|
32650
|
+
}
|
|
32651
|
+
var mirrored = null;
|
|
32652
|
+
try {
|
|
32653
|
+
mirrored = w.sessionStorage.getItem(TTP_LOOPBACK_SESSION_KEY);
|
|
32654
|
+
} catch (e) {/* storage blocked */}
|
|
32655
|
+
if (mirrored === '0' || mirrored === '1') return mirrored === '1';
|
|
32656
|
+
var stored = null;
|
|
32657
|
+
try {
|
|
32658
|
+
stored = w.localStorage.getItem('ttp_loopback');
|
|
32659
|
+
} catch (e) {/* storage blocked */}
|
|
32660
|
+
if (stored === '0' || stored === '1') return stored === '1';
|
|
32661
|
+
return null;
|
|
32662
|
+
}
|
|
32663
|
+
function isLoopbackAecSupported(win) {
|
|
32664
|
+
var w = win || (typeof window !== 'undefined' ? window : null);
|
|
32665
|
+
if (!w) return false;
|
|
32666
|
+
return typeof w.RTCPeerConnection === 'function' && typeof w.MediaStream === 'function';
|
|
32667
|
+
}
|
|
32668
|
+
|
|
32669
|
+
/**
|
|
32670
|
+
* Raise Opus quality on a loopback that has no bandwidth cost, and disable the
|
|
32671
|
+
* two features that are wrong for synthesized speech on a local link:
|
|
32672
|
+
*
|
|
32673
|
+
* - usedtx=0: discontinuous transmission replaces "silence" with comfort
|
|
32674
|
+
* noise. TTS leading/trailing silence is real audio the segment ledger
|
|
32675
|
+
* accounts for; letting the codec invent it would desynchronize playedMs.
|
|
32676
|
+
* - maxaveragebitrate: the default ~32 kbps is chosen for real networks. This
|
|
32677
|
+
* link is a memcpy, so spend bits instead of degrading a voice the customer
|
|
32678
|
+
* is paying for.
|
|
32679
|
+
*
|
|
32680
|
+
* Returns the SDP unchanged if there is no Opus line to munge — never throws,
|
|
32681
|
+
* because a failed munge must not cost us the route.
|
|
32682
|
+
*/
|
|
32683
|
+
function preferHighQualityOpus(sdp) {
|
|
32684
|
+
var bitrate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 128000;
|
|
32685
|
+
if (typeof sdp !== 'string' || !sdp) return sdp;
|
|
32686
|
+
var rtpmap = sdp.match(/^a=rtpmap:(\d+)\s+opus\/48000(?:\/2)?/mi);
|
|
32687
|
+
if (!rtpmap) return sdp;
|
|
32688
|
+
var pt = rtpmap[1];
|
|
32689
|
+
var params = "stereo=0;useinbandfec=1;usedtx=0;maxaveragebitrate=".concat(bitrate);
|
|
32690
|
+
var fmtpRe = new RegExp("^a=fmtp:".concat(pt, " (.*)$"), 'mi');
|
|
32691
|
+
if (fmtpRe.test(sdp)) {
|
|
32692
|
+
return sdp.replace(fmtpRe, function (line, existing) {
|
|
32693
|
+
// Drop any key we are about to set, keep everything else the browser chose.
|
|
32694
|
+
var kept = existing.split(';').filter(function (kv) {
|
|
32695
|
+
return !/^\s*(stereo|useinbandfec|usedtx|maxaveragebitrate)\s*=/i.test(kv);
|
|
32696
|
+
}).filter(function (kv) {
|
|
32697
|
+
return kv.trim().length > 0;
|
|
32698
|
+
});
|
|
32699
|
+
return "a=fmtp:".concat(pt, " ").concat(kept.concat(params.split(';')).join(';'));
|
|
32700
|
+
});
|
|
32701
|
+
}
|
|
32702
|
+
return sdp.replace(rtpmap[0], "".concat(rtpmap[0], "\r\na=fmtp:").concat(pt, " ").concat(params));
|
|
32703
|
+
}
|
|
32704
|
+
|
|
32705
|
+
/**
|
|
32706
|
+
* Wire two peer connections together in this tab and return the remote end of
|
|
32707
|
+
* the given stream.
|
|
32708
|
+
*
|
|
32709
|
+
* Resolves { remoteStream, close } once the receiving side reports connected
|
|
32710
|
+
* AND a track has arrived — both, because either alone has been seen without
|
|
32711
|
+
* audible audio. Rejects on timeout so a stalled negotiation degrades to the
|
|
32712
|
+
* caller's existing route rather than leaving the call silent.
|
|
32713
|
+
*
|
|
32714
|
+
* Dependencies are injected (win) so this runs under node --test.
|
|
32715
|
+
*/
|
|
32716
|
+
function createAecLoopback() {
|
|
32717
|
+
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
32718
|
+
stream = _ref.stream,
|
|
32719
|
+
win = _ref.win,
|
|
32720
|
+
_ref$timeoutMs = _ref.timeoutMs,
|
|
32721
|
+
timeoutMs = _ref$timeoutMs === void 0 ? 4000 : _ref$timeoutMs,
|
|
32722
|
+
_ref$bitrate = _ref.bitrate,
|
|
32723
|
+
bitrate = _ref$bitrate === void 0 ? 128000 : _ref$bitrate;
|
|
32724
|
+
var w = win || (typeof window !== 'undefined' ? window : null);
|
|
32725
|
+
if (!w) return Promise.reject(new Error('loopback: no window'));
|
|
32726
|
+
if (!isLoopbackAecSupported(w)) return Promise.reject(new Error('loopback: RTCPeerConnection unavailable'));
|
|
32727
|
+
var tracks = stream && typeof stream.getAudioTracks === 'function' ? stream.getAudioTracks() : [];
|
|
32728
|
+
if (!tracks.length) return Promise.reject(new Error('loopback: source stream has no audio track'));
|
|
32729
|
+
|
|
32730
|
+
// No ICE servers: both ends are in this process, so host candidates are the
|
|
32731
|
+
// only ones that can possibly be useful. Asking for STUN would leak the
|
|
32732
|
+
// user's IP to a third party for a connection that never leaves the tab.
|
|
32733
|
+
var pcSend = new w.RTCPeerConnection({
|
|
32734
|
+
iceServers: []
|
|
32735
|
+
});
|
|
32736
|
+
var pcRecv = new w.RTCPeerConnection({
|
|
32737
|
+
iceServers: []
|
|
32738
|
+
});
|
|
32739
|
+
var settled = false;
|
|
32740
|
+
var timer = null;
|
|
32741
|
+
var remoteStream = new w.MediaStream();
|
|
32742
|
+
var close = function close() {
|
|
32743
|
+
if (timer) {
|
|
32744
|
+
clearTimeout(timer);
|
|
32745
|
+
timer = null;
|
|
32746
|
+
}
|
|
32747
|
+
try {
|
|
32748
|
+
pcSend.close();
|
|
32749
|
+
} catch (e) {/* already closed */}
|
|
32750
|
+
try {
|
|
32751
|
+
pcRecv.close();
|
|
32752
|
+
} catch (e) {/* already closed */}
|
|
32753
|
+
};
|
|
32754
|
+
return new Promise(function (resolve, reject) {
|
|
32755
|
+
var fail = function fail(err) {
|
|
32756
|
+
if (settled) return;
|
|
32757
|
+
settled = true;
|
|
32758
|
+
close();
|
|
32759
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
32760
|
+
};
|
|
32761
|
+
var maybeResolve = function maybeResolve() {
|
|
32762
|
+
if (settled) return;
|
|
32763
|
+
if (!remoteStream.getAudioTracks().length) return;
|
|
32764
|
+
var state = pcRecv.connectionState || pcRecv.iceConnectionState;
|
|
32765
|
+
if (state !== 'connected' && state !== 'completed') return;
|
|
32766
|
+
settled = true;
|
|
32767
|
+
if (timer) {
|
|
32768
|
+
clearTimeout(timer);
|
|
32769
|
+
timer = null;
|
|
32770
|
+
}
|
|
32771
|
+
resolve({
|
|
32772
|
+
remoteStream: remoteStream,
|
|
32773
|
+
close: close,
|
|
32774
|
+
pcSend: pcSend,
|
|
32775
|
+
pcRecv: pcRecv
|
|
32776
|
+
});
|
|
32777
|
+
};
|
|
32778
|
+
timer = setTimeout(function () {
|
|
32779
|
+
return fail(new Error('loopback: negotiation timed out'));
|
|
32780
|
+
}, timeoutMs);
|
|
32781
|
+
pcSend.onicecandidate = function (e) {
|
|
32782
|
+
if (e.candidate) {
|
|
32783
|
+
try {
|
|
32784
|
+
pcRecv.addIceCandidate(e.candidate);
|
|
32785
|
+
} catch (err) {/* late candidate */}
|
|
32786
|
+
}
|
|
32787
|
+
};
|
|
32788
|
+
pcRecv.onicecandidate = function (e) {
|
|
32789
|
+
if (e.candidate) {
|
|
32790
|
+
try {
|
|
32791
|
+
pcSend.addIceCandidate(e.candidate);
|
|
32792
|
+
} catch (err) {/* late candidate */}
|
|
32793
|
+
}
|
|
32794
|
+
};
|
|
32795
|
+
pcRecv.ontrack = function (e) {
|
|
32796
|
+
var track = e.track || e.streams && e.streams[0] && e.streams[0].getAudioTracks()[0];
|
|
32797
|
+
if (track) remoteStream.addTrack(track);
|
|
32798
|
+
maybeResolve();
|
|
32799
|
+
};
|
|
32800
|
+
pcRecv.onconnectionstatechange = function () {
|
|
32801
|
+
var s = pcRecv.connectionState;
|
|
32802
|
+
if (s === 'failed' || s === 'closed') return fail(new Error("loopback: receiver ".concat(s)));
|
|
32803
|
+
maybeResolve();
|
|
32804
|
+
};
|
|
32805
|
+
pcRecv.oniceconnectionstatechange = function () {
|
|
32806
|
+
var s = pcRecv.iceConnectionState;
|
|
32807
|
+
if (s === 'failed') return fail(new Error('loopback: ice failed'));
|
|
32808
|
+
maybeResolve();
|
|
32809
|
+
};
|
|
32810
|
+
_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
32811
|
+
var offer, tunedOffer, answer, tunedAnswer;
|
|
32812
|
+
return _regenerator().w(function (_context) {
|
|
32813
|
+
while (1) switch (_context.n) {
|
|
32814
|
+
case 0:
|
|
32815
|
+
tracks.forEach(function (t) {
|
|
32816
|
+
return pcSend.addTrack(t, stream);
|
|
32817
|
+
});
|
|
32818
|
+
_context.n = 1;
|
|
32819
|
+
return pcSend.createOffer();
|
|
32820
|
+
case 1:
|
|
32821
|
+
offer = _context.v;
|
|
32822
|
+
tunedOffer = {
|
|
32823
|
+
type: offer.type,
|
|
32824
|
+
sdp: preferHighQualityOpus(offer.sdp, bitrate)
|
|
32825
|
+
};
|
|
32826
|
+
_context.n = 2;
|
|
32827
|
+
return pcSend.setLocalDescription(tunedOffer);
|
|
32828
|
+
case 2:
|
|
32829
|
+
_context.n = 3;
|
|
32830
|
+
return pcRecv.setRemoteDescription(tunedOffer);
|
|
32831
|
+
case 3:
|
|
32832
|
+
_context.n = 4;
|
|
32833
|
+
return pcRecv.createAnswer();
|
|
32834
|
+
case 4:
|
|
32835
|
+
answer = _context.v;
|
|
32836
|
+
tunedAnswer = {
|
|
32837
|
+
type: answer.type,
|
|
32838
|
+
sdp: preferHighQualityOpus(answer.sdp, bitrate)
|
|
32839
|
+
};
|
|
32840
|
+
_context.n = 5;
|
|
32841
|
+
return pcRecv.setLocalDescription(tunedAnswer);
|
|
32842
|
+
case 5:
|
|
32843
|
+
_context.n = 6;
|
|
32844
|
+
return pcSend.setRemoteDescription(tunedAnswer);
|
|
32845
|
+
case 6:
|
|
32846
|
+
maybeResolve();
|
|
32847
|
+
case 7:
|
|
32848
|
+
return _context.a(2);
|
|
32849
|
+
}
|
|
32850
|
+
}, _callee);
|
|
32851
|
+
}))().catch(fail);
|
|
32852
|
+
});
|
|
32853
|
+
}
|
|
32854
|
+
|
|
32855
|
+
/***/ }),
|
|
32856
|
+
|
|
32362
32857
|
/***/ "./src/v2/utils/AudioFormatConverter.js":
|
|
32363
32858
|
/*!**********************************************!*\
|
|
32364
32859
|
!*** ./src/v2/utils/AudioFormatConverter.js ***!
|
|
@@ -35963,7 +36458,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35963
36458
|
return;
|
|
35964
36459
|
}
|
|
35965
36460
|
this._ensureAboutStyles();
|
|
35966
|
-
var version = true ? "2.48.
|
|
36461
|
+
var version = true ? "2.48.20" : 0;
|
|
35967
36462
|
var convId = this._getLastConversationId();
|
|
35968
36463
|
var t = function t(k, fb) {
|
|
35969
36464
|
try {
|