@polytts/browser-adapters 0.1.0 → 0.1.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.
- package/dist/kokoro.worker.d.ts +1 -0
- package/dist/kokoro.worker.js +145 -0
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
//#region src/adapters/kokoro.worker.ts
|
|
2
|
+
const KOKORO_ERROR_LOG_LEVEL = 40;
|
|
3
|
+
const abortedRequestIds = /* @__PURE__ */ new Set();
|
|
4
|
+
let tts = null;
|
|
5
|
+
let currentModuleUrl;
|
|
6
|
+
const DEFAULT_MODULE_KEY = "kokoro-js";
|
|
7
|
+
const kokoroModuleCache = /* @__PURE__ */ new Map();
|
|
8
|
+
async function loadKokoroBrowserModule(moduleUrl) {
|
|
9
|
+
const key = moduleUrl ?? DEFAULT_MODULE_KEY;
|
|
10
|
+
const cached = kokoroModuleCache.get(key);
|
|
11
|
+
if (cached) return cached;
|
|
12
|
+
const loadPromise = moduleUrl ? import(
|
|
13
|
+
/* @vite-ignore */
|
|
14
|
+
moduleUrl
|
|
15
|
+
) : import("kokoro-js");
|
|
16
|
+
kokoroModuleCache.set(key, loadPromise);
|
|
17
|
+
try {
|
|
18
|
+
return await loadPromise;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
kokoroModuleCache.delete(key);
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function ensureNotAborted(requestId) {
|
|
25
|
+
if (abortedRequestIds.has(requestId)) throw new DOMException("Aborted", "AbortError");
|
|
26
|
+
}
|
|
27
|
+
function configureKokoroEnvironment(env) {
|
|
28
|
+
if (!env) return;
|
|
29
|
+
if (!("caches" in globalThis) || globalThis.caches == null) {
|
|
30
|
+
env.useBrowserCache = false;
|
|
31
|
+
env.useWasmCache = false;
|
|
32
|
+
}
|
|
33
|
+
env.logLevel = Math.max(env.logLevel ?? KOKORO_ERROR_LOG_LEVEL, KOKORO_ERROR_LOG_LEVEL);
|
|
34
|
+
}
|
|
35
|
+
async function loadTTS(KokoroTTS, modelRef, preferredDevice, requestId) {
|
|
36
|
+
const progress_callback = (progress) => {
|
|
37
|
+
if (progress.status === "progress" && progress.loaded != null && progress.total) self.postMessage({
|
|
38
|
+
type: "progress",
|
|
39
|
+
requestId,
|
|
40
|
+
progress: progress.loaded / progress.total
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
const tryLoad = (device) => KokoroTTS.from_pretrained(modelRef, {
|
|
44
|
+
dtype: device === "webgpu" ? "fp32" : "q8",
|
|
45
|
+
device,
|
|
46
|
+
progress_callback
|
|
47
|
+
});
|
|
48
|
+
if (preferredDevice === "wasm") return {
|
|
49
|
+
tts: await tryLoad("wasm"),
|
|
50
|
+
device: "wasm"
|
|
51
|
+
};
|
|
52
|
+
try {
|
|
53
|
+
return {
|
|
54
|
+
tts: await tryLoad("webgpu"),
|
|
55
|
+
device: "webgpu"
|
|
56
|
+
};
|
|
57
|
+
} catch {
|
|
58
|
+
return {
|
|
59
|
+
tts: await tryLoad("wasm"),
|
|
60
|
+
device: "wasm"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
self.onmessage = async (event) => {
|
|
65
|
+
const message = event.data;
|
|
66
|
+
if (message.type === "abort") {
|
|
67
|
+
abortedRequestIds.add(message.requestId);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const { requestId } = message;
|
|
71
|
+
try {
|
|
72
|
+
switch (message.type) {
|
|
73
|
+
case "load": {
|
|
74
|
+
currentModuleUrl = message.moduleUrl;
|
|
75
|
+
const kokoroModule = await loadKokoroBrowserModule(currentModuleUrl);
|
|
76
|
+
configureKokoroEnvironment(kokoroModule.env);
|
|
77
|
+
const loaded = await loadTTS(kokoroModule.KokoroTTS, message.modelRef, message.device, requestId);
|
|
78
|
+
tts = loaded.tts;
|
|
79
|
+
ensureNotAborted(requestId);
|
|
80
|
+
const voices = Object.entries(tts.voices ?? {}).map(([id, info]) => ({
|
|
81
|
+
id,
|
|
82
|
+
name: info?.name ?? id,
|
|
83
|
+
language: info?.language ?? (id.startsWith("b") ? "en-GB" : "en-US"),
|
|
84
|
+
gender: info?.gender
|
|
85
|
+
}));
|
|
86
|
+
self.postMessage({
|
|
87
|
+
type: "done",
|
|
88
|
+
requestId,
|
|
89
|
+
voices,
|
|
90
|
+
device: loaded.device
|
|
91
|
+
});
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "generate": {
|
|
95
|
+
if (!tts) throw new Error("Kokoro model not loaded");
|
|
96
|
+
ensureNotAborted(requestId);
|
|
97
|
+
const result = await tts.generate(message.text, { voice: message.voiceId });
|
|
98
|
+
ensureNotAborted(requestId);
|
|
99
|
+
const audio = result.audio;
|
|
100
|
+
const buffer = audio.buffer.slice(audio.byteOffset, audio.byteOffset + audio.byteLength);
|
|
101
|
+
self.postMessage({
|
|
102
|
+
type: "done",
|
|
103
|
+
requestId,
|
|
104
|
+
audio: buffer,
|
|
105
|
+
sampleRate: result.sampling_rate ?? 24e3
|
|
106
|
+
}, [buffer]);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case "stream": {
|
|
110
|
+
if (!tts) throw new Error("Kokoro model not loaded");
|
|
111
|
+
const { TextSplitterStream } = await loadKokoroBrowserModule(currentModuleUrl);
|
|
112
|
+
const splitter = new TextSplitterStream();
|
|
113
|
+
const stream = tts.stream(splitter, { voice: message.voiceId });
|
|
114
|
+
splitter.push(message.text);
|
|
115
|
+
splitter.close();
|
|
116
|
+
for await (const { audio } of stream) {
|
|
117
|
+
ensureNotAborted(requestId);
|
|
118
|
+
const samples = audio.audio;
|
|
119
|
+
const buffer = samples.buffer.slice(samples.byteOffset, samples.byteOffset + samples.byteLength);
|
|
120
|
+
self.postMessage({
|
|
121
|
+
type: "chunk",
|
|
122
|
+
requestId,
|
|
123
|
+
audio: buffer,
|
|
124
|
+
sampleRate: audio.sampling_rate ?? 24e3
|
|
125
|
+
}, [buffer]);
|
|
126
|
+
}
|
|
127
|
+
ensureNotAborted(requestId);
|
|
128
|
+
self.postMessage({
|
|
129
|
+
type: "done",
|
|
130
|
+
requestId
|
|
131
|
+
});
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (error.name !== "AbortError") self.postMessage({
|
|
137
|
+
type: "error",
|
|
138
|
+
requestId,
|
|
139
|
+
message: error instanceof Error ? error.message : String(error)
|
|
140
|
+
});
|
|
141
|
+
} finally {
|
|
142
|
+
abortedRequestIds.delete(requestId);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polytts/browser-adapters",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Official browser adapter implementations for polytts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adapters",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"kokoro-js": "latest",
|
|
40
40
|
"onnxruntime-web": "latest",
|
|
41
41
|
"phonemizer": "latest",
|
|
42
|
-
"@polytts/core": "0.1.
|
|
42
|
+
"@polytts/core": "0.1.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^25.6.0",
|