voicekit-client 0.1.0 → 0.2.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/README.md +37 -1
- package/index.d.ts +63 -0
- package/package.json +1 -1
- package/src/index.js +102 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Official TypeScript/JavaScript wrapper for [VoiceKit](https://ttsapi.ru):
|
|
4
4
|
neural speech synthesis, transcription, sentiment analysis, and batch operations.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Requires Node.js 18+ (global `fetch`). WebSocket streaming uses the `ws` package.
|
|
7
7
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
@@ -96,6 +96,23 @@ const fxFile = await client.downloadAudioEffects(fxJob.job_id);
|
|
|
96
96
|
await writeFile("voice_fx.mp3", fxFile);
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
### Audio cleaning (Pro/Business)
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
// Denoise + normalize an existing file as a background job
|
|
103
|
+
const cleanJob = await client.cleanAudio("noisy.wav"); // one-click preset
|
|
104
|
+
// or with options:
|
|
105
|
+
// const cleanJob = await client.cleanAudio("noisy.wav", { options: { denoise: { strength: 0.8 } } });
|
|
106
|
+
|
|
107
|
+
let cleanResult = await client.getAudioCleaningJob(cleanJob.job_id);
|
|
108
|
+
while (!["completed", "failed"].includes(cleanResult.status)) {
|
|
109
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
110
|
+
cleanResult = await client.getAudioCleaningJob(cleanJob.job_id);
|
|
111
|
+
}
|
|
112
|
+
const cleanFile = await client.downloadAudioCleaning(cleanJob.job_id);
|
|
113
|
+
await writeFile("voice_clean.wav", cleanFile);
|
|
114
|
+
```
|
|
115
|
+
|
|
99
116
|
### WebSocket streaming (Pro/Business)
|
|
100
117
|
|
|
101
118
|
```js
|
|
@@ -117,6 +134,25 @@ for await (const event of vad.events()) {
|
|
|
117
134
|
vad.close();
|
|
118
135
|
```
|
|
119
136
|
|
|
137
|
+
### Voice ID (Pro/Business)
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
// Voice passport: language, gender, age, emotion, speaker embedding, AI-vs-human
|
|
141
|
+
const passport = await client.voiceId("recording.wav");
|
|
142
|
+
|
|
143
|
+
// Voice biometrics on your own profiles
|
|
144
|
+
const profile = await client.enrollVoice("speaker.wav", "Alice"); // { profile_id: "voice_…" }
|
|
145
|
+
|
|
146
|
+
const check = await client.verifyVoice("check.wav", profile.profile_id);
|
|
147
|
+
// { profile_id: "voice_…", similarity: 0.81, verified: true, threshold: 0.7 }
|
|
148
|
+
|
|
149
|
+
const match = await client.identifyVoice("check.wav"); // 1:N across your profiles
|
|
150
|
+
// { best_match: {…}, matches: […], threshold: 0.7 }
|
|
151
|
+
|
|
152
|
+
const profiles = await client.listVoiceProfiles();
|
|
153
|
+
await client.deleteVoiceProfile(profile.profile_id);
|
|
154
|
+
```
|
|
155
|
+
|
|
120
156
|
## Configuration
|
|
121
157
|
|
|
122
158
|
| Option | Default | Description |
|
package/index.d.ts
CHANGED
|
@@ -35,6 +35,12 @@ export class VoiceKitClient {
|
|
|
35
35
|
getTranscriptionJob(jobId: string): Promise<Record<string, unknown>>;
|
|
36
36
|
subtitles(jobId: string, format?: "vtt" | "srt"): Promise<string>;
|
|
37
37
|
vad(audio: AudioSource): Promise<Record<string, unknown>>;
|
|
38
|
+
voiceId(audio: AudioSource): Promise<VoiceIdResult>;
|
|
39
|
+
enrollVoice(audio: AudioSource, name?: string): Promise<Record<string, unknown>>;
|
|
40
|
+
verifyVoice(audio: AudioSource, profileId: string): Promise<VoiceVerificationResult>;
|
|
41
|
+
identifyVoice(audio: AudioSource, profileIds?: string[]): Promise<VoiceIdentificationResult>;
|
|
42
|
+
listVoiceProfiles(): Promise<Array<Record<string, unknown>>>;
|
|
43
|
+
deleteVoiceProfile(profileId: string): Promise<void>;
|
|
38
44
|
|
|
39
45
|
analyze(audio: AudioSource, opts?: AudioOptions): Promise<Record<string, unknown>>;
|
|
40
46
|
analyzeSync(audio: AudioSource, opts?: AudioOptions): Promise<Record<string, unknown>>;
|
|
@@ -67,6 +73,13 @@ export class VoiceKitClient {
|
|
|
67
73
|
getVideoEffectsJob(jobId: string): Promise<Record<string, unknown>>;
|
|
68
74
|
downloadVideoEffects(jobId: string): Promise<Uint8Array>;
|
|
69
75
|
|
|
76
|
+
cleanAudio(
|
|
77
|
+
audio: AudioSource,
|
|
78
|
+
opts?: AudioCleaningOptions,
|
|
79
|
+
): Promise<Record<string, unknown>>;
|
|
80
|
+
getAudioCleaningJob(jobId: string): Promise<Record<string, unknown>>;
|
|
81
|
+
downloadAudioCleaning(jobId: string): Promise<Uint8Array>;
|
|
82
|
+
|
|
70
83
|
batchSynthesize(items: Array<Record<string, unknown>>): Promise<Record<string, unknown>>;
|
|
71
84
|
batchAnalyze(items: Array<Record<string, unknown>>): Promise<Record<string, unknown>>;
|
|
72
85
|
getBatch(batchId: string): Promise<Record<string, unknown>>;
|
|
@@ -106,6 +119,56 @@ export interface VideoEffectsOptions {
|
|
|
106
119
|
webhookUrl?: string;
|
|
107
120
|
}
|
|
108
121
|
|
|
122
|
+
export interface AudioCleaningOptions {
|
|
123
|
+
options?: Record<string, unknown>;
|
|
124
|
+
outputFormat?: "wav" | "mp3" | "ogg";
|
|
125
|
+
webhookUrl?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface VoiceIdResult {
|
|
129
|
+
language: string;
|
|
130
|
+
language_confidence: number;
|
|
131
|
+
duration_seconds: number;
|
|
132
|
+
speech_ratio: number;
|
|
133
|
+
gender: string;
|
|
134
|
+
gender_confidence: number;
|
|
135
|
+
age_group: string;
|
|
136
|
+
age_confidence: number;
|
|
137
|
+
emotional_background: string;
|
|
138
|
+
emotions?: Record<string, number>;
|
|
139
|
+
ai_probability?: number;
|
|
140
|
+
fake_detection_available: boolean;
|
|
141
|
+
speaker_embedding?: number[];
|
|
142
|
+
nearest_voices: VoiceIdMatch[];
|
|
143
|
+
processing_time_ms: number;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface VoiceIdMatch {
|
|
147
|
+
voice_id: string;
|
|
148
|
+
similarity: number;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface VoiceVerificationResult {
|
|
152
|
+
profile_id: string;
|
|
153
|
+
similarity: number;
|
|
154
|
+
verified: boolean;
|
|
155
|
+
threshold: number;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface VoiceIdentificationResult {
|
|
159
|
+
best_match?: VoiceIdMatch;
|
|
160
|
+
matches: VoiceIdMatch[];
|
|
161
|
+
threshold: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface VoiceProfile {
|
|
165
|
+
profile_id: string;
|
|
166
|
+
name: string;
|
|
167
|
+
source: string;
|
|
168
|
+
clone_voice_id?: string;
|
|
169
|
+
created_at: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
109
172
|
export interface SynthesisAsyncOptions {
|
|
110
173
|
voice?: string;
|
|
111
174
|
format?: "wav";
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Official JavaScript/TypeScript SDK for VoiceKit.
|
|
3
3
|
*
|
|
4
|
-
* Requires Node.js 18+ (global `fetch`, web streams).
|
|
4
|
+
* Requires Node.js 18+ (global `fetch`, web streams). WebSocket streaming uses the `ws` package.
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* import { VoiceKitClient } from "voicekit-client";
|
|
@@ -227,6 +227,67 @@ export class VoiceKitClient {
|
|
|
227
227
|
return this.#postForm("/v1/vad", audio, {});
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Analyze a voice recording and return a voice passport (language, gender,
|
|
232
|
+
* age group, emotional background, speaker embedding and an AI-vs-human
|
|
233
|
+
* probability — beta, null when the detector is not configured).
|
|
234
|
+
* @param {AudioSource} audio
|
|
235
|
+
* @returns {Promise<Record<string, unknown>>}
|
|
236
|
+
*/
|
|
237
|
+
voiceId(audio) {
|
|
238
|
+
return this.#postForm("/v1/voice-id", audio, {});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Enroll an audio clip as a reusable voice profile (voiceprint).
|
|
243
|
+
* @param {AudioSource} audio
|
|
244
|
+
* @param {string} [name]
|
|
245
|
+
* @returns {Promise<Record<string, unknown>>}
|
|
246
|
+
*/
|
|
247
|
+
enrollVoice(audio, name) {
|
|
248
|
+
return this.#postForm("/v1/voice-id/enroll", audio, { name });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Verify an audio clip against an enrolled profile (1:1).
|
|
253
|
+
* @param {AudioSource} audio
|
|
254
|
+
* @param {string} profileId
|
|
255
|
+
* @returns {Promise<Record<string, unknown>>}
|
|
256
|
+
*/
|
|
257
|
+
verifyVoice(audio, profileId) {
|
|
258
|
+
return this.#postForm("/v1/voice-id/verify", audio, { profile_id: profileId });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Identify the closest matching profile for an audio clip (1:N).
|
|
263
|
+
* @param {AudioSource} audio
|
|
264
|
+
* @param {string[]} [profileIds]
|
|
265
|
+
* @returns {Promise<Record<string, unknown>>}
|
|
266
|
+
*/
|
|
267
|
+
identifyVoice(audio, profileIds) {
|
|
268
|
+
const data = profileIds && profileIds.length
|
|
269
|
+
? { profile_ids: profileIds.join(",") }
|
|
270
|
+
: {};
|
|
271
|
+
return this.#postForm("/v1/voice-id/identify", audio, data);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* List the caller's enrolled voice profiles.
|
|
276
|
+
* @returns {Promise<Array<Record<string, unknown>>>}
|
|
277
|
+
*/
|
|
278
|
+
listVoiceProfiles() {
|
|
279
|
+
return this.#getJson("/v1/voice-id/profiles");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Delete an enrolled voice profile by id.
|
|
284
|
+
* @param {string} profileId
|
|
285
|
+
* @returns {Promise<void>}
|
|
286
|
+
*/
|
|
287
|
+
async deleteVoiceProfile(profileId) {
|
|
288
|
+
await this.#request(`/v1/voice-id/profiles/${profileId}`, { method: "DELETE" });
|
|
289
|
+
}
|
|
290
|
+
|
|
230
291
|
// ──────────────────────── Analysis ─────────────────────────────────
|
|
231
292
|
|
|
232
293
|
/**
|
|
@@ -375,6 +436,46 @@ export class VoiceKitClient {
|
|
|
375
436
|
return new Uint8Array(await response.arrayBuffer());
|
|
376
437
|
}
|
|
377
438
|
|
|
439
|
+
// ──────────────────────── Audio cleaning ───────────────────────────
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Start an async audio-cleaning job; poll with `getAudioCleaningJob()`.
|
|
443
|
+
* With no `options` the one-click preset (denoise + normalize) is applied.
|
|
444
|
+
* @param {AudioSource} audio
|
|
445
|
+
* @param {{ options?: Record<string, unknown>, outputFormat?: string, webhookUrl?: string }} [opts]
|
|
446
|
+
* @returns {Promise<Record<string, unknown>>}
|
|
447
|
+
*/
|
|
448
|
+
async cleanAudio(audio, opts = {}) {
|
|
449
|
+
const { options, outputFormat = "wav", webhookUrl } = opts;
|
|
450
|
+
const { data, filename } = await audioToParts(audio);
|
|
451
|
+
const form = new FormData();
|
|
452
|
+
form.append("audio", new Blob([data], { type: "audio/wav" }), filename);
|
|
453
|
+
form.append("output_format", outputFormat);
|
|
454
|
+
if (options) form.append("options", JSON.stringify(options));
|
|
455
|
+
const query = webhookUrl ? `?webhookUrl=${encodeURIComponent(webhookUrl)}` : "";
|
|
456
|
+
const response = await this.#request(`/v1/audio/clean${query}`, { method: "POST", form });
|
|
457
|
+
return response.json();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Poll an audio-cleaning job; returns the manifest when completed.
|
|
462
|
+
* @param {string} jobId
|
|
463
|
+
* @returns {Promise<Record<string, unknown>>}
|
|
464
|
+
*/
|
|
465
|
+
getAudioCleaningJob(jobId) {
|
|
466
|
+
return this.#getJson(`/v1/audio/clean/${jobId}`);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Download the cleaned audio for a completed audio-cleaning job.
|
|
471
|
+
* @param {string} jobId
|
|
472
|
+
* @returns {Promise<Uint8Array>}
|
|
473
|
+
*/
|
|
474
|
+
async downloadAudioCleaning(jobId) {
|
|
475
|
+
const response = await this.#request(`/v1/audio/clean/${jobId}/audio`);
|
|
476
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
477
|
+
}
|
|
478
|
+
|
|
378
479
|
// ──────────────────────── Batch ────────────────────────────────────
|
|
379
480
|
|
|
380
481
|
/**
|