@cartesia/cartesia-js 0.0.3 → 0.0.4-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.turbo/turbo-build.log +44 -32
  2. package/CHANGELOG.md +6 -0
  3. package/dist/audio/index.cjs +404 -0
  4. package/dist/audio/index.d.cts +5 -0
  5. package/dist/audio/index.d.ts +1 -1
  6. package/dist/audio/index.js +9 -395
  7. package/dist/audio/utils.cjs +157 -0
  8. package/dist/audio/{utils.d.mts → utils.d.cts} +3 -3
  9. package/dist/audio/utils.d.ts +1 -1
  10. package/dist/audio/utils.js +16 -147
  11. package/dist/{chunk-3CYTAFLF.mjs → chunk-4MHF74A7.js} +15 -5
  12. package/dist/{chunk-FRIBQZPN.mjs → chunk-5TSWLYOW.js} +1 -1
  13. package/dist/{chunk-XSFPHPPG.mjs → chunk-MJIFZWHS.js} +1 -1
  14. package/dist/chunk-OVI3W3GG.js +12 -0
  15. package/dist/{chunk-HNLIBHEN.mjs → chunk-R4P7LWVZ.js} +1 -11
  16. package/dist/{lib/index.mjs → chunk-S6A27RQL.js} +3 -4
  17. package/dist/chunk-XPIMIAAE.js +17 -0
  18. package/dist/{index-DSBmfK9-.d.mts → index-C2_3XFxn.d.cts} +9 -4
  19. package/dist/{index-qwAyxV5I.d.ts → index-DgwnZezj.d.ts} +8 -3
  20. package/dist/index.cjs +419 -0
  21. package/dist/index.d.cts +6 -0
  22. package/dist/index.d.ts +6 -0
  23. package/dist/index.js +13 -0
  24. package/dist/lib/client.cjs +43 -0
  25. package/dist/lib/{client.d.mts → client.d.cts} +1 -1
  26. package/dist/lib/client.js +7 -42
  27. package/dist/lib/constants.cjs +38 -0
  28. package/dist/lib/constants.js +8 -35
  29. package/dist/lib/index.cjs +418 -0
  30. package/dist/lib/{index.d.mts → index.d.cts} +3 -3
  31. package/dist/lib/index.d.ts +1 -1
  32. package/dist/lib/index.js +10 -409
  33. package/dist/react/index.cjs +597 -0
  34. package/dist/react/{index.d.mts → index.d.cts} +4 -3
  35. package/dist/react/index.d.ts +2 -1
  36. package/dist/react/index.js +71 -455
  37. package/dist/react/utils.cjs +57 -0
  38. package/dist/react/utils.d.cts +7 -0
  39. package/dist/react/utils.d.ts +7 -0
  40. package/dist/react/utils.js +7 -0
  41. package/dist/types/index.cjs +18 -0
  42. package/dist/types/index.js +1 -18
  43. package/package.json +10 -7
  44. package/src/audio/index.ts +15 -0
  45. package/src/index.ts +3 -0
  46. package/src/react/index.ts +48 -10
  47. package/src/react/utils.ts +11 -0
  48. package/dist/audio/index.d.mts +0 -5
  49. package/dist/audio/index.mjs +0 -9
  50. package/dist/audio/utils.mjs +0 -25
  51. package/dist/lib/client.mjs +0 -7
  52. package/dist/lib/constants.mjs +0 -10
  53. package/dist/react/index.mjs +0 -130
  54. package/index.ts +0 -3
  55. /package/dist/{types/index.mjs → chunk-FXPGR372.js} +0 -0
  56. /package/dist/lib/{constants.d.mts → constants.d.cts} +0 -0
  57. /package/dist/types/{index.d.mts → index.d.cts} +0 -0
@@ -1,18 +1 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __copyProps = (to, from, except, desc) => {
7
- if (from && typeof from === "object" || typeof from === "function") {
8
- for (let key of __getOwnPropNames(from))
9
- if (!__hasOwnProp.call(to, key) && key !== except)
10
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
- }
12
- return to;
13
- };
14
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
-
16
- // src/types/index.ts
17
- var types_exports = {};
18
- module.exports = __toCommonJS(types_exports);
1
+ import "../chunk-FXPGR372.js";
package/package.json CHANGED
@@ -4,21 +4,24 @@
4
4
  "name": "Cartesia",
5
5
  "url": "https://cartesia.ai"
6
6
  },
7
- "version": "0.0.3",
7
+ "version": "0.0.4-alpha.0",
8
8
  "description": "Client for the Cartesia API.",
9
- "main": "./dist/index.js",
10
- "module": "./dist/index.mjs",
9
+ "type": "module",
10
+ "module": "./dist/index.js",
11
11
  "types": "./dist/index.d.ts",
