@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.
- package/.turbo/turbo-build.log +44 -32
- package/CHANGELOG.md +6 -0
- package/dist/audio/index.cjs +404 -0
- package/dist/audio/index.d.cts +5 -0
- package/dist/audio/index.d.ts +1 -1
- package/dist/audio/index.js +9 -395
- package/dist/audio/utils.cjs +157 -0
- package/dist/audio/{utils.d.mts → utils.d.cts} +3 -3
- package/dist/audio/utils.d.ts +1 -1
- package/dist/audio/utils.js +16 -147
- package/dist/{chunk-3CYTAFLF.mjs → chunk-4MHF74A7.js} +15 -5
- package/dist/{chunk-FRIBQZPN.mjs → chunk-5TSWLYOW.js} +1 -1
- package/dist/{chunk-XSFPHPPG.mjs → chunk-MJIFZWHS.js} +1 -1
- package/dist/chunk-OVI3W3GG.js +12 -0
- package/dist/{chunk-HNLIBHEN.mjs → chunk-R4P7LWVZ.js} +1 -11
- package/dist/{lib/index.mjs → chunk-S6A27RQL.js} +3 -4
- package/dist/chunk-XPIMIAAE.js +17 -0
- package/dist/{index-DSBmfK9-.d.mts → index-C2_3XFxn.d.cts} +9 -4
- package/dist/{index-qwAyxV5I.d.ts → index-DgwnZezj.d.ts} +8 -3
- package/dist/index.cjs +419 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +13 -0
- package/dist/lib/client.cjs +43 -0
- package/dist/lib/{client.d.mts → client.d.cts} +1 -1
- package/dist/lib/client.js +7 -42
- package/dist/lib/constants.cjs +38 -0
- package/dist/lib/constants.js +8 -35
- package/dist/lib/index.cjs +418 -0
- package/dist/lib/{index.d.mts → index.d.cts} +3 -3
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +10 -409
- package/dist/react/index.cjs +597 -0
- package/dist/react/{index.d.mts → index.d.cts} +4 -3
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +71 -455
- package/dist/react/utils.cjs +57 -0
- package/dist/react/utils.d.cts +7 -0
- package/dist/react/utils.d.ts +7 -0
- package/dist/react/utils.js +7 -0
- package/dist/types/index.cjs +18 -0
- package/dist/types/index.js +1 -18
- package/package.json +10 -7
- package/src/audio/index.ts +15 -0
- package/src/index.ts +3 -0
- package/src/react/index.ts +48 -10
- package/src/react/utils.ts +11 -0
- package/dist/audio/index.d.mts +0 -5
- package/dist/audio/index.mjs +0 -9
- package/dist/audio/utils.mjs +0 -25
- package/dist/lib/client.mjs +0 -7
- package/dist/lib/constants.mjs +0 -10
- package/dist/react/index.mjs +0 -130
- package/index.ts +0 -3
- /package/dist/{types/index.mjs → chunk-FXPGR372.js} +0 -0
- /package/dist/lib/{constants.d.mts → constants.d.cts} +0 -0
- /package/dist/types/{index.d.mts → index.d.cts} +0 -0
package/dist/types/index.js
CHANGED
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
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.
|
|
7
|
+
"version": "0.0.4-alpha.0",
|
|
8
8
|
"description": "Client for the Cartesia API.",
|
|
9
|
-
"
|
|
10
|
-
"module": "./dist/index.
|
|
9
|
+
"type": "module",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
|
-
"import": "./dist/index.
|
|
15
|
-
"require": "./dist/index.
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
16
|
},
|
|
17
17
|
"./react": {
|
|
18
|
-
"import": "./dist/react/index.
|
|
19
|
-
"require": "./dist/react/index.
|
|
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",
|
package/src/audio/index.ts
CHANGED
|
@@ -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
package/src/react/index.ts
CHANGED
|
@@ -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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
+
}
|
package/dist/audio/index.d.mts
DELETED
package/dist/audio/index.mjs
DELETED
package/dist/audio/utils.mjs
DELETED
|
@@ -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
|
-
};
|
package/dist/lib/client.mjs
DELETED
package/dist/lib/constants.mjs
DELETED
package/dist/react/index.mjs
DELETED
|
@@ -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
|
File without changes
|
|
File without changes
|
|
File without changes
|