@cartesia/cartesia-js 0.0.4-alpha.0 → 1.0.0-alpha.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.
Files changed (87) hide show
  1. package/.turbo/turbo-build.log +64 -46
  2. package/CHANGELOG.md +6 -0
  3. package/README.md +123 -16
  4. package/dist/{chunk-XPIMIAAE.js → chunk-3FL2SNIR.js} +1 -1
  5. package/dist/chunk-3GBZUGUD.js +17 -0
  6. package/dist/chunk-4RMSIQLG.js +25 -0
  7. package/dist/chunk-BCQ63627.js +32 -0
  8. package/dist/chunk-JOHSCOLW.js +106 -0
  9. package/dist/chunk-LYPTISWL.js +75 -0
  10. package/dist/chunk-NDNN326Q.js +207 -0
  11. package/dist/chunk-WBK6LLXX.js +58 -0
  12. package/dist/chunk-WE63M7PJ.js +119 -0
  13. package/dist/{chunk-R4P7LWVZ.js → chunk-WIFMLPT5.js} +31 -6
  14. package/dist/chunk-X7SJMF2R.js +22 -0
  15. package/dist/index.cjs +391 -158
  16. package/dist/index.d.cts +7 -3
  17. package/dist/index.d.ts +7 -3
  18. package/dist/index.js +13 -6
  19. package/dist/lib/client.cjs +46 -0
  20. package/dist/lib/client.d.cts +2 -0
  21. package/dist/lib/client.d.ts +2 -0
  22. package/dist/lib/client.js +3 -3
  23. package/dist/lib/constants.cjs +11 -7
  24. package/dist/lib/constants.d.cts +2 -3
  25. package/dist/lib/constants.d.ts +2 -3
  26. package/dist/lib/constants.js +4 -6
  27. package/dist/lib/index.cjs +276 -163
  28. package/dist/lib/index.d.cts +6 -2
  29. package/dist/lib/index.d.ts +6 -2
  30. package/dist/lib/index.js +9 -6
  31. package/dist/react/index.cjs +524 -275
  32. package/dist/react/index.d.cts +20 -14
  33. package/dist/react/index.d.ts +20 -14
  34. package/dist/react/index.js +142 -98
  35. package/dist/react/utils.js +2 -2
  36. package/dist/tts/index.cjs +470 -0
  37. package/dist/tts/index.d.cts +17 -0
  38. package/dist/tts/index.d.ts +17 -0
  39. package/dist/tts/index.js +12 -0
  40. package/dist/tts/player.cjs +198 -0
  41. package/dist/tts/player.d.cts +43 -0
  42. package/dist/tts/player.d.ts +43 -0
  43. package/dist/tts/player.js +8 -0
  44. package/dist/tts/source.cjs +167 -0
  45. package/dist/tts/source.d.cts +53 -0
  46. package/dist/tts/source.d.ts +53 -0
  47. package/dist/tts/source.js +7 -0
  48. package/dist/{audio → tts}/utils.cjs +12 -53
  49. package/dist/tts/utils.d.cts +67 -0
  50. package/dist/tts/utils.d.ts +67 -0
  51. package/dist/{audio → tts}/utils.js +2 -7
  52. package/dist/{audio/index.cjs → tts/websocket.cjs} +213 -164
  53. package/dist/tts/websocket.d.cts +53 -0
  54. package/dist/tts/websocket.d.ts +53 -0
  55. package/dist/tts/websocket.js +11 -0
  56. package/dist/types/index.d.cts +50 -1
  57. package/dist/types/index.d.ts +50 -1
  58. package/dist/voices/index.cjs +155 -0
  59. package/dist/voices/index.d.cts +12 -0
  60. package/dist/voices/index.d.ts +12 -0
  61. package/dist/voices/index.js +9 -0
  62. package/package.json +2 -1
  63. package/src/index.ts +1 -0
  64. package/src/lib/client.ts +14 -1
  65. package/src/lib/constants.ts +13 -3
  66. package/src/lib/index.ts +6 -3
  67. package/src/react/index.ts +157 -103
  68. package/src/tts/index.ts +17 -0
  69. package/src/tts/player.ts +109 -0
  70. package/src/tts/source.ts +98 -0
  71. package/src/{audio → tts}/utils.ts +19 -97
  72. package/src/tts/websocket.ts +210 -0
  73. package/src/types/index.ts +63 -0
  74. package/src/voices/index.ts +47 -0
  75. package/dist/audio/index.d.cts +0 -5
  76. package/dist/audio/index.d.ts +0 -5
  77. package/dist/audio/index.js +0 -10
  78. package/dist/audio/utils.d.cts +0 -5
  79. package/dist/audio/utils.d.ts +0 -5
  80. package/dist/chunk-4MHF74A7.js +0 -272
  81. package/dist/chunk-5TSWLYOW.js +0 -113
  82. package/dist/chunk-MJIFZWHS.js +0 -18
  83. package/dist/chunk-OVI3W3GG.js +0 -12
  84. package/dist/chunk-S6A27RQL.js +0 -18
  85. package/dist/index-C2_3XFxn.d.cts +0 -163
  86. package/dist/index-DgwnZezj.d.ts +0 -163
  87. package/src/audio/index.ts +0 -297