12
12
  "exports": {
13
13
  ".": {
14
- "import": "./dist/index.mjs",
15
- "require": "./dist/index.js"
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs"
16
16
  },
17
17
  "./react": {
18
- "import": "./dist/react/index.mjs",
19
- "require": "./dist/react/index.js"
18
+ "import": "./dist/react/index.js",
19
+ "require": "./dist/react/index.cjs"
20
20
  }
21
21
  },
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
22
25
  "dependencies": {
23
26
  "base64-js": "^1.5.1",
24
27
  "emittery": "^1.0.3",
@@ -24,6 +24,11 @@ export type StreamEventData = {
24
24
  chunks: Chunk[];
25
25
  };
26
26
  message: unknown;
27
+ buffering: never;
28
+ buffered: never;
29
+ scheduled: {
30
+ playbackEndsIn: number;
31
+ };
27
32
  };
28
33
  export type ConnectionEventData = {
29
34
  open: never;
@@ -166,11 +171,13 @@ export default class extends Client {
166
171
  startNextPlaybackAt = context.currentTime;
167
172
 
168
173
  if (isComplete(chunks) || streamCompleteController.signal.aborted) {
174
+ emitter.emit("buffered");
169
175
  playChunks(chunks);
170
176
  return true; // Done playing.
171
177
  }
172
178
 
173
179
  if (getBufferDuration(chunks) > bufferDuration) {
180
+ emitter.emit("buffered");
174
181
  // Play the initial chunks that we already have.
175
182
  playChunks(chunks);
176
183
  // If the stream is not complete, play new chunks as they
@@ -182,15 +189,23 @@ export default class extends Client {
182
189
  }
183
190
  return true; // Done playing.
184
191
  }
192
+ emitter.emit("buffering");
185
193
  return false; // Need to buffer more audio.
186
194
  };
187
195
 
188
196
  if (!(await tryStart(chunks))) {
189
197
  for await (const { chunks } of emitter.events("chunk")) {
190
198
  if (await tryStart(chunks)) {
199
+ const playbackEndsIn =
200
+ Math.max(0, startNextPlaybackAt - context.currentTime) * 1000;
201
+ emitter.emit("scheduled", { playbackEndsIn });
191
202
  break;
192
203
  }
193
204
  }
205
+ } else {
206
+ const playbackEndsIn =
207
+ Math.max(0, startNextPlaybackAt - context.currentTime) * 1000;
208
+ emitter.emit("scheduled", { playbackEndsIn });
194
209
  }
195
210
  };
196
211
 
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { Cartesia as default } from "./lib";
2
+ export * from "./lib";
3
+ export * from "./types";
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import CartesiaAudio, { type Chunk, type StreamEventData } from "../audio";
3
3
  import { base64ToArray, bufferToWav } from "../audio/utils";
4
4
  import { SAMPLE_RATE } from "../lib/constants";
5
+ import { pingServer } from "./utils";
5
6
 
