mirror of
https://github.com/Mintplex-Labs/piper-tts-web.git
synced 2026-07-01 20:04:04 -04:00
refactored worker as module
This commit is contained in:
Generated
+141
-11
@@ -1,12 +1,16 @@
|
||||
{
|
||||
"name": "piper-js",
|
||||
"version": "0.0.0",
|
||||
"name": "@diffusionstudio/vits-web",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "piper-js",
|
||||
"version": "0.0.0",
|
||||
"name": "@diffusionstudio/vits-web",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"onnxruntime-web": "^1.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.3.1",
|
||||
@@ -509,6 +513,70 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/base64": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
||||
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/codegen": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
||||
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/eventemitter": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
|
||||
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/fetch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
|
||||
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.1",
|
||||
"@protobufjs/inquire": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/float": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
||||
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/inquire": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
|
||||
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/path": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
||||
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/pool": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
||||
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/utf8": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
|
||||
@@ -840,10 +908,7 @@
|
||||
"version": "20.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
|
||||
"integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
@@ -1131,6 +1196,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/flatbuffers": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.12.0.tgz",
|
||||
"integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==",
|
||||
"license": "SEE LICENSE IN LICENSE.txt"
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
|
||||
@@ -1178,6 +1249,12 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/guid-typescript": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz",
|
||||
"integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -1289,6 +1366,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
|
||||
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
@@ -1358,6 +1441,26 @@
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/onnxruntime-common": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.18.0.tgz",
|
||||
"integrity": "sha512-lufrSzX6QdKrktAELG5x5VkBpapbCeS3dQwrXbN0eD9rHvU0yAWl7Ztju9FvgAKWvwd/teEKJNj3OwM6eTZh3Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/onnxruntime-web": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.18.0.tgz",
|
||||
"integrity": "sha512-o1UKj4ABIj1gmG7ae0RKJ3/GT+3yoF0RRpfDfeoe0huzRW4FDRLfbkDETmdFAvnJEXuYDE0YT+hhkia0352StQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"flatbuffers": "^1.12.0",
|
||||
"guid-typescript": "^1.0.9",
|
||||
"long": "^5.2.3",
|
||||
"onnxruntime-common": "1.18.0",
|
||||
"platform": "^1.3.6",
|
||||
"protobufjs": "^7.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||
@@ -1392,6 +1495,12 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/platform": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
|
||||
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.39",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz",
|
||||
@@ -1421,6 +1530,30 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.2.tgz",
|
||||
"integrity": "sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.2",
|
||||
"@protobufjs/base64": "^1.1.2",
|
||||
"@protobufjs/codegen": "^2.0.4",
|
||||
"@protobufjs/eventemitter": "^1.1.0",
|
||||
"@protobufjs/fetch": "^1.1.0",
|
||||
"@protobufjs/float": "^1.0.2",
|
||||
"@protobufjs/inquire": "^1.1.0",
|
||||
"@protobufjs/path": "^1.1.2",
|
||||
"@protobufjs/pool": "^1.1.0",
|
||||
"@protobufjs/utf8": "^1.1.0",
|
||||
"@types/node": ">=13.7.0",
|
||||
"long": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
@@ -1598,10 +1731,7 @@
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "0.1.2",
|
||||
|
||||
+12
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@diffusionstudio/vits-web",
|
||||
"private": false,
|
||||
"version": "0.0.1",
|
||||
"version": "1.0.0",
|
||||
"description": "Web api for using VITS based models in the browser!",
|
||||
"type": "module",
|
||||
"files": [
|
||||
@@ -20,6 +20,14 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:diffusion-studio/vits-web.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/diffusion-studio/vits-web/issues"
|
||||
},
|
||||
"homepage": "https://github.com/diffusion-studio/vits-web#readme",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"vits",
|
||||
@@ -42,5 +50,8 @@
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.3.1",
|
||||
"vite-plugin-dts": "^3.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"onnxruntime-web": "^1.18.0"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Convert a Float32Array (PCM) to an ArrayBuffer (WAV)
|
||||
*/
|
||||
export function pcm2wav(buffer: Float32Array, numChannels: number, sampleRate: number) {
|
||||
const bufferLength = buffer.length;
|
||||
const headerLength = 44;
|
||||
const view = new DataView(new ArrayBuffer(bufferLength * numChannels * 2 + headerLength));
|
||||
|
||||
view.setUint32(0, 0x46464952, true); // "RIFF"
|
||||
view.setUint32(4, view.buffer.byteLength - 8, true); // RIFF size
|
||||
view.setUint32(8, 0x45564157, true); // "WAVE"
|
||||
|
||||
view.setUint32(12, 0x20746d66, true); // Subchunk1ID ("fmt ")
|
||||
view.setUint32(16, 0x10, true); // Subchunk1Size
|
||||
view.setUint16(20, 0x0001, true); // AudioFormat
|
||||
view.setUint16(22, numChannels, true); // NumChannels
|
||||
view.setUint32(24, sampleRate, true); // SampleRate
|
||||
view.setUint32(28, numChannels * 2 * sampleRate, true); // ByteRate
|
||||
view.setUint16(32, numChannels * 2, true); // BlockAlign
|
||||
view.setUint16(34, 16, true); // BitsPerSample
|
||||
|
||||
view.setUint32(36, 0x61746164, true); // Subchunk2ID ("data")
|
||||
view.setUint32(40, 2 * bufferLength, true); // Subchunk2Size
|
||||
|
||||
let p = headerLength;
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
const v = buffer[i];
|
||||
if (v >= 1)
|
||||
view.setInt16(p, 0x7fff, true);
|
||||
else if (v <= -1)
|
||||
view.setInt16(p, -0x8000, true);
|
||||
else
|
||||
view.setInt16(p, (v * 0x8000) | 0, true);
|
||||
p += 2;
|
||||
}
|
||||
return view.buffer;
|
||||
}
|
||||
+15
-1
@@ -1,9 +1,23 @@
|
||||
import { VoiceId } from "./types";
|
||||
|
||||
/**
|
||||
* Location of the ml models
|
||||
*/
|
||||
export const HF_BASE = 'https://huggingface.co/diffusionstudio/piper-voices/resolve/main';
|
||||
export const ONNX_BASE = "https://cdnjs.cloudflare.com/ajax/libs/onnxruntime-web/1.17.1";
|
||||
|
||||
/**
|
||||
* Inference runtime libary base path
|
||||
*/
|
||||
export const ONNX_BASE = 'https://cdnjs.cloudflare.com/ajax/libs/onnxruntime-web/1.18.0/';
|
||||
|
||||
/**
|
||||
* Path to wasm related files
|
||||
*/
|
||||
export const WASM_BASE = "https://cdn.jsdelivr.net/npm/@diffusionstudio/piper-wasm@1.0.0/build/piper_phonemize";
|
||||
|
||||
/**
|
||||
* Path to ml models on huggingface
|
||||
*/
|
||||
export const PATH_MAP: Record<VoiceId, string> = {
|
||||
'ar_JO-kareem-low': 'ar/ar_JO/kareem/low/ar_JO-kareem-low.onnx',
|
||||
'ar_JO-kareem-medium': 'ar/ar_JO/kareem/medium/ar_JO-kareem-medium.onnx',
|
||||
|
||||
+10
-10
@@ -1,16 +1,16 @@
|
||||
import { MessageData, ProgressCallback, VoiceId } from "./types";
|
||||
import { InferenceConfg, MessageData, ProgressCallback } from "./types";
|
||||
import Worker from './worker.ts?worker'
|
||||
|
||||
type PredictOptions = {
|
||||
text: string,
|
||||
voiceId: VoiceId
|
||||
};
|
||||
/**
|
||||
* Run text to speech inference in new worker thread. Fetches the model
|
||||
* first, if it has not yet been saved to opfs yet.
|
||||
*/
|
||||
export async function predict(config: InferenceConfg, callback?: ProgressCallback): Promise<Blob> {
|
||||
const worker = new Worker()
|
||||
|
||||
export async function predict(options: PredictOptions, callback?: ProgressCallback): Promise<File> {
|
||||
const worker = new Worker(new URL('./worker.ts', import.meta.url));
|
||||
worker.postMessage({ type: 'init', ...config });
|
||||
|
||||
worker.postMessage({ type: 'init', ...options });
|
||||
|
||||
return await new Promise<File>((resolve, reject) => {
|
||||
return await new Promise<Blob>((resolve, reject) => {
|
||||
function eventHandler(event: MessageEvent<MessageData>) {
|
||||
const data = event.data;
|
||||
|
||||
|
||||
+2
-20
@@ -1,32 +1,14 @@
|
||||
import './style.css'
|
||||
import typescriptLogo from './typescript.svg'
|
||||
import viteLogo from '/vite.svg'
|
||||
import * as tts from './index';
|
||||
|
||||
document.querySelector('#app')!.innerHTML = `
|
||||
<div>
|
||||
<a href="https://vitejs.dev" target="_blank">
|
||||
<img src="${viteLogo}" class="logo" alt="Vite logo" />
|
||||
</a>
|
||||
<a href="https://www.typescriptlang.org/" target="_blank">
|
||||
<img src="${typescriptLogo}" class="logo vanilla" alt="TypeScript logo" />
|
||||
</a>
|
||||
<h1>Vite + TypeScript</h1>
|
||||
<div class="card">
|
||||
<button id="btn" type="button">Predict</button>
|
||||
</div>
|
||||
<p class="read-the-docs">
|
||||
Click on the Vite and TypeScript logos to learn more
|
||||
</p>
|
||||
</div>
|
||||
<button id="btn" type="button">Predict</button>
|
||||
`
|
||||
|
||||
document.getElementById('btn')?.addEventListener('click', async () => {
|
||||
|
||||
|
||||
const voices = await tts.predict({
|
||||
text: "Upload and Download progress tracking with Fetch and Axios",
|
||||
voiceId: 'en_US-hfc_male-medium'
|
||||
voiceId: 'en_US-hfc_female-medium'
|
||||
}, console.log);
|
||||
|
||||
const audio = new Audio();
|
||||
|
||||
+21
-3
@@ -1,4 +1,5 @@
|
||||
export async function writeBlob(url: string, blob: Blob): Promise<void> {
|
||||
// only store models
|
||||
if (!url.match('https://huggingface.co')) return;
|
||||
try {
|
||||
const root = await navigator.storage.getDirectory();
|
||||
@@ -11,7 +12,7 @@ export async function writeBlob(url: string, blob: Blob): Promise<void> {
|
||||
const writable = await file.createWritable();
|
||||
await writable.write(blob);
|
||||
await writable.close();
|
||||
} catch (e) { }
|
||||
} catch (_) { }
|
||||
}
|
||||
|
||||
export async function removeBlob(url: string) {
|
||||
@@ -21,5 +22,22 @@ export async function removeBlob(url: string) {
|
||||
const path = url.split('/').at(-1)!;
|
||||
const file = await dir.getFileHandle(path); // @ts-ignore
|
||||
file.remove();
|
||||
} catch (e) { }
|
||||
}
|
||||
} catch (_) { }
|
||||
}
|
||||
|
||||
export async function readBlob(url: string): Promise<Blob | undefined> {
|
||||
if (!url.match('https://huggingface.co')) return;
|
||||
try {
|
||||
const root = await navigator.storage.getDirectory();
|
||||
const dir = await root.getDirectoryHandle('piper', {
|
||||
create: true,
|
||||
});
|
||||
|
||||
const path = url.split('/').at(-1)!;
|
||||
const file = await dir.getFileHandle(path);
|
||||
|
||||
return await file.getFile();
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -40,7 +40,7 @@ export async function stored(): Promise<VoiceId[]> {
|
||||
for await (const name of dir.keys()) {
|
||||
const key = name.split('.')[0];
|
||||
if (name.endsWith('.onnx') && key in PATH_MAP) {
|
||||
result.push(key);
|
||||
result.push(key as VoiceId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -33,7 +33,7 @@ export type ErrorMessage = {
|
||||
|
||||
export type OutputMessage = {
|
||||
type: "output";
|
||||
file: File;
|
||||
file: Blob;
|
||||
}
|
||||
|
||||
export type FetchMessage = {
|
||||
@@ -43,6 +43,11 @@ export type FetchMessage = {
|
||||
loaded: number;
|
||||
}
|
||||
|
||||
export type InferenceConfg = {
|
||||
text: string,
|
||||
voiceId: VoiceId
|
||||
};
|
||||
|
||||
export type MessageData = ErrorMessage | OutputMessage | FetchMessage;
|
||||
|
||||
export type ProgressCallback = (progress: Omit<FetchMessage, 'type'>) => void;
|
||||
|
||||
+6
-1
@@ -1,7 +1,12 @@
|
||||
import { HF_BASE } from "./fixtures";
|
||||
import { Voice } from "./types";
|
||||
|
||||
/**
|
||||
* Retrieves all available voices from huggingface
|
||||
* @returns
|
||||
*/
|
||||
export async function voices(): Promise<Voice[]> {
|
||||
const res = await fetch('https://huggingface.co/diffusionstudio/piper-voices/raw/main/voices.json');
|
||||
const res = await fetch(`${HF_BASE}/voices.json`);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error('Could not retrieve voices file from huggingface')
|
||||
|
||||
+30
-249
@@ -1,37 +1,41 @@
|
||||
declare var ort: any;
|
||||
import * as ort from 'onnxruntime-web';
|
||||
// @ts-ignore
|
||||
import { createPiperPhonemize } from './piper.js';
|
||||
import { ErrorMessage, FetchMessage, InferenceConfg, OutputMessage } from './types';
|
||||
import { HF_BASE, ONNX_BASE, PATH_MAP } from './fixtures';
|
||||
import { readBlob, writeBlob } from './opfs';
|
||||
import { fetchBlob } from './http.js';
|
||||
import { pcm2wav } from './audio';
|
||||
|
||||
const HF_BASE = "https://huggingface.co/diffusionstudio/piper-voices/resolve/main";
|
||||
const ONNX_BASE = "https://cdnjs.cloudflare.com/ajax/libs/onnxruntime-web/1.17.1";
|
||||
const WASM_BASE = "https://cdn.jsdelivr.net/npm/@diffusionstudio/piper-wasm@1.0.0/build/piper_phonemize";
|
||||
type MessageData = InferenceConfg & { type?: 'init' }
|
||||
|
||||
self.addEventListener("message", async event => {
|
||||
const WASM_URL = new URL('/piper.wasm', import.meta.url).href;
|
||||
const DATA_URL = new URL('/piper.data', import.meta.url).href;
|
||||
|
||||
async function handleMessage(event: MessageEvent<MessageData>) {
|
||||
const data = event.data;
|
||||
|
||||
if (data?.type != 'init') return;
|
||||
|
||||
const path = pathmap[data.voiceId];
|
||||
const path = PATH_MAP[data.voiceId];
|
||||
const input = JSON.stringify([{ text: data.text.trim() }])
|
||||
|
||||
const piperPhonemizeJs = (await createBlobUrl(`${WASM_BASE}.js`)).url;
|
||||
const piperPhonemizeWasm = (await createBlobUrl(`${WASM_BASE}.wasm`)).url;
|
||||
const piperPhonemizeData = (await createBlobUrl(`${WASM_BASE}.data`)).url;
|
||||
const onnxruntimeJs = (await createBlobUrl(`${ONNX_BASE}/ort.min.js`)).url;
|
||||
|
||||
importScripts(piperPhonemizeJs, onnxruntimeJs);
|
||||
const piperPhonemizeWasm = (await createBlobUrl(WASM_URL)).url;
|
||||
const piperPhonemizeData = (await createBlobUrl(DATA_URL)).url;
|
||||
|
||||
ort.env.wasm.numThreads = navigator.hardwareConcurrency;
|
||||
ort.env.wasm.wasmPaths = ONNX_BASE + '/';
|
||||
ort.env.wasm.wasmPaths = ONNX_BASE;
|
||||
|
||||
const modelConfigBlob = (await createBlobUrl(`${HF_BASE}/${path}.json`)).blob;
|
||||
const modelConfig = JSON.parse(await modelConfigBlob.text());
|
||||
|
||||
const phonemeIds: string[] = await new Promise(async resolve => {
|
||||
// @ts-ignore
|
||||
const module = await createPiperPhonemize({
|
||||
print: (data: any) => {
|
||||
resolve(JSON.parse(data).phoneme_ids);
|
||||
},
|
||||
printErr: (message: any) => {
|
||||
self.postMessage({ type: "stderr", message });
|
||||
self.postMessage({ type: "stderr", message } satisfies ErrorMessage);
|
||||
},
|
||||
locateFile: (url: string) => {
|
||||
if (url.endsWith(".wasm")) return piperPhonemizeWasm;
|
||||
@@ -62,90 +66,23 @@ self.addEventListener("message", async event => {
|
||||
|
||||
const { output: { data: pcm } } = await session.run(feeds);
|
||||
|
||||
const file = new Blob([pcm2wav(pcm, 1, sampleRate)], { type: "audio/x-wav" });
|
||||
const file = new Blob([pcm2wav(pcm as Float32Array, 1, sampleRate)], { type: "audio/x-wav" });
|
||||
|
||||
self.postMessage({ type: "output", file });
|
||||
})
|
||||
|
||||
async function fetchBlob(url: string): Promise<Blob> {
|
||||
return new Promise((resolve) => {
|
||||
let xContentLength: number;
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.responseType = "blob";
|
||||
xhr.onprogress = event => {
|
||||
self.postMessage({
|
||||
url,
|
||||
type: "fetch",
|
||||
total: xContentLength ?? event.total,
|
||||
loaded: event.loaded
|
||||
})
|
||||
}
|
||||
|
||||
xhr.onreadystatechange = () => {
|
||||
if (
|
||||
xhr.readyState >= xhr.HEADERS_RECEIVED
|
||||
&& xContentLength == undefined
|
||||
&& xhr.getAllResponseHeaders().includes("x-content-length")
|
||||
) {
|
||||
xContentLength = Number(xhr.getResponseHeader("x-content-length"));
|
||||
}
|
||||
|
||||
|
||||
if (xhr.readyState === xhr.DONE) {
|
||||
self.postMessage({
|
||||
url,
|
||||
type: "fetch",
|
||||
total: xContentLength,
|
||||
loaded: xContentLength
|
||||
})
|
||||
resolve(xhr.response);
|
||||
}
|
||||
}
|
||||
xhr.open("GET", url);
|
||||
xhr.send();
|
||||
})
|
||||
};
|
||||
|
||||
async function readBlob(url: string): Promise<Blob | undefined> {
|
||||
if (!url.match('https://huggingface.co')) return;
|
||||
|
||||
const root = await navigator.storage.getDirectory();
|
||||
const dir = await root.getDirectoryHandle('piper', {
|
||||
create: true,
|
||||
});
|
||||
|
||||
try {
|
||||
const path = url.split('/').at(-1)!;
|
||||
const file = await dir.getFileHandle(path);
|
||||
|
||||
return await file.getFile();
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async function writeBlob(url: string, blob: Blob): Promise<void> {
|
||||
if (!url.match('https://huggingface.co')) return;
|
||||
|
||||
const root = await navigator.storage.getDirectory();
|
||||
const dir = await root.getDirectoryHandle('piper', {
|
||||
create: true,
|
||||
});
|
||||
|
||||
try {
|
||||
const path = url.split('/').at(-1)!;
|
||||
const file = await dir.getFileHandle(path, { create: true });
|
||||
const writable = await file.createWritable();
|
||||
await writable.write(blob);
|
||||
await writable.close();
|
||||
} catch (e) { }
|
||||
self.postMessage({ type: "output", file } satisfies OutputMessage);
|
||||
}
|
||||
|
||||
async function createBlobUrl(url: string) {
|
||||
let blob: Blob | undefined = await readBlob(url);
|
||||
|
||||
if (!blob) {
|
||||
blob = await fetchBlob(url);
|
||||
blob = await fetchBlob(url, (data) => {
|
||||
if (url.match('https://huggingface.co')) {
|
||||
self.postMessage({
|
||||
...data,
|
||||
type: "fetch"
|
||||
} satisfies FetchMessage)
|
||||
}
|
||||
});
|
||||
await writeBlob(url, blob);
|
||||
}
|
||||
|
||||
@@ -155,160 +92,4 @@ async function createBlobUrl(url: string) {
|
||||
};
|
||||
}
|
||||
|
||||
// Float32Array (PCM) to ArrayBuffer (WAV)
|
||||
function pcm2wav(buffer: Float32Array, numChannels: number, sampleRate: number) {
|
||||
const bufferLength = buffer.length;
|
||||
const headerLength = 44;
|
||||
const view = new DataView(new ArrayBuffer(bufferLength * numChannels * 2 + headerLength));
|
||||
|
||||
view.setUint32(0, 0x46464952, true); // "RIFF"
|
||||
view.setUint32(4, view.buffer.byteLength - 8, true); // RIFF size
|
||||
view.setUint32(8, 0x45564157, true); // "WAVE"
|
||||
|
||||
view.setUint32(12, 0x20746d66, true); // Subchunk1ID ("fmt ")
|
||||
view.setUint32(16, 0x10, true); // Subchunk1Size
|
||||
view.setUint16(20, 0x0001, true); // AudioFormat
|
||||
view.setUint16(22, numChannels, true); // NumChannels
|
||||
view.setUint32(24, sampleRate, true); // SampleRate
|
||||
view.setUint32(28, numChannels * 2 * sampleRate, true); // ByteRate
|
||||
view.setUint16(32, numChannels * 2, true); // BlockAlign
|
||||
view.setUint16(34, 16, true); // BitsPerSample
|
||||
|
||||
view.setUint32(36, 0x61746164, true); // Subchunk2ID ("data")
|
||||
view.setUint32(40, 2 * bufferLength, true); // Subchunk2Size
|
||||
|
||||
let p = headerLength;
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
const v = buffer[i];
|
||||
if (v >= 1)
|
||||
view.setInt16(p, 0x7fff, true);
|
||||
else if (v <= -1)
|
||||
view.setInt16(p, -0x8000, true);
|
||||
else
|
||||
view.setInt16(p, (v * 0x8000) | 0, true);
|
||||
p += 2;
|
||||
}
|
||||
return view.buffer;
|
||||
}
|
||||
|
||||
const pathmap: any = {
|
||||
'ar_JO-kareem-low': 'ar/ar_JO/kareem/low/ar_JO-kareem-low.onnx',
|
||||
'ar_JO-kareem-medium': 'ar/ar_JO/kareem/medium/ar_JO-kareem-medium.onnx',
|
||||
'ca_ES-upc_ona-medium': 'ca/ca_ES/upc_ona/medium/ca_ES-upc_ona-medium.onnx',
|
||||
'ca_ES-upc_ona-x_low': 'ca/ca_ES/upc_ona/x_low/ca_ES-upc_ona-x_low.onnx',
|
||||
'ca_ES-upc_pau-x_low': 'ca/ca_ES/upc_pau/x_low/ca_ES-upc_pau-x_low.onnx',
|
||||
'cs_CZ-jirka-low': 'cs/cs_CZ/jirka/low/cs_CZ-jirka-low.onnx',
|
||||
'cs_CZ-jirka-medium': 'cs/cs_CZ/jirka/medium/cs_CZ-jirka-medium.onnx',
|
||||
'da_DK-talesyntese-medium': 'da/da_DK/talesyntese/medium/da_DK-talesyntese-medium.onnx',
|
||||
'de_DE-eva_k-x_low': 'de/de_DE/eva_k/x_low/de_DE-eva_k-x_low.onnx',
|
||||
'de_DE-karlsson-low': 'de/de_DE/karlsson/low/de_DE-karlsson-low.onnx',
|
||||
'de_DE-kerstin-low': 'de/de_DE/kerstin/low/de_DE-kerstin-low.onnx',
|
||||
'de_DE-mls-medium': 'de/de_DE/mls/medium/de_DE-mls-medium.onnx',
|
||||
'de_DE-pavoque-low': 'de/de_DE/pavoque/low/de_DE-pavoque-low.onnx',
|
||||
'de_DE-ramona-low': 'de/de_DE/ramona/low/de_DE-ramona-low.onnx',
|
||||
'de_DE-thorsten-high': 'de/de_DE/thorsten/high/de_DE-thorsten-high.onnx',
|
||||
'de_DE-thorsten-low': 'de/de_DE/thorsten/low/de_DE-thorsten-low.onnx',
|
||||
'de_DE-thorsten-medium': 'de/de_DE/thorsten/medium/de_DE-thorsten-medium.onnx',
|
||||
'de_DE-thorsten_emotional-medium': 'de/de_DE/thorsten_emotional/medium/de_DE-thorsten_emotional-medium.onnx',
|
||||
'el_GR-rapunzelina-low': 'el/el_GR/rapunzelina/low/el_GR-rapunzelina-low.onnx',
|
||||
'en_GB-alan-low': 'en/en_GB/alan/low/en_GB-alan-low.onnx',
|
||||
'en_GB-alan-medium': 'en/en_GB/alan/medium/en_GB-alan-medium.onnx',
|
||||
'en_GB-alba-medium': 'en/en_GB/alba/medium/en_GB-alba-medium.onnx',
|
||||
'en_GB-aru-medium': 'en/en_GB/aru/medium/en_GB-aru-medium.onnx',
|
||||
'en_GB-cori-high': 'en/en_GB/cori/high/en_GB-cori-high.onnx',
|
||||
'en_GB-cori-medium': 'en/en_GB/cori/medium/en_GB-cori-medium.onnx',
|
||||
'en_GB-jenny_dioco-medium': 'en/en_GB/jenny_dioco/medium/en_GB-jenny_dioco-medium.onnx',
|
||||
'en_GB-northern_english_male-medium': 'en/en_GB/northern_english_male/medium/en_GB-northern_english_male-medium.onnx',
|
||||
'en_GB-semaine-medium': 'en/en_GB/semaine/medium/en_GB-semaine-medium.onnx',
|
||||
'en_GB-southern_english_female-low': 'en/en_GB/southern_english_female/low/en_GB-southern_english_female-low.onnx',
|
||||
'en_GB-vctk-medium': 'en/en_GB/vctk/medium/en_GB-vctk-medium.onnx',
|
||||
'en_US-amy-low': 'en/en_US/amy/low/en_US-amy-low.onnx',
|
||||
'en_US-amy-medium': 'en/en_US/amy/medium/en_US-amy-medium.onnx',
|
||||
'en_US-arctic-medium': 'en/en_US/arctic/medium/en_US-arctic-medium.onnx',
|
||||
'en_US-danny-low': 'en/en_US/danny/low/en_US-danny-low.onnx',
|
||||
'en_US-hfc_female-medium': 'en/en_US/hfc_female/medium/en_US-hfc_female-medium.onnx',
|
||||
'en_US-hfc_male-medium': 'en/en_US/hfc_male/medium/en_US-hfc_male-medium.onnx',
|
||||
'en_US-joe-medium': 'en/en_US/joe/medium/en_US-joe-medium.onnx',
|
||||
'en_US-kathleen-low': 'en/en_US/kathleen/low/en_US-kathleen-low.onnx',
|
||||
'en_US-kristin-medium': 'en/en_US/kristin/medium/en_US-kristin-medium.onnx',
|
||||
'en_US-kusal-medium': 'en/en_US/kusal/medium/en_US-kusal-medium.onnx',
|
||||
'en_US-l2arctic-medium': 'en/en_US/l2arctic/medium/en_US-l2arctic-medium.onnx',
|
||||
'en_US-lessac-high': 'en/en_US/lessac/high/en_US-lessac-high.onnx',
|
||||
'en_US-lessac-low': 'en/en_US/lessac/low/en_US-lessac-low.onnx',
|
||||
'en_US-lessac-medium': 'en/en_US/lessac/medium/en_US-lessac-medium.onnx',
|
||||
'en_US-libritts-high': 'en/en_US/libritts/high/en_US-libritts-high.onnx',
|
||||
'en_US-libritts_r-medium': 'en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx',
|
||||
'en_US-ljspeech-high': 'en/en_US/ljspeech/high/en_US-ljspeech-high.onnx',
|
||||
'en_US-ljspeech-medium': 'en/en_US/ljspeech/medium/en_US-ljspeech-medium.onnx',
|
||||
'en_US-ryan-high': 'en/en_US/ryan/high/en_US-ryan-high.onnx',
|
||||
'en_US-ryan-low': 'en/en_US/ryan/low/en_US-ryan-low.onnx',
|
||||
'en_US-ryan-medium': 'en/en_US/ryan/medium/en_US-ryan-medium.onnx',
|
||||
'es_ES-carlfm-x_low': 'es/es_ES/carlfm/x_low/es_ES-carlfm-x_low.onnx',
|
||||
'es_ES-davefx-medium': 'es/es_ES/davefx/medium/es_ES-davefx-medium.onnx',
|
||||
'es_ES-mls_10246-low': 'es/es_ES/mls_10246/low/es_ES-mls_10246-low.onnx',
|
||||
'es_ES-mls_9972-low': 'es/es_ES/mls_9972/low/es_ES-mls_9972-low.onnx',
|
||||
'es_ES-sharvard-medium': 'es/es_ES/sharvard/medium/es_ES-sharvard-medium.onnx',
|
||||
'es_MX-ald-medium': 'es/es_MX/ald/medium/es_MX-ald-medium.onnx',
|
||||
'es_MX-claude-high': 'es/es_MX/claude/high/es_MX-claude-high.onnx',
|
||||
'fa_IR-amir-medium': 'fa/fa_IR/amir/medium/fa_IR-amir-medium.onnx',
|
||||
'fa_IR-gyro-medium': 'fa/fa_IR/gyro/medium/fa_IR-gyro-medium.onnx',
|
||||
'fi_FI-harri-low': 'fi/fi_FI/harri/low/fi_FI-harri-low.onnx',
|
||||
'fi_FI-harri-medium': 'fi/fi_FI/harri/medium/fi_FI-harri-medium.onnx',
|
||||
'fr_FR-gilles-low': 'fr/fr_FR/gilles/low/fr_FR-gilles-low.onnx',
|
||||
'fr_FR-mls-medium': 'fr/fr_FR/mls/medium/fr_FR-mls-medium.onnx',
|
||||
'fr_FR-mls_1840-low': 'fr/fr_FR/mls_1840/low/fr_FR-mls_1840-low.onnx',
|
||||
'fr_FR-siwis-low': 'fr/fr_FR/siwis/low/fr_FR-siwis-low.onnx',
|
||||
'fr_FR-siwis-medium': 'fr/fr_FR/siwis/medium/fr_FR-siwis-medium.onnx',
|
||||
'fr_FR-tom-medium': 'fr/fr_FR/tom/medium/fr_FR-tom-medium.onnx',
|
||||
'fr_FR-upmc-medium': 'fr/fr_FR/upmc/medium/fr_FR-upmc-medium.onnx',
|
||||
'hu_HU-anna-medium': 'hu/hu_HU/anna/medium/hu_HU-anna-medium.onnx',
|
||||
'hu_HU-berta-medium': 'hu/hu_HU/berta/medium/hu_HU-berta-medium.onnx',
|
||||
'hu_HU-imre-medium': 'hu/hu_HU/imre/medium/hu_HU-imre-medium.onnx',
|
||||
'is_IS-bui-medium': 'is/is_IS/bui/medium/is_IS-bui-medium.onnx',
|
||||
'is_IS-salka-medium': 'is/is_IS/salka/medium/is_IS-salka-medium.onnx',
|
||||
'is_IS-steinn-medium': 'is/is_IS/steinn/medium/is_IS-steinn-medium.onnx',
|
||||
'is_IS-ugla-medium': 'is/is_IS/ugla/medium/is_IS-ugla-medium.onnx',
|
||||
'it_IT-riccardo-x_low': 'it/it_IT/riccardo/x_low/it_IT-riccardo-x_low.onnx',
|
||||
'ka_GE-natia-medium': 'ka/ka_GE/natia/medium/ka_GE-natia-medium.onnx',
|
||||
'kk_KZ-iseke-x_low': 'kk/kk_KZ/iseke/x_low/kk_KZ-iseke-x_low.onnx',
|
||||
'kk_KZ-issai-high': 'kk/kk_KZ/issai/high/kk_KZ-issai-high.onnx',
|
||||
'kk_KZ-raya-x_low': 'kk/kk_KZ/raya/x_low/kk_KZ-raya-x_low.onnx',
|
||||
'lb_LU-marylux-medium': 'lb/lb_LU/marylux/medium/lb_LU-marylux-medium.onnx',
|
||||
'ne_NP-google-medium': 'ne/ne_NP/google/medium/ne_NP-google-medium.onnx',
|
||||
'ne_NP-google-x_low': 'ne/ne_NP/google/x_low/ne_NP-google-x_low.onnx',
|
||||
'nl_BE-nathalie-medium': 'nl/nl_BE/nathalie/medium/nl_BE-nathalie-medium.onnx',
|
||||
'nl_BE-nathalie-x_low': 'nl/nl_BE/nathalie/x_low/nl_BE-nathalie-x_low.onnx',
|
||||
'nl_BE-rdh-medium': 'nl/nl_BE/rdh/medium/nl_BE-rdh-medium.onnx',
|
||||
'nl_BE-rdh-x_low': 'nl/nl_BE/rdh/x_low/nl_BE-rdh-x_low.onnx',
|
||||
'nl_NL-mls-medium': 'nl/nl_NL/mls/medium/nl_NL-mls-medium.onnx',
|
||||
'nl_NL-mls_5809-low': 'nl/nl_NL/mls_5809/low/nl_NL-mls_5809-low.onnx',
|
||||
'nl_NL-mls_7432-low': 'nl/nl_NL/mls_7432/low/nl_NL-mls_7432-low.onnx',
|
||||
'no_NO-talesyntese-medium': 'no/no_NO/talesyntese/medium/no_NO-talesyntese-medium.onnx',
|
||||
'pl_PL-darkman-medium': 'pl/pl_PL/darkman/medium/pl_PL-darkman-medium.onnx',
|
||||
'pl_PL-gosia-medium': 'pl/pl_PL/gosia/medium/pl_PL-gosia-medium.onnx',
|
||||
'pl_PL-mc_speech-medium': 'pl/pl_PL/mc_speech/medium/pl_PL-mc_speech-medium.onnx',
|
||||
'pl_PL-mls_6892-low': 'pl/pl_PL/mls_6892/low/pl_PL-mls_6892-low.onnx',
|
||||
'pt_BR-edresson-low': 'pt/pt_BR/edresson/low/pt_BR-edresson-low.onnx',
|
||||
'pt_BR-faber-medium': 'pt/pt_BR/faber/medium/pt_BR-faber-medium.onnx',
|
||||
'pt_PT-tugão-medium': 'pt/pt_PT/tugão/medium/pt_PT-tugão-medium.onnx',
|
||||
'ro_RO-mihai-medium': 'ro/ro_RO/mihai/medium/ro_RO-mihai-medium.onnx',
|
||||
'ru_RU-denis-medium': 'ru/ru_RU/denis/medium/ru_RU-denis-medium.onnx',
|
||||
'ru_RU-dmitri-medium': 'ru/ru_RU/dmitri/medium/ru_RU-dmitri-medium.onnx',
|
||||
'ru_RU-irina-medium': 'ru/ru_RU/irina/medium/ru_RU-irina-medium.onnx',
|
||||
'ru_RU-ruslan-medium': 'ru/ru_RU/ruslan/medium/ru_RU-ruslan-medium.onnx',
|
||||
'sk_SK-lili-medium': 'sk/sk_SK/lili/medium/sk_SK-lili-medium.onnx',
|
||||
'sl_SI-artur-medium': 'sl/sl_SI/artur/medium/sl_SI-artur-medium.onnx',
|
||||
'sr_RS-serbski_institut-medium': 'sr/sr_RS/serbski_institut/medium/sr_RS-serbski_institut-medium.onnx',
|
||||
'sv_SE-nst-medium': 'sv/sv_SE/nst/medium/sv_SE-nst-medium.onnx',
|
||||
'sw_CD-lanfrica-medium': 'sw/sw_CD/lanfrica/medium/sw_CD-lanfrica-medium.onnx',
|
||||
'tr_TR-dfki-medium': 'tr/tr_TR/dfki/medium/tr_TR-dfki-medium.onnx',
|
||||
'tr_TR-fahrettin-medium': 'tr/tr_TR/fahrettin/medium/tr_TR-fahrettin-medium.onnx',
|
||||
'tr_TR-fettah-medium': 'tr/tr_TR/fettah/medium/tr_TR-fettah-medium.onnx',
|
||||
'uk_UA-lada-x_low': 'uk/uk_UA/lada/x_low/uk_UA-lada-x_low.onnx',
|
||||
'uk_UA-ukrainian_tts-medium': 'uk/uk_UA/ukrainian_tts/medium/uk_UA-ukrainian_tts-medium.onnx',
|
||||
'vi_VN-25hours_single-low': 'vi/vi_VN/25hours_single/low/vi_VN-25hours_single-low.onnx',
|
||||
'vi_VN-vais1000-medium': 'vi/vi_VN/vais1000/medium/vi_VN-vais1000-medium.onnx',
|
||||
'vi_VN-vivos-x_low': 'vi/vi_VN/vivos/x_low/vi_VN-vivos-x_low.onnx',
|
||||
'zh_CN-huayan-medium': 'zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx',
|
||||
'zh_CN-huayan-x_low': 'zh/zh_CN/huayan/x_low/zh_CN-huayan-x_low.onnx'
|
||||
}
|
||||
self.addEventListener("message", handleMessage);
|
||||
|
||||
Reference in New Issue
Block a user