mesm-tts: ten tiny English voices (ONNX + pdn WebAssembly engine), inference, samples, benchmarks
931612f Download web/v2/example.html from mesmertech/mesm-tts: direct link, hf CLI and curl.
- Browser
- Download file 2.32 kB
-
https://huggingface.co/mesmertech/mesm-tts/resolve/main/web/v2/example.html
- Command line
-
hf download hf://mesmertech/mesm-tts/web/v2/example.html
-
curl -L -o example.html https://huggingface.co/mesmertech/mesm-tts/resolve/main/web/v2/example.html
2.32 kB
| <meta charset="utf-8"> | |
| <title>Paradee: minimal example</title> | |
| <!-- Minimal integration: the worker synthesises, the AudioWorklet plays chunks as they arrive. Serve this directory over HTTP | |
| (module workers do not load from file://). ?espeak=URL points the eSpeak NG fallback at a module exporting | |
| phonemize(text, lang) when there is no bundler to resolve frontend/kokoro-phonemize.mjs's bare "phonemizer" import. --> | |
| <textarea id="text" rows="4" cols="70">Paradee runs entirely in this page. The first sound plays a few tens of milliseconds after you click.</textarea><br> | |
| <select id="model"><option value="v2f">v2f</option><option value="v2f_lite">v2f_lite</option></select> | |
| <button id="speak">Speak</button> <span id="status">loading…</span> | |
| <script type="module"> | |
| import { createPlayer } from "./player.mjs"; | |
| const $ = (id) => document.getElementById(id), espeakUrl = new URLSearchParams(location.search).get("espeak") || undefined; | |
| let worker = null, model = null, player = null, id = 0, t0 = 0; | |
| function load() { | |
| worker?.terminate(); model = $("model").value; $("status").textContent = "loading…"; | |
| worker = new Worker(new URL("./paradee-worker.mjs", import.meta.url), { type: "module" }); | |
| return new Promise((ready) => { | |
| worker.onmessage = ({ data: m }) => { | |
| if (m.op === "loaded") { $("status").textContent = `ready (${m.info.build}, ${m.info.load_ms.toFixed(0)} ms)`; ready(); } | |
| else if (m.op === "chunk" && m.id === id) player.push(m.samples); | |
| else if (m.op === "done" && m.id === id) { player.end(); $("status").textContent = `${(m.audio_s * 1000 / m.compute_ms).toFixed(0)}x real time`; } | |
| else if (m.op === "error") $("status").textContent = m.message; | |
| }; | |
| worker.postMessage({ op: "load", model, espeakUrl: espeakUrl && new URL(espeakUrl, location.href).href }); | |
| }); | |
| } | |
| let loading = load(); | |
| $("model").onchange = () => (loading = load()); | |
| $("speak").onclick = async () => { | |
| player ??= await createPlayer(); // AudioContext from a user gesture | |
| await player.resume(); await loading; | |
| id++; t0 = performance.now(); player.reset(); | |
| player.onstart = () => ($("status").textContent = `first audio after ${(performance.now() - t0).toFixed(0)} ms`); | |
| worker.postMessage({ op: "speak", id, text: $("text").value }); | |
| }; | |
| </script> | |