6
7
  export type UseAudioOptions = {
7
8
  apiKey: string | null;
@@ -15,6 +16,7 @@ interface UseAudioReturn {
15
16
  isPlaying: boolean;
16
17
  isConnected: boolean;
17
18
  isStreamed: boolean;
19
+ isBuffering: boolean;
18
20
  chunks: Chunk[];
19
21
  messages: StreamEventData["message"][];
20
22
  }
@@ -30,6 +32,7 @@ export function useAudio({ apiKey, baseUrl }: UseAudioOptions): UseAudioReturn {
30
32
  isConnected: false,
31
33
  isPlaying: false,
32
34
  isStreamed: false,
35
+ isBuffering: false,
33
36
  chunks: [],
34
37
  messages: [],
35
38
  };
@@ -45,12 +48,16 @@ export function useAudio({ apiKey, baseUrl }: UseAudioOptions): UseAudioReturn {
45
48
  const streamReturn = useRef<ReturnType<CartesiaAudio["stream"]> | null>(null);
46
49
  const [isStreamed, setIsStreamed] = useState(false);
47
50
  const [isPlaying, setIsPlaying] = useState(false);
51
+ const [isBuffering, setIsBuffering] = useState(false);
48
52
  const [isConnected, setIsConnected] = useState(false);
49
53
  const [chunks, setChunks] = useState<Chunk[]>([]);
50
54
  const [messages, setMessages] = useState<StreamEventData["message"][]>([]);
51
55
 
56
+ const latencyEndpoint = "https://api.cartesia.ai";
57
+
52
58
  const stream = useCallback(
53
59
  async (options: object) => {
60
+ setIsStreamed(false);
54
61
  streamReturn.current = audio?.stream(options) ?? null;
55
62
  if (!streamReturn.current) {
56
63
  return;
@@ -92,6 +99,9 @@ export function useAudio({ apiKey, baseUrl }: UseAudioOptions): UseAudioReturn {
92
99
  return;
93
100
  }
94
101
  setIsConnected(true);
102
+ connection.on("open", () => {
103
+ setIsConnected(true);
104
+ });
95
105
  const unsubscribe = connection.on("close", () => {
96
106
  setIsConnected(false);
97
107
  });
@@ -109,17 +119,44 @@ export function useAudio({ apiKey, baseUrl }: UseAudioOptions): UseAudioReturn {
109
119
  return () => cleanup?.();
110
120
  }, [audio]);
111
121
 
112
- const play = useCallback(
113
- async (bufferDuration = 0) => {
114
- if (isPlaying || !streamReturn.current) {
115
- return;
122
+ const play = useCallback(async () => {
123
+ if (isPlaying || !streamReturn.current) {
124
+ return;
125
+ }
126
+ setIsPlaying(true);
127
+ const ping = await pingServer(latencyEndpoint);
128
+ let bufferingTimeout: ReturnType<typeof setTimeout> | null;
129
+
130
+ let bufferDuration: number;
131
+ if (ping < 300) {
132
+ bufferDuration = 0; // No buffering for very low latency
133
+ } else if (ping > 1500) {
134
+ bufferDuration = 6; // Max buffering for very high latency (6 seconds)
135
+ } else {
136
+ bufferDuration = (ping / 1000) * 4; // Adjust buffer duration based on ping
137
+ }
138
+
139
+ streamReturn.current.once("buffering").then(() => {
140
+ bufferingTimeout = setTimeout(() => {
141
+ setIsBuffering(true);
142
+ }, 250); // Delay for 250ms before showing buffering indicator
143
+ });
144
+ streamReturn.current.once("buffered").then(() => {
145
+ if (bufferingTimeout) {
146
+ clearTimeout(bufferingTimeout); // Clear the timeout if buffering completes before 500ms
116
147
  }
117
- setIsPlaying(true);
118
- await streamReturn.current?.play({ bufferDuration });
119
- setIsPlaying(false);
120
- },
121
- [isPlaying],
122
- );
148
+ setIsBuffering(false);
149
+ });
150
+
151
+ // Wait for the playback to finish before setting isPlaying to false
152
+ streamReturn.current.once("scheduled").then((data) => {
153
+ setTimeout(() => {
154
+ setIsPlaying(false);
155
+ }, data.playbackEndsIn);
156
+ });
157
+
158
+ await streamReturn.current?.play({ bufferDuration });
159
+ }, [isPlaying]);
123
160
 
124
161
  // TODO:
125
162
  // - [] Pause and stop playback.
@@ -134,6 +171,7 @@ export function useAudio({ apiKey, baseUrl }: UseAudioOptions): UseAudioReturn {
134
171
  isPlaying,
135
172
  isConnected,
136
173
  isStreamed,
174
+ isBuffering,
137
175
  chunks,
138
176
  messages,
139
177
  };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Ping the server to calculate the round-trip time. This is useful for buffering audio in high-latency environments.
3
+ * @param url The URL to ping.
4
+ */
5
+
6
+ export async function pingServer(url: string): Promise<number> {
7
+ const start = new Date().getTime();
8
+ await fetch(url);
9
+ const end = new Date().getTime();
10
+ return end - start;
11
+ }
@@ -1,5 +0,0 @@
1
- import 'emittery';
2
- import 'partysocket';
3
- import '../lib/client.mjs';
4
- export { C as Chunk, l as ConnectionEventData, S as StreamEventData, m as StreamRequest, e as default } from '../index-DSBmfK9-.mjs';
5
- import '../types/index.mjs';
@@ -1,9 +0,0 @@
1
- import {
2
- audio_default
3
- } from "../chunk-3CYTAFLF.mjs";
4
- import "../chunk-FRIBQZPN.mjs";
5
- import "../chunk-XSFPHPPG.mjs";
6
- import "../chunk-HNLIBHEN.mjs";
7
- export {
8
- audio_default as default
9
- };
@@ -1,25 +0,0 @@
1
- import {
2
- base64ToArray,
3
- bufferToWav,
4
- createMessageHandlerForContextId,
5
- filterSentinel,
6
- getBufferDuration,
7
- getEmitteryCallbacks,
8
- getSentinel,
9
- isComplete,
10
- isSentinel,
11
- playAudioBuffer
12
- } from "../chunk-FRIBQZPN.mjs";
13
- import "../chunk-HNLIBHEN.mjs";
14
- export {
15
- base64ToArray,
16
- bufferToWav,
17
- createMessageHandlerForContextId,
18
- filterSentinel,
19
- getBufferDuration,
20
- getEmitteryCallbacks,
21
- getSentinel,
22
- isComplete,
23
- isSentinel,
24
- playAudioBuffer
25
- };
@@ -1,7 +0,0 @@
1
- import {
2
- Client
3
- } from "../chunk-XSFPHPPG.mjs";
4
- import "../chunk-HNLIBHEN.mjs";
5
- export {
6
- Client
7
- };
@@ -1,10 +0,0 @@
1
- import {
2
- BASE_URL,
3
- SAMPLE_RATE,
4
- constructWebsocketUrl
5
- } from "../chunk-HNLIBHEN.mjs";
6
- export {
7
- BASE_URL,
8
- SAMPLE_RATE,
9
- constructWebsocketUrl
10
- };
@@ -1,130 +0,0 @@
1
- import {
2
- audio_default
3
- } from "../chunk-3CYTAFLF.mjs";
4
- import {
5
- base64ToArray,
6
- bufferToWav
7
- } from "../chunk-FRIBQZPN.mjs";
8
- import "../chunk-XSFPHPPG.mjs";
9
- import {
10
- SAMPLE_RATE,
11
- __async
12
- } from "../chunk-HNLIBHEN.mjs";
13
-
14
- // src/react/index.ts
15
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
16
- function useAudio({ apiKey, baseUrl }) {
17
- if (typeof window === "undefined") {
18
- return {
19
- stream: () => {
20
- },
21
- play: () => __async(this, null, function* () {
22
- }),
23
- download: () => null,
24
- isConnected: false,
25
- isPlaying: false,
26
- isStreamed: false,
27
- chunks: [],
28
- messages: []
29
- };
30
- }
31
- const audio = useMemo(() => {
32
- if (!apiKey) {
33
- return null;
34
- }
35
- const audio2 = new audio_default({ apiKey, baseUrl });
36
- return audio2;
37
- }, [apiKey, baseUrl]);
38
- const streamReturn = useRef(null);
39
- const [isStreamed, setIsStreamed] = useState(false);
40
- const [isPlaying, setIsPlaying] = useState(false);
41
- const [isConnected, setIsConnected] = useState(false);
42
- const [chunks, setChunks] = useState([]);
43
- const [messages, setMessages] = useState([]);
44
- const stream = useCallback(
45
- (options) => __async(this, null, function* () {
46
- var _a;
47
- streamReturn.current = (_a = audio == null ? void 0 : audio.stream(options)) != null ? _a : null;
48
- if (!streamReturn.current) {
49
- return;
50
- }
51
- setMessages([]);
52
- streamReturn.current.on(
53
- "chunk",
54
- ({ chunks: chunks3 }) => {
55
- setChunks(chunks3);
56
- }
57
- );
58
- streamReturn.current.on(
59
- "message",
60
- (message) => {
61
- setMessages((messages2) => [...messages2, message]);
62
- }
63
- );
64
- const { chunks: chunks2 } = yield streamReturn.current.once("streamed");
65
- setChunks(chunks2);
66
- setIsStreamed(true);
67
- }),
68
- [audio]
69
- );
70
- const download = useCallback(() => {
71
- if (!isStreamed) {
72
- return null;
73
- }
74
- const audio2 = bufferToWav(SAMPLE_RATE, [base64ToArray(chunks)]);
75
- return new Blob([audio2], { type: "audio/wav" });
76
- }, [isStreamed, chunks]);
77
- useEffect(() => {
78
- let cleanup = () => {
79
- };
80
- function setupConnection() {
81
- return __async(this, null, function* () {
82
- try {
83
- const connection = yield audio == null ? void 0 : audio.connect();
84
- if (!connection) {
85
- return;
86
- }
87
- setIsConnected(true);
88
- const unsubscribe = connection.on("close", () => {
89
- setIsConnected(false);
90
- });
91
- return () => {
92
- unsubscribe();
93
- audio == null ? void 0 : audio.disconnect();
94
- };
95
- } catch (e) {
96
- console.error(e);
97
- }
98
- });
99
- }
100
- setupConnection().then((cleanupConnection) => {
101
- cleanup = cleanupConnection;
102
- });
103
- return () => cleanup == null ? void 0 : cleanup();
104
- }, [audio]);
105
- const play = useCallback(
106
- (bufferDuration = 0) => __async(this, null, function* () {
107
- var _a;
108
- if (isPlaying || !streamReturn.current) {
109
- return;
110
- }
111
- setIsPlaying(true);
112
- yield (_a = streamReturn.current) == null ? void 0 : _a.play({ bufferDuration });
113
- setIsPlaying(false);
114
- }),
115
- [isPlaying]
116
- );
117
- return {
118
- stream,
119
- play,
120
- download,
121
- isPlaying,
122
- isConnected,
123
- isStreamed,
124
- chunks,
125
- messages
126
- };
127
- }
128
- export {
129
- useAudio
130
- };
package/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export { Cartesia as default } from "./src/lib";
2
- export * from "./src/lib";
3
- export * from "./src/types";
File without changes
File without changes
File without changes