@elevenlabs/client 1.8.0 → 1.8.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/dist/lib.iife.js +129 -26
- package/dist/lib.iife.js.map +1 -1
- package/dist/platform/tsconfig.test.tsbuildinfo +1 -1
- package/dist/platform/web/VoiceSessionSetup.d.ts.map +1 -1
- package/dist/platform/web/VoiceSessionSetup.js +14 -2
- package/dist/platform/web/VoiceSessionSetup.js.map +1 -1
- package/dist/platform/web/audioUnlock.d.ts +11 -0
- package/dist/platform/web/audioUnlock.d.ts.map +1 -0
- package/dist/platform/web/audioUnlock.js +74 -0
- package/dist/platform/web/audioUnlock.js.map +1 -0
- package/dist/platform/web/index.d.ts.map +1 -1
- package/dist/platform/web/index.js +6 -0
- package/dist/platform/web/index.js.map +1 -1
- package/dist/platform/web/output.d.ts +3 -1
- package/dist/platform/web/output.d.ts.map +1 -1
- package/dist/platform/web/output.js +29 -3
- package/dist/platform/web/output.js.map +1 -1
- package/dist/utils/WebSocketConnection.d.ts +1 -0
- package/dist/utils/WebSocketConnection.d.ts.map +1 -1
- package/dist/utils/WebSocketConnection.js +15 -0
- package/dist/utils/WebSocketConnection.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/lib.iife.js
CHANGED
|
@@ -92,7 +92,7 @@ var ElevenLabsClient = (function(exports) {
|
|
|
92
92
|
//#region src/sourceInfo.ts
|
|
93
93
|
let sourceInfo = Object.freeze({
|
|
94
94
|
name: "js_sdk",
|
|
95
|
-
version: "1.8.
|
|
95
|
+
version: "1.8.1"
|
|
96
96
|
});
|
|
97
97
|
//#endregion
|
|
98
98
|
//#region src/utils/events.ts
|
|
@@ -21955,14 +21955,39 @@ registerProcessor("audioConcatProcessor", AudioConcatProcessor);
|
|
|
21955
21955
|
};
|
|
21956
21956
|
}
|
|
21957
21957
|
//#endregion
|
|
21958
|
+
//#region src/platform/web/compatibility.ts
|
|
21959
|
+
function isIosDevice() {
|
|
21960
|
+
return [
|
|
21961
|
+
"iPad Simulator",
|
|
21962
|
+
"iPhone Simulator",
|
|
21963
|
+
"iPod Simulator",
|
|
21964
|
+
"iPad",
|
|
21965
|
+
"iPhone",
|
|
21966
|
+
"iPod"
|
|
21967
|
+
].includes(navigator.platform) || navigator.userAgent.includes("Mac") && "ontouchend" in document;
|
|
21968
|
+
}
|
|
21969
|
+
function isAndroidDevice() {
|
|
21970
|
+
return /android/i.test(navigator.userAgent);
|
|
21971
|
+
}
|
|
21972
|
+
//#endregion
|
|
21958
21973
|
//#region src/platform/web/output.ts
|
|
21974
|
+
function maybePrimeIosPlayback({ sampleRate, format, worklet, audioElement }) {
|
|
21975
|
+
if (!isIosDevice()) return;
|
|
21976
|
+
const primeFrameCount = Math.floor(sampleRate * 100 / 1e3);
|
|
21977
|
+
const silentBuffer = format === "ulaw" ? new Uint8Array(primeFrameCount).fill(255) : new Int16Array(primeFrameCount);
|
|
21978
|
+
worklet.port.postMessage({
|
|
21979
|
+
type: "buffer",
|
|
21980
|
+
buffer: silentBuffer.buffer
|
|
21981
|
+
});
|
|
21982
|
+
audioElement.play().catch(() => {});
|
|
21983
|
+
}
|
|
21959
21984
|
var MediaDeviceOutput = class MediaDeviceOutput {
|
|
21960
|
-
static async create({ sampleRate, format, outputDeviceId, workletPaths, libsampleratePath }) {
|
|
21961
|
-
let context = null;
|
|
21985
|
+
static async create({ sampleRate, format, outputDeviceId, workletPaths, libsampleratePath, audioContext }) {
|
|
21986
|
+
let context = audioContext ?? null;
|
|
21962
21987
|
let audioElement = null;
|
|
21963
21988
|
try {
|
|
21964
21989
|
const supportsSampleRateConstraint = navigator.mediaDevices.getSupportedConstraints().sampleRate;
|
|
21965
|
-
context = new AudioContext(supportsSampleRateConstraint ? { sampleRate } : {});
|
|
21990
|
+
if (!context) context = new AudioContext(supportsSampleRateConstraint ? { sampleRate } : {});
|
|
21966
21991
|
const analyser = context.createAnalyser();
|
|
21967
21992
|
const gain = context.createGain();
|
|
21968
21993
|
audioElement = new Audio();
|
|
@@ -21988,6 +22013,12 @@ registerProcessor("audioConcatProcessor", AudioConcatProcessor);
|
|
|
21988
22013
|
});
|
|
21989
22014
|
worklet.connect(gain);
|
|
21990
22015
|
await context.resume();
|
|
22016
|
+
maybePrimeIosPlayback({
|
|
22017
|
+
sampleRate,
|
|
22018
|
+
format,
|
|
22019
|
+
worklet,
|
|
22020
|
+
audioElement
|
|
22021
|
+
});
|
|
21991
22022
|
if (outputDeviceId && audioElement.setSinkId) await audioElement.setSinkId(outputDeviceId);
|
|
21992
22023
|
return new MediaDeviceOutput(context, analyser, gain, worklet, audioElement);
|
|
21993
22024
|
} catch (error) {
|
|
@@ -22199,21 +22230,6 @@ class RawAudioProcessor extends AudioWorkletProcessor {
|
|
|
22199
22230
|
}
|
|
22200
22231
|
registerProcessor("rawAudioProcessor", RawAudioProcessor);
|
|
22201
22232
|
`);
|
|
22202
|
-
//#endregion
|
|
22203
|
-
//#region src/platform/web/compatibility.ts
|
|
22204
|
-
function isIosDevice() {
|
|
22205
|
-
return [
|
|
22206
|
-
"iPad Simulator",
|
|
22207
|
-
"iPhone Simulator",
|
|
22208
|
-
"iPod Simulator",
|
|
22209
|
-
"iPad",
|
|
22210
|
-
"iPhone",
|
|
22211
|
-
"iPod"
|
|
22212
|
-
].includes(navigator.platform) || navigator.userAgent.includes("Mac") && "ontouchend" in document;
|
|
22213
|
-
}
|
|
22214
|
-
function isAndroidDevice() {
|
|
22215
|
-
return /android/i.test(navigator.userAgent);
|
|
22216
|
-
}
|
|
22217
22233
|
//#endregion
|
|
22218
22234
|
//#region src/platform/web/input.ts
|
|
22219
22235
|
const defaultConstraints = {
|
|
@@ -22372,6 +22388,7 @@ registerProcessor("rawAudioProcessor", RawAudioProcessor);
|
|
|
22372
22388
|
inputFormat;
|
|
22373
22389
|
outputFormat;
|
|
22374
22390
|
outputListeners = /* @__PURE__ */ new Set();
|
|
22391
|
+
pendingAudioEvents = [];
|
|
22375
22392
|
constructor(socket, conversationId, inputFormat, outputFormat) {
|
|
22376
22393
|
super();
|
|
22377
22394
|
this.socket = socket;
|
|
@@ -22472,13 +22489,19 @@ registerProcessor("rawAudioProcessor", RawAudioProcessor);
|
|
|
22472
22489
|
}
|
|
22473
22490
|
}
|
|
22474
22491
|
close() {
|
|
22492
|
+
this.pendingAudioEvents = [];
|
|
22475
22493
|
this.socket.close(1e3, "User ended conversation");
|
|
22476
22494
|
}
|
|
22477
22495
|
sendMessage(message) {
|
|
22478
22496
|
this.socket.send(JSON.stringify(message));
|
|
22479
22497
|
}
|
|
22480
22498
|
addListener(listener) {
|
|
22499
|
+
const hadListeners = this.outputListeners.size > 0;
|
|
22481
22500
|
this.outputListeners.add(listener);
|
|
22501
|
+
if (hadListeners || this.pendingAudioEvents.length === 0) return;
|
|
22502
|
+
const pending = this.pendingAudioEvents;
|
|
22503
|
+
this.pendingAudioEvents = [];
|
|
22504
|
+
for (const event of pending) listener(event);
|
|
22482
22505
|
}
|
|
22483
22506
|
removeListener(listener) {
|
|
22484
22507
|
this.outputListeners.delete(listener);
|
|
@@ -22487,6 +22510,10 @@ registerProcessor("rawAudioProcessor", RawAudioProcessor);
|
|
|
22487
22510
|
super.handleMessage(parsedEvent);
|
|
22488
22511
|
if (parsedEvent.type === "audio" && parsedEvent.audio_event.audio_base_64) {
|
|
22489
22512
|
const audioEvent = { audio_base_64: parsedEvent.audio_event.audio_base_64 };
|
|
22513
|
+
if (this.outputListeners.size === 0) {
|
|
22514
|
+
this.pendingAudioEvents.push(audioEvent);
|
|
22515
|
+
return;
|
|
22516
|
+
}
|
|
22490
22517
|
this.outputListeners.forEach((listener) => listener(audioEvent));
|
|
22491
22518
|
}
|
|
22492
22519
|
}
|
|
@@ -22553,6 +22580,70 @@ registerProcessor("rawAudioProcessor", RawAudioProcessor);
|
|
|
22553
22580
|
if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
22554
22581
|
}
|
|
22555
22582
|
//#endregion
|
|
22583
|
+
//#region src/platform/web/audioUnlock.ts
|
|
22584
|
+
const STASH_TTL_MS = 3e4;
|
|
22585
|
+
const UNLOCK_EVENTS = [
|
|
22586
|
+
"touchstart",
|
|
22587
|
+
"touchend",
|
|
22588
|
+
"click"
|
|
22589
|
+
];
|
|
22590
|
+
let stashedAudioContext = null;
|
|
22591
|
+
let discardTimer = null;
|
|
22592
|
+
let unlockListenerInstalled = false;
|
|
22593
|
+
function unlockAudioContext(ctx) {
|
|
22594
|
+
const buffer = ctx.createBuffer(1, 1, 22050);
|
|
22595
|
+
const source = ctx.createBufferSource();
|
|
22596
|
+
source.buffer = buffer;
|
|
22597
|
+
source.connect(ctx.destination);
|
|
22598
|
+
source.start(0);
|
|
22599
|
+
ctx.resume().catch(() => {});
|
|
22600
|
+
}
|
|
22601
|
+
function scheduleStashDiscard() {
|
|
22602
|
+
if (discardTimer) clearTimeout(discardTimer);
|
|
22603
|
+
discardTimer = setTimeout(() => {
|
|
22604
|
+
discardTimer = null;
|
|
22605
|
+
discardStashedAudioContext();
|
|
22606
|
+
}, STASH_TTL_MS);
|
|
22607
|
+
}
|
|
22608
|
+
function clearStashDiscardTimer() {
|
|
22609
|
+
if (!discardTimer) return;
|
|
22610
|
+
clearTimeout(discardTimer);
|
|
22611
|
+
discardTimer = null;
|
|
22612
|
+
}
|
|
22613
|
+
/** Unlock iOS audio during a user gesture. No-op on non-iOS. */
|
|
22614
|
+
function unlockIosAudioForSession() {
|
|
22615
|
+
if (!isIosDevice() || stashedAudioContext) return;
|
|
22616
|
+
const ctx = new AudioContext();
|
|
22617
|
+
unlockAudioContext(ctx);
|
|
22618
|
+
stashedAudioContext = ctx;
|
|
22619
|
+
scheduleStashDiscard();
|
|
22620
|
+
}
|
|
22621
|
+
/**
|
|
22622
|
+
* Listen for the first user interaction on the page and unlock iOS audio in
|
|
22623
|
+
* the capture phase, before application click handlers run. Covers callers
|
|
22624
|
+
* (such as the convai widget) that `await` before `Conversation.startSession`.
|
|
22625
|
+
*/
|
|
22626
|
+
function installIosAudioUnlockListener() {
|
|
22627
|
+
if (!isIosDevice() || unlockListenerInstalled || typeof document === "undefined") return;
|
|
22628
|
+
unlockListenerInstalled = true;
|
|
22629
|
+
const onUserGesture = () => {
|
|
22630
|
+
unlockIosAudioForSession();
|
|
22631
|
+
};
|
|
22632
|
+
for (const event of UNLOCK_EVENTS) document.addEventListener(event, onUserGesture, true);
|
|
22633
|
+
}
|
|
22634
|
+
function takeUnlockedAudioContext() {
|
|
22635
|
+
const ctx = stashedAudioContext;
|
|
22636
|
+
stashedAudioContext = null;
|
|
22637
|
+
clearStashDiscardTimer();
|
|
22638
|
+
return ctx;
|
|
22639
|
+
}
|
|
22640
|
+
function discardStashedAudioContext() {
|
|
22641
|
+
if (!stashedAudioContext) return;
|
|
22642
|
+
stashedAudioContext.close().catch(() => {});
|
|
22643
|
+
stashedAudioContext = null;
|
|
22644
|
+
clearStashDiscardTimer();
|
|
22645
|
+
}
|
|
22646
|
+
//#endregion
|
|
22556
22647
|
//#region src/platform/web/VoiceSessionSetup.ts
|
|
22557
22648
|
function detectPlatform() {
|
|
22558
22649
|
if (isAndroidDevice()) return "android";
|
|
@@ -22569,7 +22660,7 @@ registerProcessor("rawAudioProcessor", RawAudioProcessor);
|
|
|
22569
22660
|
* Sets up WebSocket-specific input and output controllers using
|
|
22570
22661
|
* web MediaDevice APIs (AudioContext, AudioWorklet, etc.).
|
|
22571
22662
|
*/
|
|
22572
|
-
async function setupWebSocketIO(options, connection) {
|
|
22663
|
+
async function setupWebSocketIO(options, connection, audioContext) {
|
|
22573
22664
|
const [input, output] = await Promise.all([MediaDeviceInput.create({
|
|
22574
22665
|
...connection.inputFormat,
|
|
22575
22666
|
preferHeadphonesForIosDevices: options.preferHeadphonesForIosDevices,
|
|
@@ -22579,7 +22670,8 @@ registerProcessor("rawAudioProcessor", RawAudioProcessor);
|
|
|
22579
22670
|
}), MediaDeviceOutput.create({
|
|
22580
22671
|
...connection.outputFormat,
|
|
22581
22672
|
outputDeviceId: options.outputDeviceId,
|
|
22582
|
-
workletPaths: options.workletPaths
|
|
22673
|
+
workletPaths: options.workletPaths,
|
|
22674
|
+
audioContext: audioContext ?? void 0
|
|
22583
22675
|
})]);
|
|
22584
22676
|
const detachInput = attachInputToConnection(input, connection);
|
|
22585
22677
|
const detachOutput = attachConnectionToOutput(connection, output);
|
|
@@ -22602,6 +22694,7 @@ registerProcessor("rawAudioProcessor", RawAudioProcessor);
|
|
|
22602
22694
|
const useWakeLock = options.useWakeLock ?? true;
|
|
22603
22695
|
let wakeLock = null;
|
|
22604
22696
|
let preliminaryInputStream = null;
|
|
22697
|
+
let unlockedAudioContext = null;
|
|
22605
22698
|
try {
|
|
22606
22699
|
if (useWakeLock) wakeLock = await requestWakeLock();
|
|
22607
22700
|
preliminaryInputStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
@@ -22610,12 +22703,20 @@ registerProcessor("rawAudioProcessor", RawAudioProcessor);
|
|
|
22610
22703
|
const connection = await createConnection(options);
|
|
22611
22704
|
let result;
|
|
22612
22705
|
try {
|
|
22613
|
-
if (connection instanceof WebSocketConnection)
|
|
22614
|
-
|
|
22615
|
-
|
|
22616
|
-
|
|
22617
|
-
|
|
22706
|
+
if (connection instanceof WebSocketConnection) {
|
|
22707
|
+
unlockedAudioContext = takeUnlockedAudioContext();
|
|
22708
|
+
result = {
|
|
22709
|
+
connection,
|
|
22710
|
+
...await setupWebSocketIO(options, connection, unlockedAudioContext)
|
|
22711
|
+
};
|
|
22712
|
+
unlockedAudioContext = null;
|
|
22713
|
+
} else {
|
|
22714
|
+
discardStashedAudioContext();
|
|
22715
|
+
result = setupWebRTCSession(connection);
|
|
22716
|
+
}
|
|
22618
22717
|
} catch (ioError) {
|
|
22718
|
+
await unlockedAudioContext?.close().catch(() => {});
|
|
22719
|
+
unlockedAudioContext = null;
|
|
22619
22720
|
connection.close();
|
|
22620
22721
|
throw ioError;
|
|
22621
22722
|
}
|
|
@@ -22650,6 +22751,7 @@ registerProcessor("rawAudioProcessor", RawAudioProcessor);
|
|
|
22650
22751
|
await wakeLock?.release();
|
|
22651
22752
|
wakeLock = null;
|
|
22652
22753
|
} catch (_e) {}
|
|
22754
|
+
discardStashedAudioContext();
|
|
22653
22755
|
throw error;
|
|
22654
22756
|
}
|
|
22655
22757
|
}
|
|
@@ -23993,6 +24095,7 @@ registerProcessor("scribeAudioProcessor", ScribeAudioProcessor);
|
|
|
23993
24095
|
} };
|
|
23994
24096
|
//#endregion
|
|
23995
24097
|
//#region src/platform/web/index.ts
|
|
24098
|
+
installIosAudioUnlockListener();
|
|
23996
24099
|
setWebRTCAudioAdapterFactory(() => new WebAudioAdapter());
|
|
23997
24100
|
setScribeMicrophoneSetup(webScribeMicrophoneSetup);
|
|
23998
24101
|
//#endregion
|