@@ -1,163 +0,0 @@
1
- import * as emittery from 'emittery';
2
- import emittery__default from 'emittery';
3
- import { WebSocket } from 'partysocket';
4
- import { Client } from './lib/client.js';
5
-
6
- /**
7
- * Get the duration of base64-encoded audio buffer(s) in seconds.
8
- *
9
- * @param b64 The base64-encoded audio buffer, or an array of base64-encoded
10
- * audio buffers.
11
- * @returns The duration of the buffer(s) in seconds.
12
- */
13
- declare function getBufferDuration(b64: Chunk[]): number;
14
- /**
15
- * Convert base64-encoded audio buffer(s) to a Float32Array.
16
- *
17
- * @param b64 The base64-encoded audio buffer, or an array of base64-encoded
18
- * audio buffers.
19
- * @returns The audio buffer(s) as a Float32Array.
20
- */
21
- declare function base64ToArray(b64: Chunk[]): Float32Array;
22
- /**
23
- * Schedule an audio buffer to play at a given time in the passed context.
24
- *
25
- * @param b64 The base64-encoded audio buffer to play.
26
- * @param context The audio context to play the buffer in.
27
- * @param maybeStartAt The time to start playing the buffer at, or null to play
28
- * immediately.
29
- * @param onEnded The callback to call when the buffer has finished playing.
30
- * @returns The duration of the buffer in seconds.
31
- */
32
- declare function playAudioBuffer(b64: Chunk[], context: AudioContext, maybeStartAt?: number | null, onEnded?: AudioScheduledSourceNode["onended"]): number;
33
- /**
34
- * Unwraps a chunk of audio data from a message event and calls the
35
- * handler with it if the context ID matches.
36
- *
37
- * @param contextId The context ID to listen for.
38
- * @param handler The handler to call with the chunk of audio data.
39
- * @returns A message event handler.
40
- */
41
- declare function createMessageHandlerForContextId(contextId: string, handler: ({ chunk, message, }: {
42
- chunk: Chunk;
43
- message: StreamEventData["message"];
44
- }) => void): (event: MessageEvent) => void;
45
- type Sentinel = null;
46
- /**
47
- * Get a sentinel value that indicates the end of a stream.
48
- * @returns A sentinel value to indicate the end of a stream.
49
- */
50
- declare function getSentinel(): Sentinel;
51
- /**
52
- * Check if a chunk is a sentinel value (i.e. null).
53
- *
54
- * @param chunk
55
- * @returns Whether the chunk is a sentinel value.
56
- */
57
- declare function isSentinel(x: unknown): x is Sentinel;
58
- /**
59
- * Filter out null values from a collection.
60
- *
61
- * @param collection The collection to filter.
62
- * @returns The collection with null values removed.
63
- */
64
- declare function filterSentinel<T>(collection: T[]): Exclude<T, Sentinel>[];
65
- /**
66
- * Check if an array of chunks is complete by testing if the last chunk is a sentinel
67
- * value (i.e. null).
68
- * @param chunk
69
- * @returns Whether the array of chunks is complete.
70
- */
71
- declare function isComplete(chunks: Chunk[]): boolean;
72
- type EmitteryCallbacks<T> = {
73
- on: emittery__default<T>["on"];
74
- off: emittery__default<T>["off"];
75
- once: emittery__default<T>["once"];
76
- events: emittery__default<T>["events"];
77
- };
78
- /**
79
- * Get user-facing emitter callbacks for an Emittery instance.
80
- * @param emitter The Emittery instance to get callbacks for.
81
- * @returns User-facing emitter callbacks.
82
- */
83
- declare function getEmitteryCallbacks<T>(emitter: emittery__default<T>): EmitteryCallbacks<T>;
84
- /**
85
- * Converts a base64-encoded audio buffer to a WAV file.
86
- * Source: https://gist.github.com/Daninet/22edc59cf2aee0b9a90c18e553e49297
87
- * @param b64 The base64-encoded audio buffer to convert to a WAV file.
88
- */
89
- declare function bufferToWav(sampleRate: number, channelBuffers: Float32Array[]): ArrayBuffer;
90
-
91
- type Chunk = string | Sentinel;
92
- type StreamEventData = {
93
- chunk: {
94
- chunk: Chunk;
95
- chunks: Chunk[];
96
- };
97
- streamed: {
98
- chunks: Chunk[];
99
- };
100
- message: unknown;
101
- buffering: never;
102
- buffered: never;
103
- scheduled: {
104
- playbackEndsIn: number;
105
- };
106
- };
107
- type ConnectionEventData = {
108
- open: never;
109
- close: never;
110
- };
111
- type StreamRequest = {
112
- inputs: object;
113
- options: {
114
- timeout?: number;
115
- };
116
- };
117
- declare class export_default extends Client {
118
- socket?: WebSocket;
119
- isConnected: boolean;
120
- /**
121
- * Stream audio from a model.
122
- *
123
- * @param inputs - Stream options. Includes a `model` key and some `parameters`, which
124
- * are model-specific and can be found in the model's documentation.
125
- * @param options - Options for the stream.
126
- * @param options.timeout - The maximum time to wait for a chunk before cancelling the stream.
127
- * If `0`, the stream will not time out.
128
- * @returns An object with a method `play` of type `(bufferDuration: number) => Promise<void>`
129
- * that plays the audio as it arrives, with `bufferDuration` seconds of audio buffered before
130
- * starting playback.
131
- */
132
- stream(inputs: StreamRequest["inputs"], { timeout }?: StreamRequest["options"]): {
133
- on: <Name extends keyof emittery.OmnipresentEventData | keyof StreamEventData>(eventName: Name | readonly Name[], listener: (eventData: (StreamEventData & emittery.OmnipresentEventData)[Name]) => void | Promise<void>) => emittery.UnsubscribeFunction;
134
- off: <Name_1 extends keyof emittery.OmnipresentEventData | keyof StreamEventData>(eventName: Name_1 | readonly Name_1[], listener: (eventData: (StreamEventData & emittery.OmnipresentEventData)[Name_1]) => void | Promise<void>) => void;
135
- once: <Name_2 extends keyof emittery.OmnipresentEventData | keyof StreamEventData>(eventName: Name_2 | readonly Name_2[]) => emittery.EmitteryOncePromise<(StreamEventData & emittery.OmnipresentEventData)[Name_2]>;
136
- events: <Name_3 extends keyof StreamEventData>(eventName: Name_3 | readonly Name_3[]) => AsyncIterableIterator<StreamEventData[Name_3]>;
137
- play: ({ bufferDuration }: {
138
- bufferDuration: number;
139
- }) => Promise<void>;
140
- };
141
- /**
142
- * Generate a unique ID suitable for a streaming context.
143
- *
144
- * Not suitable for security purposes or as a primary key, since
145
- * it lacks the amount of entropy required for those use cases.
146
- *
147
- * @returns A unique ID.
148
- */
149
- generateId(): string;
150
- /**
151
- * Authenticate and connect to a Cartesia streaming WebSocket.
152
- *
153
- * @returns A promise that resolves when the WebSocket is connected.
154
- * @throws {Error} If the WebSocket fails to connect.
155
- */
156
- connect(): Promise<EmitteryCallbacks<ConnectionEventData>>;
157
- /**
158
- * Disconnect from the Cartesia streaming WebSocket.
159
- */
160
- disconnect(): void;
161
- }
162
-
163
- export { type Chunk as C, type EmitteryCallbacks as E, type StreamEventData as S, type Sentinel as a, base64ToArray as b, createMessageHandlerForContextId as c, getSentinel as d, export_default as e, filterSentinel as f, getBufferDuration as g, isComplete as h, isSentinel as i, getEmitteryCallbacks as j, bufferToWav as k, type ConnectionEventData as l, type StreamRequest as m, playAudioBuffer as p };
@@ -1,297 +0,0 @@
1
- import Emittery from "emittery";
2
- import { humanId } from "human-id";
3
- import { WebSocket } from "partysocket";
4
- import { Client } from "../lib/client";
5
- import { SAMPLE_RATE, constructWebsocketUrl } from "../lib/constants";
6
- import {
7
- type EmitteryCallbacks,
8
- type Sentinel,
9
- createMessageHandlerForContextId,
10
- getBufferDuration,
11
- getEmitteryCallbacks,
12
- isComplete,
13
- isSentinel,
14
- playAudioBuffer,
15
- } from "./utils";
16
-
17
- export type Chunk = string | Sentinel;
18
- export type StreamEventData = {
19
- chunk: {
20
- chunk: Chunk;
21
- chunks: Chunk[];
22
- };
23
- streamed: {
24
- chunks: Chunk[];
25
- };
26
- message: unknown;
27
- buffering: never;
28
- buffered: never;
29
- scheduled: {
30
- playbackEndsIn: number;
31
- };
32
- };
33
- export type ConnectionEventData = {
34
- open: never;
35
- close: never;
36
- };
37
- export type StreamRequest = {
38
- inputs: object;
39
- options: {
40
- timeout?: number;
41
- };
42
- };
43
-
44
- export default class extends Client {
45
- socket?: WebSocket;
46
- isConnected = false;
47
-
48
- /**
49
- * Stream audio from a model.
50
- *
51
- * @param inputs - Stream options. Includes a `model` key and some `parameters`, which
52
- * are model-specific and can be found in the model's documentation.
53
- * @param options - Options for the stream.
54
- * @param options.timeout - The maximum time to wait for a chunk before cancelling the stream.
55
- * If `0`, the stream will not time out.
56
- * @returns An object with a method `play` of type `(bufferDuration: number) => Promise<void>`
57
- * that plays the audio as it arrives, with `bufferDuration` seconds of audio buffered before
58
- * starting playback.
59
- */
60
- stream(
61
- inputs: StreamRequest["inputs"],
62
- { timeout = 0 }: StreamRequest["options"] = {},
63
- ) {
64
- if (!this.isConnected) {
65
- throw new Error("Not connected to WebSocket. Call .connect() first.");
66
- }
67
-
68
- // Send audio request.
69
- const contextId = this.generateId();
70
- this.socket?.send(
71
- JSON.stringify({
72
- data: inputs,
73
- context_id: contextId,
74
- }),
75
- );
76
-
77
- // Used to signal that the stream is complete, either because the
78
- // WebSocket has closed, or because the stream has finished.
79
- const streamCompleteController = new AbortController();
80
- // Set a timeout.
81
- let timeoutId: ReturnType<typeof setTimeout> | null = null;
82
- if (timeout > 0) {
83
- timeoutId = setTimeout(streamCompleteController.abort, timeout);
84
- }
85
- // Array of base64-encoded audio chunks, representing directly sampled
86
- // audio data, i.e. floats in the range [-1, 1].
87
- const chunks: Chunk[] = [];
88
- // Used to dispatch events.
89
- const emitter = new Emittery<StreamEventData>();
90
- const handleMessage = createMessageHandlerForContextId(
91
- contextId,
92
- async ({ chunk, message }) => {
93
- chunks.push(chunk);
94
- await emitter.emit("chunk", {
95
- chunk,
96
- chunks,
97
- });
98
- await emitter.emit("message", message);
99
- if (isSentinel(chunk)) {
100
- await emitter.emit("streamed", {
101
- chunks,
102
- });
103
- streamCompleteController.abort();
104
- } else if (timeoutId) {
105
- clearTimeout(timeoutId);
106
- timeoutId = setTimeout(streamCompleteController.abort, timeout);
107
- }
108
- },
109
- );
110
- this.socket?.addEventListener("message", handleMessage, {
111
- signal: streamCompleteController.signal,
112
- });
113
- this.socket?.addEventListener(
114
- "close",
115
- () => {
116
- streamCompleteController.abort();
117
- },
118
- {
119
- once: true,
120
- },
121
- );
122
- this.socket?.addEventListener(
123
- "error",
124
- () => {
125
- streamCompleteController.abort();
126
- },
127
- {
128
- once: true,
129
- },
130
- );
131
- streamCompleteController.signal.addEventListener("abort", () => {
132
- if (timeoutId) {
133
- clearTimeout(timeoutId);
134
- }
135
- emitter.clearListeners();
136
- });
137
-
138
- const play = async ({ bufferDuration }: { bufferDuration: number }) => {
139
- const context = new AudioContext({
140
- sampleRate: SAMPLE_RATE,
141
- });
142
-
143
- let startNextPlaybackAt = 0;
144
- const playLatestChunk = (chunk: Chunk) => {
145
- if (isSentinel(chunk)) {
146
- return true; // Indicates that playback has finished.
147
- }
148
- startNextPlaybackAt =
149
- playAudioBuffer([chunk], context, startNextPlaybackAt) +
150
- Math.max(context.currentTime, startNextPlaybackAt);
151
- return false; // Indicates that playback has not finished.
152
- };
153
-
154
- const playChunks = (chunks: Chunk[]) => {
155
- startNextPlaybackAt += playAudioBuffer(
156
- chunks,
157
- context,
158
- startNextPlaybackAt,
159
- );
160
-
161
- if (isComplete(chunks)) {
162
- return;
163
- }
164
- };
165
-
166
- // tryStart tries to start playback if the buffer duration is
167
- // already satisfied or if all the chunks have arrived. If it is
168
- // not, it returns false, indicating that the caller should call
169
- // it again when more chunks arrive.
170
- const tryStart = async (chunks: Chunk[]) => {
171
- startNextPlaybackAt = context.currentTime;
172
-
173
- if (isComplete(chunks) || streamCompleteController.signal.aborted) {
174
- emitter.emit("buffered");
175
- playChunks(chunks);
176
- return true; // Done playing.
177
- }
178
-
179
- if (getBufferDuration(chunks) > bufferDuration) {
180
- emitter.emit("buffered");
181
- // Play the initial chunks that we already have.
182
- playChunks(chunks);
183
- // If the stream is not complete, play new chunks as they
184
- // arrive.
185
- for await (const { chunk } of emitter.events("chunk")) {
186
- if (playLatestChunk(chunk)) {
187
- break;
188
- }
189
- }
190
- return true; // Done playing.
191
- }
192
- emitter.emit("buffering");
193
- return false; // Need to buffer more audio.
194
- };
195
-
196
- if (!(await tryStart(chunks))) {
197
- for await (const { chunks } of emitter.events("chunk")) {
198
- if (await tryStart(chunks)) {
199
- const playbackEndsIn =
200
- Math.max(0, startNextPlaybackAt - context.currentTime) * 1000;
201
- emitter.emit("scheduled", { playbackEndsIn });
202
- break;
203
- }
204
- }
205
- } else {
206
- const playbackEndsIn =
207
- Math.max(0, startNextPlaybackAt - context.currentTime) * 1000;
208
- emitter.emit("scheduled", { playbackEndsIn });
209
- }
210
- };
211
-
212
- return {
213
- play,
214
- ...getEmitteryCallbacks(emitter),
215
- };
216
- }
217
-
218
- /**
219
- * Generate a unique ID suitable for a streaming context.
220
- *
221
- * Not suitable for security purposes or as a primary key, since
222
- * it lacks the amount of entropy required for those use cases.
223
- *
224
- * @returns A unique ID.
225
- */
226
- generateId() {
227
- return humanId({
228
- separator: "-",
229
- capitalize: false,
230
- });
231
- }
232
-
233
- /**
234
- * Authenticate and connect to a Cartesia streaming WebSocket.
235
- *
236
- * @returns A promise that resolves when the WebSocket is connected.
237
- * @throws {Error} If the WebSocket fails to connect.
238
- */
239
- connect() {
240
- const url = constructWebsocketUrl(this.baseUrl);
241
- url.searchParams.set("api_key", this.apiKey);
242
- const emitter = new Emittery<ConnectionEventData>();
243
- this.socket = new WebSocket(url.toString());
244
- this.socket.onopen = () => {
245
- this.isConnected = true;
246
- emitter.emit("open");
247
- };
248
- this.socket.onclose = () => {
249
- this.isConnected = false;
250
- emitter.emit("close");
251
- };
252
-
253
- return new Promise<EmitteryCallbacks<ConnectionEventData>>(
254
- (resolve, reject) => {
255
- this.socket?.addEventListener(
256
- "open",
257
- () => {
258
- resolve(getEmitteryCallbacks(emitter));
259
- },
260
- {
261
- once: true,
262
- },
263
- );
264
-
265
- const aborter = new AbortController();
266
- this.socket?.addEventListener(
267
- "error",
268
- () => {
269
- aborter.abort();
270
- reject(new Error("WebSocket failed to connect."));
271
- },
272
- {
273
- signal: aborter.signal,
274
- },
275
- );
276
-
277
- this.socket?.addEventListener(
278
- "close",
279
- () => {
280
- aborter.abort();
281
- reject(new Error("WebSocket closed before it could connect."));
282
- },
283
- {
284
- signal: aborter.signal,
285
- },
286
- );
287
- },
288
- );
289
- }
290
-
291
- /**
292
- * Disconnect from the Cartesia streaming WebSocket.
293
- */
294
- disconnect() {
295
- this.socket?.close();
296
- }
297
- }