@cartesia/cartesia-js 1.0.0-alpha.1 → 1.0.0-alpha.3
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/.turbo/turbo-build.log +26 -26
- package/CHANGELOG.md +12 -0
- package/dist/chunk-2BFEKY3F.js +16 -0
- package/dist/{chunk-WE63M7PJ.js → chunk-36JBKJUN.js} +2 -2
- package/dist/{chunk-NDNN326Q.js → chunk-IQAXBRHU.js} +9 -5
- package/dist/{chunk-X7SJMF2R.js → chunk-ISRU7PLL.js} +3 -3
- package/dist/{chunk-BCQ63627.js → chunk-PQ5EVEEH.js} +4 -2
- package/dist/{chunk-JOHSCOLW.js → chunk-PQ6CIPFW.js} +20 -6
- package/dist/{chunk-LYPTISWL.js → chunk-RO7TY474.js} +13 -7
- package/dist/{chunk-WBK6LLXX.js → chunk-SGXUEFII.js} +1 -1
- package/dist/{chunk-4RMSIQLG.js → chunk-VK7LBMVI.js} +2 -2
- package/dist/index.cjs +47 -25
- package/dist/index.js +9 -9
- package/dist/lib/client.cjs +9 -10
- package/dist/lib/client.js +2 -2
- package/dist/lib/constants.cjs +9 -9
- package/dist/lib/constants.d.cts +15 -3
- package/dist/lib/constants.d.ts +15 -3
- package/dist/lib/constants.js +3 -1
- package/dist/lib/index.cjs +46 -24
- package/dist/lib/index.js +8 -8
- package/dist/react/index.cjs +63 -33
- package/dist/react/index.js +25 -17
- package/dist/tts/index.cjs +46 -24
- package/dist/tts/index.js +6 -6
- package/dist/tts/player.cjs +1 -1
- package/dist/tts/player.js +2 -2
- package/dist/tts/source.cjs +20 -6
- package/dist/tts/source.js +1 -1
- package/dist/tts/utils.cjs +13 -7
- package/dist/tts/utils.js +1 -1
- package/dist/tts/websocket.cjs +46 -24
- package/dist/tts/websocket.js +5 -5
- package/dist/voices/index.cjs +9 -10
- package/dist/voices/index.js +3 -3
- package/package.json +1 -1
- package/src/lib/client.ts +3 -2
- package/src/lib/constants.ts +18 -9
- package/src/react/index.ts +20 -8
- package/src/tts/player.ts +2 -1
- package/src/tts/source.ts +22 -5
- package/src/tts/utils.ts +15 -7
- package/src/tts/websocket.ts +5 -2
- package/dist/chunk-3GBZUGUD.js +0 -17
package/dist/tts/source.cjs
CHANGED
|
@@ -72,7 +72,7 @@ __export(source_exports, {
|
|
|
72
72
|
});
|
|
73
73
|
module.exports = __toCommonJS(source_exports);
|
|
74
74
|
var import_emittery = __toESM(require("emittery"), 1);
|
|
75
|
-
var _emitter, _buffer, _readIndex, _closed, _sampleRate;
|
|
75
|
+
var _emitter, _buffer, _readIndex, _writeIndex, _closed, _sampleRate;
|
|
76
76
|
var Source = class {
|
|
77
77
|
/**
|
|
78
78
|
* Create a new Source.
|
|
@@ -82,8 +82,9 @@ var Source = class {
|
|
|
82
82
|
*/
|
|
83
83
|
constructor({ sampleRate }) {
|
|
84
84
|
__privateAdd(this, _emitter, new import_emittery.default());
|
|
85
|
-
__privateAdd(this, _buffer,
|
|
85
|
+
__privateAdd(this, _buffer, void 0);
|
|
86
86
|
__privateAdd(this, _readIndex, 0);
|
|
87
|
+
__privateAdd(this, _writeIndex, 0);
|
|
87
88
|
__privateAdd(this, _closed, false);
|
|
88
89
|
__privateAdd(this, _sampleRate, void 0);
|
|
89
90
|
this.on = __privateGet(this, _emitter).on.bind(__privateGet(this, _emitter));
|
|
@@ -91,6 +92,7 @@ var Source = class {
|
|
|
91
92
|
this.events = __privateGet(this, _emitter).events.bind(__privateGet(this, _emitter));
|
|
92
93
|
this.off = __privateGet(this, _emitter).off.bind(__privateGet(this, _emitter));
|
|
93
94
|
__privateSet(this, _sampleRate, sampleRate);
|
|
95
|
+
__privateSet(this, _buffer, new Float32Array(1024));
|
|
94
96
|
}
|
|
95
97
|
get sampleRate() {
|
|
96
98
|
return __privateGet(this, _sampleRate);
|
|
@@ -102,7 +104,18 @@ var Source = class {
|
|
|
102
104
|
*/
|
|
103
105
|
enqueue(src) {
|
|
104
106
|
return __async(this, null, function* () {
|
|
105
|
-
|
|
107
|
+
const requiredCapacity = __privateGet(this, _writeIndex) + src.length;
|
|
108
|
+
if (requiredCapacity > __privateGet(this, _buffer).length) {
|
|
109
|
+
let newCapacity = __privateGet(this, _buffer).length;
|
|
110
|
+
while (newCapacity < requiredCapacity) {
|
|
111
|
+
newCapacity *= 2;
|
|
112
|
+
}
|
|
113
|
+
const newBuffer = new Float32Array(newCapacity);
|
|
114
|
+
newBuffer.set(__privateGet(this, _buffer));
|
|
115
|
+
__privateSet(this, _buffer, newBuffer);
|
|
116
|
+
}
|
|
117
|
+
__privateGet(this, _buffer).set(src, __privateGet(this, _writeIndex));
|
|
118
|
+
__privateSet(this, _writeIndex, __privateGet(this, _writeIndex) + src.length);
|
|
106
119
|
yield __privateGet(this, _emitter).emit("enqueue");
|
|
107
120
|
});
|
|
108
121
|
}
|
|
@@ -116,7 +129,7 @@ var Source = class {
|
|
|
116
129
|
read(dst) {
|
|
117
130
|
return __async(this, null, function* () {
|
|
118
131
|
const targetReadIndex = __privateGet(this, _readIndex) + dst.length;
|
|
119
|
-
while (!__privateGet(this, _closed) && targetReadIndex > __privateGet(this,
|
|
132
|
+
while (!__privateGet(this, _closed) && targetReadIndex > __privateGet(this, _writeIndex)) {
|
|
120
133
|
yield __privateGet(this, _emitter).emit("wait");
|
|
121
134
|
yield Promise.race([
|
|
122
135
|
__privateGet(this, _emitter).once("enqueue"),
|
|
@@ -124,8 +137,8 @@ var Source = class {
|
|
|
124
137
|
]);
|
|
125
138
|
yield __privateGet(this, _emitter).emit("read");
|
|
126
139
|
}
|
|
127
|
-
const read = Math.min(dst.length, __privateGet(this,
|
|
128
|
-
dst.set(__privateGet(this, _buffer).
|
|
140
|
+
const read = Math.min(dst.length, __privateGet(this, _writeIndex) - __privateGet(this, _readIndex));
|
|
141
|
+
dst.set(__privateGet(this, _buffer).subarray(__privateGet(this, _readIndex), __privateGet(this, _readIndex) + read));
|
|
129
142
|
__privateSet(this, _readIndex, __privateGet(this, _readIndex) + read);
|
|
130
143
|
return read;
|
|
131
144
|
});
|
|
@@ -163,5 +176,6 @@ var Source = class {
|
|
|
163
176
|
_emitter = new WeakMap();
|
|
164
177
|
_buffer = new WeakMap();
|
|
165
178
|
_readIndex = new WeakMap();
|
|
179
|
+
_writeIndex = new WeakMap();
|
|
166
180
|
_closed = new WeakMap();
|
|
167
181
|
_sampleRate = new WeakMap();
|
package/dist/tts/source.js
CHANGED
package/dist/tts/utils.cjs
CHANGED
|
@@ -42,13 +42,19 @@ __export(utils_exports, {
|
|
|
42
42
|
module.exports = __toCommonJS(utils_exports);
|
|
43
43
|
var import_base64_js = __toESM(require("base64-js"), 1);
|
|
44
44
|
function base64ToArray(b64) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
const byteArrays = filterSentinel(b64).map((b) => import_base64_js.default.toByteArray(b));
|
|
46
|
+
const totalLength = byteArrays.reduce(
|
|
47
|
+
(acc, arr) => acc + arr.length / Float32Array.BYTES_PER_ELEMENT,
|
|
48
|
+
0
|
|
49
|
+
);
|
|
50
|
+
const result = new Float32Array(totalLength);
|
|
51
|
+
let offset = 0;
|
|
52
|
+
for (const arr of byteArrays) {
|
|
53
|
+
const floats = new Float32Array(arr.buffer);
|
|
54
|
+
result.set(floats, offset);
|
|
55
|
+
offset += floats.length;
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
52
58
|
}
|
|
53
59
|
function playAudioBuffer(floats, context, startAt, sampleRate) {
|
|
54
60
|
const source = context.createBufferSource();
|
package/dist/tts/utils.js
CHANGED
package/dist/tts/websocket.cjs
CHANGED
|
@@ -100,16 +100,14 @@ var import_partysocket = require("partysocket");
|
|
|
100
100
|
var import_cross_fetch = __toESM(require("cross-fetch"), 1);
|
|
101
101
|
|
|
102
102
|
// src/lib/constants.ts
|
|
103
|
-
var BASE_URL = "https://api.cartesia.ai
|
|
104
|
-
var
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
var BASE_URL = "https://api.cartesia.ai";
|
|
104
|
+
var CARTESIA_VERSION = "2024-06-10";
|
|
105
|
+
var constructApiUrl = (baseUrl, path, { websocket = false } = {}) => {
|
|
106
|
+
const url = new URL(path, baseUrl);
|
|
107
|
+
if (websocket) {
|
|
108
|
+
url.protocol = baseUrl.replace(/^http/, "ws");
|
|
108
109
|
}
|
|
109
|
-
|
|
110
|
-
throw new Error(`Invalid protocol: ${protocol}`);
|
|
111
|
-
}
|
|
112
|
-
return new URL(`${baseUrl.replace(/^http/, protocol)}${normalizedPath}`);
|
|
110
|
+
return url;
|
|
113
111
|
};
|
|
114
112
|
|
|
115
113
|
// src/lib/client.ts
|
|
@@ -125,7 +123,8 @@ var Client = class {
|
|
|
125
123
|
const url = constructApiUrl(this.baseUrl, path);
|
|
126
124
|
return (0, import_cross_fetch.default)(url.toString(), __spreadProps(__spreadValues({}, options), {
|
|
127
125
|
headers: __spreadValues({
|
|
128
|
-
"X-API-
|
|
126
|
+
"X-API-Key": this.apiKey,
|
|
127
|
+
"Cartesia-Version": CARTESIA_VERSION
|
|
129
128
|
}, options.headers)
|
|
130
129
|
}));
|
|
131
130
|
}
|
|
@@ -133,7 +132,7 @@ var Client = class {
|
|
|
133
132
|
|
|
134
133
|
// src/tts/source.ts
|
|
135
134
|
var import_emittery = __toESM(require("emittery"), 1);
|
|
136
|
-
var _emitter, _buffer, _readIndex, _closed, _sampleRate;
|
|
135
|
+
var _emitter, _buffer, _readIndex, _writeIndex, _closed, _sampleRate;
|
|
137
136
|
var Source = class {
|
|
138
137
|
/**
|
|
139
138
|
* Create a new Source.
|
|
@@ -143,8 +142,9 @@ var Source = class {
|
|
|
143
142
|
*/
|
|
144
143
|
constructor({ sampleRate }) {
|
|
145
144
|
__privateAdd(this, _emitter, new import_emittery.default());
|
|
146
|
-
__privateAdd(this, _buffer,
|
|
145
|
+
__privateAdd(this, _buffer, void 0);
|
|
147
146
|
__privateAdd(this, _readIndex, 0);
|
|
147
|
+
__privateAdd(this, _writeIndex, 0);
|
|
148
148
|
__privateAdd(this, _closed, false);
|
|
149
149
|
__privateAdd(this, _sampleRate, void 0);
|
|
150
150
|
this.on = __privateGet(this, _emitter).on.bind(__privateGet(this, _emitter));
|
|
@@ -152,6 +152,7 @@ var Source = class {
|
|
|
152
152
|
this.events = __privateGet(this, _emitter).events.bind(__privateGet(this, _emitter));
|
|
153
153
|
this.off = __privateGet(this, _emitter).off.bind(__privateGet(this, _emitter));
|
|
154
154
|
__privateSet(this, _sampleRate, sampleRate);
|
|
155
|
+
__privateSet(this, _buffer, new Float32Array(1024));
|
|
155
156
|
}
|
|
156
157
|
get sampleRate() {
|
|
157
158
|
return __privateGet(this, _sampleRate);
|
|
@@ -163,7 +164,18 @@ var Source = class {
|
|
|
163
164
|
*/
|
|
164
165
|
enqueue(src) {
|
|
165
166
|
return __async(this, null, function* () {
|
|
166
|
-
|
|
167
|
+
const requiredCapacity = __privateGet(this, _writeIndex) + src.length;
|
|
168
|
+
if (requiredCapacity > __privateGet(this, _buffer).length) {
|
|
169
|
+
let newCapacity = __privateGet(this, _buffer).length;
|
|
170
|
+
while (newCapacity < requiredCapacity) {
|
|
171
|
+
newCapacity *= 2;
|
|
172
|
+
}
|
|
173
|
+
const newBuffer = new Float32Array(newCapacity);
|
|
174
|
+
newBuffer.set(__privateGet(this, _buffer));
|
|
175
|
+
__privateSet(this, _buffer, newBuffer);
|
|
176
|
+
}
|
|
177
|
+
__privateGet(this, _buffer).set(src, __privateGet(this, _writeIndex));
|
|
178
|
+
__privateSet(this, _writeIndex, __privateGet(this, _writeIndex) + src.length);
|
|
167
179
|
yield __privateGet(this, _emitter).emit("enqueue");
|
|
168
180
|
});
|
|
169
181
|
}
|
|
@@ -177,7 +189,7 @@ var Source = class {
|
|
|
177
189
|
read(dst) {
|
|
178
190
|
return __async(this, null, function* () {
|
|
179
191
|
const targetReadIndex = __privateGet(this, _readIndex) + dst.length;
|
|
180
|
-
while (!__privateGet(this, _closed) && targetReadIndex > __privateGet(this,
|
|
192
|
+
while (!__privateGet(this, _closed) && targetReadIndex > __privateGet(this, _writeIndex)) {
|
|
181
193
|
yield __privateGet(this, _emitter).emit("wait");
|
|
182
194
|
yield Promise.race([
|
|
183
195
|
__privateGet(this, _emitter).once("enqueue"),
|
|
@@ -185,8 +197,8 @@ var Source = class {
|
|
|
185
197
|
]);
|
|
186
198
|
yield __privateGet(this, _emitter).emit("read");
|
|
187
199
|
}
|
|
188
|
-
const read = Math.min(dst.length, __privateGet(this,
|
|
189
|
-
dst.set(__privateGet(this, _buffer).
|
|
200
|
+
const read = Math.min(dst.length, __privateGet(this, _writeIndex) - __privateGet(this, _readIndex));
|
|
201
|
+
dst.set(__privateGet(this, _buffer).subarray(__privateGet(this, _readIndex), __privateGet(this, _readIndex) + read));
|
|
190
202
|
__privateSet(this, _readIndex, __privateGet(this, _readIndex) + read);
|
|
191
203
|
return read;
|
|
192
204
|
});
|
|
@@ -224,19 +236,26 @@ var Source = class {
|
|
|
224
236
|
_emitter = new WeakMap();
|
|
225
237
|
_buffer = new WeakMap();
|
|
226
238
|
_readIndex = new WeakMap();
|
|
239
|
+
_writeIndex = new WeakMap();
|
|
227
240
|
_closed = new WeakMap();
|
|
228
241
|
_sampleRate = new WeakMap();
|
|
229
242
|
|
|
230
243
|
// src/tts/utils.ts
|
|
231
244
|
var import_base64_js = __toESM(require("base64-js"), 1);
|
|
232
245
|
function base64ToArray(b64) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
246
|
+
const byteArrays = filterSentinel(b64).map((b) => import_base64_js.default.toByteArray(b));
|
|
247
|
+
const totalLength = byteArrays.reduce(
|
|
248
|
+
(acc, arr) => acc + arr.length / Float32Array.BYTES_PER_ELEMENT,
|
|
249
|
+
0
|
|
250
|
+
);
|
|
251
|
+
const result = new Float32Array(totalLength);
|
|
252
|
+
let offset = 0;
|
|
253
|
+
for (const arr of byteArrays) {
|
|
254
|
+
const floats = new Float32Array(arr.buffer);
|
|
255
|
+
result.set(floats, offset);
|
|
256
|
+
offset += floats.length;
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
240
259
|
}
|
|
241
260
|
function createMessageHandlerForContextId(contextId, handler) {
|
|
242
261
|
return (event) => {
|
|
@@ -386,8 +405,11 @@ var WebSocket = class extends Client {
|
|
|
386
405
|
* @throws {Error} If the WebSocket fails to connect.
|
|
387
406
|
*/
|
|
388
407
|
connect() {
|
|
389
|
-
const url = constructApiUrl(this.baseUrl, "/tts/websocket",
|
|
408
|
+
const url = constructApiUrl(this.baseUrl, "/tts/websocket", {
|
|
409
|
+
websocket: true
|
|
410
|
+
});
|
|
390
411
|
url.searchParams.set("api_key", this.apiKey);
|
|
412
|
+
url.searchParams.set("cartesia_version", CARTESIA_VERSION);
|
|
391
413
|
const emitter = new import_emittery2.default();
|
|
392
414
|
this.socket = new import_partysocket.WebSocket(url.toString());
|
|
393
415
|
this.socket.onopen = () => {
|
package/dist/tts/websocket.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WebSocket
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-IQAXBRHU.js";
|
|
4
|
+
import "../chunk-PQ6CIPFW.js";
|
|
5
|
+
import "../chunk-PQ5EVEEH.js";
|
|
6
|
+
import "../chunk-2BFEKY3F.js";
|
|
7
|
+
import "../chunk-RO7TY474.js";
|
|
8
8
|
import "../chunk-WIFMLPT5.js";
|
|
9
9
|
export {
|
|
10
10
|
WebSocket as default
|
package/dist/voices/index.cjs
CHANGED
|
@@ -75,16 +75,14 @@ module.exports = __toCommonJS(voices_exports);
|
|
|
75
75
|
var import_cross_fetch = __toESM(require("cross-fetch"), 1);
|
|
76
76
|
|
|
77
77
|
// src/lib/constants.ts
|
|
78
|
-
var BASE_URL = "https://api.cartesia.ai
|
|
79
|
-
var
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
var BASE_URL = "https://api.cartesia.ai";
|
|
79
|
+
var CARTESIA_VERSION = "2024-06-10";
|
|
80
|
+
var constructApiUrl = (baseUrl, path, { websocket = false } = {}) => {
|
|
81
|
+
const url = new URL(path, baseUrl);
|
|
82
|
+
if (websocket) {
|
|
83
|
+
url.protocol = baseUrl.replace(/^http/, "ws");
|
|
83
84
|
}
|
|
84
|
-
|
|
85
|
-
throw new Error(`Invalid protocol: ${protocol}`);
|
|
86
|
-
}
|
|
87
|
-
return new URL(`${baseUrl.replace(/^http/, protocol)}${normalizedPath}`);
|
|
85
|
+
return url;
|
|
88
86
|
};
|
|
89
87
|
|
|
90
88
|
// src/lib/client.ts
|
|
@@ -100,7 +98,8 @@ var Client = class {
|
|
|
100
98
|
const url = constructApiUrl(this.baseUrl, path);
|
|
101
99
|
return (0, import_cross_fetch.default)(url.toString(), __spreadProps(__spreadValues({}, options), {
|
|
102
100
|
headers: __spreadValues({
|
|
103
|
-
"X-API-
|
|
101
|
+
"X-API-Key": this.apiKey,
|
|
102
|
+
"Cartesia-Version": CARTESIA_VERSION
|
|
104
103
|
}, options.headers)
|
|
105
104
|
}));
|
|
106
105
|
}
|
package/dist/voices/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Voices
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-SGXUEFII.js";
|
|
4
|
+
import "../chunk-PQ5EVEEH.js";
|
|
5
|
+
import "../chunk-2BFEKY3F.js";
|
|
6
6
|
import "../chunk-WIFMLPT5.js";
|
|
7
7
|
export {
|
|
8
8
|
Voices as default
|
package/package.json
CHANGED
package/src/lib/client.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fetch from "cross-fetch";
|
|
2
2
|
import type { ClientOptions } from "../types";
|
|
3
|
-
import { BASE_URL, constructApiUrl } from "./constants";
|
|
3
|
+
import { BASE_URL, CARTESIA_VERSION, constructApiUrl } from "./constants";
|
|
4
4
|
|
|
5
5
|
export class Client {
|
|
6
6
|
apiKey: string;
|
|
@@ -22,7 +22,8 @@ export class Client {
|
|
|
22
22
|
return fetch(url.toString(), {
|
|
23
23
|
...options,
|
|
24
24
|
headers: {
|
|
25
|
-
"X-API-
|
|
25
|
+
"X-API-Key": this.apiKey,
|
|
26
|
+
"Cartesia-Version": CARTESIA_VERSION,
|
|
26
27
|
...options.headers,
|
|
27
28
|
},
|
|
28
29
|
});
|
package/src/lib/constants.ts
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
export const BASE_URL = "https://api.cartesia.ai
|
|
1
|
+
export const BASE_URL = "https://api.cartesia.ai";
|
|
2
|
+
export const CARTESIA_VERSION = "2024-06-10";
|
|
2
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Construct a URL for the Cartesia API.
|
|
6
|
+
*
|
|
7
|
+
* @param baseUrl The base URL for the API.
|
|
8
|
+
* @param path The path to append to the base URL.
|
|
9
|
+
* @param options Options for the URL.
|
|
10
|
+
* @param options.websocket Whether to use the WebSocket protocol.
|
|
11
|
+
* @returns A URL object.
|
|
12
|
+
*/
|
|
3
13
|
export const constructApiUrl = (
|
|
4
14
|
baseUrl: string,
|
|
5
15
|
path: string,
|
|
6
|
-
|
|
16
|
+
{ websocket = false } = {},
|
|
7
17
|
) => {
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
|
|
18
|
+
const url = new URL(path, baseUrl);
|
|
19
|
+
if (websocket) {
|
|
20
|
+
// Using find-and-replace ensures that if the base URL uses TLS, the
|
|
21
|
+
// new protocol does too.
|
|
22
|
+
url.protocol = baseUrl.replace(/^http/, "ws");
|
|
11
23
|
}
|
|
12
|
-
|
|
13
|
-
throw new Error(`Invalid protocol: ${protocol}`);
|
|
14
|
-
}
|
|
15
|
-
return new URL(`${baseUrl.replace(/^http/, protocol)}${normalizedPath}`);
|
|
24
|
+
return url;
|
|
16
25
|
};
|
package/src/react/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { UnsubscribeFunction } from "emittery";
|
|
1
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
3
|
import { Cartesia } from "../lib";
|
|
3
4
|
import Player from "../tts/player";
|
|
@@ -91,11 +92,12 @@ export function useTTS({
|
|
|
91
92
|
if (!websocketReturn.current) {
|
|
92
93
|
return;
|
|
93
94
|
}
|
|
94
|
-
websocketReturn.current.on("message", (message) => {
|
|
95
|
+
const unsubscribe = websocketReturn.current.on("message", (message) => {
|
|
95
96
|
setMessages((messages) => [...messages, JSON.parse(message)]);
|
|
96
97
|
});
|
|
97
98
|
await websocketReturn.current.source.once("close");
|
|
98
99
|
setBufferStatus("buffered");
|
|
100
|
+
unsubscribe();
|
|
99
101
|
},
|
|
100
102
|
[websocket],
|
|
101
103
|
);
|
|
@@ -121,13 +123,21 @@ export function useTTS({
|
|
|
121
123
|
if (!connection) {
|
|
122
124
|
return;
|
|
123
125
|
}
|
|
126
|
+
const unsubscribes = <UnsubscribeFunction[]>[];
|
|
127
|
+
// The await ensures that the connection is open, so we already know that we are connected.
|
|
124
128
|
setIsConnected(true);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
// If the WebSocket is the kind that automatically reconnects, we need an additional
|
|
130
|
+
// listener for the open event to update the connection status.
|
|
131
|
+
unsubscribes.push(
|
|
132
|
+
connection.on("open", () => {
|
|
133
|
+
setIsConnected(true);
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
unsubscribes.push(
|
|
137
|
+
connection.on("close", () => {
|
|
138
|
+
setIsConnected(false);
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
131
141
|
const intervalId = setInterval(() => {
|
|
132
142
|
if (baseUrl) {
|
|
133
143
|
pingServer(new URL(baseUrl).origin).then((ping) => {
|
|
@@ -144,7 +154,9 @@ export function useTTS({
|
|
|
144
154
|
}
|
|
145
155
|
}, PING_INTERVAL);
|
|
146
156
|
return () => {
|
|
147
|
-
unsubscribe
|
|
157
|
+
for (const unsubscribe of unsubscribes) {
|
|
158
|
+
unsubscribe();
|
|
159
|
+
}
|
|
148
160
|
clearInterval(intervalId);
|
|
149
161
|
websocket?.disconnect();
|
|
150
162
|
};
|
package/src/tts/player.ts
CHANGED
|
@@ -54,7 +54,8 @@ export default class Player {
|
|
|
54
54
|
// If we've reached the end of the source, then read < buffer.length.
|
|
55
55
|
// In that case, we don't want to play the entire buffer, as that
|
|
56
56
|
// will cause repeated audio.
|
|
57
|
-
|
|
57
|
+
// So we set the buffer to the correct length.
|
|
58
|
+
const playableAudio = buffer.subarray(0, read);
|
|
58
59
|
plays.push(this.#playBuffer(playableAudio, source.sampleRate));
|
|
59
60
|
|
|
60
61
|
if (read < buffer.length) {
|
package/src/tts/source.ts
CHANGED
|
@@ -3,8 +3,9 @@ import type { SourceEventData } from "../types";
|
|
|
3
3
|
|
|
4
4
|
export default class Source {
|
|
5
5
|
#emitter = new Emittery<SourceEventData>();
|
|
6
|
-
#buffer
|
|
6
|
+
#buffer: Float32Array;
|
|
7
7
|
#readIndex = 0;
|
|
8
|
+
#writeIndex = 0;
|
|
8
9
|
#closed = false;
|
|
9
10
|
#sampleRate: number;
|
|
10
11
|
|
|
@@ -21,6 +22,7 @@ export default class Source {
|
|
|
21
22
|
*/
|
|
22
23
|
constructor({ sampleRate }: { sampleRate: number }) {
|
|
23
24
|
this.#sampleRate = sampleRate;
|
|
25
|
+
this.#buffer = new Float32Array(1024); // Initial size, can be adjusted
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
get sampleRate() {
|
|
@@ -33,8 +35,23 @@ export default class Source {
|
|
|
33
35
|
* @param src The audio to append.
|
|
34
36
|
*/
|
|
35
37
|
async enqueue(src: Float32Array) {
|
|
38
|
+
const requiredCapacity = this.#writeIndex + src.length;
|
|
39
|
+
|
|
40
|
+
// Resize buffer if necessary
|
|
41
|
+
if (requiredCapacity > this.#buffer.length) {
|
|
42
|
+
let newCapacity = this.#buffer.length;
|
|
43
|
+
while (newCapacity < requiredCapacity) {
|
|
44
|
+
newCapacity *= 2; // Double the buffer size
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const newBuffer = new Float32Array(newCapacity);
|
|
48
|
+
newBuffer.set(this.#buffer);
|
|
49
|
+
this.#buffer = newBuffer;
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
// Append the audio to the buffer.
|
|
37
|
-
this.#buffer
|
|
53
|
+
this.#buffer.set(src, this.#writeIndex);
|
|
54
|
+
this.#writeIndex += src.length;
|
|
38
55
|
await this.#emitter.emit("enqueue");
|
|
39
56
|
}
|
|
40
57
|
|
|
@@ -49,7 +66,7 @@ export default class Source {
|
|
|
49
66
|
// Read the buffer into the provided buffer.
|
|
50
67
|
const targetReadIndex = this.#readIndex + dst.length;
|
|
51
68
|
|
|
52
|
-
while (!this.#closed && targetReadIndex > this.#
|
|
69
|
+
while (!this.#closed && targetReadIndex > this.#writeIndex) {
|
|
53
70
|
// Wait for more audio to be enqueued.
|
|
54
71
|
await this.#emitter.emit("wait");
|
|
55
72
|
await Promise.race([
|
|
@@ -59,8 +76,8 @@ export default class Source {
|
|
|
59
76
|
await this.#emitter.emit("read");
|
|
60
77
|
}
|
|
61
78
|
|
|
62
|
-
const read = Math.min(dst.length, this.#
|
|
63
|
-
dst.set(this.#buffer.
|
|
79
|
+
const read = Math.min(dst.length, this.#writeIndex - this.#readIndex);
|
|
80
|
+
dst.set(this.#buffer.subarray(this.#readIndex, this.#readIndex + read));
|
|
64
81
|
this.#readIndex += read;
|
|
65
82
|
return read;
|
|
66
83
|
}
|
package/src/tts/utils.ts
CHANGED
|
@@ -10,13 +10,21 @@ import type { Chunk, EmitteryCallbacks, Sentinel } from "../types";
|
|
|
10
10
|
* @returns The audio buffer(s) as a Float32Array.
|
|
11
11
|
*/
|
|
12
12
|
export function base64ToArray(b64: Chunk[]): Float32Array {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
const byteArrays = filterSentinel(b64).map((b) => base64.toByteArray(b));
|
|
14
|
+
const totalLength = byteArrays.reduce(
|
|
15
|
+
(acc, arr) => acc + arr.length / Float32Array.BYTES_PER_ELEMENT,
|
|
16
|
+
0,
|
|
17
|
+
);
|
|
18
|
+
const result = new Float32Array(totalLength);
|
|
19
|
+
|
|
20
|
+
let offset = 0;
|
|
21
|
+
for (const arr of byteArrays) {
|
|
22
|
+
const floats = new Float32Array(arr.buffer);
|
|
23
|
+
result.set(floats, offset);
|
|
24
|
+
offset += floats.length;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return result;
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
/**
|
package/src/tts/websocket.ts
CHANGED
|
@@ -2,7 +2,7 @@ import Emittery from "emittery";
|
|
|
2
2
|
import { humanId } from "human-id";
|
|
3
3
|
import { WebSocket as PartySocketWebSocket } from "partysocket";
|
|
4
4
|
import { Client } from "../lib/client";
|
|
5
|
-
import { constructApiUrl } from "../lib/constants";
|
|
5
|
+
import { CARTESIA_VERSION, constructApiUrl } from "../lib/constants";
|
|
6
6
|
import type {
|
|
7
7
|
ConnectionEventData,
|
|
8
8
|
EmitteryCallbacks,
|
|
@@ -150,8 +150,11 @@ export default class WebSocket extends Client {
|
|
|
150
150
|
* @throws {Error} If the WebSocket fails to connect.
|
|
151
151
|
*/
|
|
152
152
|
connect() {
|
|
153
|
-
const url = constructApiUrl(this.baseUrl, "/tts/websocket",
|
|
153
|
+
const url = constructApiUrl(this.baseUrl, "/tts/websocket", {
|
|
154
|
+
websocket: true,
|
|
155
|
+
});
|
|
154
156
|
url.searchParams.set("api_key", this.apiKey);
|
|
157
|
+
url.searchParams.set("cartesia_version", CARTESIA_VERSION);
|
|
155
158
|
const emitter = new Emittery<ConnectionEventData>();
|
|
156
159
|
this.socket = new PartySocketWebSocket(url.toString());
|
|
157
160
|
this.socket.onopen = () => {
|
package/dist/chunk-3GBZUGUD.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// src/lib/constants.ts
|
|
2
|
-
var BASE_URL = "https://api.cartesia.ai/v0";
|
|
3
|
-
var constructApiUrl = (baseUrl, path, protocol) => {
|
|
4
|
-
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
5
|
-
if (!protocol) {
|
|
6
|
-
return new URL(`${baseUrl}${normalizedPath}`);
|
|
7
|
-
}
|
|
8
|
-
if (!["http", "ws"].includes(protocol)) {
|
|
9
|
-
throw new Error(`Invalid protocol: ${protocol}`);
|
|
10
|
-
}
|
|
11
|
-
return new URL(`${baseUrl.replace(/^http/, protocol)}${normalizedPath}`);
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
BASE_URL,
|
|
16
|
-
constructApiUrl
|
|
17
|
-
};
|