@cartesia/cartesia-js 1.0.0 → 1.0.2
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 +50 -50
- package/CHANGELOG.md +12 -0
- package/LICENSE.md +21 -0
- package/README.md +92 -19
- package/dist/{chunk-PQ6CIPFW.js → chunk-6YQ6KDIQ.js} +44 -5
- package/dist/{chunk-RO7TY474.js → chunk-BHY7MNGT.js} +11 -6
- package/dist/{chunk-F4QWVJY3.js → chunk-EDAAHENY.js} +2 -2
- package/dist/{chunk-WIFMLPT5.js → chunk-GHY2WEOK.js} +13 -0
- package/dist/{chunk-FN7BK4PS.js → chunk-IZBPLCGW.js} +97 -75
- package/dist/{chunk-JYLAM6VU.js → chunk-LZO6K34D.js} +2 -2
- package/dist/{chunk-3FL2SNIR.js → chunk-NQVZNVOU.js} +1 -1
- package/dist/{chunk-IEN4NCER.js → chunk-NVOCUUOF.js} +3 -3
- package/dist/chunk-PISCPZK4.js +40 -0
- package/dist/{chunk-SGXUEFII.js → chunk-UCYL2SOX.js} +18 -15
- package/dist/index.cjs +186 -103
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +15 -9
- package/dist/lib/client.cjs +35 -10
- package/dist/lib/client.d.cts +2 -2
- package/dist/lib/client.d.ts +2 -2
- package/dist/lib/client.js +2 -2
- package/dist/lib/constants.js +1 -1
- package/dist/lib/index.cjs +181 -102
- package/dist/lib/index.js +8 -8
- package/dist/react/index.cjs +286 -158
- package/dist/react/index.d.cts +5 -4
- package/dist/react/index.d.ts +5 -4
- package/dist/react/index.js +115 -66
- package/dist/react/utils.js +2 -2
- package/dist/tts/index.cjs +165 -89
- package/dist/tts/index.js +6 -6
- package/dist/tts/player.cjs +5 -0
- package/dist/tts/player.js +4 -3
- package/dist/tts/source.cjs +50 -4
- package/dist/tts/source.d.cts +16 -6
- package/dist/tts/source.d.ts +16 -6
- package/dist/tts/source.js +4 -2
- package/dist/tts/utils.cjs +18 -6
- package/dist/tts/utils.d.cts +7 -5
- package/dist/tts/utils.d.ts +7 -5
- package/dist/tts/utils.js +3 -2
- package/dist/tts/websocket.cjs +165 -89
- package/dist/tts/websocket.d.cts +12 -8
- package/dist/tts/websocket.d.ts +12 -8
- package/dist/tts/websocket.js +5 -5
- package/dist/types/index.d.cts +65 -5
- package/dist/types/index.d.ts +65 -5
- package/dist/voices/index.cjs +31 -23
- package/dist/voices/index.d.cts +2 -1
- package/dist/voices/index.d.ts +2 -1
- package/dist/voices/index.js +3 -3
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/lib/client.ts +10 -10
- package/src/react/index.ts +115 -64
- package/src/tts/source.ts +53 -7
- package/src/tts/utils.ts +26 -12
- package/src/tts/websocket.ts +42 -23
- package/src/types/index.ts +89 -4
- package/src/voices/index.ts +22 -15
- package/dist/chunk-PQ5EVEEH.js +0 -34
package/dist/types/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import emittery__default from 'emittery';
|
|
2
2
|
|
|
3
3
|
interface ClientOptions {
|
|
4
|
-
apiKey?: string;
|
|
4
|
+
apiKey?: string | (() => Promise<string>);
|
|
5
5
|
baseUrl?: string;
|
|
6
6
|
}
|
|
7
7
|
type Sentinel = null;
|
|
@@ -10,12 +10,64 @@ type ConnectionEventData = {
|
|
|
10
10
|
open: never;
|
|
11
11
|
close: never;
|
|
12
12
|
};
|
|
13
|
+
type VoiceSpecifier = {
|
|
14
|
+
mode: "id";
|
|
15
|
+
id: string;
|
|
16
|
+
} | {
|
|
17
|
+
mode: "embedding";
|
|
18
|
+
embedding: number[];
|
|
19
|
+
};
|
|
20
|
+
type Emotion = "anger" | "sadness" | "positivity" | "curiosity" | "surprise";
|
|
21
|
+
type Intensity = "lowest" | "low" | "high" | "highest";
|
|
22
|
+
type EmotionControl = Emotion | `${Emotion}:${Intensity}`;
|
|
23
|
+
type VoiceOptions = VoiceSpecifier & {
|
|
24
|
+
__experimental_controls?: {
|
|
25
|
+
speed?: "slowest" | "slow" | "normal" | "fast" | "fastest";
|
|
26
|
+
emotion?: EmotionControl[];
|
|
27
|
+
};
|
|
28
|
+
};
|
|
13
29
|
type StreamRequest = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
30
|
+
model_id: string;
|
|
31
|
+
transcript: string;
|
|
32
|
+
voice: VoiceOptions;
|
|
33
|
+
output_format?: {
|
|
34
|
+
container: string;
|
|
35
|
+
encoding: string;
|
|
36
|
+
sample_rate: number;
|
|
17
37
|
};
|
|
38
|
+
context_id?: string;
|
|
39
|
+
continue?: boolean;
|
|
40
|
+
duration?: number;
|
|
41
|
+
language?: string;
|
|
42
|
+
add_timestamps?: boolean;
|
|
43
|
+
};
|
|
44
|
+
type StreamOptions = {
|
|
45
|
+
timeout?: number;
|
|
46
|
+
};
|
|
47
|
+
type WebSocketBaseResponse = {
|
|
48
|
+
context_id: string;
|
|
49
|
+
status_code: number;
|
|
50
|
+
done: boolean;
|
|
51
|
+
};
|
|
52
|
+
type WordTimestamps = {
|
|
53
|
+
words: string[];
|
|
54
|
+
start: number[];
|
|
55
|
+
end: number[];
|
|
56
|
+
};
|
|
57
|
+
type WebSocketTimestampsResponse = WebSocketBaseResponse & {
|
|
58
|
+
type: "timestamps";
|
|
59
|
+
word_timestamps: WordTimestamps;
|
|
60
|
+
};
|
|
61
|
+
type WebSocketChunkResponse = WebSocketBaseResponse & {
|
|
62
|
+
type: "chunk";
|
|
63
|
+
data: string;
|
|
64
|
+
step_time: number;
|
|
65
|
+
};
|
|
66
|
+
type WebSocketErrorResponse = WebSocketBaseResponse & {
|
|
67
|
+
type: "error";
|
|
68
|
+
error: string;
|
|
18
69
|
};
|
|
70
|
+
type WebSocketResponse = WebSocketTimestampsResponse | WebSocketChunkResponse | WebSocketErrorResponse;
|
|
19
71
|
type EmitteryCallbacks<T> = {
|
|
20
72
|
on: emittery__default<T>["on"];
|
|
21
73
|
off: emittery__default<T>["off"];
|
|
@@ -25,9 +77,11 @@ type EmitteryCallbacks<T> = {
|
|
|
25
77
|
type CloneOptions = {
|
|
26
78
|
mode: "url";
|
|
27
79
|
link: string;
|
|
80
|
+
enhance?: boolean;
|
|
28
81
|
} | {
|
|
29
82
|
mode: "clip";
|
|
30
83
|
clip: Blob;
|
|
84
|
+
enhance?: boolean;
|
|
31
85
|
};
|
|
32
86
|
type Voice = {
|
|
33
87
|
id: string;
|
|
@@ -37,12 +91,16 @@ type Voice = {
|
|
|
37
91
|
is_public: boolean;
|
|
38
92
|
user_id: string;
|
|
39
93
|
created_at: string;
|
|
94
|
+
language: string;
|
|
40
95
|
};
|
|
41
96
|
type CreateVoice = Pick<Voice, "name" | "description" | "embedding"> & Partial<Omit<Voice, "name" | "description" | "embedding">>;
|
|
97
|
+
type UpdateVoice = Partial<Pick<Voice, "name" | "description" | "embedding">>;
|
|
42
98
|
type CloneResponse = {
|
|
43
99
|
embedding: number[];
|
|
44
100
|
};
|
|
45
101
|
type WebSocketOptions = {
|
|
102
|
+
container?: string;
|
|
103
|
+
encoding?: string;
|
|
46
104
|
sampleRate: number;
|
|
47
105
|
};
|
|
48
106
|
type SourceEventData = {
|
|
@@ -51,5 +109,7 @@ type SourceEventData = {
|
|
|
51
109
|
wait: never;
|
|
52
110
|
read: never;
|
|
53
111
|
};
|
|
112
|
+
type TypedArray = Float32Array | Int16Array | Uint8Array;
|
|
113
|
+
type Encoding = "pcm_f32le" | "pcm_s16le" | "pcm_alaw" | "pcm_mulaw";
|
|
54
114
|
|
|
55
|
-
export type { Chunk, ClientOptions, CloneOptions, CloneResponse, ConnectionEventData, CreateVoice, EmitteryCallbacks, Sentinel, SourceEventData, StreamRequest, Voice, WebSocketOptions };
|
|
115
|
+
export type { Chunk, ClientOptions, CloneOptions, CloneResponse, ConnectionEventData, CreateVoice, EmitteryCallbacks, Emotion, EmotionControl, Encoding, Intensity, Sentinel, SourceEventData, StreamOptions, StreamRequest, TypedArray, UpdateVoice, Voice, VoiceOptions, VoiceSpecifier, WebSocketBaseResponse, WebSocketChunkResponse, WebSocketErrorResponse, WebSocketOptions, WebSocketResponse, WebSocketTimestampsResponse, WordTimestamps };
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import emittery__default from 'emittery';
|
|
2
2
|
|
|
3
3
|
interface ClientOptions {
|
|
4
|
-
apiKey?: string;
|
|
4
|
+
apiKey?: string | (() => Promise<string>);
|
|
5
5
|
baseUrl?: string;
|
|
6
6
|
}
|
|
7
7
|
type Sentinel = null;
|
|
@@ -10,12 +10,64 @@ type ConnectionEventData = {
|
|
|
10
10
|
open: never;
|
|
11
11
|
close: never;
|
|
12
12
|
};
|
|
13
|
+
type VoiceSpecifier = {
|
|
14
|
+
mode: "id";
|
|
15
|
+
id: string;
|
|
16
|
+
} | {
|
|
17
|
+
mode: "embedding";
|
|
18
|
+
embedding: number[];
|
|
19
|
+
};
|
|
20
|
+
type Emotion = "anger" | "sadness" | "positivity" | "curiosity" | "surprise";
|
|
21
|
+
type Intensity = "lowest" | "low" | "high" | "highest";
|
|
22
|
+
type EmotionControl = Emotion | `${Emotion}:${Intensity}`;
|
|
23
|
+
type VoiceOptions = VoiceSpecifier & {
|
|
24
|
+
__experimental_controls?: {
|
|
25
|
+
speed?: "slowest" | "slow" | "normal" | "fast" | "fastest";
|
|
26
|
+
emotion?: EmotionControl[];
|
|
27
|
+
};
|
|
28
|
+
};
|
|
13
29
|
type StreamRequest = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
30
|
+
model_id: string;
|
|
31
|
+
transcript: string;
|
|
32
|
+
voice: VoiceOptions;
|
|
33
|
+
output_format?: {
|
|
34
|
+
container: string;
|
|
35
|
+
encoding: string;
|
|
36
|
+
sample_rate: number;
|
|
17
37
|
};
|
|
38
|
+
context_id?: string;
|
|
39
|
+
continue?: boolean;
|
|
40
|
+
duration?: number;
|
|
41
|
+
language?: string;
|
|
42
|
+
add_timestamps?: boolean;
|
|
43
|
+
};
|
|
44
|
+
type StreamOptions = {
|
|
45
|
+
timeout?: number;
|
|
46
|
+
};
|
|
47
|
+
type WebSocketBaseResponse = {
|
|
48
|
+
context_id: string;
|
|
49
|
+
status_code: number;
|
|
50
|
+
done: boolean;
|
|
51
|
+
};
|
|
52
|
+
type WordTimestamps = {
|
|
53
|
+
words: string[];
|
|
54
|
+
start: number[];
|
|
55
|
+
end: number[];
|
|
56
|
+
};
|
|
57
|
+
type WebSocketTimestampsResponse = WebSocketBaseResponse & {
|
|
58
|
+
type: "timestamps";
|
|
59
|
+
word_timestamps: WordTimestamps;
|
|
60
|
+
};
|
|
61
|
+
type WebSocketChunkResponse = WebSocketBaseResponse & {
|
|
62
|
+
type: "chunk";
|
|
63
|
+
data: string;
|
|
64
|
+
step_time: number;
|
|
65
|
+
};
|
|
66
|
+
type WebSocketErrorResponse = WebSocketBaseResponse & {
|
|
67
|
+
type: "error";
|
|
68
|
+
error: string;
|
|
18
69
|
};
|
|
70
|
+
type WebSocketResponse = WebSocketTimestampsResponse | WebSocketChunkResponse | WebSocketErrorResponse;
|
|
19
71
|
type EmitteryCallbacks<T> = {
|
|
20
72
|
on: emittery__default<T>["on"];
|
|
21
73
|
off: emittery__default<T>["off"];
|
|
@@ -25,9 +77,11 @@ type EmitteryCallbacks<T> = {
|
|
|
25
77
|
type CloneOptions = {
|
|
26
78
|
mode: "url";
|
|
27
79
|
link: string;
|
|
80
|
+
enhance?: boolean;
|
|
28
81
|
} | {
|
|
29
82
|
mode: "clip";
|
|
30
83
|
clip: Blob;
|
|
84
|
+
enhance?: boolean;
|
|
31
85
|
};
|
|
32
86
|
type Voice = {
|
|
33
87
|
id: string;
|
|
@@ -37,12 +91,16 @@ type Voice = {
|
|
|
37
91
|
is_public: boolean;
|
|
38
92
|
user_id: string;
|
|
39
93
|
created_at: string;
|
|
94
|
+
language: string;
|
|
40
95
|
};
|
|
41
96
|
type CreateVoice = Pick<Voice, "name" | "description" | "embedding"> & Partial<Omit<Voice, "name" | "description" | "embedding">>;
|
|
97
|
+
type UpdateVoice = Partial<Pick<Voice, "name" | "description" | "embedding">>;
|
|
42
98
|
type CloneResponse = {
|
|
43
99
|
embedding: number[];
|
|
44
100
|
};
|
|
45
101
|
type WebSocketOptions = {
|
|
102
|
+
container?: string;
|
|
103
|
+
encoding?: string;
|
|
46
104
|
sampleRate: number;
|
|
47
105
|
};
|
|
48
106
|
type SourceEventData = {
|
|
@@ -51,5 +109,7 @@ type SourceEventData = {
|
|
|
51
109
|
wait: never;
|
|
52
110
|
read: never;
|
|
53
111
|
};
|
|
112
|
+
type TypedArray = Float32Array | Int16Array | Uint8Array;
|
|
113
|
+
type Encoding = "pcm_f32le" | "pcm_s16le" | "pcm_alaw" | "pcm_mulaw";
|
|
54
114
|
|
|
55
|
-
export type { Chunk, ClientOptions, CloneOptions, CloneResponse, ConnectionEventData, CreateVoice, EmitteryCallbacks, Sentinel, SourceEventData, StreamRequest, Voice, WebSocketOptions };
|
|
115
|
+
export type { Chunk, ClientOptions, CloneOptions, CloneResponse, ConnectionEventData, CreateVoice, EmitteryCallbacks, Emotion, EmotionControl, Encoding, Intensity, Sentinel, SourceEventData, StreamOptions, StreamRequest, TypedArray, UpdateVoice, Voice, VoiceOptions, VoiceSpecifier, WebSocketBaseResponse, WebSocketChunkResponse, WebSocketErrorResponse, WebSocketOptions, WebSocketResponse, WebSocketTimestampsResponse, WordTimestamps };
|
package/dist/voices/index.cjs
CHANGED
|
@@ -88,20 +88,25 @@ var constructApiUrl = (baseUrl, path, { websocket = false } = {}) => {
|
|
|
88
88
|
// src/lib/client.ts
|
|
89
89
|
var Client = class {
|
|
90
90
|
constructor(options = {}) {
|
|
91
|
-
|
|
91
|
+
const apiKey = options.apiKey || process.env.CARTESIA_API_KEY;
|
|
92
|
+
if (!apiKey) {
|
|
92
93
|
throw new Error("Missing Cartesia API key.");
|
|
93
94
|
}
|
|
94
|
-
this.apiKey =
|
|
95
|
+
this.apiKey = typeof apiKey === "function" ? apiKey : () => __async(this, null, function* () {
|
|
96
|
+
return apiKey;
|
|
97
|
+
});
|
|
95
98
|
this.baseUrl = options.baseUrl || BASE_URL;
|
|
96
99
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
headers
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}, options
|
|
104
|
-
|
|
100
|
+
_fetch(_0) {
|
|
101
|
+
return __async(this, arguments, function* (path, options = {}) {
|
|
102
|
+
const url = constructApiUrl(this.baseUrl, path);
|
|
103
|
+
const headers = new Headers(options.headers);
|
|
104
|
+
headers.set("X-API-Key", yield this.apiKey());
|
|
105
|
+
headers.set("Cartesia-Version", CARTESIA_VERSION);
|
|
106
|
+
return (0, import_cross_fetch.default)(url.toString(), __spreadProps(__spreadValues({}, options), {
|
|
107
|
+
headers
|
|
108
|
+
}));
|
|
109
|
+
});
|
|
105
110
|
}
|
|
106
111
|
};
|
|
107
112
|
|
|
@@ -109,40 +114,43 @@ var Client = class {
|
|
|
109
114
|
var Voices = class extends Client {
|
|
110
115
|
list() {
|
|
111
116
|
return __async(this, null, function* () {
|
|
112
|
-
const response = yield this.
|
|
117
|
+
const response = yield this._fetch("/voices");
|
|
113
118
|
return response.json();
|
|
114
119
|
});
|
|
115
120
|
}
|
|
116
121
|
get(voiceId) {
|
|
117
122
|
return __async(this, null, function* () {
|
|
118
|
-
const response = yield this.
|
|
123
|
+
const response = yield this._fetch(`/voices/${voiceId}`);
|
|
119
124
|
return response.json();
|
|
120
125
|
});
|
|
121
126
|
}
|
|
122
127
|
create(voice) {
|
|
123
128
|
return __async(this, null, function* () {
|
|
124
|
-
const response = yield this.
|
|
129
|
+
const response = yield this._fetch("/voices", {
|
|
125
130
|
method: "POST",
|
|
126
131
|
body: JSON.stringify(voice)
|
|
127
132
|
});
|
|
128
133
|
return response.json();
|
|
129
134
|
});
|
|
130
135
|
}
|
|
136
|
+
update(id, voice) {
|
|
137
|
+
return __async(this, null, function* () {
|
|
138
|
+
const response = yield this._fetch(`/voices/${id}`, {
|
|
139
|
+
method: "PATCH",
|
|
140
|
+
body: JSON.stringify(voice)
|
|
141
|
+
});
|
|
142
|
+
return response.json();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
131
145
|
clone(options) {
|
|
132
146
|
return __async(this, null, function* () {
|
|
133
|
-
if (options.mode === "url") {
|
|
134
|
-
const response = yield this.fetch(
|
|
135
|
-
`/voices/clone/url?link=${options.link}`,
|
|
136
|
-
{
|
|
137
|
-
method: "POST"
|
|
138
|
-
}
|
|
139
|
-
);
|
|
140
|
-
return response.json();
|
|
141
|
-
}
|
|
142
147
|
if (options.mode === "clip") {
|
|
143
148
|
const formData = new FormData();
|
|
144
149
|
formData.append("clip", options.clip);
|
|
145
|
-
|
|
150
|
+
if (options.enhance !== void 0) {
|
|
151
|
+
formData.append("enhance", options.enhance.toString());
|
|
152
|
+
}
|
|
153
|
+
const response = yield this._fetch("/voices/clone/clip", {
|
|
146
154
|
method: "POST",
|
|
147
155
|
body: formData
|
|
148
156
|
});
|
package/dist/voices/index.d.cts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Client } from '../lib/client.cjs';
|
|
2
|
-
import { Voice, CreateVoice, CloneOptions, CloneResponse } from '../types/index.cjs';
|
|
2
|
+
import { Voice, CreateVoice, UpdateVoice, CloneOptions, CloneResponse } from '../types/index.cjs';
|
|
3
3
|
import 'emittery';
|
|
4
4
|
|
|
5
5
|
declare class Voices extends Client {
|
|
6
6
|
list(): Promise<Voice[]>;
|
|
7
7
|
get(voiceId: string): Promise<Voice>;
|
|
8
8
|
create(voice: CreateVoice): Promise<Voice>;
|
|
9
|
+
update(id: string, voice: UpdateVoice): Promise<Voice>;
|
|
9
10
|
clone(options: CloneOptions): Promise<CloneResponse>;
|
|
10
11
|
}
|
|
11
12
|
|
package/dist/voices/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Client } from '../lib/client.js';
|
|
2
|
-
import { Voice, CreateVoice, CloneOptions, CloneResponse } from '../types/index.js';
|
|
2
|
+
import { Voice, CreateVoice, UpdateVoice, CloneOptions, CloneResponse } from '../types/index.js';
|
|
3
3
|
import 'emittery';
|
|
4
4
|
|
|
5
5
|
declare class Voices extends Client {
|
|
6
6
|
list(): Promise<Voice[]>;
|
|
7
7
|
get(voiceId: string): Promise<Voice>;
|
|
8
8
|
create(voice: CreateVoice): Promise<Voice>;
|
|
9
|
+
update(id: string, voice: UpdateVoice): Promise<Voice>;
|
|
9
10
|
clone(options: CloneOptions): Promise<CloneResponse>;
|
|
10
11
|
}
|
|
11
12
|
|
package/dist/voices/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Voices
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-UCYL2SOX.js";
|
|
4
|
+
import "../chunk-PISCPZK4.js";
|
|
5
5
|
import "../chunk-2BFEKY3F.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-GHY2WEOK.js";
|
|
7
7
|
export {
|
|
8
8
|
Voices as default
|
|
9
9
|
};
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/src/lib/client.ts
CHANGED
|
@@ -3,29 +3,29 @@ import type { ClientOptions } from "../types";
|
|
|
3
3
|
import { BASE_URL, CARTESIA_VERSION, constructApiUrl } from "./constants";
|
|
4
4
|
|
|
5
5
|
export class Client {
|
|
6
|
-
apiKey: string
|
|
6
|
+
apiKey: () => Promise<string>;
|
|
7
7
|
baseUrl: string;
|
|
8
8
|
|
|
9
9
|
constructor(options: ClientOptions = {}) {
|
|
10
|
-
|
|
10
|
+
const apiKey = options.apiKey || process.env.CARTESIA_API_KEY;
|
|
11
|
+
if (!apiKey) {
|
|
11
12
|
throw new Error("Missing Cartesia API key.");
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
this.apiKey = (options.apiKey || process.env.CARTESIA_API_KEY)!;
|
|
15
|
+
this.apiKey = typeof apiKey === "function" ? apiKey : async () => apiKey;
|
|
16
16
|
this.baseUrl = options.baseUrl || BASE_URL;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
protected async _fetch(path: string, options: RequestInit = {}) {
|
|
20
20
|
const url = constructApiUrl(this.baseUrl, path);
|
|
21
|
+
const headers = new Headers(options.headers);
|
|
22
|
+
|
|
23
|
+
headers.set("X-API-Key", await this.apiKey());
|
|
24
|
+
headers.set("Cartesia-Version", CARTESIA_VERSION);
|
|
21
25
|
|
|
22
26
|
return fetch(url.toString(), {
|
|
23
27
|
...options,
|
|
24
|
-
headers
|
|
25
|
-
"X-API-Key": this.apiKey,
|
|
26
|
-
"Cartesia-Version": CARTESIA_VERSION,
|
|
27
|
-
...options.headers,
|
|
28
|
-
},
|
|
28
|
+
headers,
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
}
|
package/src/react/index.ts
CHANGED
|
@@ -4,12 +4,14 @@ import { Cartesia } from "../lib";
|
|
|
4
4
|
import Player from "../tts/player";
|
|
5
5
|
import type Source from "../tts/source";
|
|
6
6
|
import type WebSocket from "../tts/websocket";
|
|
7
|
+
import type { StreamRequest } from "../types";
|
|
7
8
|
import { pingServer } from "./utils";
|
|
8
9
|
|
|
9
10
|
export type UseTTSOptions = {
|
|
10
|
-
apiKey: string | null;
|
|
11
|
+
apiKey: string | (() => Promise<string>) | null;
|
|
11
12
|
baseUrl?: string;
|
|
12
13
|
sampleRate: number;
|
|
14
|
+
onError?: (error: Error) => void;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
export type PlaybackStatus = "inactive" | "playing" | "paused" | "finished";
|
|
@@ -20,7 +22,7 @@ export type Metrics = {
|
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
export interface UseTTSReturn {
|
|
23
|
-
buffer: (options:
|
|
25
|
+
buffer: (options: StreamRequest) => Promise<void>;
|
|
24
26
|
play: (bufferDuration?: number) => Promise<void>;
|
|
25
27
|
pause: () => Promise<void>;
|
|
26
28
|
resume: () => Promise<void>;
|
|
@@ -47,6 +49,7 @@ export function useTTS({
|
|
|
47
49
|
apiKey,
|
|
48
50
|
baseUrl,
|
|
49
51
|
sampleRate,
|
|
52
|
+
onError,
|
|
50
53
|
}: UseTTSOptions): UseTTSReturn {
|
|
51
54
|
if (typeof window === "undefined") {
|
|
52
55
|
return {
|
|
@@ -72,7 +75,11 @@ export function useTTS({
|
|
|
72
75
|
}
|
|
73
76
|
const cartesia = new Cartesia({ apiKey, baseUrl });
|
|
74
77
|
baseUrl = baseUrl ?? cartesia.baseUrl;
|
|
75
|
-
return cartesia.tts.websocket({
|
|
78
|
+
return cartesia.tts.websocket({
|
|
79
|
+
container: "raw",
|
|
80
|
+
encoding: "pcm_f32le",
|
|
81
|
+
sampleRate,
|
|
82
|
+
});
|
|
76
83
|
}, [apiKey, baseUrl, sampleRate]);
|
|
77
84
|
const websocketReturn = useRef<ReturnType<WebSocket["send"]> | null>(null);
|
|
78
85
|
const player = useRef<Player | null>(null);
|
|
@@ -85,23 +92,35 @@ export function useTTS({
|
|
|
85
92
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
86
93
|
|
|
87
94
|
const buffer = useCallback(
|
|
88
|
-
async (options:
|
|
95
|
+
async (options: StreamRequest) => {
|
|
89
96
|
websocketReturn.current?.stop(); // Abort the previous request if it exists.
|
|
90
97
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
try {
|
|
99
|
+
setMessages([]);
|
|
100
|
+
setBufferStatus("buffering");
|
|
101
|
+
websocketReturn.current = websocket?.send(options) ?? null;
|
|
102
|
+
if (!websocketReturn.current) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const unsubscribe = websocketReturn.current.on("message", (message) => {
|
|
106
|
+
const parsedMessage = JSON.parse(message);
|
|
107
|
+
setMessages((messages) => [...messages, parsedMessage]);
|
|
108
|
+
if (parsedMessage.error) {
|
|
109
|
+
onError?.(new Error(parsedMessage.error));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
await websocketReturn.current.source.once("close");
|
|
113
|
+
setBufferStatus("buffered");
|
|
114
|
+
unsubscribe();
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error instanceof Error) {
|
|
117
|
+
onError?.(error);
|
|
118
|
+
} else {
|
|
119
|
+
console.error(error);
|
|
120
|
+
}
|
|
96
121
|
}
|
|
97
|
-
const unsubscribe = websocketReturn.current.on("message", (message) => {
|
|
98
|
-
setMessages((messages) => [...messages, JSON.parse(message)]);
|
|
99
|
-
});
|
|
100
|
-
await websocketReturn.current.source.once("close");
|
|
101
|
-
setBufferStatus("buffered");
|
|
102
|
-
unsubscribe();
|
|
103
122
|
},
|
|
104
|
-
[websocket],
|
|
123
|
+
[websocket, onError],
|
|
105
124
|
);
|
|
106
125
|
|
|
107
126
|
const metrics = useMemo(() => {
|
|
@@ -173,64 +192,96 @@ export function useTTS({
|
|
|
173
192
|
}, [websocket, baseUrl]);
|
|
174
193
|
|
|
175
194
|
const play = useCallback(async () => {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
195
|
+
try {
|
|
196
|
+
if (playbackStatus === "playing" || !websocketReturn.current) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (player.current) {
|
|
200
|
+
// Stop the current player if it exists.
|
|
201
|
+
await player.current.stop();
|
|
202
|
+
}
|
|
183
203
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const unsubscribes = [];
|
|
187
|
-
unsubscribes.push(
|
|
188
|
-
websocketReturn.current.source.on("wait", () => {
|
|
189
|
-
setIsWaiting(true);
|
|
190
|
-
}),
|
|
191
|
-
);
|
|
192
|
-
unsubscribes.push(
|
|
193
|
-
websocketReturn.current.source.on("read", () => {
|
|
194
|
-
setIsWaiting(false);
|
|
195
|
-
}),
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
player.current = new Player({
|
|
199
|
-
bufferDuration: bufferDuration ?? DEFAULT_BUFFER_DURATION,
|
|
200
|
-
});
|
|
201
|
-
// Wait for the playback to finish before setting isPlaying to false.
|
|
202
|
-
await player.current.play(websocketReturn.current.source);
|
|
204
|
+
setPlaybackStatus("playing");
|
|
203
205
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
const unsubscribes = [];
|
|
207
|
+
unsubscribes.push(
|
|
208
|
+
websocketReturn.current.source.on("wait", () => {
|
|
209
|
+
setIsWaiting(true);
|
|
210
|
+
}),
|
|
211
|
+
);
|
|
212
|
+
unsubscribes.push(
|
|
213
|
+
websocketReturn.current.source.on("read", () => {
|
|
214
|
+
setIsWaiting(false);
|
|
215
|
+
}),
|
|
216
|
+
);
|
|
208
217
|
|
|
209
|
-
|
|
210
|
-
|
|
218
|
+
player.current = new Player({
|
|
219
|
+
bufferDuration: bufferDuration ?? DEFAULT_BUFFER_DURATION,
|
|
220
|
+
});
|
|
221
|
+
// Wait for the playback to finish before setting isPlaying to false.
|
|
222
|
+
await player.current.play(websocketReturn.current.source);
|
|
223
|
+
|
|
224
|
+
for (const unsubscribe of unsubscribes) {
|
|
225
|
+
// Deregister the event listeners (.on()) that we registered above to avoid memory leaks.
|
|
226
|
+
unsubscribe();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
setPlaybackStatus("finished");
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if (error instanceof Error) {
|
|
232
|
+
onError?.(error);
|
|
233
|
+
} else {
|
|
234
|
+
console.error(error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}, [playbackStatus, bufferDuration, onError]);
|
|
211
238
|
|
|
212
239
|
const pause = useCallback(async () => {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
240
|
+
try {
|
|
241
|
+
await player.current?.pause();
|
|
242
|
+
setPlaybackStatus("paused");
|
|
243
|
+
} catch (error) {
|
|
244
|
+
if (error instanceof Error) {
|
|
245
|
+
onError?.(error);
|
|
246
|
+
} else {
|
|
247
|
+
console.error(error);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}, [onError]);
|
|
216
251
|
|
|
217
252
|
const resume = useCallback(async () => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
253
|
+
try {
|
|
254
|
+
await player.current?.resume();
|
|
255
|
+
setPlaybackStatus("playing");
|
|
256
|
+
} catch (error) {
|
|
257
|
+
if (error instanceof Error) {
|
|
258
|
+
onError?.(error);
|
|
259
|
+
} else {
|
|
260
|
+
console.error(error);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}, [onError]);
|
|
221
264
|
|
|
222
265
|
const toggle = useCallback(async () => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
266
|
+
try {
|
|
267
|
+
await player.current?.toggle();
|
|
268
|
+
setPlaybackStatus((status) => {
|
|
269
|
+
if (status === "playing") {
|
|
270
|
+
return "paused";
|
|
271
|
+
}
|
|
272
|
+
if (status === "paused") {
|
|
273
|
+
return "playing";
|
|
274
|
+
}
|
|
275
|
+
return status;
|
|
276
|
+
});
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (error instanceof Error) {
|
|
279
|
+
onError?.(error);
|
|
280
|
+
} else {
|
|
281
|
+
console.error(error);
|
|
230
282
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}, []);
|
|
283
|
+
}
|
|
284
|
+
}, [onError]);
|
|
234
285
|
|
|
235
286
|
return {
|
|
236
287
|
buffer,
|