GitHub avatar

Fox's Blog

NEW

PiperHub: 536 open source TTS voices, 7 emotions in one model, zero GPU, zero bill

VITS, espeak, ONNX, LJSpeech: how Piper makes a Raspberry Pi talk. Multi-emotion Fortune in 7 emotions, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs, plus native pipertts 10x faster than the Python wrapper.

Introduction

Making a Discord bot talk is expensive. Not the text: a local LLM generates sentences for a few cents of GPU. The voice is billed on every sentence. Synthesis APIs charge per character, and when the bot talks all night, the bill follows.

That is the problem I had with Luna Protocol. My Discord bot generates its messages with a local LLM, then has them read aloud by a Piper voice, converts to OGG, and sends them as voice messages. All on my own server, for 0 euros per sentence, even at 3am when Luna decides to tell her life story on her own.

Staying at 0 euros means finding free voices and hosting them yourself. And that is where it got complicated: models scattered across dozens of Hugging Face repos, unclear licenses, no way to know where a given voice comes from or if it still works. So I built PiperHub, an open source catalog of voices for Piper TTS.

Today's numbers: 536 models listed, 71 languages covered, 3755 speakers listed. Every model is linked to its source dataset when I know it. Provenance is part of the sound, as the homepage says.

One question I get asked a lot remains: how does Piper compare against Kokoro, PocketTTS, sanoTTS and ElevenLabs? And why is the pipertts module from Fortune 10x faster than the official Python wrapper? This article opens up the engines, with code to back it up.

PiperHub, the catalog

Before opening the engines, let us look at the problem PiperHub solves.

Where the voices come from

The code is on github.com/fox3000foxy/piper-bank. Astro, static site, zero server: English at the root, 15 other locales under /<code>/ (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). Same pattern as my blog, actually.

Nobody edits the source of truth by hand. It is a pipeline:

npm install
npm run data    # regenerates src/data/*.json (python3 scripts/generate_data.py)
npx astro dev   # local dev
npm run build   # static build into dist/

Concretely: a crawler repo maintains models_mapping.csv and datasets_mapping.csv, mirrored voices live on Hugging Face, samples are audio files. npm run data pulls all of that and spits out src/data/*.json. The site only reads static JSON.

In the current raw data (555 entries, the site shows 536, the crawl is always a bit ahead of the display):

Quality Models
medium 437
high 67
low 50
Sample rate Models
22050 Hz 486
16000 Hz 47

486 voices at 22050 Hz, the Piper standard. 47 at 16000 Hz, the small fast voices for embedded. The phonemizer is espeak in 509 cases. I will come back to that, it is the heart of the system.

Provenance, or why every model has a dataset

The thing that makes PiperHub useful instead of just a list is the model <-> dataset link. Example taken from the home page: the voice Astra - Valorant Piper TTS is linked to the dataset Astra - LJSpeech Dataset. You open both pages, you check the repos and licenses on each side.

130 datasets referenced. And the catalog is honest about the state of the troops: 25 models are flagged usable: false (broken files, or piperPlus format that needs a forked runtime). The pipertts module rejects these voices with an explicit error instead of crashing weirdly later. 48 models are multi-speaker, including the two we will care about.

For guides, the site does the job in three steps: Build a dataset, Train a voice, Use a voice, plus a Piper Plus page. LJSpeech-format dataset as input, ONNX as output. Simple to say, long to do, hence the next section.

Piper under the hood: VITS, espeak, ONNX

One sentence, three steps

When you ask Piper to say "bonjour", this happens:

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

Zero separate vocoder. That is VITS: an end-to-end model that goes straight from (phonemized) text to waveform. Where old pipelines did text -> spectrogram (Tacotron) then spectrogram -> audio (HiFi-GAN, two models, two trainings), VITS learns everything at once with a conditional VAE, normalizing flows and a GAN-style discriminator on the decoder. One training, one model, one ONNX export.

The decoder outputs 22050 Hz directly. At that sample rate, one second of speech equals 22050 numbers. The model is small (a few dozen MB in medium quality) and inference is pure CPU: matrix multiplications on an optimized ONNX graph. Hence the Raspberry Pi as an official target. Piper was born in Rhasspy, an open source voice assistant built to run on Pi.

espeak-ng, the phonemizer that does all the dirty work

Before VITS, you have to turn text into phonemes. 509 models out of 555 use espeak:

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng knows the pronunciation of dozens of languages. The VITS model never learns French spelling with its eau pronounced o. It learns phonemes to sound. That simplifies training enormously and explains why Piper covers 71 languages: for a new language, you plug in the matching espeak voice and you train.

The ones that skip espeak have their own phonemizer, and that is where it gets exotic: hebrew (Nakdimon ONNX + 494 lines of IPA rules), pinyin (full BERT g2pW with polyphone disambiguation, the same Chinese character is pronounced differently depending on the word), thai (real unmodified TLTK), japanese (lindera segmentation + UniDic). The pipertts module ships all seven, verified identical to the Python reference. I will come back to that.

The dataset: LJSpeech, always LJSpeech

The standard training format is LJSpeech: a wavs/ folder full of clips, plus a metadata.csv:

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, one clip equals one sentence, a few seconds. The original dataset is 13,000 English sentences read by a single speaker, public domain. To train a French voice, you record the same structure in French: aligned text + audio, a few hours are enough for decent medium, more for high.

The three catalog qualities roughly match model size + training time. Low: small, fast, robotic. Medium: the sweet spot, with 437 models. High: bigger, slower, more natural, 67 models, including the Fortunes.

The three magic knobs

Every Piper voice exposes three inference parameters, with almost universal defaults:

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667): voice variability. Higher means more expressive, lower means more stable. Output is stochastic: two syntheses of the same sentence give two slightly different audios.
  • noise_w (0.8): timing variability, the rhythm.
  • length_scale (1.0): speed. Above 1 it slows down, below it speeds up. The simplest way to make Luna talk faster when she gets excited.

Remember noise_scale, it will matter for emotions.

Fortune multi: 7 emotions, 1 single ONNX

Alright, let us move to the local star.

The catalog lists three Fortune models trained by ItsRealFortune (hi):

Model Repo Voices
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

The FR multi is this:

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

Seven emotional variations in a single ONNX file. Same for the EN multi, with a historic typo in the names: suprised instead of surprised. It is in the weights, I keep it, it is part of the folklore.

How does a speakerId work?

Multi-speaker VITS is the same model with one extra input: a speaker embedding. At training time, each clip is tagged with its style, the model learns an embedding table (one per style) along with the rest. At inference time, you pick the row of the table:

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = normal ... 5 = surprise
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

Same sentence, seven readings. speakerId: 5 (surprise) with a slightly boosted noise_scale, and you get a believable exclamation. speakerId: 4 (triste), length_scale: 1.1 to slow down, and you get a funeral announcement. That is how Luna modulates her voice: the brain picks the emotion (valence/arousal, see my article on centroids), the TTS picks the speakerId.

The dataset behind it is soberly called Fortune French - Dataset, with a license named "Fortune's Café". Yes, the license is named like a coffee shop. No, it stays.

Why it is smart economically

Seven emotions equal one download, one load into RAM, one ONNX session. The alternative (seven separate models) would multiply storage and force a graph reload on every emotion switch, unusable for a bot that changes tone mid-sentence. Here, speakerId is just an index into a tensor. Marginal cost: zero.

The competitors: Kokoro, PocketTTS, sanoTTS

Piper shares the CPU with three serious challengers, three different philosophies.

Kokoro 82M, the quality/price king

Kokoro, by hexgrad. 82 million parameters, modified StyleTTS 2 architecture with ISTFTNet decoder, with no diffusion step. The lack of diffusion is what does it: no iterative denoising loop, so ultra fast inference.

Kokoro-82M
Params 82M
Weights ~300 MB (164 MB in FP16)
License Apache 2.0 (commercial OK)
Languages 8, 54 preset voices + blending
CPU near real time
GPU 36 to 96x real time
Blind tests at Google and Amazon level

Trained on under 100h of audio for about 1000 dollars, and blind listeners rank it at the level of premium Google and Amazon voices. That is why it blew up: premium quality, free, local, permissive.

Against Piper: Kokoro wins on pure naturalness (StyleTTS 2 > small VITS) and license (Apache 2.0 vs GPL-3.0 engine for Piper). Piper wins on coverage (71 languages vs 8, 3755 speakers vs 54 voices) and the community voice ecosystem. For English narration, Kokoro is hard to beat. For French with 7 emotions, Turkish or Swahili, Piper is often the only one in the race.

PocketTTS (Kyutai), cloning on CPU

Kyutai plays a different tune: a small model with voice cloning that runs on CPU.

Model Params WER UTMOS SIM CPU speed (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x real time
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, cloning included

Sub 1% WER (it pronounces correctly), UTMOS 4.36 (excellent perceived quality), SIM 0.923 (the clone really sounds like the target). And the small versions go down to 1.9M parameters with cloning. 1.9M. My Sabaki router is almost that size in JSON.

Against Piper: PocketTTS wins on cloning (Piper reads with fixed voices) and flagship compactness. Piper wins on languages (the small is English-only for now) and ecosystem maturity (536 ready voices vs "train your own").

sanoTTS, the model that fits on a 3 dollar chip

sanoTTS (sano means "small" in Nepali), it is the Bareiron of TTS: a full neural stack that goes from phonemes to 22.05 kHz PCM on microcontroller.

System Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M parameters. 2 MB per voice. pip install sanotts, sanotts say "Hello" --voice amy, it talks. In the browser too.

But look at the scores: SCOREQ 2.29 vs 3.94 for TinyTTS, UTMOS 2.45. It talks, yes. It sings, no. It is the ultimate tradeoff: at 0.29M you fit everywhere but quality follows size. Piper medium is 100x more weights for a voice in another league.

The summary table

Piper Kokoro 82M PocketTTS sanoTTS
Typical params ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Languages 71 8 EN (+ smalls EN) multilingual, small
Voices available 3755 speakers 54 + blending cloning (yours) a few voices
Cloning no no (native) yes, CPU no
Engine license GPL-3.0 Apache 2.0 open (Kyutai) open
Trainable at home yes (LJSpeech) yes (heavier) yes yes
FR quality good (multi emotion) fair (EN-first) EN-only weak

Your pick depends on your constraint, there is no absolute winner: pure EN quality -> Kokoro. CPU cloning -> PocketTTS. Microcontroller -> sanoTTS. Language coverage + emotional French voices + zero infra -> Piper.

ElevenLabs, the billed Goliath

And then there is ElevenLabs. 70+ languages, expressive Eleven v3 with audio tags and dramatic dialogues, top quality in blind tests. The Jev of TTS, in a way: everybody has seen it go by, everybody has benchmarked against it.

2026 pricing, in round numbers:

API model Price / 1000 characters
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 characters make about 1 minute of audio. So $0.05 to $0.10 per minute. The $99 Pro plan includes about 990k characters, overage hits at the same rate. For a YouTube channel shipping one voiced video per day (my valorant-short-maker does exactly that, but with free Piper), count $1.50 to $3 per month for each daily minute of voiceover. Ten minutes per day: $15 to $30/month. You can pay it, but you count it.

When it is worth it: when the voice is the product (dubbing, audiobook, ad), when you need 70 languages tomorrow morning, when cloning has to be perfect on the first try. But it is never worth it for: a Discord bot that talks all night, internal notifications, dev, exploding volumes. Luna telling her life story at 3am on ElevenLabs is my power bill crying.

The math is simple: Piper = 0 euros marginal, "good" quality. ElevenLabs = meter running, "excellent" quality. In between, there is a world where "good and free" wins. That is the whole PiperHub bet.

pipertts: why native beats the Python wrapper by 10x

The repo itsrealfortune/pipertts, 75 commits: a native port of Piper in TypeScript. README title: "In-process Piper TTS for Node.js. No Python required."

The cost of spawn

The historic wrapper (PiperTTS) is python3 -m piper spawned as a subprocess on every synthesis:

synthèse = spawn(python) + chargement du modèle + inférence + kill

Each sentence pays for interpreter startup + weight loading. Measured on en_US-lessac-medium: ~2.2s per line.

Native (PiperNativeTTS) keeps the ONNX session hot in memory:

load une fois -> session persistante -> inférence par phrase

Measured the same way: ~0.22s per line. The 10x comes from there: you kill spawn and reload, miraculous inference has nothing to do with it. The ONNX inference itself is the same on both sides.

As a bonus, the first load caches the optimized graph next to the .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), which makes later loads about 35% faster. The cache key combines platform, arch and ORT version, and it refreshes if the model changes. Disable with cacheOptimizedModel: false.

The espeak bridge: 0.4ms vs 15ms

Same logic for phonemization. The wrapper calls the espeak-ng CLI binary per sentence: about 15ms of spawn each time. Native embeds an N-API bridge (src/native/espeak-bridge/, port of espeakbridge.c), with prebuilds for Linux x64/arm64: ~0.4ms per call, byte-identical phonemes. Without the addon, automatic fallback to CLI. PIPER_ESPEAK_BRIDGE=0 to force CLI.

The 7 phoneme_type, all ported

Type Engine Notes
espeak N-API bridge or CLI byte-identical clauses
text codepoints passthrough trivial
hebrew Nakdimon ONNX + IPA rules identical outputs
lithuanian espeak + 189k entry stress dict identical outputs
pinyin BERT g2pW ONNX + WordPiece polyphone disambiguation OK
thai real unmodified TLTK under Pyodide/WASM 7/7 identical (1st call slow, rest fast)
japanese lindera-wasm + UniDic + TS mora engine exact segmentation, no lexical accent

And Arabic is diacritized automatically (tashkeel, can be disabled). 509 espeak voices in the catalog go through the fast bridge, exotics through their dedicated engine. 71 tests in the suite, documented constraint where it hurts (Japanese accent, UniDic ships no data, assumed, nothing hidden).

Formats and streaming

wav by default, raw PCM, mp3, ogg, opus: ffmpeg when available, else pure JS fallbacks (lamejs for mp3, opusscript + Ogg muxer for opus, 0.993 correlation with source). And above all synthesizeChunks(): an async generator that yields per sentence, with sentenceSilence between sentences. For Luna, that means starting to play audio while the end is still synthesizing. TTS streaming with no dedicated server.

Full usage, with the PiperHub catalog built in (hub: + automatic mirror fallback if the author repo stops serving files):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // straight for Discord
  lengthScale: 0.95,     // a touch faster
});

Community voices (GLaDOS and game characters) missing from the official manifest. That is exactly why PiperHub exists, and pipertts knows it well: hub:<slug>, piperhub page URL, or bare slug, all work. Voices flagged usable: false are refused with a clear error.

What was sacrificed

Every missing feature funds another one. The honest list:

Piper sounds robotic next to ElevenLabs. Medium VITS at 22050 Hz is clean but flat compared to a big expressive model. Multi-emotion partly compensates (7 styles > 1 flat style), but an ElevenLabs v3 audiobook stays above. That is the price of tens of MB.

espeak gets it wrong. Proper nouns, anglicisms, abbreviations: rule-based phonemization has blind spots. "Spoiler" in French, espeak reads it as best it can. Big models with neural G2P fail less. In prod, you need an exception dict for business terms.

Zero native cloning. Piper reads with fixed voices. If you want YOUR voice, go PocketTTS or ElevenLabs. Multi-speaker gives variations, never your timbre.

Japanese stays with no lexical accent. UniDic ships no accent data, so segmentation is exact but melody is approximate. Documented in the README, nothing under the rug.

Thai lags on first call. TLTK runs under unmodified Pyodide/WASM: the first call takes seconds (loading), later ones are fast. 7/7 identical to reference, but plan a warm-up.

Data is hungry. A decent high voice needs hours of clean aligned audio. The Fortune dataset is hours of recording + cleanup. Zero magic: garbage in, robot out.

The catalog has dead voices. 25 models flagged usable: false. It is assumed and filtered, but it reminds you that voice crowdsourcing is also abandonware crowdsourcing.

The final result

On one side an ElevenLabs meter, on the other a Raspberry Pi:

  • PiperHub: 536 models listed, 71 languages, 3755 speakers, 130 linked datasets, dataset -> training -> usage guides
  • Piper: end-to-end VITS, espeak, CPU ONNX, LJSpeech, low/medium/high, 22050 Hz standard
  • Fortune multi: 7 FR emotions + 7 EN, 1 ONNX each, speakerId to switch, "Fortune's Café" license
  • Kokoro 82M: premium EN quality, Apache 2.0, 54 voices, near real time CPU
  • PocketTTS: CPU cloning, ~100M then 2-18M, WER 0.92%
  • sanoTTS: 0.29M, 2 MB, microcontroller, quality that follows size
  • ElevenLabs: $0.05-0.10/1000 characters, 70+ languages, top quality, meter running
  • native pipertts: ~0.22s/line vs ~2.2s wrapper, espeak bridge 0.4ms, 7 phoneme_type, wav/raw/mp3/ogg/opus, per sentence streaming

Luna talks with that every day. 0 euros of TTS, brain-driven emotions, direct OGG for Discord. Against ElevenLabs you can hear it, obviously. But at 3am when she decides to tell her life story, my bill sleeps easy.

The philosophy

p2r3 said: "I just love the idea that this tiny 1 dollar chip can run something as advanced as Minecraft."

Same trip here, applied to voice: I love the idea that a few dozen MB of ONNX can make a Raspberry Pi talk in 71 languages, with no cloud, no account, no meter. It obviously replaces no dubbing studio. It replaces the annoying case where you want a decent, local, free voice that talks at night without billing you.

Tradeoffs, written in black and white:

  • premium quality -> small VITS: a bit robotic, but local and free
  • one model per emotion -> speakerId: 7 voices for the price of one in RAM
  • Python spawn -> persistent session: 10x faster, zero Python to install
  • wild catalog -> linked provenance: every voice knows where it comes from and if it still works
  • billed API -> 0 euros marginal: less pretty than ElevenLabs, but the bill sleeps easy

The 3 things to remember:

  1. Piper = the CPU TTS : VITS + espeak + ONNX, 71 languages, voices in tens of MB that run on Pi. Less pretty than ElevenLabs, infinitely cheaper.
  2. Fortune multi = 7 emotions for the price of one : one ONNX, speakerId to switch, FR + EN. The speaker embedding instead of 7 models.
  3. Native kills spawn : pipertts keeps the ONNX session hot: 0.22s/line vs 2.2s, espeak bridge 0.4ms vs 15ms CLI. Same inference, 10x less waiting.

If the project interests you, the catalog is piperhub.org, the site code github.com/fox3000foxy/piper-bank, the native module github.com/itsrealfortune/pipertts. And if you want to hear Luna tell her life story at 3am, it is on Discord xD

NEW

PiperHub : 536 voix TTS open source, 7 émotions en un modèle, zéro GPU, zéro facture

VITS, espeak, ONNX, LJSpeech : comment Piper fait parler un Raspberry Pi. Fortune multi en 7 émotions, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs -- et pipertts natif 10x plus rapide que le wrapper Python.

Introduction

Faire parler un bot Discord, ça coûte cher. Pas le texte : un LLM local génère des phrases pour quelques centimes de GPU. La voix, elle, se paie à chaque phrase. Les API de synthèse facturent au caractère, et quand le bot parle toute la nuit, la facture suit.

C'est le problème que j'ai eu avec Luna Protocol. Mon bot Discord génère ses messages avec un LLM local, puis les fait lire à voix haute par une voix Piper, convertit en OGG, et les envoie comme messages vocaux. Le tout sur mon propre serveur, pour 0 euro par phrase, même à 3h du matin quand Luna décide de raconter sa vie toute seule.

Rester à 0 euro, ça veut dire trouver des voix gratuites et les héberger soi-même. Et c'est là que ça s'est compliqué : des modèles éparpillés sur des dizaines de dépôts Hugging Face, des licences floues, aucun moyen de savoir d'où vient telle voix ni si elle fonctionne encore. J'ai donc construit PiperHub, un catalogue open source de voix pour Piper TTS.

Les chiffres du jour : 536 modèles affichés, 71 langues représentées, 3755 speakers listés. Chaque modèle est lié à son dataset d'origine quand on le connaît -- la provenance fait partie du son, comme dit la page d'accueil.

Reste une question que l'on me pose souvent : que vaut Piper face à Kokoro, PocketTTS, sanoTTS et ElevenLabs ? Et pourquoi le module pipertts de Fortune est-il 10x plus rapide que le wrapper Python officiel ? Cet article ouvre les moteurs, avec le code à l'appui.

PiperHub, le catalogue

Avant d'ouvrir les moteurs, regardons le problème que PiperHub résout.

D'où viennent les voix

Le code est sur github.com/fox3000foxy/piper-bank. Astro, site statique, zéro serveur : l'anglais à la racine, 15 autres locales sous /<code>/ (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). Même pattern que mon blog, en fait.

La source de vérité, personne l'édite à la main. C'est un pipeline :

npm install
npm run data    # régénère src/data/*.json (python3 scripts/generate_data.py)
npx astro dev   # dev local
npm run build   # build statique dans dist/

Concrètement : un repo crawler maintient models_mapping.csv et datasets_mapping.csv, les voix en miroir vivent sur Hugging Face, les samples sont des fichiers audio. npm run data tire tout ça et recrache src/data/*.json. Le site ne fait que lire du JSON statique.

Dans les données brutes du moment (555 entrées, le site en affiche 536 -- le crawl a toujours un peu d'avance sur l'affichage) :

Qualité Modèles
medium 437
high 67
low 50
Sample rate Modèles
22050 Hz 486
16000 Hz 47

486 voix en 22050 Hz, le standard Piper. 47 en 16000 Hz, les petites voix rapides pour l'embarqué. Le phonemizer, c'est espeak dans 509 cas -- on y revient, c'est le coeur du système.

La provenance, ou pourquoi chaque modèle a un dataset

Le truc qui rend PiperHub utile au lieu d'être juste une liste, c'est le lien modèle <-> dataset. Exemple pris sur la home : la voix Astra - Valorant Piper TTS est liée au dataset Astra - LJSpeech Dataset. Tu ouvres les deux pages, tu vérifies les repos et les licences de chaque côté.

130 datasets référencés. Et le catalogue est honnête sur l'état des troupes : 25 modèles sont flaggés usable: false (fichiers cassés, ou format piperPlus qui demande un runtime fork). Le module pipertts refuse ces voix avec une erreur explicite au lieu de crasher bizarrement plus tard. 48 modèles sont multi-speakers -- dont les deux qui vont nous intéresser.

Côté guides, le site fait le taf en trois étapes : Build a dataset, Train a voice, Use a voice, plus une page Piper Plus. Dataset au format LJSpeech en entrée, ONNX en sortie. Simple à dire, long à faire -- d'où la section suivante.

Piper sous le capot : VITS, espeak, ONNX

Une phrase, trois étapes

Quand tu demandes à Piper de dire "bonjour", il se passe ça :

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

Zéro vocoder séparé. C'est ça, VITS : un modèle end-to-end qui va du texte (phonémisé) au waveform directement. Là où les vieux pipelines faisaient texte -> spectrogramme (Tacotron) puis spectrogramme -> audio (HiFi-GAN, deux modèles, deux entraînements), VITS apprend tout d'un coup avec un VAE conditionnel, des flows normalisants et un discriminateur façon GAN sur le décodeur. Un seul entraînement, un seul modèle, un seul export ONNX.

Le décodeur crache du 22050 Hz directement. À ce sample rate, une seconde de parole = 22050 nombres. Le modèle est petit (quelques dizaines de Mo en qualité medium) et l'inférence est du pur CPU : multiplications de matrices sur un graphe ONNX optimisé. D'où le Raspberry Pi comme cible officielle -- Piper est né dans Rhasspy, un assistant vocal open source fait pour tourner sur Pi.

espeak-ng, le phonémiseur qui fait tout le sale boulot

Avant VITS, faut transformer du texte en phonèmes. 509 modèles sur 555 utilisent espeak :

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng connaît la prononciation de dizaines de langues. Le modèle VITS n'apprend jamais l'orthographe française avec ses eau qui se prononcent o -- il apprend des phonèmes vers du son. Ça simplifie énormément l'entraînement et ça explique pourquoi Piper couvre 71 langues : pour une nouvelle langue, tu branches la voix espeak correspondante et tu entraînes.

Celles qui se passent d'espeak ont leur propre phonémiseur, et c'est là que ça devient exotique : hebrew (Nakdimon ONNX + 494 lignes de règles IPA), pinyin (BERT g2pW complet avec désambiguïsation des polyphones -- le même caractère chinois se prononce différemment selon le mot), thai (vrai TLTK non modifié), japanese (segmentation lindera + UniDic). Le module pipertts les porte tous les sept, vérifiés identiques à la référence Python. On y revient.

Le dataset : du LJSpeech, toujours du LJSpeech

Le format d'entraînement standard, c'est LJSpeech : un dossier wavs/ plein de clips, plus un metadata.csv :

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, un clip = une phrase, quelques secondes. Le dataset originel c'est 13 000 phrases d'anglais lues par une seule locutrice, domaine public. Pour entraîner une voix française, tu enregistres la même structure en français : texte + audio alignés, quelques heures suffisent pour du medium correct, plus pour du high.

Les trois qualités du catalogue correspondent grosso modo à la taille du modèle + la durée d'entraînement. Low : petit, rapide, robotique. Medium : le sweet spot, avec 437 modèles. High : plus gros, plus lent, plus naturel -- 67 modèles, dont les Fortune.

Les trois boutons magiques

Chaque voix Piper expose trois paramètres d'inférence, avec des défauts quasi universels :

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667) : variabilité de la voix. Plus haut = plus expressif, plus bas = plus stable. La sortie est stochastique : deux synthèses de la même phrase donnent deux audios légèrement différents.
  • noise_w (0.8) : variabilité du timing, le rythme.
  • length_scale (1.0) : vitesse. Au-dessus de 1 ça ralentit, en dessous ça accélère. Le moyen le plus simple de faire parler Luna plus vite quand elle s'énerve.

Retiens noise_scale, il va servir pour les émotions.

Fortune multi : 7 émotions, 1 seul ONNX

Bon, passons à la star locale.

Le catalogue liste trois modèles Fortune entraînés par ItsRealFortune (coucou) :

Modèle Repo Voix
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

Le multi FR, c'est ça :

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

Sept déclinaisons émotionnelles dans un seul fichier ONNX. Le multi EN pareil, avec une coquille historique dans les noms : suprised au lieu de surprised. Elle est dans les poids, on la garde, ça fait partie du folklore.

Comment ça marche, un speakerId ?

VITS multi-speaker, c'est le même modèle avec une entrée en plus : un embedding de speaker. À l'entraînement, chaque clip est taggé avec son style, le modèle apprend une table d'embeddings (une par style) en même temps que le reste. À l'inférence, tu choisis la ligne de la table :

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = normal ... 5 = surprise
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

Même phrase, sept lectures. speakerId: 5 (surprise) avec un noise_scale un peu poussé, et t'as une exclamation crédible. speakerId: 4 (triste), length_scale: 1.1 pour ralentir, et t'as une annonce funèbre. C'est comme ça que Luna module sa voix : le cerveau choisit l'émotion (valence/arousal, cf. mon article sur les centroids), le TTS choisit le speakerId.

Le dataset derrière s'appelle sobrement Fortune French - Dataset, avec une licence nommée "Fortune's Café". Oui, la licence s'appelle comme un café. Non, elle reste.

Pourquoi c'est malin économiquement

Sept émotions = un seul téléchargement, un seul chargement en RAM, une seule session ONNX. L'alternative (sept modèles séparés) multiplierait le stockage et forcerait à reloader le graphe à chaque changement d'émotion -- injouable pour un bot qui change de ton en pleine phrase. Là, speakerId c'est juste un index dans un tenseur. Coût marginal : zéro.

Les concurrents : Kokoro, PocketTTS, sanoTTS

Piper partage le CPU avec trois challengers sérieux, trois philosophies différentes.

Kokoro 82M, le roi qualité/prix

Kokoro, par hexgrad. 82 millions de paramètres, architecture StyleTTS 2 modifiée avec décodeur ISTFTNet, sans étape de diffusion. C'est l'absence de diffusion qui fait tout : aucune boucle de débruitage itérative, donc inférence ultra rapide.

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

Entraîné sur moins de 100h d'audio pour environ 1000 dollars, et les auditeurs aveugles le classent au niveau des voix premium de Google et Amazon. C'est pour ça qu'il a explosé : qualité premium, gratuit, local, permissif.

Face à Piper : Kokoro gagne sur la naturalité pure (StyleTTS 2 > VITS petite taille) et la licence (Apache 2.0 contre moteur en GPL-3.0 pour Piper). Piper gagne sur la couverture (71 langues contre 8, 3755 speakers contre 54 voix) et l'écosystème de voix communautaires. Pour de la narration anglaise, Kokoro est dur à battre. Pour du français avec 7 émotions, du turc ou du swahili, Piper est souvent le seul en lice.

PocketTTS (Kyutai), le clonage sur CPU

Kyutai joue une autre partition : petit modèle avec clonage de voix, qui tourne sur CPU.

Modèle Params WER UTMOS SIM Vitesse CPU (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER sous 1% (il prononce juste), UTMOS 4.36 (qualité perçue excellente), SIM 0.923 (le clone ressemble vraiment à la cible). Et les versions small descendent à 1.9M paramètres avec clonage. 1.9M. Mon routeur Sabaki fait presque cette taille en JSON.

Face à Piper : PocketTTS gagne sur le clonage (Piper lit avec des voix fixes) et la compacité du flagship. Piper gagne sur les langues (le small est anglais-only pour l'instant) et la maturité de l'écosystème (536 voix prêtes contre "entraîne la tienne").

sanoTTS, le modèle qui tient sur une puce à 3 dollars

sanoTTS (sano = "petit" en népalais), c'est le Bareiron du TTS : un stack neuronal complet qui va des phonèmes au PCM 22.05 kHz sur microcontrôleur.

Système Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M paramètres. 2 Mo la voix. pip install sanotts, sanotts say "Hello" --voice amy, ça parle. En navigateur aussi.

Mais regarde les scores : SCOREQ 2.29 contre 3.94 pour TinyTTS, UTMOS 2.45. Ça parle, oui. Ça chante, non. C'est le tradeoff ultime : à 0.29M tu tiens partout mais la qualité suit la taille. Piper medium, c'est 100x plus de poids pour une voix dans une autre ligue.

Le tableau qui résume

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

Le choix dépend de ta contrainte, y'a pas de gagnant absolu : qualité EN pure -> Kokoro. Clonage CPU -> PocketTTS. Microcontrôleur -> sanoTTS. Couverture de langues + voix françaises émotionnelles + zéro infra -> Piper.

ElevenLabs, le Goliath facturé

Et puis y'a ElevenLabs. 70+ langues, Eleven v3 expressif avec audio tags et dialogues dramatiques, qualité top des blind tests. Le Jev du TTS, en quelque sorte : tout le monde l'a vu passer, tout le monde a benchmarké contre.

Le pricing 2026, en chiffres ronds :

Modèle API Prix / 1000 caractères
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 caractères font environ 1 minute d'audio. Donc $0.05 à $0.10 la minute. Le plan Pro à $99 inclut ~990k caractères, l'overage tape au même tarif. Pour une chaîne YouTube qui sort une vidéo par jour avec voix off (mon valorant-short-maker fait exactement ça, mais avec Piper gratuit), compte $1.50 à $3 par mois pour chaque minute quotidienne de voix off. Dix minutes par jour : $15 à $30/mois. Ça se paie, mais ça se compte.

Quand ça vaut le coup : quand la voix est le produit (doublage, audiobook, pub), quand il faut 70 langues demain matin, quand le clonage doit être parfait du premier coup. En revanche ça vaut jamais le coup pour : bot Discord qui parle toute la nuit, notifications internes, dev, volumes qui explosent. Luna qui raconte sa vie à 3h du matin en ElevenLabs, c'est ma facture EDF qui pleure.

Le calcul est simple : Piper = 0 euro marginal, qualité "bonne". ElevenLabs = compteur qui tourne, qualité "excellente". Entre les deux, y'a un monde où "bonne et gratuite" gagne -- c'est tout le pari PiperHub.

pipertts : pourquoi le natif met 10x au wrapper Python

Le repo itsrealfortune/pipertts, 75 commits : un port natif de Piper en TypeScript. Titre du README : "In-process Piper TTS for Node.js. No Python required."

Le coût du spawn

Le wrapper historique (PiperTTS), c'est python3 -m piper spawné en sous-processus à chaque synthèse :

synthèse = spawn(python) + chargement du modèle + inférence + kill

Chaque phrase paie le démarrage de l'interpréteur + le chargement des poids. Mesuré sur en_US-lessac-medium : ~2.2s par ligne.

Le natif (PiperNativeTTS) garde la session ONNX chaude en mémoire :

load une fois -> session persistante -> inférence par phrase

Mesuré pareil : ~0.22s par ligne. Le 10x vient de là : on tue le spawn et le reload, l'inférence miraculeuse n'a rien à voir là-dedans. L'inférence ONNX elle-même est la même des deux côtés.

En bonus, le premier load met en cache le graphe optimisé à côté du .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), ce qui rend les chargements suivants ~35% plus rapides. La clé du cache combine platform, arch et version d'ORT, et il se rafraîchit si le modèle change. Désactivable avec cacheOptimizedModel: false.

Le pont espeak : 0.4ms contre 15ms

Même logique pour la phonémisation. Le wrapper appelle le binaire espeak-ng en CLI par phrase : ~15ms de spawn à chaque fois. Le natif embarque un bridge N-API (src/native/espeak-bridge/, port du espeakbridge.c), avec prebuilds pour Linux x64/arm64 : ~0.4ms par appel, phonèmes byte-identiques. Sans l'addon, repli automatique sur le CLI. PIPER_ESPEAK_BRIDGE=0 pour forcer le CLI.

Les 7 phoneme_type, tous portés

Type Moteur Notes
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

Et l'arabe est diacritisé automatiquement (tashkeel, désactivable). 509 voix espeak du catalogue passent par le bridge rapide, les exotiques par leur moteur dédié. 71 tests dans la suite, contrainte documentée là où ça coince (l'accent japonais, UniDic livre aucune donnée -- assumé, rien de caché).

Les formats et le streaming

wav par défaut, raw PCM, mp3, ogg, opus : ffmpeg quand il est là, sinon fallbacks pur JS (lamejs pour mp3, opusscript + muxeur Ogg pour opus, corrélation 0.993 avec la source). Et surtout synthesizeChunks() : un générateur async qui yield par phrase, avec sentenceSilence entre les phrases. Pour Luna, ça veut dire commencer à jouer l'audio pendant que la fin se synthétise -- du streaming de TTS sans serveur dédié.

Usage complet, avec le catalogue PiperHub intégré (hub: + fallback miroir automatique si le repo auteur ne sert plus les fichiers) :

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // direct pour Discord
  lengthScale: 0.95,     // un poil plus vite
});

Voix communautaires (GLaDOS et persos de jeux) absentes du manifest officiel -- voilà exactement pourquoi PiperHub existe, et pipertts le sait bien : hub:<slug>, URL de page piperhub, ou slug nu, tout marche. Les voix flaggées usable: false sont refusées avec une erreur claire.

Ce qui a été sacrifié

Chaque feature absente finance une autre. La liste honnête :

Piper sonne robotique à côté d'ElevenLabs. VITS medium sur 22050 Hz, c'est propre mais plat comparé à un gros modèle expressif. Le multi-émotions compense en partie (7 styles > 1 style plat), mais un audiobook ElevenLabs v3 reste au-dessus. C'est le prix des dizaines de Mo.

espeak se trompe. Noms propres, anglicismes, abréviations : la phonémisation par règles a ses angles morts. "Spoiler" en français, espeak le lit comme il peut. Les gros modèles avec G2P neuronal se plantent moins. En prod, faut un dico d'exceptions pour les termes métier.

Zéro clonage natif. Piper lit avec des voix fixes. Si tu veux TA voix, direction PocketTTS ou ElevenLabs. Le multi-speaker donne des variations, jamais ton timbre.

Le japonais reste sans accent lexical. UniDic livre aucune donnée d'accent, donc la segmentation est exacte mais la mélodie est approximative. Documenté dans le README, rien sous le tapis.

Le thaï rame au premier appel. TLTK tourne sous Pyodide/WASM non modifié : le premier appel prend des secondes (chargement), les suivants sont rapides. 7/7 identiques à la référence, mais prévois un warm-up.

Les données ont faim. Une voix high correcte demande des heures d'audio propre aligné. Le dataset Fortune, c'est des heures d'enregistrement + nettoyage. Zéro magie : garbage in, robot out.

Le catalogue a des voix mortes. 25 modèles flaggés usable: false. C'est assumé et filtré, mais ça rappelle que le crowdsourcing de voix, c'est aussi du crowdsourcing d'abandon.

Le résultat final

D'un côté un compteur ElevenLabs, de l'autre un Raspberry Pi :

  • PiperHub : 536 modèles affichés, 71 langues, 3755 speakers, 130 datasets liés, guides dataset -> training -> usage
  • Piper : VITS end-to-end, espeak, ONNX CPU, LJSpeech, low/medium/high, 22050 Hz standard
  • Fortune multi : 7 émotions FR + 7 EN, 1 ONNX chacun, speakerId pour switcher, licence "Fortune's Café"
  • Kokoro 82M : qualité premium EN, Apache 2.0, 54 voix, quasi temps réel CPU
  • PocketTTS : clonage CPU, ~100M puis 2-18M, WER 0.92%
  • sanoTTS : 0.29M, 2 Mo, microcontrôleur, qualité qui suit la taille
  • ElevenLabs : $0.05-0.10/1000 caractères, 70+ langues, top qualité, compteur qui tourne
  • pipertts natif : ~0.22s/ligne contre ~2.2s wrapper, bridge espeak 0.4ms, 7 phoneme_type, wav/raw/mp3/ogg/opus, streaming par phrase

Luna parle avec ça tous les jours. 0 euro de TTS, des émotions pilotées par le cerveau, du OGG direct pour Discord. Face à ElevenLabs ça s'entend, évidemment. Mais à 3h du matin quand elle décide de raconter sa vie, ma facture dort tranquille.

La philo

p2r3 disait : "J'aime juste l'idée que cette toute petite puce à 1 balles puisse faire tourner quelque chose d'aussi avancé que Minecraft."

Même délire ici, appliqué à la voix : j'aime l'idée que quelques dizaines de Mo d'ONNX fassent parler un Raspberry Pi en 71 langues, sans cloud, sans compte, sans compteur. Ça remplace évidemment aucun studio de doublage. Ça remplace le cas relou où tu veux une voix correcte, locale, gratuite, qui parle la nuit sans te facturer.

Les tradeoffs, écrits noir sur blanc :

  • qualité premium -> VITS petit : un peu robotique, mais local et gratuit
  • un modèle par émotion -> speakerId : 7 voix pour le prix d'une en RAM
  • spawn Python -> session persistante : 10x plus rapide, zéro Python à installer
  • catalogue sauvage -> provenance liée : chaque voix sait d'où elle vient et si elle marche encore
  • API facturée -> 0 euro marginal : moins beau qu'ElevenLabs, mais la facture dort tranquille

Les 3 trucs à retenir :

  1. Piper = le TTS du CPU -- VITS + espeak + ONNX, 71 langues, des voix en dizaines de Mo qui tournent sur Pi. Moins beau qu'ElevenLabs, infiniment moins cher.
  2. Fortune multi = 7 émotions pour le prix d'une -- un seul ONNX, speakerId pour switcher, FR + EN. L'embedding de speaker au lieu de 7 modèles.
  3. Le natif tue le spawn -- pipertts garde la session ONNX chaude : 0.22s/ligne contre 2.2s, bridge espeak 0.4ms contre 15ms CLI. Même inférence, 10x moins d'attente.

Si le projet t'intéresse, le catalogue c'est piperhub.org, le code du site github.com/fox3000foxy/piper-bank, le module natif github.com/itsrealfortune/pipertts. Et si tu veux entendre Luna raconter sa vie à 3h du mat, c'est sur Discord xD

NEW

PiperHub:536 个开源 TTS 声音,7 种情感一个模型,零 GPU,零账单

VITS、espeak、ONNX、LJSpeech:Piper 如何让树莓派开口说话。7 种情感的 Fortune multi、Kokoro 82M、PocketTTS、sanoTTS、ElevenLabs,以及比官方 Python wrapper 快 10 倍的原生 pipertts。

Introduction

让 Discord 机器人开口说话,成本很高。贵的不是文本:本地 LLM 花几分钱 GPU 就能生成句子。贵的是声音。合成 API 按字符收费,机器人讲一整夜,账单就跟着涨。

这就是我在 Luna Protocol 遇到的问题。我的 Discord 机器人用本地 LLM 生成消息,再用 Piper 的声音朗读,转成 OGG,当作语音消息发出去。全都在我自己的服务器上跑,每句话 0 欧元,就算凌晨 3 点 Luna 决定自己讲人生故事也一样。

要保持 0 欧元,就得找到免费声音并自己托管。麻烦就在这里:模型散落在几十个 Hugging Face 仓库里,许可证模糊不清,根本没法知道某个声音从哪来,还能不能用。于是我做了 PiperHub,一个 Piper TTS 声音的开源目录。

今天的数据:536 个模型、71 种语言、3755 个 speaker。只要知道来源,每个模型都会链接到原始 dataset。用首页的话说,出处也是声音的一部分。

还剩一个常被问到的问题:面对 Kokoro、PocketTTS、sanoTTS 和 ElevenLabs,Piper 怎么样?为什么 Fortune 的 pipertts 模块比官方 Python wrapper 快 10 倍?这篇文章把引擎拆开讲,附代码。

PiperHub,目录

先看 PiperHub 解决的问题,再拆引擎。

声音从哪里来

代码在 github.com/fox3000foxy/piper-bank。Astro 静态站,零服务器:英文在根目录,另外 15 个语言在 /<code>/ 下面(fr、de、es、it、pt、ru、tr、ja、ko、zh、id、hi、ar、vi、th)。其实跟我的博客一个套路。

唯一真相来源,没人手动改。是个 pipeline:

npm install
npm run data    # 重新生成 src/data/*.json (python3 scripts/generate_data.py)
npx astro dev   # 本地开发
npm run build   # 输出静态构建到 dist/

具体来说:一个 crawler 仓库维护 models_mapping.csv 和 datasets_mapping.csv,镜像声音放在 Hugging Face, sample 是音频文件。npm run data 把这些全拉下来,吐出 src/data/*.json。网站只读静态 JSON。

当前原始数据(555 条,网站显示 536 条,爬取永远比展示快一点):

Qualité Modèles
medium 437
high 67
low 50
Sample rate Modèles
22050 Hz 486
16000 Hz 47

486 个 22050 Hz 的声音,Piper 标准。47 个 16000 Hz,给嵌入式用的轻量快速声音。 phonemizer 有 509 个用的是 espeak,后面会回来讲,这是系统的心脏。

出处,或者说为什么每个模型都有 dataset

PiperHub 有用而不是只有列表,关键就是模型和 dataset 的链接。首页拿的例子:声音 Astra - Valorant Piper TTS 链接到 dataset Astra - LJSpeech Dataset。打开两个页面,两边的仓库和许可证都能核对。

引用了 130 个 dataset。目录对现状也很诚实:25 个模型标了 usable: false(文件损坏,或者是需要 fork runtime 的 piperPlus 格式)。pipertts 模块遇到这些声音会直接抛明确错误,而不是后面莫名其妙崩掉。48 个模型是多 speaker,其中两个是我们接下来要讲的。

指南方面,网站分三步:Build a dataset、Train a voice、Use a voice,再加一个 Piper Plus 页面。输入是 LJSpeech 格式 dataset,输出是 ONNX。说起来简单,做起来很长,所以才有下一节。

Piper 引擎盖下:VITS、espeak、ONNX

一句话,三步

让 Piper 说一句“bonjour”,会发生这些:

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

没有独立 vocoder。这就是 VITS:从文本(已音素化)直达波形的端到端模型。老 pipeline 是文本到频谱(Tacotron),再频谱到音频(HiFi-GAN,两个模型,训练两次),VITS 用条件 VAE、normalizing flow 和解码器上的 GAN 式 discriminator 一次全学会。一次训练,一个模型,一次 ONNX 导出。

解码器直接输出 22050 Hz。这个采样率下,一秒语音就是 22050 个数字。模型很小(medium 品质几十 MB),推理是纯 CPU:在优化过的 ONNX 图上做矩阵乘法。所以树莓派是官方目标。Piper 生于 Rhasspy,一个为跑在 Pi 上的开源语音助手。

espeak-ng,干脏活的 phonemizer

在 VITS 之前,要先把文本转成音素。555 个模型里 509 个用 espeak:

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng 懂几十种语言的发音。VITS 模型永远学不到法语拼写里 eau 读 o 这种事,它学的是音素到声音。这让训练简单得多,也解释了 Piper 为什么覆盖 71 种语言:新语言接上对应的 espeak 声音再训练就行。

不用 espeak 的都有自己的 phonemizer,开始有点异域风情:hebrew(Nakdimon ONNX 加 494 行 IPA 规则)、pinyin(完整 BERT g2pW,带多音字消歧,同一个汉字在不同词里读音不同)、thai(原版未改 TLTK)、japanese(lindera 分词加 UniDic)。pipertts 模块七个全带,都验证过和 Python 参考版一致。后面再讲。

dataset:LJSpeech,永远 LJSpeech

标准训练格式是 LJSpeech:一个装满短 clip 的 wavs/ 文件夹,加一个 metadata.csv:

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized,一个 clip 一句话,几秒钟。原始 dataset 是同一个女声读的 13000 句英语,public domain。要训练法语声音,就按同样结构录法语:文本加对齐音频,medium 品质几小时就够,high 要更多。

目录里的三个品质,大致对应模型大小加训练时长。Low:小、快、机械感。Medium:甜点区,437 个模型。High:更大、更慢、更自然,67 个模型,包括 Fortune 那几个。

三个魔法按钮

每个 Piper 声音都有三个推理参数,默认值几乎通用:

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale(0.667):声音的变化幅度。越高越有表现力,越低越稳定。输出是随机的:同一句话合成两次,音频会略有不同。
  • noise_w(0.8):时长的变化幅度,也就是节奏。
  • length_scale(1.0):语速。大于 1 变慢,小于 1 变快。Luna 生气时让她讲快一点,最简单的办法就是这个。

记住 noise_scale,讲情感时会用到。

Fortune multi:7 种情感,1 个 ONNX

好,讲本地明星。

目录里列了 ItsRealFortune(是我啦)训练的三个 Fortune 模型:

Modèle Repo Voix
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

法语 multi 是这样的:

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

七种情感变化装在一个 ONNX 文件里。英语 multi 一样,名字里有个历史 typo:suprised 而不是 surprised。它在权重里,就留着吧,算民间传说的一部分。

speakerId 是怎么工作的

多 speaker VITS,就是同一个模型多一个输入:speaker embedding。训练时每个 clip 都打上风格标签,模型在学其余部分的同时,学会一张 embedding 表(每种风格一行)。推理时选表里的一行:

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = 普通版 ... 5 = 惊讶版
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

同一句话,七种读法。speakerId: 5(惊讶)配上稍高的 noise_scale,感叹就很可信了。speakerId: 4(悲伤)配 length_scale: 1.1 放慢,就是讣告播报。Luna 就是这样换声音的:大脑选情感(valence 和 arousal,看我那篇 centroid 文章),TTS 选 speakerId。

背后的 dataset 名字很朴素,Fortune French - Dataset,许可证叫“Fortune's Café”。对,许可证名字像咖啡馆。就留着吧。

为什么这在经济上很聪明

七种情感等于一次下载、一次 RAM 加载、一个 ONNX session。替代方案(七个独立模型)会让存储翻倍,每次换情感都要重载图,对一句话里换语气的机器人根本没法用。这里 speakerId 只是 tensor 里的一个索引。边际成本:零。

竞争者:Kokoro、PocketTTS、sanoTTS

Piper 和三个认真的挑战者共享 CPU,三种不同哲学。

Kokoro 82M,性价比之王

Kokoro,作者 hexgrad。8200 万参数,魔改 StyleTTS 2 架构加 ISTFTNet 解码器,没有扩散步骤。没有扩散就是一切:没有迭代去噪循环,所以推理超快。

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

训练用了不到 100 小时音频,花了约 1000 美元,盲听被评到和 Google、Amazon 的付费声音一个级别。所以它爆了:付费品质,免费,本地,宽松许可。

对 Piper:纯自然度 Kokoro 赢(StyleTTS 2 比小 VITS 强),许可证也赢(Apache 2.0 对 Piper 引擎的 GPL-3.0)。覆盖面 Piper 赢(71 种语言对 8 种,3755 个 speaker 对 54 个声音),社区声音生态也赢。英语旁白 Kokoro 很难打。带 7 种情感的法语、土耳其语、斯瓦希里语,Piper 经常是唯一选手。

PocketTTS(Kyutai),CPU 上克隆

Kyutai 走另一条路:小模型带声音克隆,跑在 CPU 上。

Modèle Params WER UTMOS SIM Vitesse CPU (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER 不到 1%(发音准),UTMOS 4.36(感知质量优秀),SIM 0.923(克隆体真像目标)。small 版低到 190 万参数还带克隆。190 万。我的 Sabaki 路由器 JSON 都快这个大小了。

对 Piper:克隆 PocketTTS 赢(Piper 只会用固定声音读),旗舰的小体积也赢。语言 Piper 赢(small 目前只有英文),生态成熟度 Piper 赢(536 个现成声音对“自己训一个”)。

sanoTTS,3 美元芯片上跑的模型

sanoTTS(sano 在尼泊尔语里是“小”),是 TTS 界的 Bareiron:从音素到 22.05 kHz PCM 的完整神经栈跑在单片机上。

Système Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

29 万参数。一个声音 2 MB。pip install sanotts,sanotts say "Hello" --voice amy,就出声了。浏览器里也行。

但看分数:SCOREQ 2.29 对 TinyTTS 的 3.94,UTMOS 2.45。能说话,对。能唱歌,不行。这就是终极 tradeoff:29 万哪里都能跑,但质量跟着体积走。Piper medium 重 100 倍,声音完全另一个级别。

总结表

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

选哪个看你的约束,没有绝对赢家:纯英语品质选 Kokoro。CPU 克隆选 PocketTTS。单片机选 sanoTTS。语言覆盖加法语情感声音加零基建选 Piper。

ElevenLabs,收费的巨人

然后是 ElevenLabs。70 多种语言,表现力强的 Eleven v3 带 audio tags 和戏剧化对白,盲测品质第一。有点像 TTS 界的 Jev:谁都见过,谁都拿它当 benchmark 比过。

2026 年定价,取整:

Modèle API Prix / 1000 caractères
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 字符约等于 1 分钟音频。所以一分钟 0.05 到 0.10 美元。99 美元 Pro 套餐含约 99 万字符,超量同价。每天出一条配音视频的 YouTube 频道(我的 valorant-short-maker 就是干这个的,但用免费 Piper),每天每分钟配音每月 1.50 到 3 美元。每天 10 分钟:每月 15 到 30 美元。付得起,但要算账。

什么时候值:声音本身就是产品时(配音、有声书、广告),明天早上就要 70 种语言时,克隆一次就要完美时。什么时候永远不值:整夜说话的 Discord 机器人、内部通知、开发、量爆炸时。让 Luna 凌晨 3 点讲人生还用 ElevenLabs,我的电费账单会哭。

账很好算:Piper 边际 0 欧元,品质“不错”。ElevenLabs 计表在转,品质“优秀”。中间有个“不错还免费”能赢的世界,这就是 PiperHub 的全部赌注。

pipertts:为什么原生比 Python wrapper 快 10 倍

仓库 itsrealfortune/pipertts,75 个 commit:Piper 的 TypeScript 原生移植。README 标题:“In-process Piper TTS for Node.js. No Python required.”

spawn 的成本

老 wrapper(PiperTTS)是每次合成都 spawn 一个 python3 -m piper 子进程:

synthèse = spawn(python) + chargement du modèle + inférence + kill

每句话都要付解释器启动加权重加载的钱。在 en_US-lessac-medium 上实测:每行约 2.2 秒。

原生(PiperNativeTTS)把 ONNX session 保温在内存里:

load une fois -> session persistante -> inférence par phrase

同样条件实测:每行约 0.22 秒。10 倍就从这里来:杀掉了 spawn 和重载,和 ONNX 推理神不神奇没关系。两边的 ONNX 推理本身是一样的。

附赠:首次 load 会在 .onnx 旁边缓存优化过的图(<model>.ortopt-<platform>-<arch>-v<ort>.onnx),之后加载快约 35%。缓存 key 组合了 platform、arch 和 ORT 版本,模型变了会刷新。cacheOptimizedModel: false 可关。

espeak 桥:0.4ms 对 15ms

音素化也一样。wrapper 每句话调一次 espeak-ng 二进制 CLI:每次约 15ms spawn。原生内置 N-API bridge(src/native/espeak-bridge/,移植自 espeakbridge.c),带 Linux x64 和 arm64 prebuild:每次调用约 0.4ms,音素字节级一致。没有 addon 就自动回落 CLI。PIPER_ESPEAK_BRIDGE=0 强制走 CLI。

7 种 phoneme_type,全移植了

Type Moteur Notes
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

阿拉伯语自动加元音符号(tashkeel,可关)。目录里 509 个 espeak 声音走高速桥,异域的走专用引擎。测试套件 71 个测试,卡住的地方都写明了(日语重音,UniDic 没给数据,认了,没藏着)。

格式和流式

默认 wav,raw PCM,mp3,ogg,opus:有 ffmpeg 就用,没有就纯 JS fallback(mp3 用 lamejs,opus 用 opusscript 加 Ogg muxer,和源的相关性 0.993)。关键是 synthesizeChunks():按句子 yield 的异步生成器,句间加 sentenceSilence。对 Luna 来说,尾巴还在合成,前面已经开始放了,不用专用服务器的 TTS 流式。

完整用法,带 PiperHub 目录集成(hub: 加作者仓库挂掉时的自动镜像 fallback):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // 直接给 Discord 用
  lengthScale: 0.95,     // 稍微快一点
});

社区声音(GLaDOS 和游戏角色)不在官方 manifest 里,这正是 PiperHub 存在的原因,pipertts 也懂:hub:<slug>、piperhub 页面 URL、裸 slug,都行。标 usable: false 的声音会给明确错误拒绝掉。

牺牲掉的东西

每个缺的功能都在供养另一个功能。诚实清单:

Piper 在 ElevenLabs 旁边听起来像机器人。 22050 Hz 的 VITS medium,干净但比起大表现力模型还是平。多情感补了一部分(7 种风格比 1 种平板强),但 ElevenLabs v3 的有声书还是更高。这是几十 MB 的代价。

espeak 会读错。 专有名词、外来词、缩写:规则音素化有死角。法语里的“Spoiler”,espeak 只能按它理解的读。神经 G2P 的大模型错得少。生产环境得给业务词准备例外词典。

原生克隆为零。 Piper 只会用固定声音读。想要你自己的声音,去 PocketTTS 或 ElevenLabs。多 speaker 给的是变化,不是你的音色。

日语还是没有词汇重音。 UniDic 不给重音数据,所以切分准,语调近似。README 里写明了,没藏东西。

泰语第一次调用慢。 TLTK 跑在未改 Pyodide/WASM 里:第一次加载要几秒,后面快。和参考版 7/7 一致,但要预留 warm-up。

数据很饿。 一个像样的 high 声音要几小时干净对齐的音频。Fortune dataset 是几小时录音加清洗。零魔法:进来垃圾,出去机器人。

目录里有死声音。 25 个模型标了 usable: false。认了,也过滤了,但这提醒我们,声音众包,也是弃坑众包。

最终结果

一边是 ElevenLabs 的计表,一边是树莓派:

  • PiperHub:展示 536 模型,71 语言,3755 speaker,130 个关联 dataset,dataset 到 training 到 usage 的指南
  • Piper:VITS 端到端,espeak,ONNX CPU,LJSpeech,low 和 medium 和 high,标准 22050 Hz
  • Fortune multi:法语 7 情感加英语 7 情感,各 1 个 ONNX,切换用 speakerId,许可证“Fortune's Café”
  • Kokoro 82M:付费级英语品质,Apache 2.0,54 个声音,CPU 接近实时
  • PocketTTS:CPU 克隆,约 100M 到 2-18M,WER 0.92%
  • sanoTTS:0.29M,2 MB,单片机,质量跟着体积走
  • ElevenLabs:1000 字符 0.05-0.10 美元,70 多种语言,顶级品质,计表在转
  • 原生 pipertts:wrapper 约 2.2 秒每行对约 0.22 秒每行,espeak 桥 0.4ms,7 种 phoneme_type,wav 和 raw 和 mp3 和 ogg 和 opus,按句流式

Luna 每天就用这个说话。TTS 0 欧元,大脑控制情感,直接给 Discord 的 OGG。和 ElevenLabs 比当然听得出来。但凌晨 3 点她决定讲人生时,我的账单睡得很安稳。

哲学

p2r3 说过:“我就喜欢那种感觉,1 块钱的小芯片能跑起 Minecraft 这么高级的东西。”

声音上也一样:我就喜欢几十 MB ONNX 让树莓派讲 71 种语言,不用云,不用账号,不用计表。当然替代不了配音棚。替代的是那种烦人的场景:想要一个凑合、本地、免费的声音,半夜说话还不收费。

tradeoff,白纸黑字:

  • 付费品质换小 VITS:有点机械感,但本地免费
  • 每种情感一个模型换 speakerId:一份 RAM 的价钱得 7 个声音
  • Python spawn 换常驻 session:快 10 倍,零 Python 安装
  • 野生目录换出处关联:每个声音都知道从哪来,还能不能用
  • 收费 API 换边际 0 欧元:没 ElevenLabs 好听,但账单睡得安稳

记住三件事:

  1. Piper 就是 CPU 的 TTS。VITS 加 espeak 加 ONNX,71 种语言,跑在 Pi 上的几十 MB 声音。没 ElevenLabs 好听,但便宜无数倍。
  2. Fortune multi 是一个的价钱得 7 种情感。一个 ONNX,切换用 speakerId,法语加英语。用 speaker embedding 代替 7 个模型。
  3. 原生杀掉了 spawn。pipertts 把 ONNX session 保温:2.2 秒每行对 0.22 秒,espeak CLI 15ms 对桥 0.4ms。同样推理,少 10 倍等待。

感兴趣的话,目录在 piperhub.org,网站代码在 github.com/fox3000foxy/piper-bank,原生模块在 github.com/itsrealfortune/pipertts。想听 Luna 凌晨 3 点讲人生,来 Discord 吧 xD

NEW

PiperHub:オープンソースTTS音声536種、1モデルで7感情、GPU不要、料金ゼロ

VITS、espeak、ONNX、LJSpeech: PiperがRaspberry Piをどう喋らせるか。7感情のFortune multi、Kokoro 82M、PocketTTS、sanoTTS、ElevenLabs、そして公式Pythonラッパーより10倍速いネイティブpipertts。

Introduction

Discordボットを喋らせるのは金がかかる。テキストは安い。ローカルLLMならGPU代は数セントで文を作れる。でも声は一文ごとに課金される。合成APIは文字数課金だし、ボットが一晩中喋れば請求もそれに付いてくる。

これは私がLuna Protocolで直面した問題だ。私のDiscordボットはローカルLLMでメッセージを作り、Piperの声で読み上げ、OGGに変換してボイスメッセージとして送る。全部自分のサーバー上で、1文あたり0ユーロで動く。Lunaが午前3時に一人で自分の人生を語り出しても同じだ。

0ユーロを維持するには、無料の声を見つけて自分でホストするしかない。そこで面倒になった。モデルは数十のHugging Faceリポジトリに散らばり、ライセンスは曖昧で、ある声がどこから来たのか、今も動くのかを知る手段がない。だから私はPiperHubを作った。Piper TTS用音声のオープンソースカタログだ。

今日の数字:モデル536件、言語71種、スピーカー3755人。出自が分かるモデルはすべて元のデータセットにリンクしている。出自は音の一部だ、とトップページにも書いてある通りだ。

よく聞かれる質問が一つ残っている。Kokoro、PocketTTS、sanoTTS、ElevenLabsに対してPiperはどうなのか。そしてFortuneのpiperttsモジュールはなぜ公式Pythonラッパーより10倍速いのか。この記事ではコードと一緒に中身を開けて見せる。

PiperHub、カタログ

エンジンを開ける前に、PiperHubが解く問題を見てみよう。

声はどこから来るのか

コードはgithub.com/fox3000foxy/piper-bankにある。Astro製の静的サイトでサーバーはゼロ。英語がルートにあり、/ <code>/以下に他15ロケール(fr、de、es、it、pt、ru、tr、ja、ko、zh、id、hi、ar、vi、th)。実は私のブログと同じパターンだ。

信頼できる唯一の情報源は、誰も手で編集しない。パイプラインだ。

npm install
npm run data    # src/data/*.json を再生成 (python3 scripts/generate_data.py)
npx astro dev   # ローカル開発
npm run build   # dist/ に静的ビルド

実際にはこうなっている。crawlerリポジトリがmodels_mapping.csvとdatasets_mapping.csvを管理し、ミラーされた音声はHugging Faceにあり、サンプルは音声ファイルだ。npm run dataが全部引っ張ってきてsrc/data/*.jsonを吐き出す。サイトは静的JSONを読むだけだ。

現時点の生データ(555件、サイト表示は536件。クロールは表示より少し先行する)では:

Qualité Modèles
medium 437
high 67
low 50
Sample rate Modèles
22050 Hz 486
16000 Hz 47

22050 Hzの声が486件、Piperの標準だ。16000 Hzが47件、組み込み向けの軽い高速な声だ。フォネマイザーは509件でespeakを使っている。ここに戻ってくる。これがシステムの心臓だ。

出自、つまり各モデルがデータセットを持つ理由

PiperHubをただのリストではなく役立つものにしているのが、モデルとデータセットのリンクだ。トップの例で言うと、Astra - Valorant Piper TTSという声はAstra - LJSpeech Datasetというデータセットにリンクしている。両方のページを開いて、リポジトリとライセンスを両側で確認できる。

参照データセットは130件。そしてカタログは現状に正直だ。25モデルにusable: falseのフラグが付いている(壊れたファイル、またはforkランタイムが必要なpiperPlus形式)。piperttsモジュールはこういう声を後で変にクラッシュする代わりに明示的なエラーで拒否する。48モデルがマルチスピーカーで、そのうち2つがこれから重要になる。

ガイド面では、サイトは3ステップで仕事をしている。Build a dataset、Train a voice、Use a voice、あとPiper Plusのページだ。入力はLJSpeech形式のデータセット、出力はONNX。言うのは簡単で、やるのは長い。だから次のセクションがある。

Piperの中身:VITS、espeak、ONNX

一文、三ステップ

Piperに「bonjour」と言わせると、こうなる。

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

別のボコーダーはゼロ。これがVITSだ。テキスト(音素化済み)から波形まで直接行くエンドツーエンドモデルだ。昔のパイプラインはテキストからスペクトログラム(Tacotron)、スペクトログラムから音声(HiFi-GAN、モデル2つ、学習2回)だったが、VITSは条件付きVAE、normalizing flow、デコーダー上のGAN風discriminatorで全部一気に学ぶ。学習1回、モデル1つ、ONNXエクスポート1つだ。

デコーダーは22050 Hzを直接吐く。このサンプルレートでは、1秒の発話は22050個の数値だ。モデルは小さい(medium品質で数十MB)し、推論は純粋なCPU処理だ。最適化されたONNXグラフ上の行列積だ。だからRaspberry Piが公式ターゲットなのだ。PiperはRhasspy生まれで、Pi上で動くためのオープンソース音声アシスタント向けに作られた。

espeak-ng、汚れ仕事をするフォネマイザー

VITSの前に、テキストを音素に変える必要がある。555モデルのうち509がespeakを使っている。

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ngは数十言語の発音を知っている。VITSモデルはeauをoと読むフランス語の綴りを学ぶことはない。音素から音への変換を学ぶのだ。これで学習は大幅に簡単になるし、Piperが71言語をカバーする理由もここにある。新しい言語では対応するespeak音声を繋いで学習するだけだ。

espeakを使わない声は独自フォネマイザーを持ち、ここからが変わり種だ。hebrew(Nakdimon ONNXと494行のIPAルール)、pinyin(多音字の曖昧さ解消付きBERT g2pWフルセット。同じ漢字でも単語で読みが変わる)、thai(無改変の本物TLTK)、japanese(linderaによる分割とUniDic)。piperttsモジュールは7種全部を持ち、Pythonリファレンスと同一だと検証済みだ。後で戻る。

データセット:LJSpeech、いつもLJSpeech

標準の学習形式はLJSpeechだ。クリップだらけのwavs/フォルダとmetadata.csvだ。

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalizedで、1クリップは1文、数秒だ。元のデータセットは1人の女性話者による英語13000文でパブリックドメインだ。フランス語音声を学習するなら同じ構造をフランス語で録る。テキストと音声のアライメント済みペアで、まともなmedium品質なら数時間で足りるし、highならもっと要る。

カタログの3品質はだいたいモデルサイズと学習時間に対応する。Lowは小さい、速い、ロボットっぽい。Mediumはいいとこ取りで437モデル。Highは大きい、遅い、自然で67モデル、Fortune勢を含む。

3つの魔法のボタン

各Piper音声は3つの推論パラメーターを持ち、デフォルトはほぼ共通だ。

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale(0.667):声のばらつき。高くすると表現豊かに、低くすると安定する。出力は確率的だ。同じ文の2回の合成は少し違う音声になる。
  • noise_w(0.8):タイミングのばらつき、リズムだ。
  • length_scale(1.0):速さ。1より上は遅く、下は速くなる。Lunaが怒ったときに速く喋らせる一番簡単な方法だ。

noise_scaleを覚えておこう。感情で使う。

Fortune multi:7感情、1つのONNX

さて、地元のスターの話だ。

カタログにはItsRealFortune(どうも)が学習したFortuneモデル3つが載っている。

Modèle Repo Voix
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

FR multiはこうだ。

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

1つのONNXファイルに7種の感情バリエーションだ。EN multiも同じで、名前に歴史的なタイプミスがある。surprisedではなくsuprisedだ。重みの中にあるのでそのまま残す。伝承の一部だ。

speakerIdの仕組み

マルチスピーカーVITSは同じモデルに入力が一つ増えたものだ。スピーカー埋め込みだ。学習時、各クリップにスタイルのタグが付き、モデルは残りと同時に埋め込みテーブル(スタイルごとに1つ)を学ぶ。推論時はテーブルの行を選ぶ。

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = 通常 ... 5 = 驚き
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

同じ文を7通りに読む。speakerId: 5(surprise)にnoise_scaleを少し上げれば説得力ある感嘆になる。speakerId: 4(triste)にlength_scale: 1.1で遅くすれば訃報の読み上げになる。Lunaが声を変える仕組みはこれだ。脳が感情を選び(valenceとarousal、centroidの記事参照)、TTSがspeakerIdを選ぶ。

裏のデータセット名は素朴にFortune French - Datasetで、ライセンス名は「Fortune's Café」だ。そう、ライセンス名がカフェっぽい。そのままだ。

なぜ経済的に賢いのか

7感情はダウンロード1回、RAMロード1回、ONNXセッション1つだ。代替案(モデル7個分け)だとストレージは倍増し、感情を変えるたびグラフをリロードすることになる。文の途中でトーンを変えるボットには無理だ。ここではspeakerIdはテンソルのインデックスに過ぎない。追加コストはゼロだ。

ライバル:Kokoro、PocketTTS、sanoTTS

PiperはCPUの座を3つの真面目な挑戦者と分け合う。哲学が3つ違う。

Kokoro 82M、コスパの王

Kokoro、作者はhexgradだ。8200万パラメーター、改変StyleTTS 2アーキテクチャにISTFTNetデコーダー、拡散ステップなし。拡散がないのが全てだ。反復ノイズ除去ループがないので推論が超高速だ。

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

100時間未満の音声、約1000ドルで学習され、ブラインドテストではGoogleやAmazonのプレミアム音声と同列に置かれる。爆発した理由はここだ。プレミアム品質、無料、ローカル、寛容ライセンス。

Piperとの対決では、純粋な自然さはKokoroが勝つ(StyleTTS 2は小型VITSより上)し、ライセンスも勝つ(PiperのエンジンはGPL-3.0に対してApache 2.0)。カバレッジはPiperが勝つ(8言語に対する71言語、54声に対する3755スピーカー)し、コミュニティ音声のエコシステムも勝つ。英語ナレーションならKokoroは倒しにくい。7感情のフランス語、トルコ語、スワヒリ語なら、Piperが唯一の候補になることが多い。

PocketTTS(Kyutai)、CPUでのクローニング

Kyutaiは別の手を打つ。小さな声クローン付きモデルでCPU上で動く。

Modèle Params WER UTMOS SIM Vitesse CPU (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WERは1%未満(発音が正しい)、UTMOS 4.36(知覚品質が優秀)、SIM 0.923(クローンが本当にターゲットに似る)。small版は190万パラメーターまで落ちてクローン付きだ。190万だ。私のSabakiルーターはJSONでほぼこのサイズだ。

Piperとの対決では、クローンはPocketTTSが勝つ(Piperは固定声で読む)し、主力モデルの小ささも勝つ。言語はPiperが勝つ(smallは今のところ英語のみ)し、エコシステムの成熟も勝つ(「自分で学習して」に対する ready-to-use 536声)。

sanoTTS、3ドルのチップに載るモデル

sanoTTS(sanoはネパール語で「小さい」)はTTSのBareironだ。音素から22.05 kHz PCMまでの完全なニューラルスタックがマイコン上で動く。

Système Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

パラメーター29万。音声2MBだ。pip install sanottsしてsanotts say "Hello" --voice amyで喋る。ブラウザでも動く。

でもスコアを見よう。SCOREQはTinyTTSの3.94に対して2.29、UTMOSは2.45だ。喋る、うん。歌う、いいえ。これが究極のトレードオフだ。29万ならどこでも動くが品質はサイズなりだ。Piper mediumは100倍の重みで別リーグの声だ。

まとめ表

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

選び方は制約次第で、絶対の勝者はいない。純粋な英語品質ならKokoro。CPUクローンならPocketTTS。マイコンならsanoTTS。言語カバーと感情つきフランス語声とインフラゼロならPiperだ。

ElevenLabs、課金される巨人

そしてElevenLabsがいる。70超言語、表現豊かなEleven v3 with audio tagsとドラマチックな対話、ブラインドテストでトップ品質だ。TTS界のJevみたいなものだ。皆見たことがあり、皆ベンチマークの相手にした。

2026年の価格はざっくりこうだ。

Modèle API Prix / 1000 caractères
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000文字は約1分の音声だ。つまり1分あたり0.05から0.10ドルだ。99ドルのProプランは約99万文字込みで、超過分も同単価だ。毎日ナレーション付き動画を出すYouTubeチャンネル(私のvalorant-short-makerはまさにそれを無料のPiperでやる)なら、毎日のナレーション1分ごとに月1.50から3ドルだ。1日10分なら月15から30ドルだ。払えるが、数える金額だ。

払う価値がある場合:声自体が商品のとき(吹き替え、オーディオブック、広告)、70言語が明日朝に要るとき、クローンが一発で完璧に要るとき。逆に払う価値がない場合:一晩中喋るDiscordボット、社内通知、開発、量が爆発する場合だ。午前3時に人生を語るLunaをElevenLabsで動かしたら、私の電気代が泣く。

計算は簡単だ。Piperは限界費用0ユーロで品質は「良い」。ElevenLabsはメーターが回って品質は「最高」。その間に「良くて無料」が勝つ世界がある。それがPiperHubの賭けの全てだ。

pipertts:ネイティブがPythonラッパーに10倍勝つ理由

リポジトリitsrealfortune/pipertts、75コミット。PiperのTypeScriptネイティブ移植だ。READMEのタイトルは「In-process Piper TTS for Node.js. No Python required.」だ。

spawnのコスト

旧来ラッパー(PiperTTS)は合成ごとにpython3 -m piperを子プロセスspawnする。

synthèse = spawn(python) + chargement du modèle + inférence + kill

各文がインタープリター起動と重みロードを払う。en_US-lessac-mediumでの実測は1行あたり約2.2秒だ。

ネイティブ(PiperNativeTTS)はONNXセッションをメモリに温かく保つ。

load une fois -> session persistante -> inférence par phrase

同じ条件の実測は1行あたり約0.22秒だ。10倍はここから来る。spawnとリロードを殺した結果で、推論の魔法とは関係ない。ONNX推論自体は両側で同じだ。

おまけに、初回ロードで最適化済みグラフを.onnxの横にキャッシュする(<model>.ortopt-<platform>-<arch>-v<ort>.onnx)。次回以降のロードが約35%速くなる。キャッシュキーはplatform、arch、ORTバージョンの組み合わせで、モデルが変われば更新される。cacheOptimizedModel: falseで無効化できる。

espeakブリッジ:0.4ms対15ms

フォネマイズも同じ理屈だ。ラッパーは文ごとにespeak-ngバイナリをCLI呼び出しする。毎回約15msのspawnだ。ネイティブはN-APIブリッジを内蔵し(src/native/espeak-bridge/、espeakbridge.cの移植)、Linux x64とarm64向けprebuild付きだ。1呼び出し約0.4msで音素はバイト同一だ。アドオンなしならCLIに自動フォールバックする。PIPER_ESPEAK_BRIDGE=0でCLI強制だ。

7種のphoneme_typeを全部移植

Type Moteur Notes
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

アラビア語は自動で母音記号付けされる(tashkeel、無効化可)。カタログのespeak声509件は高速ブリッジ経由で、変わり種は専用エンジン経由だ。テストスイートに71テスト、詰まる所は文書化済み(日本語アクセント、UniDicにデータなし。想定内、隠しなし)。

形式とストリーミング

デフォルトwav、raw PCM、mp3、ogg、opusだ。ffmpegがあればそれを使い、なければ純JSフォールバック(lamejs for mp3、opusscriptとOgg muxer for opus、ソースとの相関0.993)。そして何よりsynthesizeChunks()だ。文ごとにyieldする非同期ジェネレーターで、文間にsentenceSilenceを挟む。Lunaの場合、末尾を合成しながら先頭の再生を始められる。専用サーバーなしのTTSストリーミングだ。

PiperHubカタログ統合付きのフル使用例(hub:と、作者リポジトリがファイルを出さなくなった場合の自動ミラーフォールバック)。

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // Discord向けに直接
  lengthScale: 0.95,     // 少し速め
});

コミュニティ音声(GLaDOSやゲームキャラ)は公式manifestにない。PiperHubが存在する理由そのもので、piperttsは知っている。hub:<slug>、piperhubページURL、slugだけ、全部動く。usable: falseの声は明快なエラーで拒否される。

犠牲にしたもの

ない機能の各々が別の機能を支えている。正直リストだ。

PiperはElevenLabsの隣ではロボットっぽく聞こえる。 22050 HzのVITS mediumは綺麗だが大型表現モデルに比べると平板だ。マルチ感情で一部補う(平板1種より7スタイル)が、ElevenLabs v3のオーディオブックはやはり上だ。数十MBの代償だ。

espeakは間違える。 固有名詞、外来語、略語にはルールベース音素化の死角がある。フランス語の「Spoiler」はespeakなりに読む。ニューラルG2Pの大型モデルは間違いが少ない。本番では業務用語の例外辞書が要る。

ネイティブのクローンはゼロだ。 Piperは固定声で読む。自分の声が欲しければPocketTTSかElevenLabsだ。マルチスピーカーは variation をくれるが、自分の声色はくれない。

日本語は語彙アクセントなしのままだ。 UniDicにアクセントデータがないので、分割は正確だが抑揚は近似だ。READMEに文書化済みで、隠しはなしだ。

タイ語は初回呼び出しが重い。 TLTKは無改変Pyodide/WASMで動く。初回はロードで数秒かかり、以降は速い。リファレンスと7/7同一だが、ウォームアップを見込もう。

データは腹ペコだ。 まともなhigh音声には時間単位の綺麗にアライメントされた音声が要る。Fortuneデータセットは何時間もの録音と掃除だ。魔法ゼロだ。ゴミを入れればロボットが出る。

カタログには死んだ声がある。 25モデルにusable: falseフラグだ。想定内でフィルタ済みだが、声のクラウドソーシングは放棄のクラウドソーシングでもあると分かる。

最終結果

片方にElevenLabsのメーター、片方にRaspberry Piだ。

  • PiperHub:表示536モデル、71言語、3755スピーカー、リンク済み130データセット、datasetからtrainingからusageへのガイド
  • Piper:VITSエンドツーエンド、espeak、ONNX CPU、LJSpeech、lowとmediumとhigh、標準22050 Hz
  • Fortune multi:FR 7感情とEN 7感情、各1 ONNX、切り替えはspeakerId、ライセンス「Fortune's Café」
  • Kokoro 82M:プレミアムな英語品質、Apache 2.0、54声、CPUほぼリアルタイム
  • PocketTTS:CPUクローン、約100Mから2-18M、WER 0.92%
  • sanoTTS:0.29M、2MB、マイコン、サイズなりの品質
  • ElevenLabs:1000文字0.05-0.10ドル、70超言語、トップ品質、回るメーター
  • ネイティブpipertts:ラッパー約2.2秒/行に対して約0.22秒/行、espeakブリッジ0.4ms、7 phoneme_type、wavとrawとmp3とoggとopus、文ごとのストリーミング

Lunaは毎日これで喋る。TTS代0ユーロ、脳操縦の感情、Discord向け直接OGGだ。ElevenLabsと比べれば分かる、もちろん。でも午前3時に彼女が人生を語り出しても、請求はぐっすり眠っている。

考え方

p2r3は言った。「1ドルくらいのあのちっちゃいチップでMinecraftみたいなすごいものが動くって発想が好きなんだ。」

声に当てはめた同じノリだ。数十MBのONNXでRaspberry Piが71言語で喋る。クラウドなし、アカウントなし、メーターなしが好きなんだ。吹き替えスタジオの代わりにはもちろんならない。面倒なケースの代わりになる。まともでローカルで無料な声が欲しくて、夜に喋っても請求されないケースだ。

トレードオフを白黒はっきり書く。

  • プレミアム品質から小型VITSへ:少しロボットっぽいがローカルで無料
  • 感情ごとに1モデルからspeakerIdへ:RAM上は1つ分の値段で7声
  • Python spawnから永続セッションへ:10倍速く、Python installゼロ
  • 野生カタログから出自リンクへ:各声は出自と動作状態が分かる
  • 課金APIから限界費用0ユーロへ:ElevenLabsほど綺麗ではないが請求は安眠する

覚えるべき3つ:

  1. PiperはCPUのTTSだ。VITSとespeakとONNX、71言語、Piで動く数十MBの声だ。ElevenLabsほど綺麗ではないが、限りなく安い。
  2. Fortune multiは1つ分の値段で7感情だ。ONNX1つ、切り替えはspeakerId、FRとENだ。モデル7個の代わりにスピーカー埋め込みだ。
  3. ネイティブはspawnを殺す。piperttsはONNXセッションを温存する。1行2.2秒に対して0.22秒、espeak CLI 15msに対してブリッジ0.4msだ。同じ推論で待ち時間は10分の1だ。

興味があれば、カタログはpiperhub.org、サイトのコードはgithub.com/fox3000foxy/piper-bank、ネイティブモジュールはgithub.com/itsrealfortune/piperttsだ。午前3時に人生を語るLunaを聞きたければDiscordでどうぞ xD

NEW

PiperHub:오픈소스 TTS 음성 536종,7가지 감정 1개 모델,GPU 없이,요금 없이

VITS、espeak、ONNX、LJSpeech:Piper가 Raspberry Pi를 어떻게 말하게 하는지.7가지 감정의 Fortune multi、Kokoro 82M、PocketTTS、sanoTTS、ElevenLabs,그리고 공식 Python 래퍼보다 10배 빠른 네이티브 pipertts.

Introduction

Discord 봇을 말하게 하는 건 돈이 들어요. 비싼 건 텍스트가 아니에요. 로컬 LLM이면 GPU 몇 푼으로 문장을 만들 수 있어요. 비싼 건 목소리예요. 합성 API는 글자 수대로 과금하고, 봇이 밤새 떠들면 청구서도 따라 올라가요.

이게 제가 Luna Protocol에서 겪은 문제예요. 제 Discord 봇은 로컬 LLM으로 메시지를 만들고, Piper 음성으로 읽게 하고, OGG로 바꿔서 음성 메시지로 보내요. 전부 제 서버에서 돌고, 한 문장당 0유로예요. 새벽 3시에 Luna가 혼자 인생 이야기를 시작해도 똑같아요.

0유로를 유지하려면 무료 음성을 찾아서 직접 호스팅해야 해요. 여기서부터 꼬였어요. 모델은 수십 개 Hugging Face 저장소에 흩어져 있고, 라이선스는 흐릿하고, 어떤 음성이 어디서 왔는지, 아직 돌아가는지 알 방법이 없어요. 그래서 PiperHub를 만들었어요. Piper TTS 음성용 오픈소스 카탈로그예요.

오늘 숫자:모델 536개,언어 71종,스피커 3755명이에요. 출처를 아는 모델은 전부 원본 dataset에 연결해 두었어요. 홈페이지 말대로, 출처도 소리의 일부니까요.

자주 받는 질문이 하나 남아 있어요. Kokoro, PocketTTS, sanoTTS, ElevenLabs 앞에서 Piper는 어떤지. 그리고 Fortune의 pipertts 모듈은 왜 공식 Python 래퍼보다 10배 빠른지. 이 글에서는 코드와 함께 엔진을 열어 보여드릴게요.

PiperHub,카탈로그

엔진을 열기 전에, PiperHub가 푸는 문제부터 볼게요.

음성은 어디서 오나요

코드는 github.com/fox3000foxy/piper-bank에 있어요. Astro 정적 사이트고 서버는 없어요. 영어가 루트에 있고, 다른 15개 로케일이 /<code>/ 아래에 있어요(fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). 사실 제 블로그랑 같은 패턴이에요.

유일한 진실 원천은 아무도 손으로 고치지 않아요. 파이프라인이에요.

npm install
npm run data    # src/data/*.json 재생성 (python3 scripts/generate_data.py)
npx astro dev   # 로컬 개발
npm run build   # dist/에 정적 빌드

구체적으로는 이렇게 되어 있어요. crawler 저장소가 models_mapping.csv랑 datasets_mapping.csv를 관리하고, 미러 음성은 Hugging Face에 있고, 샘플은 오디오 파일이에요. npm run data가 그걸 다 가져와서 src/data/*.json을 뱉어내요. 사이트는 정적 JSON만 읽어요.

지금 생데이터(555개, 사이트 표시는 536개. 크롤이 표시보다 항상 조금 앞서요)에서는:

Qualité Modèles
medium 437
high 67
low 50
Sample rate Modèles
22050 Hz 486
16000 Hz 47

22050 Hz 음성이 486개, Piper 표준이에요. 16000 Hz가 47개, 임베디드용 가볍고 빠른 음성이에요. 포네마이저는 509건에서 espeak을 써요. 여기로 돌아올게요. 시스템의 심장이에요.

출처,즉 각 모델이 dataset을 갖는 이유

PiperHub를 그냥 목록이 아니라 쓸모 있게 만드는 게 모델과 dataset 연결이에요. 홈페이지에서 가져온 예로 말하면, Astra - Valorant Piper TTS라는 음성이 Astra - LJSpeech Dataset이라는 dataset에 연결되어 있어요. 두 페이지를 열어서 양쪽 저장소랑 라이선스를 확인할 수 있어요.

참조 dataset 130개예요. 그리고 카탈로그는 현황에 정직해요. 25개 모델에 usable: false 플래그가 붙어 있어요(깨진 파일, 아니면 fork 런타임이 필요한 piperPlus 형식). pipertts 모듈은 이런 음성을 나중에 이상하게 터뜨리는 대신 명확한 에러로 거부해요. 48개 모델이 멀티 스피커인데, 그중 둘이 앞으로 중요해져요.

가이드 쪽은 사이트가 세 단계로 일을 해요. Build a dataset, Train a voice, Use a voice, 거기에 Piper Plus 페이지예요. 입력은 LJSpeech 형식 dataset, 출력은 ONNX예요. 말은 쉽고 하기는 길어요. 그래서 다음 섹션이 있어요.

Piper 속살:VITS,espeak,ONNX

한 문장,세 단계

Piper에게 "bonjour"라고 말시키면 이렇게 돼요.

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

별도 보코더는 없어요. 이게 VITS예요. 텍스트(음소화된)에서 파형까지 직접 가는 엔드투엔드 모델이에요. 옛날 파이프라인은 텍스트에서 스펙트로그램(Tacotron), 스펙트로그램에서 오디오(HiFi-GAN, 모델 2개, 학습 2번)였는데, VITS는 조건부 VAE, normalizing flow, 디코더 위 GAN 방식 discriminator로 한 번에 다 배워요. 학습 1번, 모델 1개, ONNX 추출 1개예요.

디코더가 22050 Hz를 바로 뱉어요. 이 샘플레이트에서는 1초 발화가 숫자 22050개예요. 모델은 작고(medium 품질로 수십 MB), 추론은 순수 CPU예요. 최적화된 ONNX 그래프 위 행렬 곱이에요. 그래서 Raspberry Pi가 공식 타깃이에요. Piper는 Rhasspy에서 태어났고, Pi에서 돌리기 위한 오픈소스 음성 비서용으로 만들어졌어요.

espeak-ng,더러운 일을 하는 포네마이저

VITS 전에 텍스트를 음소로 바꿔야 해요. 555개 모델 중 509개가 espeak을 써요.

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng는 수십 개 언어 발음을 알아요. VITS 모델은 eau를 o로 읽는 프랑스어 철자를 절대 배우지 않아요. 음소에서 소리로 가는 걸 배워요. 이게 학습을 엄청 쉽게 만들고, Piper가 71개 언어를 커버하는 이유도 설명해요. 새 언어는 해당 espeak 음성을 연결하고 학습하면 돼요.

espeak을 안 쓰는 음성은 자기 포네마이저가 있고, 여기서부터 이국적이에요. hebrew(Nakdimon ONNX에 IPA 규칙 494줄), pinyin(다의어 해소 포함 BERT g2pW 풀세트. 같은 한자도 단어에 따라 다르게 읽어요), thai(무수정 진짜 TLTK), japanese(lindera 분할과 UniDic). pipertts 모듈은 일곱 개를 다 품고 있고, Python 레퍼런스와 동일하다고 검증됐어요. 뒤에서 돌아올게요.

dataset:LJSpeech,언제나 LJSpeech

표준 학습 형식은 LJSpeech예요. 클립 가득한 wavs/ 폴더에 metadata.csv예요.

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized이고, 클립 하나에 한 문장, 몇 초예요. 원본 dataset은 한 여성 화자가 읽은 영어 13000문장이고 public domain이에요. 프랑스어 음성을 학습하려면 같은 구조를 프랑스어로 녹음해요. 텍스트와 정렬된 오디오, medium 품질이면 몇 시간이면 되고, high면 더 필요해요.

카탈로그의 세 품질은 대충 모델 크기와 학습 시간에 대응해요. Low는 작고, 빠르고, 로봇 같아요. Medium은 sweet spot이고 437개 모델이에요. High는 더 크고, 더 느리고, 더 자연스럽고 67개 모델, Fortune 포함이에요.

세 개의 마법 버튼

각 Piper 음성은 세 개 추론 파라미터를 갖고, 기본값은 거의 공통이에요.

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale(0.667):목소리 변화폭이에요. 높이면 표현이 풍부해지고, 낮추면 안정돼요. 출력은 확률적이에요. 같은 문장을 두 번 합성하면 오디오가 조금 달라요.
  • noise_w(0.8):타이밍 변화폭, 리듬이에요.
  • length_scale(1.0):속도예요. 1보다 크면 느려지고, 작으면 빨라져요. Luna가 화났을 때 빨리 말하게 하는 가장 쉬운 방법이에요.

noise_scale을 기억해 두세요. 감정에서 써요.

Fortune multi:7가지 감정,1개 ONNX

자, 이제 현지 스타예요.

카탈로그에는 ItsRealFortune(저요!)이 학습한 Fortune 모델 세 개가 올라 있어요.

Modèle Repo Voix
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

FR multi는 이렇게 되어 있어요.

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

하나의 ONNX 파일에 일곱 가지 감정 variation이에요. EN multi도 같고, 이름에 역사적 오타가 있어요. surprised가 아니라 suprised예요. 가중치 안에 있으니 그대로 둬요. 전설의 일부예요.

speakerId는 어떻게 돌아가나요

멀티 스피커 VITS는 같은 모델에 입력이 하나 더 있어요. 스피커 임베딩이에요. 학습 때 각 클립에 스타일 태그가 붙고, 모델은 나머지를 배우면서 임베딩 테이블(스타일당 한 줄)도 같이 배워요. 추론 때는 테이블 행을 골라요.

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = 평범 ... 5 = 놀람
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

같은 문장을 일곱 가지로 읽어요. speakerId: 5(놀람)에 noise_scale을 조금 올리면 믿음직한 감탄이 돼요. speakerId: 4(슬픔)에 length_scale: 1.1로 늦추면 부고 방송이 돼요. Luna가 목소리를 바꾸는 방식이 이거예요. 뇌가 감정을 고르고(valence와 arousal, centroid 글 참고), TTS가 speakerId를 골라요.

뒤 dataset 이름은 소박하게 Fortune French - Dataset이고, 라이선스 이름은 "Fortune's Café"예요. 네, 라이선스 이름이 카페 같아요. 그대로 둬요.

왜 경제적으로 영리한가요

일곱 감정이 다운로드 1번, RAM 로드 1번, ONNX 세션 1개예요. 대안(모델 일곱 개 분리)은 저장소를 몇 배로 늘리고, 감정을 바꿀 때마다 그래프를 다시 로드해야 해요. 문장 중간에 톤을 바꾸는 봇에는 불가능해요. 여기서는 speakerId가 텐서 속 인덱스일 뿐이에요. 한계 비용은 제로예요.

경쟁자:Kokoro,PocketTTS,sanoTTS

Piper는 CPU 자리를 세 진지한 도전자와 나눠요. 철학이 셋 다 달라요.

Kokoro 82M,가성비 왕

Kokoro, 만든 사람은 hexgrad예요. 8200만 파라미터, 수정 StyleTTS 2 아키텍처에 ISTFTNet 디코더, 확산 단계 없음이에요. 확산이 없다는 게 전부예요. 반복 디노이징 루프가 없으니 추론이 엄청 빨라요.

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

100시간 미만 오디오, 약 1000달러로 학습했고, 블라인드 청취에서 Google, Amazon 프리미엄 음성과 같은 급으로 꼽혀요. 터진 이유가 여기예요. 프리미엄 품질, 무료, 로컬, 관대한 라이선스.

Piper와 붙으면 순수 자연스러움은 Kokoro가 이겨요(StyleTTS 2가 소형 VITS보다 위예요) 라이선스도 이겨요(Piper 엔진 GPL-3.0에 Apache 2.0). 커버리지는 Piper가 이겨요(8개 언어 대 71개 언어, 54개 음성 대 3755 스피커) 커뮤니티 음성 생태계도 이겨요. 영어 내레이션이면 Kokoro가 세요. 7가지 감정 프랑스어, 터키어, 스와힐리어면 Piper가 유일한 후보인 경우가 많아요.

PocketTTS(Kyutai),CPU에서 클로닝

Kyutai는 다른 수를 둬요. 작은 음성 클로닝 포함 모델이고 CPU에서 돌아요.

Modèle Params WER UTMOS SIM Vitesse CPU (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER 1% 미만(발음이 정확해요), UTMOS 4.36(체감 품질이 훌륭해요), SIM 0.923(클론이 진짜 타깃을 닮아요). small 버전은 190만 파라미터까지 내려가는데 클로닝이 돼요. 190만이요. 제 Sabaki 라우터가 JSON으로 거의 이 크기예요.

Piper와 붙으면 클론은 PocketTTS가 이겨요(Piper는 고정 음성으로 읽어요) 주력 모델 작기도 이겨요. 언어는 Piper가 이겨요(small은 지금 영어 전용이에요) 생태계 성숙도도 이겨요(바로 쓰는 536 음성 대 "네가 학습해").

sanoTTS,3달러 칩에 올라가는 모델

sanoTTS(sano는 네팔어로 "작은")는 TTS계의 Bareiron이에요. 음소에서 22.05 kHz PCM까지 완전한 신경 스택이 마이크로컨트롤러 위에서 돌아요.

Système Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

29만 파라미터예요. 음성 2MB예요. pip install sanotts하고 sanotts say "Hello" --voice amy 하면 말해요. 브라우저에서도 돼요.

근데 점수를 보세요. SCOREQ는 TinyTTS 3.94에 2.29, UTMOS는 2.45예요. 말은 해요, 네. 노래는 안 돼요. 이게 궁극 tradeoff예요. 29만이면 어디든 들어가지만 품질은 크기 따라가요. Piper medium은 100배 무게로 다른 리그 음성이에요.

정리 표

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

선택은 제약에 달렸고 절대 승자는 없어요. 순수 영어 품질이면 Kokoro예요. CPU 클론이면 PocketTTS예요. 마이크로컨트롤러면 sanoTTS예요. 언어 커버와 감정 프랑스어 음성과 인프라 제로면 Piper예요.

ElevenLabs,과금되는 거인

그리고 ElevenLabs가 있어요. 70개 넘는 언어, 표현 풍부한 Eleven v3 with audio tags와 드라마틱한 대화, 블라인드 테스트 최상 품질이에요. TTS계 Jev 같은 거예요. 다들 봤고, 다들 벤치마크 상대로 삼았어요.

2026년 가격, 어림잡아 보면:

Modèle API Prix / 1000 caractères
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000자가 약 1분 오디오예요. 그러니까 1분에 0.05에서 0.10달러예요. 99달러 Pro 플랜은 약 99만 자 포함이고 초과분도 같은 단가예요. 매일 내레이션 영상을 올리는 YouTube 채널(제 valorant-short-maker가 딱 그걸 무료 Piper로 해요)이면, 매일 내레이션 1분당 월 1.50에서 3달러예요. 하루 10분이면 월 15에서 30달러예요. 낼 수는 있는데 세는 돈이에요.

낼 가치가 있을 때:목소리 자체가 상품일 때(더빙, 오디오북, 광고), 내일 아침까지 70개 언어가 필요할 때, 클론이 한 번에 완벽해야 할 때요. 반대로 절대 아닐 때:밤새 떠드는 Discord 봇, 내부 알림, 개발, 양이 터질 때예요. 새벽 3시에 인생 이야기하는 Luna를 ElevenLabs로 돌리면, 제 전기요금이 울어요.

계산은 쉬워요. Piper는 한계 비용 0유로에 품질 "좋아요". ElevenLabs는 미터기가 돌고 품질 "훌륭해요". 그 사이에 "좋고 무료"가 이기는 세상이 있어요. 그게 PiperHub 내기의 전부예요.

pipertts:네이티브가 Python 래퍼를 10배로 이기는 이유

저장소 itsrealfortune/pipertts, 75개 커밋이에요. Piper의 TypeScript 네이티브 포팅이에요. README 제목은 "In-process Piper TTS for Node.js. No Python required."예요.

spawn 비용

옛날 래퍼(PiperTTS)는 합성마다 python3 -m piper를 자식 프로세스로 spawn해요.

synthèse = spawn(python) + chargement du modèle + inférence + kill

각 문장이 인터프리터 시작과 가중치 로드를 내요. en_US-lessac-medium 실측으로 한 줄당 약 2.2초예요.

네이티브(PiperNativeTTS)는 ONNX 세션을 메모리에 따뜻하게 둬요.

load une fois -> session persistante -> inférence par phrase

같은 조건 실측으로 한 줄당 약 0.22초예요. 10배는 여기서 와요. spawn이랑 리로드를 죽인 결과고, ONNX 추론이 신기해서가 아니에요. 양쪽 ONNX 추론 자체는 같아요.

덤으로 첫 로드 때 최적화된 그래프를 .onnx 옆에 캐시해요(<model>.ortopt-<platform>-<arch>-v<ort>.onnx). 다음 로드가 약 35% 빨라져요. 캐시 키는 platform, arch, ORT 버전을 조합하고, 모델이 바뀌면 새로고침돼요. cacheOptimizedModel: false로 끌 수 있어요.

espeak 브리지:0.4ms 대 15ms

음소화도 같은 논리예요. 래퍼는 문장마다 espeak-ng 바이너리를 CLI로 불러요. 매번 약 15ms spawn이에요. 네이티브는 N-API 브리지를 내장하고(src/native/espeak-bridge/, espeakbridge.c 포팅), Linux x64와 arm64 프리빌드 포함이에요. 호출당 약 0.4ms에 음소는 바이트 동일해요. 애드온이 없으면 CLI로 자동 폴백해요. PIPER_ESPEAK_BRIDGE=0으로 CLI 강제예요.

7종 phoneme_type,전부 포팅

Type Moteur Notes
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

아랍어는 자동으로 모음 부호가 찍혀요(tashkeel, 끌 수 있어요). 카탈로그 espeak 음성 509건은 빠른 브리지로 가고, 이국적인 건 전용 엔진으로 가요. 테스트 스위트에 71개 테스트, 막히는 곳은 문서화했어요(일본어 악센트, UniDic에 데이터 없음. 인정한 거고 숨긴 거 없어요).

형식과 스트리밍

기본 wav, raw PCM, mp3, ogg, opus예요. ffmpeg가 있으면 쓰고, 없으면 순수 JS 폴백(mp3용 lamejs, opus용 opusscript와 Ogg muxer, 원본과 상관계수 0.993). 그리고 무엇보다 synthesizeChunks()예요. 문장마다 yield하는 비동기 제너레이터고 문장 사이에 sentenceSilence를 넣어요. Luna에게는 끝을 합성하면서 앞을 재생하기 시작한다는 뜻이에요. 전용 서버 없는 TTS 스트리밍이에요.

PiperHub 카탈로그 통합 포함 전체 사용법(hub:에 작성자 저장소가 파일을 안 주면 자동 미러 폴백):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // Discord에 바로
  lengthScale: 0.95,     // 조금 빠르게
});

커뮤니티 음성(GLaDOS랑 게임 캐릭터)은 공식 manifest에 없어요. PiperHub가 존재하는 이유 그 자체고, pipertts도 알아요. hub:<slug>, piperhub 페이지 URL, 그냥 slug, 다 돼요. usable: false 음성은 명확한 에러로 거부돼요.

희생한 것들

없는 기능마다 다른 기능을 먹여 살려요. 정직 리스트예요.

Piper는 ElevenLabs 옆에서 로봇 같이 들려요. 22050 Hz VITS medium은 깔끔하지만 대형 표현 모델 앞에서는 평평해요. 멀티 감정으로 일부 만회해요(평평 1종보다 7 스타일) 그래도 ElevenLabs v3 오디오북이 위예요. 수십 MB 대가예요.

espeak은 틀려요. 고유명사, 외래어, 약어에는 규칙 기반 음소화 사각지대가 있어요. 프랑스어 "Spoiler"는 espeak 나름대로 읽어요. 신경 G2P 대형 모델은 덜 틀려요. 실전에서는 업무 용어 예외 사전이 필요해요.

네이티브 클론은 제로예요. Piper는 고정 음성으로 읽어요. 자기 목소리가 탐나면 PocketTTS나 ElevenLabs로 가세요. 멀티 스피커는 variation을 주지 자기 음색을 주지 않아요.

일본어는 어휘 악센트 없이 그대로예요. UniDic에 악센트 데이터가 없어서 분할은 정확하지만 멜로디는 근사예요. README에 문서화했고 숨긴 거 없어요.

태국어는 첫 호출이 무거워요. TLTK가 무수정 Pyodide/WASM으로 돌아요. 첫 호출은 로딩에 몇 초 걸리고 이후는 빨라요. 레퍼런스와 7/7 동일하지만 웜업은 잡으세요.

데이터가 배고파요. 제대로 된 high 음성 하나에 시간 단위 깨끗하게 정렬된 오디오가 필요해요. Fortune dataset은 몇 시간 녹음과 정리예요. 마법은 제로예요. 쓰레기를 넣으면 로봇이 나와요.

카탈로그에 죽은 음성이 있어요. 25개 모델에 usable: false 플래그예요. 인정하고 필터링했지만, 음성 크라우드소싱이 포기 크라우드소싱이기도 하다는 걸 알려줘요.

최종 결과

한쪽에 ElevenLabs 미터기, 한쪽에 Raspberry Pi예요.

  • PiperHub:표시 536 모델, 71 언어, 3755 스피커, 연결된 130 dataset, dataset에서 training에서 usage로 가는 가이드
  • Piper:VITS 엔드투엔드, espeak, ONNX CPU, LJSpeech, low와 medium과 high, 표준 22050 Hz
  • Fortune multi:FR 7감정과 EN 7감정, 각 1 ONNX, 전환은 speakerId, 라이선스 "Fortune's Café"
  • Kokoro 82M:프리미엄 영어 품질, Apache 2.0, 54 음성, CPU 거의 실시간
  • PocketTTS:CPU 클론, 약 100M에서 2-18M, WER 0.92%
  • sanoTTS:0.29M, 2MB, 마이크로컨트롤러, 크기 따라가는 품질
  • ElevenLabs:1000자 0.05-0.10달러, 70개 넘는 언어, 최고 품질, 도는 미터기
  • 네이티브 pipertts:래퍼 약 2.2초/줄 대 약 0.22초/줄, espeak 브리지 0.4ms, 7종 phoneme_type, wav와 raw와 mp3와 ogg와 opus, 문장별 스트리밍

Luna는 매일 이걸로 말해요. TTS 0유로, 뇌 조종 감정, Discord용 직접 OGG예요. ElevenLabs랑 비교하면 들리죠, 당연히요. 근데 새벽 3시에 걔가 인생 이야기를 시작해도, 제 청구서는 쿨쿨 자요.

철학

p2r3가 말했어요. "1달러짜리 그 조그만 칩이 Minecraft 같은 대단한 걸 돌릴 수 있다는 발상이 좋아."

목소리에 적용한 같은 느낌이에요. 수십 MB ONNX로 Raspberry Pi가 71개 언어로 말하는 게 좋아요. 클라우드 없이, 계정 없이, 미터기 없이요. 더빙 스튜디오를 대신할 수는 당연히 없어요. 짜증 나는 경우를 대신해요. 괜찮고, 로컬이고, 무료인 목소리가 필요하고, 밤에 말해도 과금 안 되는 경우요.

tradeoff, 흑백으로 또박또박 적어요.

  • 프리미엄 품질에서 소형 VITS로:조금 로봇 같지만 로컬이고 무료
  • 감정당 1모델에서 speakerId로:RAM 위 1개 값으로 7 음성
  • Python spawn에서 영속 세션으로:10배 빠르고 Python 설치 제로
  • 야생 카탈로그에서 출처 연결로:각 음성은 출처와 동작 상태를 알아요
  • 과금 API에서 한계 비용 0유로로:ElevenLabs만큼 예쁘진 않지만 청구서는 쿨쿨 자요

기억할 3가지:

  1. Piper는 CPU의 TTS예요. VITS와 espeak와 ONNX, 71개 언어, Pi에서 도는 수십 MB 음성이에요. ElevenLabs만큼 예쁘진 않지만 한없이 싸요.
  2. Fortune multi는 1개 값으로 7감정이에요. ONNX 1개, 전환은 speakerId, FR과 EN이에요. 모델 일곱 개 대신 스피커 임베딩이에요.
  3. 네이티브는 spawn을 죽여요. pipertts는 ONNX 세션을 따뜻하게 둬요. 1줄 2.2초 대 0.22초, espeak CLI 15ms 대 브리지 0.4ms예요. 같은 추론에 대기는 10분의 1이에요.

관심 있으면 카탈로그는 piperhub.org, 사이트 코드는 github.com/fox3000foxy/piper-bank, 네이티브 모듈은 github.com/itsrealfortune/pipertts예요. 새벽 3시에 인생 이야기하는 Luna를 듣고 싶으면 Discord에서 봐요 xD

NEW

PiperHub: 536 açık kaynak TTS sesi, tek modelde 7 duygu, sıfır GPU, sıfır fatura

VITS, espeak, ONNX, LJSpeech: Piper bir Raspberry Pi'yi nasıl konuşturuyor. 7 duygulu Fortune multi, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs - ve resmi Python wrapper'dan 10 kat hızlı natif pipertts.

Giriş

Bir Discord botunu konuşturmak pahalı. Metin değil: yerel bir LLM birkaç sentlik GPU ile cümle üretiyor. Ses ise her cümlede ödeniyor. Sentez API'leri karakter başına ücret alıyor, bot bütün gece konuşunca fatura da peşinden geliyor.

Luna Protocol ile yaşadığım sorun buydu. Discord botum mesajlarını yerel LLM ile üretiyor, sonra bir Piper sesiyle yüksek sesle okutuyor, OGG'ye çeviriyor ve sesli mesaj olarak gönderiyor. Hepsi kendi sunucumda, cümle başına 0 euro, sabah 3'te Luna tek başına hayatını anlatmaya karar verdiğinde bile.

0 euroda kalmak, ücretsiz sesler bulup kendin host etmek demek. İşin karıştığı yer de burası: onlarca Hugging Face reposuna dağılmış modeller, muğlak lisanslar, şu sesin nereden geldiğini ya da hâlâ çalışıp çalışmadığını bilmenin yolu yok. Ben de PiperHub'ı kurdum, Piper TTS için açık kaynak bir ses kataloğu.

Günün rakamları: görüntülenen 536 model, temsil edilen 71 dil, listelenen 3755 speaker. Bildiğimde her model kaynak datasetine bağlı, ana sayfanın dediği gibi kaynak sesin bir parçası.

Bana sık sorulan bir soru kaldı: Kokoro, PocketTTS, sanoTTS ve ElevenLabs karşısında Piper ne durumda? Ve Fortune'un pipertts modülü neden resmi Python wrapper'dan 10 kat hızlı? Bu yazı motorları açıyor, kod kanıtıyla.

PiperHub, katalog

Motorları açmadan önce PiperHub'un çözdüğü soruna bakalım.

Sesler nereden geliyor

Kod github.com/fox3000foxy/piper-bank adresinde. Astro, statik site, sıfır sunucu: kökte İngilizce, /<code>/ altında 15 locale daha (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). Aslında blogumla aynı pattern.

Gerçek kaynağı kimse elle düzenlemiyor. Bu bir pipeline:

npm install
npm run data    # src/data/*.json dosyalarını yeniden üretir (python3 scripts/generate_data.py)
npx astro dev   # yerel dev
npm run build   # dist/ içine statik build

Somut olarak: bir crawler repo models_mapping.csv ve datasets_mapping.csv dosyalarını tutuyor, ayna sesler Hugging Face'te yaşıyor, sample'lar ses dosyaları. npm run data hepsini çekip src/data/*.json olarak kusuyor. Site sadece statik JSON okuyor.

Şu anki ham veride (555 kayıt, site 536 gösteriyor, crawl görüntüden hep biraz önde):

Kalite Model
medium 437
high 67
low 50
Sample rate Model
22050 Hz 486
16000 Hz 47

22050 Hz'de 486 ses, Piper standardı. 16000 Hz'de 47 ses, gömülü için hızlı küçük sesler. Phonemizer 509 vakada espeak, oraya döneceğiz, sistemin kalbi bu.

Kaynak, ya da her modelin neden bir dataseti var

PiperHub'u sadece liste olmaktan çıkarıp işe yarar yapan şey model <-> dataset bağı. Anasayfadan alınan örnek: Astra - Valorant Piper TTS sesi Astra - LJSpeech Dataset'e bağlı. İki sayfayı açıyorsun, iki tarafın repo ve lisanslarını kontrol ediyorsun.

Referans verilen 130 dataset. Katalog birliklerin durumu konusunda dürüst: 25 model usable: false işaretli (bozuk dosyalar ya da fork runtime isteyen piperPlus formatı). pipertts modülü bu sesleri sonradan tuhaf crash vermek yerine açık hatayla reddediyor. 48 model multi-speaker, ilgimizi çekecek ikisi dahil.

Rehber tarafında site işi üç adımda bitiriyor: Build a dataset, Train a voice, Use a voice, artı bir Piper Plus sayfası. Girdi olarak LJSpeech formatında dataset, çıktı olarak ONNX. Söylemesi kolay, yapması uzun, o yüzden sonraki bölüm.

Kaputun altında Piper: VITS, espeak, ONNX

Bir cümle, üç adım

Piper'dan "bonjour" demesini istediğinde olan şu:

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

Ayrı vocoder yok. VITS bu demek: metinden (fonemlenmiş) doğrudan waveform'a giden uçtan uca tek model. Eski pipeline'lar metin -> spektrogram (Tacotron) sonra spektrogram -> ses (HiFi-GAN, iki model, iki eğitim) yaparken, VITS koşullu VAE, normalleştirici flow'lar ve decoder üzerinde GAN tarzı discriminator ile hepsini bir anda öğreniyor. Tek eğitim, tek model, tek ONNX export.

Decoder doğrudan 22050 Hz kusuyor. Bu sample rate'te bir saniye konuşma 22050 sayıya eşit. Model küçük (medium kalitede birkaç düzine MB) ve çıkarım saf CPU: optimize ONNX grafiği üzerinde matris çarpımları. Raspberry Pi'nin resmi hedef olmasının sebebi bu, Piper Rhasspy'de doğdu, Pi'de çalışmak için yapılmış açık kaynak sesli asistan.

espeak-ng, bütün kirli işi yapan fonemleyici

VITS'ten önce metni fonemlere çevirmek gerek. 555 modelden 509'u espeak kullanıyor:

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng onlarca dilin telaffuzunu biliyor. VITS modeli eau'nun o diye okunduğu Fransızca imlayı hiç öğrenmiyor, fonemlerden sese öğreniyor. Bu eğitimi enorm şekilde basitleştiriyor ve Piper'ın neden 71 dili kapsadığını açıklıyor: yeni dil için uygun espeak sesini bağlıyorsun ve eğitiyorsun.

espeak'siz yapanların kendi fonemleyicisi var, burası egzotikleşiyor: hebrew (Nakdimon ONNX + 494 satır IPA kuralı), pinyin (polyphone belirsizliğini çözen tam BERT g2pW, aynı Çince karakter kelimeye göre farklı okunuyor), thai (değiştirilmemiş gerçek TLTK), japanese (lindera segmentasyon + UniDic). pipertts modülü yedisini de taşıyor, Python referansıyla birebir doğrulanmış. Oraya döneceğiz.

Dataset: LJSpeech, hep LJSpeech

Standart eğitim formatı LJSpeech: kliplerle dolu wavs/ klasörü artı metadata.csv:

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, bir klip bir cümle, birkaç saniye. Orijinal dataset tek kadın konuşmacının okuduğu 13 000 İngilizce cümle, kamu malı. Fransızca ses eğitmek için aynı yapıyı Fransızca kaydediyorsun: hizalanmış metin + ses, düzgün medium için birkaç saat yeter, high için daha fazla.

Katalogdaki üç kalite kabaca model boyutu + eğitim süresine denk geliyor. Low: küçük, hızlı, robotik. Medium: tatlı nokta, 437 modelle. High: daha büyük, daha yavaş, daha doğal, Fortune'lar dahil 67 model.

Üç sihirli düğme

Her Piper sesi üç çıkarım parametresi sunuyor, neredeyse evrensel varsayılanlarla:

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667): ses değişkenliği. Yüksekse daha etkileyici, düşükse daha stabil. Çıktı stokastik: aynı cümlenin iki sentezi biraz farklı iki ses veriyor.
  • noise_w (0.8): zamanlama değişkenliği, ritim.
  • length_scale (1.0): hız. 1'in üstü yavaşlatıyor, altı hızlandırıyor. Luna sinirlenince daha hızlı konuşturmanın en kolay yolu.

noise_scale'i aklında tut, duygular için lazım olacak.

Fortune multi: 7 duygu, 1 ONNX

Tamam, yerel yıldıza geçelim.

Katalog ItsRealFortune'un eğittiği üç Fortune modelini listeliyor (selam):

Model Repo Ses
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

FR multi şu:

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

Tek ONNX dosyasında yedi duygusal varyasyon. EN multi aynı, isimlerde tarihi yazım hatasıyla: surprised yerine suprised. Ağırlıkların içinde, tutuyoruz, folklorun parçası.

SpeakerId nasıl çalışıyor?

Multi-speaker VITS, ek bir girdili aynı model: speaker embedding. Eğitimde her klip stiliyle etiketleniyor, model gerisiyle aynı anda bir embedding tablosu (stil başına bir tane) öğreniyor. Çıkarımda tablonun satırını seçiyorsun:

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = normal ... 5 = surprise
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

Aynı cümle, yedi okuyuş. Biraz yükseltilmiş noise_scale ile speakerId: 5 (surprise), inandırıcı bir ünlemin oluyor. Yavaşlatmak için length_scale: 1.1 ile speakerId: 4 (triste), cenaze duyurun hazır. Luna sesini böyle modüle ediyor: beyin duyguyu seçiyor (valence/arousal, centroid yazıma bak), TTS speakerId'yi seçiyor.

Arkasındaki dataset sade isimli Fortune French - Dataset, lisansın adı "Fortune's Café". Evet, lisansın adı kafe gibi. Hayır, değişmiyor.

Neden ekonomik olarak akıllıca

Yedi duygu tek indirme, RAM'de tek yükleme, tek ONNX oturumu demek. Alternatif (yedi ayrı model) depolamayı katlar ve her duygu değişiminde grafiği yeniden yükletirdi, cümle ortasında ton değiştiren bot için oynanamaz. Burada speakerId tensörde bir indeksten ibaret. Marjinal maliyet: sıfır.

Rakipler: Kokoro, PocketTTS, sanoTTS

Piper CPU'yu üç ciddi rakiplerle paylaşıyor, üç farklı felsefe.

Kokoro 82M, fiyat performans kralı

Kokoro, hexgrad'den. 82 milyon parametre, ISTFTNet decoder'lı değiştirilmiş StyleTTS 2 mimarisi, difüzyon adımı yok. Her şeyi yapan difüzyonun yokluğu: iteratif gürültü giderme döngüsü yok, yani ultra hızlı çıkarım.

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

100 saatten az sesle yaklaşık 1000 dolara eğitildi, kör dinleyiciler onu Google ve Amazon'un premium sesleriyle aynı seviyeye koyuyor. Patlamasının sebebi bu: premium kalite, ücretsiz, yerel, permissif.

Piper'a karşı: Kokoro saf doğallıkta (StyleTTS 2 küçük VITS'ten iyi) ve lisansta kazanıyor (Piper için GPL-3.0 motora karşı Apache 2.0). Piper kapsamda (8'e karşı 71 dil, 54 sese karşı 3755 speaker) ve topluluk ses ekosisteminde kazanıyor. İngilizce anlatım için Kokoro'yu yenmek zor. 7 duygulu Fransızca, Türkçe ya da Swahili için Piper genelde tek yarışmacı.

PocketTTS (Kyutai), CPU'da klonlama

Kyutai başka telden çalıyor: CPU'da çalışan ses klonlamalı küçük model.

Model Params WER UTMOS SIM CPU hızı (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER %1'in altında (doğru telaffuz ediyor), UTMOS 4.36 (mükemmel algılanan kalite), SIM 0.923 (klon hedefe gerçekten benziyor). Small sürümler klonlama dahil 1.9M parametreye iniyor. 1.9M. Sabaki router'ım JSON olarak neredeyse bu boyutta.

Piper'a karşı: PocketTTS klonlamada kazanıyor (Piper sabit seslerle okuyor) ve flagship kompaktlığında. Piper dillerde kazanıyor (small şimdilik sadece İngilizce) ve ekosistem olgunluğunda (kendin eğit'e karşı hazır 536 ses).

sanoTTS, 3 dolarlık çipe sığan model

sanoTTS (Nepalcede sano "küçük" demek), TTS'in Bareiron'u: fonemlerden 22.05 kHz PCM'e giden tam nöral stack mikrodenetleyicide.

Sistem Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M parametre. Ses başına 2 MB. pip install sanotts, sanotts say "Hello" --voice amy, konuşuyor. Tarayıcıda da.

Ama skorlara bak: TinyTTS'in 3.94'üne karşı SCOREQ 2.29, UTMOS 2.45. Konuşuyor, evet. Şarkı söylemiyor, hayır. Nihai tradeoff bu: 0.29M'de her yere sığarsın ama kalite boyutu takip ediyor. Piper medium başka ligde ses için 100 kat ağırlık demek.

Özet tablo

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

Seçim kısıtına bağlı, mutlak kazanan yok: saf EN kalitesi Kokoro. CPU klonlama PocketTTS. Mikrodenetleyici sanoTTS. Dil kapsamı + duygulu Fransızca sesler + sıfır altyapı Piper.

ElevenLabs, faturalı Goliath

Bir de ElevenLabs var. 70+ dil, audio tag'li ve dramatik diyaloglu etkileyici Eleven v3, kör testlerde top kalite. TTS'in Jev'i gibi bir şey: herkes görmüştür, herkes karşılaştırmıştır.

2026 fiyatlandırması, yuvarlak rakamlarla:

API Modeli Fiyat / 1000 karakter
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 karakter yaklaşık 1 dakika ses yapıyor. Yani dakika başına $0.05 ila $0.10. 99 dolarlık Pro plan yaklaşık 990k karakter içeriyor, aşım aynı tarifeden vuruyor. Seslendirmeli günde bir video çıkaran YouTube kanalı için (benim valorant-short-maker'ım tam bunu yapıyor ama ücretsiz Piper ile), günlük her seslendirme dakikası için ayda $1.50 ila $3 hesapla. Günde on dakika: ayda $15 ila $30. Ödenir ama sayılır.

Ne zaman değer: sesin ta kendisi ürün olduğunda (dublaj, sesli kitap, reklam), yarın sabah 70 dil lazım olduğunda, klonlamanın ilk seferde mükemmel olması gerektiğinde. Buna karşılık şunlar için asla değmez: bütün gece konuşan Discord botu, dahili bildirimler, dev, patlayan hacimler. ElevenLabs ile sabah 3'te hayatını anlatan Luna, ağlayan elektrik faturam demek.

Hesap basit: Piper sıfır marjinal euro, "iyi" kalite. ElevenLabs dönen sayaç, "mükemmel" kalite. Arada "iyi ve ücretsiz"in kazandığı bir dünya var, PiperHub'un bütün iddiası bu.

pipertts: natif neden Python wrapper'a 10 kat fark atıyor

Repo itsrealfortune/pipertts, 75 commit: Piper'ın TypeScript'e natif portu. README başlığı: "In-process Piper TTS for Node.js. No Python required."

Spawn maliyeti

Tarihi wrapper (PiperTTS), her sentezde alt süreçte spawn edilen python3 -m piper:

synthèse = spawn(python) + chargement du modèle + inférence + kill

Her cümle yorumlayıcı başlatma + ağırlık yükleme ödüyor. en_US-lessac-medium üzerinde ölçüldü: satır başına yaklaşık 2.2 sn.

Natif (PiperNativeTTS) ONNX oturumunu bellekte sıcak tutuyor:

load une fois -> session persistante -> inférence par phrase

Aynı şekilde ölçüldü: satır başına yaklaşık 0.22 sn. 10 kat buradan geliyor: spawn ve reload'u öldürüyoruz, mucizevi çıkarımın bununla alakası yok. ONNX çıkarımının kendisi iki tarafta da aynı.

Bonus, ilk yükleme optimize grafiği .onnx yanına cache'liyor (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), sonraki yüklemeleri yaklaşık %35 hızlandırıyor. Cache anahtarı platform, arch ve ORT sürümünü birleştiriyor, model değişirse tazeleniyor. cacheOptimizedModel: false ile kapatılabilir.

espeak köprüsü: 0.4ms'ye karşı 15ms

Fonemleme için aynı mantık. Wrapper cümle başına CLI ile espeak-ng binary'sini çağırıyor: her seferinde yaklaşık 15ms spawn. Natif N-API bridge gömüyor (src/native/espeak-bridge/, espeakbridge.c portu), Linux x64/arm64 prebuild'leriyle: çağrı başına yaklaşık 0.4ms, bayt bayt aynı fonemler. Addon yoksa CLI'ye otomatik düşüyor. CLI'yi zorlamak için PIPER_ESPEAK_BRIDGE=0.

7 phoneme_type, hepsi taşındı

Tür Motor Notlar
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

Arapça otomatik harekeleniyor (tashkeel, kapatılabilir). Katalogdaki 509 espeak sesi hızlı köprüden geçiyor, egzotikler özel motoruyla. Suite'te 71 test, tıkandığı yerde kısıt belgeli (Japonca vurgu, UniDic veri vermiyor, kabul edildi, gizlenen bir şey yok).

Formatlar ve streaming

Varsayılan wav, raw PCM, mp3, ogg, opus: varsa ffmpeg, yoksa saf JS fallback'ler (mp3 için lamejs, opus için opusscript + Ogg muxer, kaynakla 0.993 korelasyon). Ve özellikle synthesizeChunks(): cümleler arası sentenceSilence ile cümle başına veren async generator. Luna için bu, son sentezlenirken sesi oynatmaya başlamak demek, özel sunucusuz TTS streaming.

PiperHub kataloğu entegre tam kullanım (hub: + yazar reposu dosyaları sunmuyorsa otomatik ayna fallback):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // Discord için direkt
  lengthScale: 0.95,     // biraz daha hızlı
});

Resmi manifestte yok topluluk sesleri (GLaDOS ve oyun karakterleri), PiperHub'un var olma sebebi tam bu, pipertts de bunu iyi biliyor: hub:<slug>, piperhub sayfa URL'si ya da çıplak slug, hepsi çalışıyor. usable: false işaretli sesler açık hatayla reddediliyor.

Nelerden vazgeçildi

Eksik her özellik bir başkasını finanse ediyor. Dürüst liste:

Piper ElevenLabs yanında robotik geliyor. 22050 Hz'de VITS medium temiz ama büyük etkileyici modele kıyasla düz. Multi-duygu kısmen telafi ediyor (düz 1 stilden iyi 7 stil), ama ElevenLabs v3 sesli kitap üstte kalıyor. Onlarca MB'nin bedeli bu.

espeak yanlış okuyor. Özel isimler, İngilizce alıntılar, kısaltmalar: kurallı fonemlemenin kör noktaları var. Fransızca "Spoiler"ı espeak elinden geldiğince okuyor. Nöral G2P'li büyük modeller daha az çuvallıyor. Prod'da iş terimleri için istisna sözlüğü gerek.

Sıfır natif klonlama. Piper sabit seslerle okuyor. SENİN sesini istiyorsan istikamet PocketTTS ya da ElevenLabs. Multi-speaker varyasyon veriyor, tınıını asla.

Japonca sözlüksel vurgusuz kalıyor. UniDic vurgu verisi vermiyor, segmentasyon tam ama melodi yaklaşık. README'de belgeli, halının altına süpürülen bir şey yok.

Tayca ilk çağrıda sürünüyor. TLTK değiştirilmemiş Pyodide/WASM altında çalışıyor: ilk çağrı saniyeler sürüyor (yükleme), sonrakiler hızlı. Referansla 7/7 aynı ama ısınma öngör.

Veriler aç. Düzgün high ses hizalanmış temiz ses saatleri istiyor. Fortune dataseti saatlerce kayıt + temizlik demek. Sihir yok: çöp girer, robot çıkar.

Katalogda ölü sesler var. 25 model usable: false işaretli. Kabul edildi ve filtrelendi ama ses crowdsourcing'inin terk crowdsourcing'i de olduğunu hatırlatıyor.

Nihai sonuç

Bir yanda ElevenLabs sayacı, öbür yanda Raspberry Pi:

  • PiperHub: görüntülenen 536 model, 71 dil, 3755 speaker, bağlı 130 dataset, dataset -> training -> usage rehberleri
  • Piper: uçtan uca VITS, espeak, ONNX CPU, LJSpeech, low/medium/high, standart 22050 Hz
  • Fortune multi: 7 FR + 7 EN duygu, her biri 1 ONNX, geçiş için speakerId, "Fortune's Café" lisansı
  • Kokoro 82M: premium EN kalite, Apache 2.0, 54 ses, neredeyse gerçek zamanlı CPU
  • PocketTTS: CPU klonlama, yaklaşık 100M sonra 2-18M, WER %0.92
  • sanoTTS: 0.29M, 2 MB, mikrodenetleyici, boyutu takip eden kalite
  • ElevenLabs: $0.05-0.10/1000 karakter, 70+ dil, top kalite, dönen sayaç
  • natif pipertts: wrapper ~2.2 sn/satıra karşı ~0.22 sn/satır, espeak köprüsü 0.4ms, 7 phoneme_type, wav/raw/mp3/ogg/opus, cümle başına streaming

Luna her gün bununla konuşuyor. 0 euro TTS, beyinle yönetilen duygular, Discord için direkt OGG. ElevenLabs karşısında duyuluyor, tabii. Ama sabah 3'te hayatını anlatmaya karar verdiğinde faturam mışıl mışıl uyuyor.

Felsefe

p2r3 demişti: "J'aime juste l'idée que cette toute petite puce à 1 balles puisse faire tourner quelque chose d'aussi avancé que Minecraft."

Burada aynı kafa, sese uygulanmış: birkaç düzine MB ONNX'un Raspberry Pi'yi bulut olmadan, hesap olmadan, sayaç olmadan 71 dilde konuşturması fikrini seviyorum. Hiçbir dublaj stüdyosunun yerini tutmadığı açık. Tutulduğu yer, yerel, ücretsiz, gece seni ücretlendirmeden konuşan düzgün ses istediğin gıcık durum.

Tradeoff'lar, beyaz üstüne siyah yazılmış:

  • premium kalite -> küçük VITS: biraz robotik ama yerel ve ücretsiz
  • duygu başına bir model -> speakerId: RAM'de bir fiyatına 7 ses
  • Python spawn -> kalıcı oturum: 10 kat hızlı, kurulacak Python yok
  • vahşi katalog -> bağlı kaynak: her ses nereden geldiğini ve hâlâ çalışıp çalışmadığını biliyor
  • faturalı API -> sıfır marjinal euro: ElevenLabs'tan çirkin ama fatura mışıl uyuyor

Akılda kalacak 3 şey:

  1. Piper CPU'nun TTS'i - VITS + espeak + ONNX, 71 dil, Pi'de çalışan onlarca MB'lık sesler. ElevenLabs'tan çirkin, sonsuz ucuz.
  2. Fortune multi biri fiyatına 7 duygu - tek ONNX, geçiş için speakerId, FR + EN. 7 model yerine speaker embedding.
  3. Natif spawn'ı öldürüyor - pipertts ONNX oturumunu sıcak tutuyor: 2.2 sn/satıra karşı 0.22 sn/satır, 15ms CLI'ye karşı 0.4ms espeak köprüsü. Aynı çıkarım, 10 kat az bekleme.

Proje ilgini çekerse katalog piperhub.org adresinde, site kodu github.com/fox3000foxy/piper-bank adresinde, natif modül github.com/itsrealfortune/pipertts adresinde. Luna'nın sabah 3'te hayatını anlatmasını dinlemek istersen Discord'da görüşürüz xD

NEW

PiperHub : 536 voci TTS open source, 7 emozioni in un modello, zero GPU, zero fattura

VITS, espeak, ONNX, LJSpeech : come Piper fa parlare un Raspberry Pi. Fortune multi in 7 emozioni, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs -- e pipertts nativo 10x più veloce del wrapper Python.

Introduction

Far parlare un bot Discord costa caro. Non il testo : un LLM locale genera frasi per pochi centesimi di GPU. La voce, invece, si paga a ogni frase. Le API di sintesi fatturano a carattere, e quando il bot parla tutta la notte, la fattura segue.

È il problema che ho avuto con Luna Protocol. Il mio bot Discord genera i suoi messaggi con un LLM locale, poi li fa leggere ad alta voce da una voce Piper, li converte in OGG e li invia come messaggi vocali. Il tutto sul mio server, a 0 euro per frase, anche alle 3 di notte quando Luna decide di raccontare la sua vita da sola.

Restare a 0 euro vuol dire trovare voci gratuite e ospitarle da te. Ed è qui che si è complicato tutto : modelli sparsi su decine di repository Hugging Face, licenze poco chiare, nessun modo di sapere da dove viene una certa voce né se funziona ancora. Così ho costruito PiperHub, un catalogo open source di voci per Piper TTS.

I numeri di oggi : 536 modelli mostrati, 71 lingue rappresentate, 3755 speaker elencati. Ogni modello è collegato al suo dataset di origine quando lo si conosce -- la provenienza fa parte del suono, come dice la homepage.

Resta una domanda che mi fanno spesso : come se la cava Piper contro Kokoro, PocketTTS, sanoTTS ed ElevenLabs ? E perché il modulo pipertts di Fortune è 10x più veloce del wrapper Python ufficiale ? Questo articolo apre i motori, con il codice a supporto.

PiperHub, le catalogue

Prima di aprire i motori, vediamo il problema che PiperHub risolve.

D'où viennent les voix

Il codice sta su github.com/fox3000foxy/piper-bank. Astro, sito statico, zero server : l'inglese alla radice, 15 altre lingue sotto /<code>/ (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). Stesso schema del mio blog, in pratica.

La fonte di verità, nessuno la modifica a mano. È una pipeline :

npm install
npm run data    # rigenera src/data/*.json (python3 scripts/generate_data.py)
npx astro dev   # dev locale
npm run build   # build statica in dist/

In concreto : un repo crawler mantiene models_mapping.csv e datasets_mapping.csv, le voci mirror vivono su Hugging Face, i sample sono file audio. npm run data tira tutto e risputa fuori src/data/*.json. Il sito si limita a leggere JSON statico.

Nei dati grezzi del momento (555 voci, il sito ne mostra 536 -- il crawl è sempre un po' avanti rispetto alla visualizzazione) :

Qualité Modèles
medium 437
high 67
low 50
Sample rate Modèles
22050 Hz 486
16000 Hz 47

486 voci a 22050 Hz, lo standard Piper. 47 a 16000 Hz, le vocine veloci per l'embarcato. Il phonemizer è espeak in 509 casi -- ci torniamo, è il cuore del sistema.

La provenance, ou pourquoi chaque modèle a un dataset

La cosa che rende PiperHub utile invece di essere solo una lista è il legame modello <-> dataset. Esempio preso dalla home : la voce Astra - Valorant Piper TTS è collegata al dataset Astra - LJSpeech Dataset. Apri le due pagine, verifichi i repo e le licenze da entrambi i lati.

130 dataset referenziati. E il catalogo è onesto sullo stato delle truppe : 25 modelli sono flaggati usable: false (file rotti, o formato piperPlus che richiede un runtime fork). Il modulo pipertts rifiuta queste voci con un errore esplicito invece di crashare in modo strano più tardi. 48 modelli sono multi-speaker -- compresi i due che ci interesseranno.

Lato guide, il sito fa il suo lavoro in tre passi : Build a dataset, Train a voice, Use a voice, più una pagina Piper Plus. Dataset in formato LJSpeech in ingresso, ONNX in uscita. Facile a dirsi, lungo a farsi -- da qui la sezione successiva.

Piper sous le capot : VITS, espeak, ONNX

Une phrase, trois étapes

Quando chiedi a Piper di dire "buongiorno", succede questo :

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

Zero vocoder separato. Questo è VITS : un modello end-to-end che va dal testo (fonemizzato) alla waveform direttamente. Laddove le vecchie pipeline facevano testo -> spettrogramma (Tacotron) poi spettrogramma -> audio (HiFi-GAN, due modelli, due training), VITS impara tutto in una volta con un VAE condizionale, flow normalizzanti e un discriminatore stile GAN sul decoder. Un solo training, un solo modello, un solo export ONNX.

Il decoder sputa fuori 22050 Hz direttamente. A questo sample rate, un secondo di parlato = 22050 numeri. Il modello è piccolo (qualche decina di MB in qualità medium) e l'inferenza è pura CPU : moltiplicazioni di matrici su un grafo ONNX ottimizzato. Da qui il Raspberry Pi come target ufficiale -- Piper è nato in Rhasspy, un assistente vocale open source fatto per girare su Pi.

espeak-ng, le phonémiseur qui fait tout le sale boulot

Prima di VITS, bisogna trasformare il testo in fonemi. 509 modelli su 555 usano espeak :

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng conosce la pronuncia di decine di lingue. Il modello VITS non impara mai l'ortografia francese con i suoi eau che si pronunciano o -- impara dai fonemi verso il suono. Questo semplifica enormemente il training e spiega perché Piper copre 71 lingue : per una nuova lingua, colleghi la voce espeak corrispondente e alleni.

Quelle che fanno a meno di espeak hanno il loro fonemizzatore, ed è qui che diventa esotico : hebrew (Nakdimon ONNX + 494 righe di regole IPA), pinyin (BERT g2pW completo con disambiguazione dei polifoni -- lo stesso carattere cinese si pronuncia diversamente a seconda della parola), thai (vero TLTK non modificato), japanese (segmentazione lindera + UniDic). Il modulo pipertts li include tutti e sette, verificati identici al riferimento Python. Ci torniamo.

Le dataset : du LJSpeech, toujours du LJSpeech

Il formato standard di training è LJSpeech : una cartella wavs/ piena di clip, più un metadata.csv :

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, una clip = una frase, qualche secondo. Il dataset originale sono 13.000 frasi di inglese lette da un'unica speaker, pubblico dominio. Per allenare una voce italiana, registri la stessa struttura in italiano : testo + audio allineati, poche ore bastano per un medium decente, di più per un high.

Le tre qualità del catalogo corrispondono grosso modo alla dimensione del modello + la durata del training. Low : piccolo, veloce, robotico. Medium : il punto ideale, con 437 modelli. High : più grosso, più lento, più naturale -- 67 modelli, compresi i Fortune.

Les trois boutons magiques

Ogni voce Piper espone tre parametri di inferenza, con default quasi universali :

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667) : variabilità della voce. Più alto = più espressivo, più basso = più stabile. L'output è stocastico : due sintesi della stessa frase danno due audio leggermente diversi.
  • noise_w (0.8) : variabilità del timing, il ritmo.
  • length_scale (1.0) : velocità. Sopra 1 rallenta, sotto accelera. Il modo più semplice per far parlare Luna più in fretta quando si arrabbia.

Ricordati noise_scale, servirà per le emozioni.

Fortune multi : 7 émotions, 1 seul ONNX

Ok, passiamo alla star locale.

Il catalogo elenca tre modelli Fortune allenati da ItsRealFortune (ciao) :

Modèle Repo Voix
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

Il multi FR è questo :

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

Sette declinazioni emotive in un solo file ONNX. Il multi EN uguale, con un refuso storico nei nomi : suprised invece di surprised. Sta nei pesi, lo teniamo, fa parte del folclore.

Comment ça marche, un speakerId ?

VITS multi-speaker è lo stesso modello con un input in più : un embedding di speaker. Al training, ogni clip è taggata con il suo stile, il modello impara una tabella di embedding (una per stile) insieme al resto. All'inferenza, scegli la riga della tabella :

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = normale ... 5 = sorpresa
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

Stessa frase, sette letture. speakerId: 5 (sorpresa) con un noise_scale un po' spinto, e hai un'esclamazione credibile. speakerId: 4 (triste), length_scale: 1.1 per rallentare, e hai un annuncio funebre. È così che Luna modula la sua voce : il cervello sceglie l'emozione (valenza/arousal, cfr. il mio articolo sui centroidi), il TTS sceglie lo speakerId.

Il dataset dietro si chiama sobriamente Fortune French - Dataset, con una licenza chiamata "Fortune's Café". Sì, la licenza si chiama come un bar. No, resta così.

Pourquoi c'est malin économiquement

Sette emozioni = un solo download, un solo caricamento in RAM, una sola sessione ONNX. L'alternativa (sette modelli separati) moltiplicherebbe lo storage e costringerebbe a ricaricare il grafo a ogni cambio di emozione -- ingiocabile per un bot che cambia tono in piena frase. Qui speakerId è solo un indice in un tensore. Costo marginale : zero.

Les concurrents : Kokoro, PocketTTS, sanoTTS

Piper condivide la CPU con tre sfidanti seri, tre filosofie diverse.

Kokoro 82M, le roi qualité/prix

Kokoro, di hexgrad. 82 milioni di parametri, architettura StyleTTS 2 modificata con decoder ISTFTNet, senza fase di diffusione. È l'assenza di diffusione che fa tutto : nessun loop iterativo di denoising, quindi inferenza ultra rapida.

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

Allenato su meno di 100h di audio per circa 1000 dollari, e gli ascoltatori ciechi lo classificano al livello delle voci premium di Google e Amazon. È per questo che è esploso : qualità premium, gratis, locale, permissivo.

Contro Piper : Kokoro vince sulla naturalezza pura (StyleTTS 2 > VITS di piccola taglia) e sulla licenza (Apache 2.0 contro motore in GPL-3.0 per Piper). Piper vince sulla copertura (71 lingue contro 8, 3755 speaker contro 54 voci) e sull'ecosistema di voci della community. Per narrazione in inglese, Kokoro è difficile da battere. Per italiano con 7 emozioni, turco o swahili, Piper è spesso l'unico in gara.

PocketTTS (Kyutai), le clonage sur CPU

Kyutai gioca un'altra partita : piccolo modello con clonazione vocale, che gira su CPU.

Modèle Params WER UTMOS SIM Vitesse CPU (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER sotto l'1% (pronuncia giusto), UTMOS 4.36 (qualità percepita eccellente), SIM 0.923 (il clone assomiglia davvero al target). E le versioni small scendono a 1.9M parametri con clonazione. 1.9M. Il mio router Sabaki fa quasi questa dimensione in JSON.

Contro Piper : PocketTTS vince sulla clonazione (Piper legge con voci fisse) e sulla compattezza del flagship. Piper vince sulle lingue (lo small per ora è solo inglese) e sulla maturità dell'ecosistema (536 voci pronte contro "allena la tua").

sanoTTS, le modèle qui tient sur une puce à 3 dollars

sanoTTS (sano = "piccolo" in nepalese), è il Bareiron del TTS : uno stack neurale completo che va dai fonemi al PCM 22.05 kHz su microcontrollore.

Système Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M parametri. 2 MB la voce. pip install sanotts, sanotts say "Hello" --voice amy, e parla. Anche nel browser.

Ma guarda i punteggi : SCOREQ 2.29 contro 3.94 per TinyTTS, UTMOS 2.45. Parla, sì. Non canta. È il tradeoff ultimo : a 0.29M stai ovunque ma la qualità segue la taglia. Piper medium sono 100x più pesi per una voce di un'altra lega.

Le tableau qui résume

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

La scelta dipende dal tuo vincolo, non c'è un vincitore assoluto : qualità EN pura -> Kokoro. Clonazione CPU -> PocketTTS. Microcontrollore -> sanoTTS. Copertura di lingue + voci italiane emozionali + zero infra -> Piper.

ElevenLabs, le Goliath facturé

E poi c'è ElevenLabs. 70+ lingue, Eleven v3 espressivo con audio tag e dialoghi drammatici, qualità top nei blind test. Il Jev del TTS, in un certo senso : tutti l'hanno visto passare, tutti ci hanno fatto benchmark contro.

Il pricing 2026, in cifre tonde :

Modèle API Prix / 1000 caractères
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 caratteri fanno circa 1 minuto di audio. Quindi da $0.05 a $0.10 al minuto. Il piano Pro a $99 include circa 990k caratteri, l'overage picchia alla stessa tariffa. Per un canale YouTube che pubblica un video al giorno con voiceover (il mio valorant-short-maker fa esattamente questo, ma con Piper gratis), conta da $1.50 a $3 al mese per ogni minuto quotidiano di voiceover. Dieci minuti al giorno : da $15 a $30/mese. Si paga, ma si conta.

Quando ne vale la pena : quando la voce è il prodotto (doppiaggio, audiolibro, pub), quando ti servono 70 lingue per domani mattina, quando la clonazione deve essere perfetta al primo colpo. Invece non ne vale mai la pena per : bot Discord che parla tutta la notte, notifiche interne, dev, volumi che esplodono. Luna che racconta la sua vita alle 3 di notte con ElevenLabs, è la mia bolletta che piange.

Il calcolo è semplice : Piper = 0 euro marginali, qualità "buona". ElevenLabs = contatore che gira, qualità "eccellente". In mezzo c'è un mondo dove "buona e gratis" vince -- è tutta la scommessa di PiperHub.

pipertts : pourquoi le natif met 10x au wrapper Python

Il repo itsrealfortune/pipertts, 75 commit : un port nativo di Piper in TypeScript. Titolo del README : "In-process Piper TTS for Node.js. No Python required."

Le coût du spawn

Il wrapper storico (PiperTTS) è python3 -m piper spawnato in un sottoprocesso a ogni sintesi :

synthèse = spawn(python) + chargement du modèle + inférence + kill

Ogni frase paga l'avvio dell'interprete + il caricamento dei pesi. Misurato su en_US-lessac-medium : ~2.2s per riga.

Il nativo (PiperNativeTTS) tiene la sessione ONNX calda in memoria :

load une fois -> session persistante -> inférence par phrase

Misurato uguale : ~0.22s per riga. Il 10x viene da qui : uccidi spawn e reload, l'inferenza miracolosa non c'entra niente. L'inferenza ONNX in sé è la stessa da entrambi i lati.

In bonus, il primo load mette in cache il grafo ottimizzato accanto al .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), il che rende i caricamenti successivi circa il 35% più veloci. La chiave della cache combina platform, arch e versione di ORT, e si aggiorna se il modello cambia. Disattivabile con cacheOptimizedModel: false.

Le pont espeak : 0.4ms contre 15ms

Stessa logica per la fonemizzazione. Il wrapper chiama il binario espeak-ng in CLI per frase : circa 15ms di spawn ogni volta. Il nativo include un bridge N-API (src/native/espeak-bridge/, port di espeakbridge.c), con prebuild per Linux x64/arm64 : ~0.4ms per chiamata, fonemi byte-identici. Senza l'addon, fallback automatico sulla CLI. PIPER_ESPEAK_BRIDGE=0 per forzare la CLI.

Les 7 phoneme_type, tous portés

Type Moteur Notes
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

E l'arabo è diacritizzato automaticamente (tashkeel, disattivabile). 509 voci espeak del catalogo passano dal bridge veloce, le esotiche dal loro motore dedicato. 71 test nella suite, vincolo documentato dove si inceppa (l'accento giapponese, UniDic non dà nessun dato -- dichiarato, niente nascosto).

Les formats et le streaming

wav di default, raw PCM, mp3, ogg, opus : ffmpeg quando c'è, altrimenti fallback puro JS (lamejs per mp3, opusscript + muxer Ogg per opus, correlazione 0.993 con la sorgente). E soprattutto synthesizeChunks() : un generatore async che yielda per frase, con sentenceSilence tra le frasi. Per Luna vuol dire iniziare a riprodurre l'audio mentre la fine si sintetizza -- streaming di TTS senza server dedicato.

Uso completo, con il catalogo PiperHub integrato (hub: + fallback mirror automatico se il repo autore non serve più i file) :

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // diretto per Discord
  lengthScale: 0.95,     // un filo più veloce
});

Voci community (GLaDOS e personaggi dei giochi) assenti dal manifest ufficiale -- ecco esattamente perché esiste PiperHub, e pipertts lo sa bene : hub:<slug>, URL di pagina piperhub, o slug nudo, tutto funziona. Le voci flaggate usable: false sono rifiutate con un errore chiaro.

Ce qui a été sacrifié

Ogni feature assente ne finanzia un'altra. La lista onesta :

Piper suona robotico accanto a ElevenLabs. VITS medium su 22050 Hz è pulito ma piatto rispetto a un grosso modello espressivo. Il multi-emozione compensa in parte (7 stili > 1 stile piatto), ma un audiolibro ElevenLabs v3 resta sopra. È il prezzo delle decine di MB.

espeak sbaglia. Nomi propri, anglicismi, abbreviazioni : la fonemizzazione a regole ha i suoi punti ciechi. "Spoiler" in italiano, espeak lo legge come può. I grossi modelli con G2P neurale sbagliano meno. In prod ti serve un dizionario di eccezioni per i termini di settore.

Zero clonazione nativa. Piper legge con voci fisse. Se vuoi LA TUA voce, vai su PocketTTS o ElevenLabs. Il multi-speaker dà variazioni, mai il tuo timbro.

Il giapponese resta senza accento lessicale. UniDic non dà nessun dato di accento, quindi la segmentazione è esatta ma la melodia è approssimativa. Documentato nel README, niente sotto il tappeto.

Il thai arranca alla prima chiamata. TLTK gira sotto Pyodide/WASM non modificato : la prima chiamata prende secondi (caricamento), le successive sono veloci. 7/7 identici al riferimento, ma prevedi un warm-up.

I dati hanno fame. Una voce high decente richiede ore di audio pulito allineato. Il dataset Fortune sono ore di registrazione + pulizia. Zero magia : garbage in, robot out.

Il catalogo ha voci morte. 25 modelli flaggati usable: false. È dichiarato e filtrato, ma ricorda che il crowdsourcing di voci è anche crowdsourcing di abbandono.

Le résultat final

Da un lato un contatore ElevenLabs, dall'altro un Raspberry Pi :

  • PiperHub : 536 modelli mostrati, 71 lingue, 3755 speaker, 130 dataset collegati, guide dataset -> training -> usage
  • Piper : VITS end-to-end, espeak, ONNX CPU, LJSpeech, low/medium/high, 22050 Hz standard
  • Fortune multi : 7 emozioni FR + 7 EN, 1 ONNX ciascuno, speakerId per switchare, licenza "Fortune's Café"
  • Kokoro 82M : qualità premium EN, Apache 2.0, 54 voci, quasi tempo reale CPU
  • PocketTTS : clonazione CPU, ~100M poi 2-18M, WER 0.92%
  • sanoTTS : 0.29M, 2 MB, microcontrollore, qualità che segue la taglia
  • ElevenLabs : $0.05-0.10/1000 caratteri, 70+ lingue, top qualità, contatore che gira
  • pipertts nativo : ~0.22s/riga contro ~2.2s wrapper, bridge espeak 0.4ms, 7 phoneme_type, wav/raw/mp3/ogg/opus, streaming per frase

Luna parla con questo ogni giorno. 0 euro di TTS, emozioni pilotate dal cervello, OGG diretto per Discord. Contro ElevenLabs si sente, ovvio. Ma alle 3 di notte quando decide di raccontare la sua vita, la mia fattura dorme tranquilla.

La philo

p2r3 diceva : "J'aime juste l'idée que cette toute petite puce à 1 balles puisse faire tourner quelque chose d'aussi avancé que Minecraft."

Stesso trip qui, applicato alla voce : mi piace l'idea che poche decine di MB di ONNX facciano parlare un Raspberry Pi in 71 lingue, senza cloud, senza account, senza contatore. Ovvio che non sostituisce nessuno studio di doppiaggio. Sostituisce il caso noioso dove vuoi una voce decente, locale, gratis, che parla la notte senza fatturarti.

I tradeoff, scritti nero su bianco :

  • qualità premium -> VITS piccolo : un po' robotico, ma locale e gratis
  • un modello per emozione -> speakerId : 7 voci al prezzo di una in RAM
  • spawn Python -> sessione persistente : 10x più veloce, zero Python da installare
  • catalogo selvaggio -> provenienza collegata : ogni voce sa da dove viene e se funziona ancora
  • API fatturata -> 0 euro marginali : meno bello di ElevenLabs, ma la fattura dorme tranquilla

Le 3 cose da ricordare :

  1. Piper = il TTS della CPU -- VITS + espeak + ONNX, 71 lingue, voci da decine di MB che girano su Pi. Meno bello di ElevenLabs, infinitamente meno caro.
  2. Fortune multi = 7 emozioni al prezzo di una -- un solo ONNX, speakerId per switchare, FR + EN. L'embedding di speaker invece di 7 modelli.
  3. Il nativo uccide lo spawn -- pipertts tiene la sessione ONNX calda : 0.22s/riga contro 2.2s, bridge espeak 0.4ms contro 15ms CLI. Stessa inferenza, 10x meno attesa.

Se il progetto ti interessa, il catalogo è piperhub.org, il codice del sito github.com/fox3000foxy/piper-bank, il modulo nativo github.com/itsrealfortune/pipertts. E se vuoi sentire Luna raccontare la sua vita alle 3 del mattino, è su Discord xD

NEW

PiperHub: 536 Open-Source-TTS-Stimmen, 7 Emotionen in einem Modell, null GPU, null Rechnung

VITS, espeak, ONNX, LJSpeech: wie Piper einen Raspberry Pi zum Sprechen bringt. Fortune multi in 7 Emotionen, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs, und natives pipertts 10x schneller als der Python-Wrapper.

Introduction

Einen Discord-Bot zum Sprechen zu bringen kostet Geld. Nicht der Text: Ein lokales LLM erzeugt Sätze für ein paar Cent GPU. Die Stimme dagegen zahlst du bei jedem Satz. Synthese-APIs rechnen pro Zeichen ab, und wenn der Bot die ganze Nacht redet, folgt die Rechnung.

Genau das Problem hatte ich mit Luna Protocol. Mein Discord-Bot erzeugt seine Nachrichten mit einem lokalen LLM, lässt sie dann von einer Piper-Stimme vorlesen, konvertiert nach OGG und schickt sie als Sprachnachrichten. Alles auf meinem eigenen Server, für 0 Euro pro Satz, selbst um 3 Uhr morgens, wenn Luna beschließt, ganz allein ihr Leben zu erzählen.

Bei 0 Euro zu bleiben heißt, gratis Stimmen zu finden und selbst zu hosten. Und da wurde es kompliziert: Modelle verstreut über dutzende Hugging-Face-Repos, unklare Lizenzen, keine Möglichkeit zu wissen, woher eine Stimme kommt oder ob sie noch läuft. Also habe ich PiperHub gebaut, einen Open-Source-Katalog für Piper-TTS-Stimmen.

Die Zahlen von heute: 536 Modelle gelistet, 71 Sprachen vertreten, 3755 Speaker gelistet. Jedes Modell ist mit seinem Quell-Dataset verlinkt, soweit ich es kenne. Herkunft gehört zum Klang, wie die Startseite sagt.

Eine Frage bleibt, die man mir oft stellt: Was taugt Piper gegen Kokoro, PocketTTS, sanoTTS und ElevenLabs? Und warum ist das pipertts-Modul von Fortune 10x schneller als der offizielle Python-Wrapper? Dieser Artikel öffnet die Motoren, mit Code als Beleg.

PiperHub, der Katalog

Bevor wir die Motoren öffnen, schauen wir uns das Problem an, das PiperHub löst.

Woher die Stimmen kommen

Der Code liegt auf github.com/fox3000foxy/piper-bank. Astro, statische Seite, null Server: Englisch im Root, 15 weitere Locales unter /<code>/ (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). Eigentlich dasselbe Muster wie mein Blog.

Niemand pflegt die Source of Truth von Hand. Es ist eine Pipeline:

npm install
npm run data    # generiert src/data/*.json neu (python3 scripts/generate_data.py)
npx astro dev   # lokales Dev
npm run build   # statischer Build nach dist/

Konkret: Ein Crawler-Repo pflegt models_mapping.csv und datasets_mapping.csv, gespiegelte Stimmen leben auf Hugging Face, Samples sind Audiodateien. npm run data zieht das alles und spuckt src/data/*.json aus. Die Seite liest nur statisches JSON.

In den aktuellen Rohdaten (555 Einträge, die Seite zeigt 536, der Crawl ist der Anzeige immer etwas voraus):

Qualität Modelle
medium 437
high 67
low 50
Sample rate Modelle
22050 Hz 486
16000 Hz 47

486 Stimmen in 22050 Hz, der Piper-Standard. 47 in 16000 Hz, die kleinen schnellen Stimmen für Embedded. Der Phonemizer ist in 509 Fällen espeak. Dazu komme ich zurück, das ist das Herz des Systems.

Die Herkunft, oder warum jedes Modell ein Dataset hat

Was PiperHub nützlich statt nur eine Liste macht, ist die Modell <-> Dataset-Verlinkung. Beispiel von der Home: Die Stimme Astra - Valorant Piper TTS ist mit dem Dataset Astra - LJSpeech Dataset verlinkt. Du öffnest beide Seiten, prüfst Repos und Lizenzen auf jeder Seite.

130 Datasets referenziert. Und der Katalog ist ehrlich über den Zustand der Truppen: 25 Modelle sind als usable: false geflaggt (kaputte Dateien, oder piperPlus-Format, das eine Fork-Runtime braucht). Das pipertts-Modul lehnt diese Stimmen mit einem klaren Fehler ab, statt später komisch zu crashen. 48 Modelle sind Multi-Speaker, darunter die zwei, die uns interessieren werden.

Bei Guides macht die Seite den Job in drei Schritten: Build a dataset, Train a voice, Use a voice, plus eine Piper-Plus-Seite. Dataset im LJSpeech-Format als Input, ONNX als Output. Leicht gesagt, lang gemacht, daher der nächste Abschnitt.

Piper unter der Haube: VITS, espeak, ONNX

Ein Satz, drei Schritte

Wenn du Piper bittest, "bonjour" zu sagen, passiert das:

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

Null separater Vocoder. Das ist VITS: Ein End-to-End-Modell, das direkt von (phonemisiertem) Text zum Waveform geht. Wo alte Pipelines Text -> Spektrogramm (Tacotron) und dann Spektrogramm -> Audio (HiFi-GAN, zwei Modelle, zwei Trainings) machten, lernt VITS alles auf einmal mit Conditional VAE, Normalizing Flows und einem GAN-artigen Diskriminator auf dem Decoder. Ein Training, ein Modell, ein ONNX-Export.

Der Decoder spuckt direkt 22050 Hz aus. Bei dieser Sample Rate ist eine Sekunde Sprache gleich 22050 Zahlen. Das Modell ist klein (ein paar dutzend MB in Medium-Qualität) und die Inferenz ist pure CPU: Matrixmultiplikationen auf einem optimierten ONNX-Graphen. Daher der Raspberry Pi als offizielles Ziel. Piper wurde in Rhasspy geboren, einem Open-Source-Sprachassistenten für den Pi.

espeak-ng, der Phonemizer, der die ganze Drecksarbeit macht

Vor VITS musst du Text in Phoneme verwandeln. 509 von 555 Modellen nutzen espeak:

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng kennt die Aussprache dutzender Sprachen. Das VITS-Modell lernt nie französische Rechtschreibung mit ihrem eau, das wie o klingt. Es lernt Phoneme zu Klang. Das vereinfacht das Training enorm und erklärt, warum Piper 71 Sprachen abdeckt: Für eine neue Sprache hängst du die passende espeak-Stimme dran und trainierst.

Die, die ohne espeak auskommen, haben ihren eigenen Phonemizer, und da wird es exotisch: hebrew (Nakdimon ONNX + 494 Zeilen IPA-Regeln), pinyin (vollständiges BERT g2pW mit Polyphon-Disambiguierung, dasselbe chinesische Zeichen klingt je nach Wort anders), thai (echtes unverändertes TLTK), japanese (Lindera-Segmentierung + UniDic). Das pipertts-Modul bringt alle sieben mit, verifiziert identisch zur Python-Referenz. Dazu komme ich zurück.

Das Dataset: LJSpeech, immer LJSpeech

Das Standard-Trainingsformat ist LJSpeech: Ein wavs/-Ordner voller Clips, plus metadata.csv:

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, ein Clip ist ein Satz, ein paar Sekunden. Das Original-Dataset sind 13.000 englische Sätze, gelesen von einer einzigen Sprecherin, Public Domain. Um eine französische Stimme zu trainieren, nimmst du dieselbe Struktur auf Französisch auf: alignierter Text + Audio, ein paar Stunden reichen für ordentliches Medium, mehr für High.

Die drei Katalog-Qualitäten entsprechen grob Modellgröße + Trainingsdauer. Low: klein, schnell, roboterhaft. Medium: der Sweet Spot, mit 437 Modellen. High: größer, langsamer, natürlicher, 67 Modelle, darunter die Fortunes.

Die drei magischen Knöpfe

Jede Piper-Stimme hat drei Inferenz-Parameter, mit fast universellen Defaults:

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667): Variabilität der Stimme. Höher ist expressiver, niedriger ist stabiler. Die Ausgabe ist stochastisch: Zwei Synthesen desselben Satzes geben zwei leicht unterschiedliche Audios.
  • noise_w (0.8): Timing-Variabilität, der Rhythmus.
  • length_scale (1.0): Geschwindigkeit. Über 1 wird langsamer, darunter schneller. Der einfachste Weg, Luna schneller sprechen zu lassen, wenn sie sich aufregt.

Merk dir noise_scale, es wird für die Emotionen wichtig.

Fortune multi: 7 Emotionen, 1 einziges ONNX

Gut, kommen wir zum lokalen Star.

Der Katalog listet drei Fortune-Modelle, trainiert von ItsRealFortune (hallo):

Modell Repo Stimmen
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

Das Multi FR ist das:

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

Sieben emotionale Varianten in einer einzigen ONNX-Datei. Das Multi EN genauso, mit einem historischen Tippfehler in den Namen: suprised statt surprised. Er steckt in den Gewichten, ich behalte ihn, das gehört zur Folklore.

Wie funktioniert eine speakerId?

Multi-Speaker-VITS ist dasselbe Modell mit einem Extra-Input: einem Speaker-Embedding. Beim Training wird jeder Clip mit seinem Stil getaggt, das Modell lernt parallel eine Embedding-Tabelle (eine pro Stil). Bei der Inferenz wählst du die Zeile der Tabelle:

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = normal ... 5 = surprise
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

Gleicher Satz, sieben Lesungen. speakerId: 5 (surprise) mit etwas aufgedrehtem noise_scale, und du hast einen glaubwürdigen Ausruf. speakerId: 4 (triste), length_scale: 1.1 zum Verlangsamen, und du hast eine Traueransage. So moduliert Luna ihre Stimme: Das Gehirn wählt die Emotion (Valenz/Arousal, siehe mein Artikel über Centroids), das TTS wählt die speakerId.

Das Dataset dahinter heißt nüchtern Fortune French - Dataset, mit einer Lizenz namens "Fortune's Café". Ja, die Lizenz heißt wie ein Café. Nein, sie bleibt.

Warum das ökonomisch clever ist

Sieben Emotionen sind ein Download, ein Laden in den RAM, eine ONNX-Session. Die Alternative (sieben getrennte Modelle) würde den Speicher vervielfachen und bei jedem Emotionswechsel ein Graph-Reload erzwingen, unspielbar für einen Bot, der mitten im Satz den Ton wechselt. Hier ist speakerId nur ein Index in einen Tensor. Grenzkosten: null.

Die Konkurrenten: Kokoro, PocketTTS, sanoTTS

Piper teilt sich die CPU mit drei ernsthaften Herausforderern, drei verschiedenen Philosophien.

Kokoro 82M, der Preis-Leistungs-König

Kokoro, von hexgrad. 82 Millionen Parameter, modifizierte StyleTTS-2-Architektur mit ISTFTNet-Decoder, ohne Diffusions-Schritt. Das Fehlen der Diffusion macht alles aus: Keine iterative Denoising-Schleife, also ultraschnelle Inferenz.

Kokoro-82M
Params 82M
Gewichte ~300 MB (164 MB in FP16)
Lizenz Apache 2.0 (kommerziell OK)
Sprachen 8, 54 Preset-Stimmen + Blending
CPU fast Echtzeit
GPU 36 bis 96x Echtzeit
Blindtests auf Google- und Amazon-Niveau

Trainiert auf unter 100h Audio für etwa 1000 Dollar, und blinde Hörer ranken es auf Niveau der Premium-Stimmen von Google und Amazon. Darum ist es explodiert: Premium-Qualität, gratis, lokal, permissiv.

Gegen Piper: Kokoro gewinnt bei purer Natürlichkeit (StyleTTS 2 > kleines VITS) und Lizenz (Apache 2.0 gegen GPL-3.0-Engine bei Piper). Piper gewinnt bei Abdeckung (71 Sprachen gegen 8, 3755 Speaker gegen 54 Stimmen) und Community-Stimmen-Ökosystem. Für englische Narration ist Kokoro schwer zu schlagen. Für Französisch mit 7 Emotionen, Türkisch oder Swahili ist Piper oft der einzige im Rennen.

PocketTTS (Kyutai), das Klonen auf CPU

Kyutai spielt eine andere Partie: kleines Modell mit Stimmen-Klonen, das auf CPU läuft.

Modell Params WER UTMOS SIM CPU-Speed (1 Thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x Echtzeit
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, Klonen inklusive

WER unter 1% (es spricht korrekt aus), UTMOS 4.36 (exzellente wahrgenommene Qualität), SIM 0.923 (der Klon klingt wirklich wie das Ziel). Und die Small-Versionen gehen runter bis 1.9M Parameter mit Klonen. 1.9M. Mein Sabaki-Router ist fast so groß in JSON.

Gegen Piper: PocketTTS gewinnt beim Klonen (Piper liest mit festen Stimmen) und der Kompaktheit des Flaggschiffs. Piper gewinnt bei Sprachen (das Small ist vorerst English-only) und Ökosystem-Reife (536 fertige Stimmen gegen "trainier deine eigene").

sanoTTS, das Modell, das auf einen 3-Dollar-Chip passt

sanoTTS (sano heißt "klein" auf Nepali), das ist der Bareiron des TTS: Ein kompletter neuronaler Stack, der von Phonemen zu 22.05-kHz-PCM geht, auf Mikrocontroller.

System Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M Parameter. 2 MB pro Stimme. pip install sanotts, sanotts say "Hello" --voice amy, und es spricht. Im Browser auch.

Aber schau dir die Scores an: SCOREQ 2.29 gegen 3.94 für TinyTTS, UTMOS 2.45. Es spricht, ja. Es singt, nein. Das ist der ultimative Tradeoff: Mit 0.29M passt du überall rein, aber die Qualität folgt der Größe. Piper Medium hat 100x mehr Gewichte für eine Stimme in einer anderen Liga.

Die Tabelle, die alles zusammenfasst

Piper Kokoro 82M PocketTTS sanoTTS
Typische Params ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Sprachen 71 8 EN (+ Smalls EN) mehrsprachig, klein
Stimmen verf. 3755 Speaker 54 + Blending Klonen (deine) wenige Stimmen
Klonen nein nein (nativ) ja, CPU nein
Engine-Lizenz GPL-3.0 Apache 2.0 offen (Kyutai) offen
Daheim trainierbar ja (LJSpeech) ja (schwerer) ja ja
FR-Qualität gut (multi Emotion) okay (EN-first) EN-only schwach

Deine Wahl hängt von deiner Einschränkung ab, es gibt keinen absoluten Gewinner: pure EN-Qualität -> Kokoro. CPU-Klonen -> PocketTTS. Mikrocontroller -> sanoTTS. Sprachabdeckung + emotionale französische Stimmen + null Infra -> Piper.

ElevenLabs, der abrechnende Goliath

Und dann gibt es ElevenLabs. 70+ Sprachen, expressives Eleven v3 mit Audio-Tags und dramatischen Dialogen, Top-Qualität in Blindtests. Gewissermaßen der Jev des TTS: Jeder hat es vorbeiziehen sehen, jeder hat dagegen gebenchmarkt.

Das 2026er Pricing, in runden Zahlen:

API-Modell Preis / 1000 Zeichen
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 Zeichen sind etwa 1 Minute Audio. Also $0.05 bis $0.10 pro Minute. Der Pro-Plan für $99 enthält ca. 990k Zeichen, Overage schlägt mit demselben Tarif zu. Für einen YouTube-Kanal, der täglich ein vertontes Video raushaut (mein valorant-short-maker macht genau das, aber mit gratis Piper), rechne $1.50 bis $3 pro Monat für jede tägliche Voiceover-Minute. Zehn Minuten pro Tag: $15 bis $30/Monat. Das kann man zahlen, aber man zählt es.

Wann es sich lohnt: Wenn die Stimme das Produkt ist (Synchron, Hörbuch, Werbung), wenn du morgen früh 70 Sprachen brauchst, wenn das Klonen beim ersten Versuch perfekt sein muss. Dagegen lohnt es sich nie für: Discord-Bot, der die ganze Nacht redet, interne Notifications, Dev, explodierende Volumen. Luna, die um 3 Uhr morgens auf ElevenLabs ihr Leben erzählt, das ist meine Stromrechnung, die weint.

Die Rechnung ist einfach: Piper ist 0 Euro marginal, Qualität "gut". ElevenLabs ist laufender Zähler, Qualität "exzellent". Dazwischen gibt es eine Welt, in der "gut und gratis" gewinnt. Das ist die ganze PiperHub-Wette.

pipertts: warum Nativ den Python-Wrapper um 10x schlägt

Das Repo itsrealfortune/pipertts, 75 Commits: Ein nativer Piper-Port in TypeScript. README-Titel: "In-process Piper TTS for Node.js. No Python required."

Die Kosten des Spawns

Der historische Wrapper (PiperTTS) ist python3 -m piper, gespawnt als Subprozess bei jeder Synthese:

synthèse = spawn(python) + chargement du modèle + inférence + kill

Jeder Satz zahlt Interpreter-Start + Gewichte-Laden. Gemessen auf en_US-lessac-medium: ~2.2s pro Zeile.

Nativ (PiperNativeTTS) hält die ONNX-Session warm im Speicher:

load une fois -> session persistante -> inférence par phrase

Gleich gemessen: ~0.22s pro Zeile. Das 10x kommt daher: Du killst Spawn und Reload, mit wundersamer Inferenz hat das nichts zu tun. Die ONNX-Inferenz selbst ist auf beiden Seiten dieselbe.

Als Bonus cacht der erste Load den optimierten Graphen neben der .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), was spätere Loads ca. 35% schneller macht. Der Cache-Key kombiniert Platform, Arch und ORT-Version, und erfrischt sich, wenn das Modell sich ändert. Abschaltbar mit cacheOptimizedModel: false.

Die espeak-Brücke: 0.4ms gegen 15ms

Gleiche Logik für die Phonemisierung. Der Wrapper ruft pro Satz das espeak-ng-Binary per CLI auf: ca. 15ms Spawn jedes Mal. Nativ bettet eine N-API-Bridge ein (src/native/espeak-bridge/, Port von espeakbridge.c), mit Prebuilds für Linux x64/arm64: ~0.4ms pro Call, Byte-identische Phoneme. Ohne das Addon automatischer Fallback aufs CLI. PIPER_ESPEAK_BRIDGE=0, um das CLI zu erzwingen.

Die 7 phoneme_type, alle portiert

Typ Engine Notes
espeak N-API-Bridge oder CLI Byte-identische Clauses
text Codepoint-Passthrough trivial
hebrew Nakdimon ONNX + IPA-Regeln identische Outputs
lithuanian espeak + 189k-Einträge-Stress-Dict identische Outputs
pinyin BERT g2pW ONNX + WordPiece Polyphon-Disambiguierung OK
thai echtes unverändertes TLTK unter Pyodide/WASM 7/7 identisch (1. Call langsam, Rest schnell)
japanese lindera-wasm + UniDic + TS-Mora-Engine exakte Segmentierung, kein lexikalischer Akzent

Und Arabisch wird automatisch diakritisiert (Tashkeel, abschaltbar). 509 espeak-Stimmen aus dem Katalog laufen über die schnelle Bridge, Exoten über ihre dedizierte Engine. 71 Tests in der Suite, dokumentierte Einschränkung, wo es klemmt (der japanische Akzent, UniDic liefert null Daten, angenommen, nichts versteckt).

Die Formate und das Streaming

wav default, raw PCM, mp3, ogg, opus: ffmpeg, wenn da, sonst pure JS-Fallbacks (lamejs für mp3, opusscript + Ogg-Muxer für opus, 0.993 Korrelation mit der Quelle). Und vor allem synthesizeChunks(): Ein Async-Generator, der pro Satz yielded, mit sentenceSilence zwischen Sätzen. Für Luna heißt das, Audio abzuspielen, während das Ende noch synthetisiert. TTS-Streaming ohne dedizierten Server.

Volle Nutzung, mit eingebautem PiperHub-Katalog (hub: + automatischer Mirror-Fallback, falls das Autoren-Repo die Dateien nicht mehr serviert):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // direkt für Discord
  lengthScale: 0.95,     // einen Tick schneller
});

Community-Stimmen (GLaDOS und Game-Charaktere) fehlen im offiziellen Manifest. Genau darum gibt es PiperHub, und pipertts weiß das gut: hub:<slug>, PiperHub-Seiten-URL oder nackter Slug, alles geht. Als usable: false geflaggte Stimmen werden mit klarem Fehler abgelehnt.

Was geopfert wurde

Jedes fehlende Feature finanziert ein anderes. Die ehrliche Liste:

Piper klingt roboterhaft neben ElevenLabs. Medium-VITS auf 22050 Hz ist sauber, aber flach gegen ein großes expressives Modell. Multi-Emotion kompensiert teils (7 Stile > 1 flacher Stil), aber ein ElevenLabs-v3-Hörbuch bleibt drüber. Das ist der Preis der dutzenden MB.

espeak irrt sich. Eigennamen, Anglizismen, Abkürzungen: Regelbasierte Phonemisierung hat blinde Flecken. "Spoiler" auf Französisch liest espeak so gut es kann. Große Modelle mit neuronalem G2P irren sich seltener. In Prod brauchst du ein Ausnahme-Dict für Fachbegriffe.

Null natives Klonen. Piper liest mit festen Stimmen. Wenn du DEINE Stimme willst, ab zu PocketTTS oder ElevenLabs. Multi-Speaker gibt Varianten, nie dein Timbre.

Japanisch bleibt ohne lexikalischen Akzent. UniDic liefert null Akzentdaten, also ist die Segmentierung exakt, aber die Melodie approximativ. Im README dokumentiert, nichts unterm Teppich.

Thai lahmt beim ersten Call. TLTK läuft unter unverändertem Pyodide/WASM: Der erste Call dauert Sekunden (Laden), spätere sind schnell. 7/7 identisch zur Referenz, aber plane einen Warm-up ein.

Die Daten haben Hunger. Eine ordentliche High-Stimme braucht Stunden sauberes aligniertes Audio. Das Fortune-Dataset sind Stunden Aufnahme + Cleanup. Null Magie: Garbage in, Roboter out.

Der Katalog hat tote Stimmen. 25 Modelle als usable: false geflaggt. Das ist angenommen und gefiltert, aber es erinnert daran, dass Stimmen-Crowdsourcing auch Abbruch-Crowdsourcing ist.

Das Endergebnis

Auf der einen Seite ein ElevenLabs-Zähler, auf der anderen ein Raspberry Pi:

  • PiperHub: 536 Modelle gelistet, 71 Sprachen, 3755 Speaker, 130 verlinkte Datasets, Dataset -> Training -> Nutzungs-Guides
  • Piper: End-to-End-VITS, espeak, CPU-ONNX, LJSpeech, low/medium/high, 22050 Hz Standard
  • Fortune multi: 7 FR-Emotionen + 7 EN, je 1 ONNX, speakerId zum Umschalten, "Fortune's Café"-Lizenz
  • Kokoro 82M: Premium-EN-Qualität, Apache 2.0, 54 Stimmen, fast Echtzeit-CPU
  • PocketTTS: CPU-Klonen, ~100M dann 2-18M, WER 0.92%
  • sanoTTS: 0.29M, 2 MB, Mikrocontroller, Qualität folgt der Größe
  • ElevenLabs: $0.05-0.10/1000 Zeichen, 70+ Sprachen, Top-Qualität, laufender Zähler
  • natives pipertts: ~0.22s/Zeile gegen ~2.2s Wrapper, espeak-Bridge 0.4ms, 7 phoneme_type, wav/raw/mp3/ogg/opus, Streaming pro Satz

Luna spricht jeden Tag damit. 0 Euro TTS, gehirngesteuerte Emotionen, direktes OGG für Discord. Gegen ElevenLabs hört man es, klar. Aber um 3 Uhr morgens, wenn sie beschließt, ihr Leben zu erzählen, schläft meine Rechnung ruhig.

Die Philo

p2r3 sagte: "Ich liebe einfach die Idee, dass dieser winzige 1-Dollar-Chip etwas so Fortgeschrittenes wie Minecraft laufen lassen kann."

Derselbe Trip hier, angewandt auf Stimme: Ich liebe die Idee, dass ein paar dutzend MB ONNX einen Raspberry Pi in 71 Sprachen sprechen lassen, ohne Cloud, ohne Account, ohne Zähler. Das ersetzt offensichtlich kein Synchronstudio. Es ersetzt den nervigen Fall, wo du eine ordentliche, lokale, gratis Stimme willst, die nachts spricht, ohne dich abzurechnen.

Tradeoffs, schwarz auf weiß geschrieben:

  • Premium-Qualität -> kleines VITS: etwas roboterhaft, aber lokal und gratis
  • ein Modell pro Emotion -> speakerId: 7 Stimmen zum Preis einer im RAM
  • Python-Spawn -> persistente Session: 10x schneller, null zu installierendes Python
  • wilder Katalog -> verlinkte Herkunft: Jede Stimme weiß, woher sie kommt und ob sie noch läuft
  • abgerechnete API -> 0 Euro marginal: weniger hübsch als ElevenLabs, aber die Rechnung schläft ruhig

Die 3 Dinge zum Merken:

  1. Piper ist das CPU-TTS : VITS + espeak + ONNX, 71 Sprachen, Stimmen in dutzenden MB, die auf Pi laufen. Weniger hübsch als ElevenLabs, unendlich billiger.
  2. Fortune multi sind 7 Emotionen zum Preis einer : ein einziges ONNX, speakerId zum Umschalten, FR + EN. Das Speaker-Embedding statt 7 Modellen.
  3. Nativ killt den Spawn : pipertts hält die ONNX-Session warm: 0.22s/Zeile gegen 2.2s, espeak-Bridge 0.4ms gegen 15ms CLI. Gleiche Inferenz, 10x weniger Warten.

Wenn dich das Projekt interessiert, der Katalog ist piperhub.org, der Seitencode github.com/fox3000foxy/piper-bank, das native Modul github.com/itsrealfortune/pipertts. Und wenn du Luna um 3 Uhr morgens ihr Leben erzählen hören willst, das ist auf Discord xD

NEW

PiperHub : 536 open-source TTS-голосов, 7 эмоций в одной модели, ноль GPU, ноль счетов

VITS, espeak, ONNX, LJSpeech : как Piper заставляет Raspberry Pi говорить. Fortune multi в 7 эмоциях, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs -- и нативный pipertts в 10 раз быстрее официального Python-враппера.

Introduction

Озвучить Discord-бота стоит дорого. Не текст : локальный LLM генерирует фразы за копейки GPU. Голос оплачивается за каждую фразу. API синтеза берут плату за символы, и когда бот болтает всю ночь, счёт растёт следом.

У меня была именно такая проблема с Luna Protocol. Мой Discord-бот генерирует сообщения локальным LLM, потом зачитывает их вслух голосом Piper, конвертирует в OGG и отправляет как голосовые сообщения. Всё на моём собственном сервере, за 0 евро за фразу, даже в 3 часа ночи, когда Луна решает сама рассказать о своей жизни.

Держаться на нуле значит найти бесплатные голоса и хостить их самому. И тут всё усложнилось : модели разбросаны по десяткам репозиториев Hugging Face, лицензии мутные, никак не узнать, откуда взялся тот или иной голос и работает ли он вообще. Поэтому я построил PiperHub, open-source каталог голосов для Piper TTS.

Цифры на сегодня : 536 моделей показано, 71 язык представлен, 3755 speakers в списке. Каждая модель привязана к своему исходному датасету, когда он известен, -- происхождение это часть звука, как сказано на главной.

Остаётся вопрос, который мне задают часто : чего стоит Piper против Kokoro, PocketTTS, sanoTTS и ElevenLabs ? И почему модуль pipertts от Fortune в 10 раз быстрее официального Python-враппера ? В этой статье вскрываю движки, с кодом в подтверждение.

PiperHub, le catalogue

Прежде чем вскрывать движки, посмотрим, какую проблему решает PiperHub.

D'où viennent les voix

Код лежит на github.com/fox3000foxy/piper-bank. Astro, статический сайт, ноль серверов : английский в корне, ещё 15 локалей под /<code>/ (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). Та же схема, что у моего блога, в общем-то.

Источник истины никто руками не правит. Это пайплайн :

npm install
npm run data    # регенерирует src/data/*.json (python3 scripts/generate_data.py)
npx astro dev   # локальный dev
npm run build   # статический build в dist/

Конкретно : репозиторий-краулер поддерживает models_mapping.csv и datasets_mapping.csv, зеркала голосов живут на Hugging Face, сэмплы это аудиофайлы. npm run data стягивает всё это и выплёвывает src/data/*.json. Сайт только читает статичный JSON.

В сырых данных на текущий момент (555 записей, на сайте показано 536, -- краул всегда чуть впереди отображения) :

Qualité Modèles
medium 437
high 67
low 50
Sample rate Modèles
22050 Hz 486
16000 Hz 47

486 голосов на 22050 Гц, стандарт Piper. 47 на 16000 Гц, маленькие быстрые голоса для встраиваемых систем. Фонемизатор это espeak в 509 случаях, -- к этому ещё вернёмся, это сердце системы.

La provenance, ou pourquoi chaque modèle a un dataset

То, что делает PiperHub полезным, а не просто списком, это связь модель <-> датасет. Пример с главной : голос Astra - Valorant Piper TTS привязан к датасету Astra - LJSpeech Dataset. Открываешь обе страницы, проверяешь репозитории и лицензии с обеих сторон.

130 датасетов в каталоге. И каталог честно показывает состояние войск : 25 моделей помечены usable: false (битые файлы или формат piperPlus, которому нужен форкнутый рантайм). Модуль pipertts отвергает такие голоса с понятной ошибкой вместо того, чтобы странно крашиться позже. 48 моделей мультиспикерные, -- включая две, которые нас заинтересуют.

По гайдам сайт закрывает базу в три шага : Build a dataset, Train a voice, Use a voice, плюс страница Piper Plus. На входе датасет в формате LJSpeech, на выходе ONNX. Сказать просто, делать долго, -- отсюда следующий раздел.

Piper sous le capot : VITS, espeak, ONNX

Une phrase, trois étapes

Когда ты просишь Piper сказать "bonjour", происходит вот что :

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

Ноль отдельного вокодера. В этом и суть VITS : end-to-end модель, которая идёт от текста (фонемизированного) straight к waveform. Тогда как старые пайплайны делали текст -> спектрограмма (Tacotron), а потом спектрограмма -> аудио (HiFi-GAN, две модели, два обучения), VITS учит всё разом с условным VAE, нормализующими флоу и GAN-дискриминатором на декодере. Одно обучение, одна модель, один экспорт в ONNX.

Декодер выдаёт straight 22050 Гц. На этом sample rate секунда речи = 22050 чисел. Модель маленькая (десятки мегабайт в качестве medium), а инференс это чистый CPU : перемножение матриц на оптимизированном графе ONNX. Отсюда Raspberry Pi как официальная цель, -- Piper родился в Rhasspy, open-source голосовом ассистенте, сделанном для работы на Pi.

espeak-ng, le phonémiseur qui fait tout le sale boulot

Перед VITS надо превратить текст в фонемы. 509 моделей из 555 используют espeak :

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng знает произношение десятков языков. Модель VITS никогда не учит французскую орфографию с её eau, которые читаются как o, -- она учит отображение фонем в звук. Это радикально упрощает обучение и объясняет, почему Piper покрывает 71 язык : для нового языка подключаешь соответствующий голос espeak и обучаешь.

Те, что обходятся без espeak, тащат собственный фонемизатор, и тут начинается экзотика : hebrew (Nakdimon ONNX + 494 строки IPA-правил), pinyin (полный BERT g2pW с разрешением полисемии, -- один и тот же китайский иероглиф читается по-разному в зависимости от слова), thai (настоящий немодифицированный TLTK), japanese (сегментация lindera + UniDic). Модуль pipertts везёт все семь, сверенных до идентичности с Python-референсом. Ещё вернёмся.

Le dataset : du LJSpeech, toujours du LJSpeech

Стандартный формат обучения это LJSpeech : папка wavs/, полная клипов, плюс metadata.csv :

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, один клип = одна фраза, несколько секунд. Исходный датасет это 13 000 фраз на английском, прочитанных одной дикторшей, общественное достояние. Чтобы обучить русский голос, записываешь ту же структуру по-русски : выровненные текст + аудио, нескольких часов хватит на приличный medium, больше на high.

Три качества в каталоге примерно соответствуют размеру модели + длительности обучения. Low : маленький, быстрый, роботизированный. Medium : золотая середина, 437 моделей. High : больше, медленнее, естественнее, -- 67 моделей, включая Fortune.

Les trois boutons magiques

Каждый голос Piper выставляет три параметра инференса, с почти универсальными дефолтами :

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667) : вариативность голоса. Выше = выразительнее, ниже = стабильнее. Выход стохастический : два синтеза одной фразы дают два чуть разных аудио.
  • noise_w (0.8) : вариативность тайминга, ритм.
  • length_scale (1.0) : скорость. Выше 1 замедляет, ниже ускоряет. Простейший способ заставить Луну говорить быстрее, когда она злится.

Запомни noise_scale, он пригодится для эмоций.

Fortune multi : 7 émotions, 1 seul ONNX

Ладно, переходим к местной звезде.

В каталоге три модели Fortune, обученные ItsRealFortune (привет) :

Modèle Repo Voix
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

Мульти FR это вот что :

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

Семь эмоциональных окрасок в одном файле ONNX. Мульти EN такой же, с исторической опечаткой в именах : suprised вместо surprised. Она в весах, оставляем, это часть фольклора.

Comment ça marche, un speakerId ?

Мультиспикерный VITS это та же модель с одним дополнительным входом : эмбеддингом спикера. При обучении каждый клип помечен своим стилем, модель учит таблицу эмбеддингов (по одному на стиль) вместе со всем остальным. На инференсе ты выбираешь строку таблицы :

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = обычный ... 5 = удивление
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

Одна фраза, семь прочтений. speakerId: 5 (удивление) с чуть подкрученным noise_scale, и у тебя убедительное восклицание. speakerId: 4 (грусть), length_scale: 1.1 для замедления, и у тебя траурное объявление. Именно так Луна модулирует голос : мозг выбирает эмоцию (валентность/возбуждение, см. мою статью про центроиды), TTS выбирает speakerId.

Датасет за этим стоит под скромным именем Fortune French - Dataset, с лицензией под названием "Fortune's Café". Да, лицензия называется как кафе. Нет, она остаётся.

Pourquoi c'est malin économiquement

Семь эмоций = одно скачивание, одна загрузка в RAM, одна ONNX-сессия. Альтернатива (семь отдельных моделей) умножила бы хранилище и заставляла бы перезагружать граф при каждой смене эмоции, -- неиграбельно для бота, который меняет тон прямо посреди фразы. А тут speakerId это просто индекс в тензоре. Маржинальная стоимость : ноль.

Les concurrents : Kokoro, PocketTTS, sanoTTS

Piper делит CPU с тремя серьёзными претендентами, тремя разными философиями.

Kokoro 82M, le roi qualité/prix

Kokoro от hexgrad. 82 миллиона параметров, модифицированная архитектура StyleTTS 2 с декодером ISTFTNet, без этапа диффузии. Отсутствие диффузии решает всё : никакого итеративного цикла денойзинга, отсюда ультрабыстрый инференс.

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

Обучен на менее чем 100 часах аудио примерно за 1000 долларов, а слепые слушатели ставят его на уровень премиальных голосов Google и Amazon. Потому он и взлетел : премиальное качество, бесплатно, локально, пермиссивно.

Против Piper : Kokoro выигрывает в чистой естественности (StyleTTS 2 > маленький VITS) и в лицензии (Apache 2.0 против движка под GPL-3.0 у Piper). Piper выигрывает в покрытии (71 язык против 8, 3755 спикеров против 54 голосов) и в экосистеме голосов сообщества. Для английской наррации Kokoro трудно побить. Для французского с 7 эмоциями, турецкого или суахили Piper часто вообще единственный в гонке.

PocketTTS (Kyutai), le clonage sur CPU

Kyutai играет другую партию : маленькая модель с клонированием голоса, которая работает на CPU.

Modèle Params WER UTMOS SIM Vitesse CPU (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER ниже 1% (произносит правильно), UTMOS 4.36 (воспринимаемое качество отличное), SIM 0.923 (клон реально похож на цель). А small-версии спускаются до 1.9M параметров с клонированием. 1.9M. Мой роутер Sabaki весит почти столько в JSON.

Против Piper : PocketTTS выигрывает в клонировании (Piper читает фиксированными голосами) и в компактности флагмана. Piper выигрывает в языках (small пока только английский) и в зрелости экосистемы (536 готовых голосов против "обучи свой").

sanoTTS, le modèle qui tient sur une puce à 3 dollars

sanoTTS (sano = "маленький" по-непальски), это Bareiron мира TTS : полный нейронный стек от фонем до PCM 22.05 кГц на микроконтроллере.

Système Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M параметров. 2 МБ голос. pip install sanotts, sanotts say "Hello" --voice amy, и оно говорит. В браузере тоже.

Но смотри на оценки : SCOREQ 2.29 против 3.94 у TinyTTS, UTMOS 2.45. Говорит, да. Не поёт. Это ультимативный tradeoff : на 0.29M ты влезаешь везде, но качество следует за размером. Piper medium это в 100 раз больше весов за голос совсем другой лиги.

Le tableau qui résume

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

Выбор зависит от твоего ограничения, абсолютного победителя нет : чистое EN-качество -> Kokoro. Клонирование на CPU -> PocketTTS. Микроконтроллер -> sanoTTS. Покрытие языков + французские эмоциональные голоса + ноль инфры -> Piper.

ElevenLabs, le Goliath facturé

А ещё есть ElevenLabs. 70+ языков, выразительный Eleven v3 с audio-тегами и драматическими диалогами, топовое качество в слепых тестах. Jev мира TTS, в каком-то смысле : все его видели, все бенчмаркали против него.

Прайсинг 2026, в круглых цифрах :

Modèle API Prix / 1000 caractères
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 символов это примерно 1 минута аудио. То есть от $0.05 до $0.10 за минуту. План Pro за $99 включает около 990k символов, оверэйдж бьёт по тому же тарифу. Для YouTube-канала, который выпускает по видео в день с озвучкой (мой valorant-short-maker делает ровно это, но на бесплатном Piper), считай от $1.50 до $3 в месяц за каждую ежедневную минуту озвучки. Десять минут в день : $15-30/мес. Платить можно, но считать приходится.

Когда оно того стоит : когда голос это продукт (дубляж, аудиокнига, реклама), когда надо 70 языков к завтрашнему утру, когда клонирование должно получиться идеально с первого раза. А вот когда не стоит никогда : Discord-бот, который болтает всю ночь, внутренние уведомления, дев, объёмы, которые взрываются. Луна, рассказывающая о своей жизни в 3 часа ночи на ElevenLabs, это мой счёт за свет в слезах.

Арифметика простая : Piper = 0 евро маржинально, качество "хорошее". ElevenLabs = крутящийся счётчик, качество "отличное". Между ними целый мир, где "хорошее и бесплатное" выигрывает, -- в этом вся ставка PiperHub.

pipertts : pourquoi le natif met 10x au wrapper Python

Репозиторий itsrealfortune/pipertts, 75 коммитов : нативный порт Piper на TypeScript. Заголовок README : "In-process Piper TTS for Node.js. No Python required."

Le coût du spawn

Исторический враппер (PiperTTS) это python3 -m piper, спавнящийся в субпроцессе при каждом синтезе :

synthèse = spawn(python) + chargement du modèle + inférence + kill

Каждая фраза оплачивает старт интерпретатора + загрузку весов. Замерено на en_US-lessac-medium : ~2.2s на строку.

Натив (PiperNativeTTS) держит ONNX-сессию горячей в памяти :

load une fois -> session persistante -> inférence par phrase

Замерено так же : ~0.22s на строку. 10x берутся отсюда : убиваем спавн и перезагрузку, чудесный инференс тут вообще ни при чём. Сам инференс ONNX с обеих сторон одинаковый.

Бонусом первая загрузка кеширует оптимизированный граф рядом с .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), что делает следующие загрузки примерно на 35% быстрее. Ключ кеша комбинирует platform, arch и версию ORT, и обновляется, если модель изменилась. Отключается через cacheOptimizedModel: false.

Le pont espeak : 0.4ms contre 15ms

Та же логика для фонемизации. Враппер дёргает бинарник espeak-ng через CLI на фразу : около 15ms спавна каждый раз. Натив встраивает N-API бридж (src/native/espeak-bridge/, порт espeakbridge.c), с пребилдами под Linux x64/arm64 : ~0.4ms на вызов, фонемы байт-в-байт. Без аддона автоматический откат на CLI. PIPER_ESPEAK_BRIDGE=0 чтобы форсировать CLI.

Les 7 phoneme_type, tous portés

Type Moteur Notes
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

А арабский диакритизируется автоматически (ташкиль, отключаемо). 509 espeak-голосов каталога идут через быстрый бридж, экзотика через свой движок. 71 тест в сьюте, ограничение задокументировано там, где жмёт (японское ударение, UniDic не даёт никаких данных, -- признано, ничего не спрятано).

Les formats et le streaming

wav по умолчанию, raw PCM, mp3, ogg, opus : ffmpeg, когда он есть, иначе чисто-JS фолбэки (lamejs для mp3, opusscript + Ogg-муксер для opus, корреляция 0.993 с источником). И главное synthesizeChunks() : async-генератор, который yield'ит по фразам, с sentenceSilence между фразами. Для Луны это значит начать проигрывать аудио, пока конец ещё синтезируется, -- стриминг TTS без выделенного сервера.

Полное использование, со встроенным каталогом PiperHub (hub: + автоматический mirror-фолбэк, если авторский репозиторий больше не отдаёт файлы) :

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // straight в Discord
  lengthScale: 0.95,     // чуть быстрее
});

Голоса сообщества (GLaDOS и персонажи игр), отсутствующие в официальном манифесте, -- вот ровно зачем существует PiperHub, и pipertts это хорошо знает : hub:<slug>, URL страницы piperhub или голый slug, всё работает. Голоса с флагом usable: false отвергаются с понятной ошибкой.

Ce qui a été sacrifié

Каждая отсутствующая фича финансирует другую. Честный список :

Piper звучит роботизированно рядом с ElevenLabs. VITS medium на 22050 Гц это чисто, но плоско рядом с большой выразительной моделью. Мультиэмоции частично компенсируют (7 стилей > 1 плоского стиля), но аудиокнига ElevenLabs v3 всё равно выше. Это цена десятков мегабайт.

espeak ошибается. Имена собственные, англицизмы, аббревиатуры : у фонемизации по правилам есть слепые зоны. "Spoiler" по-французски espeak читает как может. Большие модели с нейронным G2P ошибаются реже. В проде нужен словарь исключений для твоих терминов.

Ноль нативного клонирования. Piper читает фиксированными голосами. Если хочешь СВОЙ голос, тебе в PocketTTS или ElevenLabs. Мультиспикер даёт вариации, но не твой тембр.

Японский остаётся без лексического ударения. UniDic не даёт никаких данных об ударении, поэтому сегментация точная, а мелодика приблизительная. Задокументировано в README, ничего под ковром.

Тайский тормозит на первом вызове. TLTK крутится под немодифицированным Pyodide/WASM : первый вызов занимает секунды (загрузка), следующие быстрые. 7/7 идентично референсу, но предусмотри warm-up.

Данные голодны. Приличный high-голос требует часов чистого выровненного аудио. Датасет Fortune это часы записи + чистки. Ноль магии : garbage in, robot out.

В каталоге есть мёртвые голоса. 25 моделей с флагом usable: false. Это признано и отфильтровано, но напоминает : краудсорсинг голосов это ещё и краудсорсинг заброшенности.

Le résultat final

С одной стороны счётчик ElevenLabs, с другой Raspberry Pi :

  • PiperHub : 536 показанных моделей, 71 язык, 3755 спикеров, 130 привязанных датасетов, гайды dataset -> training -> usage
  • Piper : end-to-end VITS, espeak, ONNX CPU, LJSpeech, low/medium/high, стандарт 22050 Гц
  • Fortune multi : 7 эмоций FR + 7 EN, по 1 ONNX, speakerId для переключения, лицензия "Fortune's Café"
  • Kokoro 82M : премиальное EN-качество, Apache 2.0, 54 голоса, почти реалтайм на CPU
  • PocketTTS : клонирование на CPU, ~100M потом 2-18M, WER 0.92%
  • sanoTTS : 0.29M, 2 МБ, микроконтроллер, качество следует за размером
  • ElevenLabs : $0.05-0.10/1000 символов, 70+ языков, топ-качество, крутящийся счётчик
  • нативный pipertts : ~0.22s/строка против ~2.2s у враппера, бридж espeak 0.4ms, 7 phoneme_type, wav/raw/mp3/ogg/opus, стриминг по фразам

Луна говорит на этом каждый день. 0 евро за TTS, эмоции под управлением мозга, OGG straight в Discord. Против ElevenLabs слышно, конечно. Но в 3 часа ночи, когда она решает рассказать о своей жизни, мой счёт спит спокойно.

La philo

p2r3 говорил : "J'aime juste l'idée que cette toute petite puce à 1 balles puisse faire tourner quelque chose d'aussi avancé que Minecraft."

Тот же трип здесь, применённый к голосу : мне нравится идея, что несколько десятков мегабайт ONNX заставляют Raspberry Pi говорить на 71 языке, без облака, без аккаунта, без счётчика. Это, конечно, не заменит ни одну студию дубляжа. Оно заменяет тот нудный случай, когда тебе нужен приличный голос, локальный, бесплатный, который говорит ночью и не выставляет счёт.

Трейдоффы, чёрным по белому :

  • премиальное качество -> маленький VITS : слегка роботизированно, зато локально и бесплатно
  • модель на эмоцию -> speakerId : 7 голосов по цене одного в RAM
  • спавн Python -> постоянная сессия : в 10 раз быстрее, ноль Python для установки
  • дикий каталог -> привязанное происхождение : каждый голос знает, откуда он и работает ли ещё
  • платный API -> 0 евро маржинально : менее красиво, чем ElevenLabs, зато счёт спит спокойно

3 вещи, которые стоит запомнить :

  1. Piper = TTS для CPU -- VITS + espeak + ONNX, 71 язык, голоса в десятки мегабайт, которые крутятся на Pi. Менее красиво, чем ElevenLabs, бесконечно дешевле.
  2. Fortune multi = 7 эмоций по цене одной -- один ONNX, speakerId для переключения, FR + EN. Эмбеддинг спикера вместо 7 моделей.
  3. Натив убивает спавн -- pipertts держит ONNX-сессию горячей : 0.22s/строка против 2.2s, бридж espeak 0.4ms против 15ms CLI. Тот же инференс, в 10 раз меньше ожидания.

Если проект интересен, каталог это piperhub.org, код сайта github.com/fox3000foxy/piper-bank, нативный модуль github.com/itsrealfortune/pipertts. А если хочешь послушать, как Луна рассказывает о своей жизни в 3 часа ночи, это в Discord xD

NEW

PiperHub: 536 voces TTS open source, 7 emociones en un modelo, cero GPU, cero factura

VITS, espeak, ONNX, LJSpeech: cómo Piper hace hablar a una Raspberry Pi. Fortune multi en 7 emociones, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs, y pipertts nativo 10x más rápido que el wrapper de Python.

Introduction

Hacer hablar a un bot de Discord cuesta caro. No el texto: un LLM local genera frases por unos céntimos de GPU. La voz, en cambio, se paga en cada frase. Las API de síntesis cobran por carácter, y cuando el bot habla toda la noche, la factura sube.

Ese es el problema que tuve con Luna Protocol. Mi bot de Discord genera sus mensajes con un LLM local, luego los lee en voz alta con una voz Piper, los convierte a OGG y los envía como mensajes de voz. Todo en mi propio servidor, por 0 euros por frase, incluso a las 3 de la mañana cuando a Luna le da por contar su vida ella sola.

Quedarte en 0 euros significa encontrar voces gratis y alojarlas tú. Y ahí se complicó: modelos repartidos por decenas de repos de Hugging Face, licencias poco claras, sin forma de saber de dónde viene tal voz ni si todavía funciona. Así que construí PiperHub, un catálogo open source de voces para Piper TTS.

Las cifras de hoy: 536 modelos publicados, 71 idiomas representados, 3755 speakers listados. Cada modelo está enlazado a su dataset de origen cuando lo conozco. La procedencia forma parte del sonido, como dice la página principal.

Queda una pregunta que me hacen a menudo: ¿qué vale Piper frente a Kokoro, PocketTTS, sanoTTS y ElevenLabs? ¿Y por qué el módulo pipertts de Fortune es 10x más rápido que el wrapper oficial de Python? Este artículo abre los motores, con código para probarlo.

Antes de abrir los motores, veamos el problema que resuelve PiperHub.

De dónde vienen las voces

El código está en github.com/fox3000foxy/piper-bank. Astro, sitio estático, cero servidor: el inglés en la raíz, 15 locales más bajo /<code>/ (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). El mismo patrón que mi blog, de hecho.

Nadie edita la fuente de la verdad a mano. Es un pipeline:

npm install
npm run data    # regenera src/data/*.json (python3 scripts/generate_data.py)
npx astro dev   # dev local
npm run build   # build estática en dist/

En concreto: un repo crawler mantiene models_mapping.csv y datasets_mapping.csv, las voces espejo viven en Hugging Face, los samples son archivos de audio. npm run data lo descarga todo y escupe src/data/*.json. El sitio solo lee JSON estático.

En los datos brutos del momento (555 entradas, el sitio muestra 536, el crawl siempre va un poco por delante de la vista):

Calidad Modelos
medium 437
high 67
low 50
Sample rate Modelos
22050 Hz 486
16000 Hz 47

486 voces en 22050 Hz, el estándar Piper. 47 en 16000 Hz, las voces pequeñas y rápidas para embedded. El fonemizador es espeak en 509 casos. Volveré a eso, es el corazón del sistema.

La procedencia, o por qué cada modelo tiene un dataset

Lo que hace útil a PiperHub en vez de ser solo una lista es el enlace modelo <-> dataset. Ejemplo tomado de la home: la voz Astra - Valorant Piper TTS está enlazada al dataset Astra - LJSpeech Dataset. Abres ambas páginas, verificas los repos y las licencias de cada lado.

130 datasets referenciados. Y el catálogo es honesto sobre el estado de las tropas: 25 modelos marcados usable: false (archivos rotos, o formato piperPlus que pide un runtime fork). El módulo pipertts rechaza estas voces con un error explícito en vez de fallar de forma rara más tarde. 48 modelos son multi-speaker, incluidos los dos que nos van a interesar.

En guías, el sitio hace el trabajo en tres pasos: Build a dataset, Train a voice, Use a voice, más una página Piper Plus. Dataset en formato LJSpeech como entrada, ONNX como salida. Fácil de decir, largo de hacer, de ahí la sección siguiente.

Piper bajo el capó: VITS, espeak, ONNX

Una frase, tres pasos

Cuando le pides a Piper que diga "bonjour", pasa esto:

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

Cero vocoder separado. Eso es VITS: un modelo end-to-end que va del texto (fonemizado) al waveform directamente. Donde los pipelines viejos hacían texto -> espectrograma (Tacotron) y luego espectrograma -> audio (HiFi-GAN, dos modelos, dos entrenamientos), VITS lo aprende todo de golpe con un VAE condicional, flows normalizantes y un discriminador tipo GAN en el decodificador. Un solo entrenamiento, un solo modelo, una sola exportación ONNX.

El decodificador escupe 22050 Hz directamente. A ese sample rate, un segundo de voz son 22050 números. El modelo es pequeño (unas decenas de MB en calidad medium) y la inferencia es pura CPU: multiplicaciones de matrices en un grafo ONNX optimizado. De ahí la Raspberry Pi como objetivo oficial. Piper nació en Rhasspy, un asistente de voz open source hecho para correr en Pi.

espeak-ng, el fonemizador que hace todo el trabajo sucio

Antes de VITS, hay que transformar texto en fonemas. 509 modelos de 555 usan espeak:

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng conoce la pronunciación de decenas de idiomas. El modelo VITS nunca aprende la ortografía francesa con sus eau que se pronuncian o. Aprende de fonemas a sonido. Eso simplifica muchísimo el entrenamiento y explica por qué Piper cubre 71 idiomas: para un idioma nuevo, conectas la voz espeak correspondiente y entrenas.

Las que pasan de espeak tienen su propio fonemizador, y ahí se pone exótico: hebrew (Nakdimon ONNX + 494 líneas de reglas IPA), pinyin (BERT g2pW completo con desambiguación de polífonos, el mismo carácter chino se pronuncia distinto según la palabra), thai (verdadero TLTK sin modificar), japanese (segmentación lindera + UniDic). El módulo pipertts los trae los siete, verificados idénticos a la referencia Python. Volveré a eso.

El dataset: LJSpeech, siempre LJSpeech

El formato estándar de entrenamiento es LJSpeech: una carpeta wavs/ llena de clips, más un metadata.csv:

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, un clip es una frase, unos segundos. El dataset original son 13.000 frases en inglés leídas por una sola locutora, dominio público. Para entrenar una voz francesa, grabas la misma estructura en francés: texto + audio alineados, unas horas bastan para un medium correcto, más para un high.

Las tres calidades del catálogo corresponden más o menos al tamaño del modelo + el tiempo de entrenamiento. Low: pequeño, rápido, robótico. Medium: el punto ideal, con 437 modelos. High: más grande, más lento, más natural, 67 modelos, incluidos los Fortune.

Los tres botones mágicos

Cada voz Piper expone tres parámetros de inferencia, con valores casi universales:

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667): variabilidad de la voz. Más alto es más expresivo, más bajo es más estable. La salida es estocástica: dos síntesis de la misma frase dan dos audios un poco distintos.
  • noise_w (0.8): variabilidad del timing, el ritmo.
  • length_scale (1.0): velocidad. Por encima de 1 frena, por debajo acelera. La forma más simple de hacer que Luna hable más rápido cuando se emociona.

Quédate con noise_scale, servirá para las emociones.

Fortune multi: 7 emociones, 1 solo ONNX

Bien, pasemos a la estrella local.

El catálogo lista tres modelos Fortune entrenados por ItsRealFortune (hola):

Modelo Repo Voces
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

El multi FR es esto:

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

Siete versiones emocionales en un solo archivo ONNX. El multi EN igual, con una errata histórica en los nombres: suprised en vez de surprised. Está en los pesos, la conservo, forma parte del folclore.

Cómo funciona un speakerId

VITS multi-speaker es el mismo modelo con una entrada más: un embedding de speaker. En el entrenamiento, cada clip va etiquetado con su estilo, el modelo aprende una tabla de embeddings (una por estilo) a la vez que el resto. En inferencia, eliges la fila de la tabla:

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = normal ... 5 = surprise
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

Misma frase, siete lecturas. speakerId: 5 (surprise) con un noise_scale un poco alto, y tienes una exclamación creíble. speakerId: 4 (triste), length_scale: 1.1 para frenar, y tienes un anuncio fúnebre. Así modula Luna su voz: el cerebro elige la emoción (valencia/arousal, ver mi artículo sobre centroides), el TTS elige el speakerId.

El dataset detrás se llama sobriamente Fortune French - Dataset, con una licencia llamada "Fortune's Café". Sí, la licencia se llama como un café. No, se queda.

Por qué es listo en lo económico

Siete emociones es una sola descarga, una sola carga en RAM, una sola sesión ONNX. La alternativa (siete modelos separados) multiplicaría el almacenamiento y obligaría a recargar el grafo en cada cambio de emoción, imposible para un bot que cambia de tono en mitad de la frase. Aquí, speakerId es solo un índice en un tensor. Coste marginal: cero.

Los competidores: Kokoro, PocketTTS, sanoTTS

Piper comparte la CPU con tres rivales serios, tres filosofías distintas.

Kokoro 82M, el rey calidad/precio

Kokoro, por hexgrad. 82 millones de parámetros, arquitectura StyleTTS 2 modificada con decodificador ISTFTNet, sin paso de difusión. La ausencia de difusión lo hace todo: ningún bucle de denoising iterativo, así que inferencia ultrarrápida.

Kokoro-82M
Params 82M
Pesos ~300 MB (164 MB en FP16)
Licencia Apache 2.0 (comercial OK)
Idiomas 8, 54 voces preset + blending
CPU casi tiempo real
GPU 36 a 96x tiempo real
Tests ciegos al nivel de Google y Amazon

Entrenado con menos de 100h de audio por unos 1000 dólares, y los oyentes ciegos lo ponen al nivel de las voces premium de Google y Amazon. Por eso explotó: calidad premium, gratis, local, permisivo.

Frente a Piper: Kokoro gana en naturalidad pura (StyleTTS 2 > VITS pequeño) y licencia (Apache 2.0 contra motor GPL-3.0 para Piper). Piper gana en cobertura (71 idiomas contra 8, 3755 speakers contra 54 voces) y ecosistema de voces comunitarias. Para narración en inglés, Kokoro es duro de batir. Para francés con 7 emociones, turco o suajili, Piper suele ser el único en liza.

PocketTTS (Kyutai), la clonación en CPU

Kyutai juega otra partida: modelo pequeño con clonación de voz que corre en CPU.

Modelo Params WER UTMOS SIM Velocidad CPU (1 hilo)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x tiempo real
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonación incluida

WER bajo 1% (pronuncia bien), UTMOS 4.36 (calidad percibida excelente), SIM 0.923 (el clon se parece de verdad al objetivo). Y las versiones small bajan a 1.9M parámetros con clonación. 1.9M. Mi router Sabaki casi ocupa eso en JSON.

Frente a Piper: PocketTTS gana en clonación (Piper lee con voces fijas) y compacidad del flagship. Piper gana en idiomas (el small es inglés only por ahora) y madurez del ecosistema (536 voces listas contra "entrena la tuya").

sanoTTS, el modelo que cabe en un chip de 3 dólares

sanoTTS (sano es "pequeño" en nepalí), es el Bareiron del TTS: un stack neuronal completo que va de fonemas a PCM 22.05 kHz en microcontrolador.

Sistema Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M parámetros. 2 MB por voz. pip install sanotts, sanotts say "Hello" --voice amy, y habla. En navegador también.

Pero mira los scores: SCOREQ 2.29 contra 3.94 para TinyTTS, UTMOS 2.45. Habla, sí. Canta, no. Es el tradeoff final: con 0.29M cabes en todas partes pero la calidad sigue al tamaño. Piper medium son 100x más pesos para una voz de otra liga.

La tabla resumen

Piper Kokoro 82M PocketTTS sanoTTS
Params típicos ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Idiomas 71 8 EN (+ smalls EN) multilingüe, pequeño
Voces disp. 3755 speakers 54 + blending clonación (la tuya) pocas voces
Clonación no no (nativo) sí, CPU no
Licencia motor GPL-3.0 Apache 2.0 open (Kyutai) open
Entrenable en casa sí (LJSpeech) sí (más pesado) sí sí
Calidad FR buena (multi emoción) aceptable (EN-first) EN-only floja

Tu elección depende de tu límite, no hay ganador absoluto: calidad EN pura -> Kokoro. Clonación CPU -> PocketTTS. Microcontrolador -> sanoTTS. Cobertura de idiomas + voces francesas emocionales + cero infra -> Piper.

ElevenLabs, el Goliat facturado

Y luego está ElevenLabs. 70+ idiomas, Eleven v3 expresivo con audio tags y diálogos dramáticos, calidad top en blind tests. El Jev del TTS, en cierto modo: todo el mundo lo ha visto pasar, todo el mundo ha hecho benchmark contra él.

El pricing 2026, en cifras redondas:

Modelo API Precio / 1000 caracteres
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 caracteres son más o menos 1 minuto de audio. O sea $0.05 a $0.10 el minuto. El plan Pro de $99 incluye unos 990k caracteres, el overage pega con la misma tarifa. Para un canal de YouTube que saca un vídeo al día con voz en off (mi valorant-short-maker hace justo eso, pero con Piper gratis), cuenta $1.50 a $3 al mes por cada minuto diario de voz en off. Diez minutos al día: $15 a $30/mes. Se puede pagar, pero se cuenta.

Cuándo vale la pena: cuando la voz es el producto (doblaje, audiolibro, publi), cuando necesitas 70 idiomas mañana por la mañana, cuando la clonación tiene que ser perfecta a la primera. En cambio nunca vale la pena para: bot de Discord que habla toda la noche, notificaciones internas, dev, volúmenes que explotan. Luna contando su vida a las 3 de la mañana en ElevenLabs es mi factura de la luz llorando.

El cálculo es simple: Piper es 0 euros marginal, calidad "buena". ElevenLabs es contador girando, calidad "excelente". Entre los dos hay un mundo donde "buena y gratis" gana. Esa es toda la apuesta de PiperHub.

pipertts: por qué lo nativo humilla al wrapper de Python por 10x

El repo itsrealfortune/pipertts, 75 commits: un port nativo de Piper en TypeScript. Título del README: "In-process Piper TTS for Node.js. No Python required."

El coste del spawn

El wrapper histórico (PiperTTS) es python3 -m piper lanzado en subproceso en cada síntesis:

synthèse = spawn(python) + chargement du modèle + inférence + kill

Cada frase paga el arranque del intérprete + la carga de los pesos. Medido en en_US-lessac-medium: ~2.2s por línea.

Lo nativo (PiperNativeTTS) mantiene la sesión ONNX caliente en memoria:

load une fois -> session persistante -> inférence par phrase

Medido igual: ~0.22s por línea. El 10x sale de ahí: matas el spawn y la recarga, la inferencia milagrosa no tiene nada que ver. La inferencia ONNX en sí es la misma en ambos lados.

De bonus, la primera carga cachea el grafo optimizado junto al .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), lo que hace las cargas siguientes un 35% más rápidas. La clave del caché combina platform, arch y versión de ORT, y se refresca si el modelo cambia. Desactivable con cacheOptimizedModel: false.

El puente espeak: 0.4ms contra 15ms

Misma lógica para la fonemización. El wrapper llama al binario espeak-ng en CLI por frase: unos 15ms de spawn cada vez. Lo nativo trae un bridge N-API (src/native/espeak-bridge/, port de espeakbridge.c), con prebuilds para Linux x64/arm64: ~0.4ms por llamada, fonemas byte idénticos. Sin el addon, repliegue automático al CLI. PIPER_ESPEAK_BRIDGE=0 para forzar el CLI.

Los 7 phoneme_type, todos portados

Tipo Motor Notas
espeak bridge N-API o CLI cláusulas byte idénticas
text passthrough de codepoints trivial
hebrew Nakdimon ONNX + reglas IPA salidas idénticas
lithuanian espeak + dicc. de stress 189k entradas salidas idénticas
pinyin BERT g2pW ONNX + WordPiece desambiguación de polífonos OK
thai verdadero TLTK sin modificar bajo Pyodide/WASM 7/7 idénticos (1a llamada lenta, resto rápido)
japanese lindera-wasm + UniDic + motor de moras TS segmentación exacta, sin acento léxico

Y el árabe se diacritiza automáticamente (tashkeel, desactivable). 509 voces espeak del catálogo pasan por el puente rápido, las exóticas por su motor dedicado. 71 tests en la suite, límite documentado donde duele (el acento japonés, UniDic no trae ningún dato, asumido, nada oculto).

Los formatos y el streaming

wav por defecto, raw PCM, mp3, ogg, opus: ffmpeg cuando está, si no fallbacks en JS puro (lamejs para mp3, opusscript + muxer Ogg para opus, correlación 0.993 con la fuente). Y sobre todo synthesizeChunks(): un generador async que devuelve por frase, con sentenceSilence entre frases. Para Luna, eso es empezar a reproducir el audio mientras el final se sintetiza. Streaming de TTS sin servidor dedicado.

Uso completo, con el catálogo PiperHub integrado (hub: + fallback espejo automático si el repo del autor ya no sirve los archivos):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // directo para Discord
  lengthScale: 0.95,     // un poco más rápido
});

Voces comunitarias (GLaDOS y personajes de juegos) ausentes del manifest oficial. Justo por eso existe PiperHub, y pipertts lo sabe bien: hub:<slug>, URL de página piperhub, o slug desnudo, todo vale. Las voces marcadas usable: false se rechazan con un error claro.

Lo que se sacrificó

Cada feature ausente financia otra. La lista honesta:

Piper suena robótico al lado de ElevenLabs. VITS medium en 22050 Hz es limpio pero plano comparado con un modelo grande expresivo. Lo multi emoción compensa en parte (7 estilos > 1 estilo plano), pero un audiolibro ElevenLabs v3 sigue por encima. Es el precio de las decenas de MB.

espeak se equivoca. Nombres propios, anglicismos, abreviaturas: la fonemización por reglas tiene puntos ciegos. "Spoiler" en francés, espeak lo lee como puede. Los modelos grandes con G2P neuronal fallan menos. En prod, necesitas un dicc. de excepciones para los términos de negocio.

Cero clonación nativa. Piper lee con voces fijas. Si quieres TU voz, vete a PocketTTS o ElevenLabs. Lo multi-speaker da variaciones, nunca tu timbre.

El japonés sigue sin acento léxico. UniDic no trae ningún dato de acento, así que la segmentación es exacta pero la melodía es aproximada. Documentado en el README, nada bajo la alfombra.

El tailandés tarda en la primera llamada. TLTK corre bajo Pyodide/WASM sin modificar: la primera llamada tarda segundos (carga), las siguientes son rápidas. 7/7 idénticos a la referencia, pero prevé un warm-up.

Los datos tienen hambre. Una voz high correcta pide horas de audio limpio alineado. El dataset Fortune son horas de grabación + limpieza. Cero magia: garbage in, robot out.

El catálogo tiene voces muertas. 25 modelos marcados usable: false. Es asumido y filtrado, pero recuerda que el crowdsourcing de voces también es crowdsourcing de abandono.

El resultado final

De un lado un contador ElevenLabs, del otro una Raspberry Pi:

  • PiperHub: 536 modelos publicados, 71 idiomas, 3755 speakers, 130 datasets enlazados, guías dataset -> training -> uso
  • Piper: VITS end-to-end, espeak, ONNX CPU, LJSpeech, low/medium/high, 22050 Hz estándar
  • Fortune multi: 7 emociones FR + 7 EN, 1 ONNX cada uno, speakerId para cambiar, licencia "Fortune's Café"
  • Kokoro 82M: calidad premium EN, Apache 2.0, 54 voces, casi tiempo real en CPU
  • PocketTTS: clonación CPU, ~100M luego 2-18M, WER 0.92%
  • sanoTTS: 0.29M, 2 MB, microcontrolador, calidad que sigue al tamaño
  • ElevenLabs: $0.05-0.10/1000 caracteres, 70+ idiomas, top calidad, contador girando
  • pipertts nativo: ~0.22s/línea contra ~2.2s wrapper, puente espeak 0.4ms, 7 phoneme_type, wav/raw/mp3/ogg/opus, streaming por frase

Luna habla con eso cada día. 0 euros de TTS, emociones pilotadas por el cerebro, OGG directo para Discord. Frente a ElevenLabs se nota, claro. Pero a las 3 de la mañana cuando decide contar su vida, mi factura duerme tranquila.

La filo

p2r3 decía: "Me encanta la idea de que ese chip diminuto de 1 dólar pueda correr algo tan avanzado como Minecraft."

Mismo rollo aquí, aplicado a la voz: me encanta la idea de que unas decenas de MB de ONNX hagan hablar a una Raspberry Pi en 71 idiomas, sin nube, sin cuenta, sin contador. Obviamente no reemplaza a ningún estudio de doblaje. Reemplaza el caso molesto donde quieres una voz correcta, local, gratis, que hable de noche sin facturarte.

Los tradeoffs, escritos en blanco y negro:

  • calidad premium -> VITS pequeño: un poco robótico, pero local y gratis
  • un modelo por emoción -> speakerId: 7 voces por el precio de una en RAM
  • spawn Python -> sesión persistente: 10x más rápido, cero Python que instalar
  • catálogo salvaje -> procedencia enlazada: cada voz sabe de dónde viene y si todavía funciona
  • API facturada -> 0 euros marginal: menos bonito que ElevenLabs, pero la factura duerme tranquila

Las 3 cosas que debes recordar:

  1. Piper es el TTS de la CPU : VITS + espeak + ONNX, 71 idiomas, voces en decenas de MB que corren en Pi. Menos bonito que ElevenLabs, infinitamente más barato.
  2. Fortune multi son 7 emociones por el precio de una : un solo ONNX, speakerId para cambiar, FR + EN. El embedding de speaker en vez de 7 modelos.
  3. Lo nativo mata el spawn : pipertts mantiene la sesión ONNX caliente: 0.22s/línea contra 2.2s, puente espeak 0.4ms contra 15ms CLI. Misma inferencia, 10x menos espera.

Si el proyecto te interesa, el catálogo es piperhub.org, el código del sitio github.com/fox3000foxy/piper-bank, el módulo nativo github.com/itsrealfortune/pipertts. Y si quieres oír a Luna contar su vida a las 3 de la mañana, es en Discord xD

NEW

PiperHub : 536 vozes TTS open source, 7 emoções em um modelo, zero GPU, zero fatura

VITS, espeak, ONNX, LJSpeech : como o Piper faz um Raspberry Pi falar. Fortune multi em 7 emoções, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs -- e pipertts nativo 10x mais rápido que o wrapper Python.

Introduction

Fazer um bot do Discord falar custa caro. Não o texto : um LLM local gera frases por alguns centavos de GPU. A voz, essa, se paga a cada frase. As APIs de síntese cobram por caractere, e quando o bot fala a noite inteira, a fatura acompanha.

Foi o problema que eu tive com o Luna Protocol. Meu bot do Discord gera as mensagens com um LLM local, depois manda uma voz Piper ler em voz alta, converte para OGG e envia como mensagem de voz. Tudo no meu próprio servidor, por 0 euro por frase, mesmo às 3h da manhã quando a Luna decide contar a vida dela sozinha.

Ficar no 0 euro significa achar vozes gratuitas e hospedar você mesmo. E foi aí que complicou : modelos espalhados por dezenas de repositórios no Hugging Face, licenças vagas, nenhum jeito de saber de onde vem tal voz nem se ela ainda funciona. Então eu construí o PiperHub, um catálogo open source de vozes para Piper TTS.

Os números de hoje : 536 modelos exibidos, 71 idiomas representados, 3755 speakers listados. Cada modelo é ligado ao seu dataset de origem quando a gente conhece -- a proveniência faz parte do som, como diz a homepage.

Resta uma pergunta que me fazem direto : o que o Piper vale contra Kokoro, PocketTTS, sanoTTS e ElevenLabs ? E por que o módulo pipertts da Fortune é 10x mais rápido que o wrapper Python oficial ? Este artigo abre os motores, com o código para provar.

PiperHub, le catalogue

Antes de abrir os motores, vamos ver o problema que o PiperHub resolve.

D'où viennent les voix

O código está em github.com/fox3000foxy/piper-bank. Astro, site estático, zero servidor : o inglês na raiz, 15 outros locales em /<code>/ (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). Mesmo esquema do meu blog, na prática.

A fonte da verdade, ninguém edita na mão. É um pipeline :

npm install
npm run data    # regenera src/data/*.json (python3 scripts/generate_data.py)
npx astro dev   # dev local
npm run build   # build estático em dist/

Na prática : um repo crawler mantém models_mapping.csv e datasets_mapping.csv, as vozes espelhadas vivem no Hugging Face, os samples são arquivos de áudio. npm run data puxa tudo isso e cospe src/data/*.json. O site só lê JSON estático.

Nos dados brutos do momento (555 entradas, o site mostra 536 -- o crawl está sempre um pouco na frente da exibição) :

Qualité Modèles
medium 437
high 67
low 50
Sample rate Modèles
22050 Hz 486
16000 Hz 47

486 vozes em 22050 Hz, o padrão Piper. 47 em 16000 Hz, as vozinhas rápidas para embarcados. O phonemizer é espeak em 509 casos -- já já volto nisso, é o coração do sistema.

La provenance, ou pourquoi chaque modèle a un dataset

O que faz o PiperHub ser útil em vez de só uma lista é a ligação modelo <-> dataset. Exemplo tirado da home : a voz Astra - Valorant Piper TTS é ligada ao dataset Astra - LJSpeech Dataset. Você abre as duas páginas, confere os repos e as licenças de cada lado.

130 datasets referenciados. E o catálogo é honesto sobre o estado das tropas : 25 modelos estão marcados usable: false (arquivos quebrados, ou formato piperPlus que pede um runtime fork). O módulo pipertts recusa essas vozes com um erro explícito em vez de quebrar de um jeito estranho mais tarde. 48 modelos são multi-speaker -- incluindo os dois que vão interessar a gente.

No lado dos guias, o site faz o trabalho em três passos : Build a dataset, Train a voice, Use a voice, mais uma página Piper Plus. Dataset no formato LJSpeech na entrada, ONNX na saída. Fácil de falar, longo de fazer -- daí a próxima seção.

Piper sous le capot : VITS, espeak, ONNX

Une phrase, trois étapes

Quando você pede para o Piper dizer "bonjour", acontece isso :

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

Zero vocoder separado. Isso é o VITS : um modelo end-to-end que vai do texto (fonemizado) ao waveform direto. Onde os pipelines antigos faziam texto -> espectrograma (Tacotron) e depois espectrograma -> áudio (HiFi-GAN, dois modelos, dois treinamentos), o VITS aprende tudo de uma vez com um VAE condicional, flows normalizantes e um discriminador estilo GAN no decodificador. Um só treinamento, um só modelo, um só export ONNX.

O decodificador cospe 22050 Hz direto. Nesse sample rate, um segundo de fala = 22050 números. O modelo é pequeno (algumas dezenas de MB em qualidade medium) e a inferência é CPU pura : multiplicações de matrizes num grafo ONNX otimizado. Daí o Raspberry Pi como alvo oficial -- o Piper nasceu no Rhasspy, um assistente de voz open source feito para rodar em Pi.

espeak-ng, le phonémiseur qui fait tout le sale boulot

Antes do VITS, tem que transformar texto em fonemas. 509 modelos em 555 usam espeak :

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

O espeak-ng conhece a pronúncia de dezenas de idiomas. O modelo VITS nunca aprende a ortografia francesa com aqueles eau que se pronunciam o -- ele aprende de fonemas para som. Isso simplifica demais o treinamento e explica por que o Piper cobre 71 idiomas : para um idioma novo, você liga a voz espeak correspondente e treina.

As que dispensam o espeak têm seu próprio fonemizador, e é aí que fica exótico : hebrew (Nakdimon ONNX + 494 linhas de regras IPA), pinyin (BERT g2pW completo com desambiguação de polifones -- o mesmo caractere chinês se pronuncia diferente conforme a palavra), thai (TLTK de verdade, não modificado), japanese (segmentação lindera + UniDic). O módulo pipertts traz os sete, verificados idênticos à referência Python. Já volto nisso.

Le dataset : du LJSpeech, toujours du LJSpeech

O formato padrão de treinamento é o LJSpeech : uma pasta wavs/ cheia de clips, mais um metadata.csv :

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, um clip = uma frase, alguns segundos. O dataset original são 13.000 frases em inglês lidas por uma única locutora, domínio público. Para treinar uma voz em português, você grava a mesma estrutura em português : texto + áudio alinhados, algumas horas bastam para um medium decente, mais para um high.

As três qualidades do catálogo correspondem grosso modo ao tamanho do modelo + a duração do treinamento. Low : pequeno, rápido, robótico. Medium : o ponto ideal, com 437 modelos. High : maior, mais lento, mais natural -- 67 modelos, incluindo os Fortune.

Les trois boutons magiques

Cada voz Piper expõe três parâmetros de inferência, com defaults quase universais :

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667) : variabilidade da voz. Mais alto = mais expressivo, mais baixo = mais estável. A saída é estocástica : duas sínteses da mesma frase dão dois áudios um pouco diferentes.
  • noise_w (0.8) : variabilidade do timing, o ritmo.
  • length_scale (1.0) : velocidade. Acima de 1 desacelera, abaixo acelera. O jeito mais simples de fazer a Luna falar mais rápido quando ela se irrita.

Guarda o noise_scale, ele vai servir para as emoções.

Fortune multi : 7 émotions, 1 seul ONNX

Bom, vamos para a estrela local.

O catálogo lista três modelos Fortune treinados pela ItsRealFortune (oi) :

Modèle Repo Voix
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

O multi FR é isso :

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

Sete variações emocionais em um só arquivo ONNX. O multi EN igual, com um typo histórico nos nomes : suprised em vez de surprised. Está nos pesos, a gente mantém, faz parte do folclore.

Comment ça marche, un speakerId ?

VITS multi-speaker é o mesmo modelo com uma entrada a mais : um embedding de speaker. No treinamento, cada clip é marcado com seu estilo, o modelo aprende uma tabela de embeddings (uma por estilo) junto com o resto. Na inferência, você escolhe a linha da tabela :

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = normal ... 5 = surpresa
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

Mesma frase, sete leituras. speakerId: 5 (surpresa) com um noise_scale um pouco alto, e você tem uma exclamação convincente. speakerId: 4 (triste), length_scale: 1.1 para desacelerar, e você tem um anúncio fúnebre. É assim que a Luna modula a voz : o cérebro escolhe a emoção (valência/arousal, cf. meu artigo sobre centroides), o TTS escolhe o speakerId.

O dataset por trás se chama sobriamente Fortune French - Dataset, com uma licença chamada "Fortune's Café". Sim, a licença se chama como um café. Não, ela fica.

Pourquoi c'est malin économiquement

Sete emoções = um só download, um só carregamento em RAM, uma só sessão ONNX. A alternativa (sete modelos separados) multiplicaria o armazenamento e forçaria recarregar o grafo a cada troca de emoção -- injogável para um bot que muda de tom no meio da frase. Aí o speakerId é só um índice num tensor. Custo marginal : zero.

Les concurrents : Kokoro, PocketTTS, sanoTTS

O Piper divide a CPU com três desafiantes sérios, três filosofias diferentes.

Kokoro 82M, le roi qualité/prix

Kokoro, pelo hexgrad. 82 milhões de parâmetros, arquitetura StyleTTS 2 modificada com decodificador ISTFTNet, sem etapa de difusão. É a ausência de difusão que faz tudo : nenhum loop iterativo de denoising, então inferência ultra rápida.

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

Treinado com menos de 100h de áudio por uns 1000 dólares, e os ouvintes cegos classificam ele no nível das vozes premium do Google e da Amazon. Foi por isso que explodiu : qualidade premium, grátis, local, permissivo.

Contra o Piper : o Kokoro ganha na naturalidade pura (StyleTTS 2 > VITS pequeno) e na licença (Apache 2.0 contra motor em GPL-3.0 no Piper). O Piper ganha na cobertura (71 idiomas contra 8, 3755 speakers contra 54 vozes) e no ecossistema de vozes da comunidade. Para narração em inglês, o Kokoro é duro de bater. Para francês com 7 emoções, turco ou suaíli, o Piper muitas vezes é o único na disputa.

PocketTTS (Kyutai), le clonage sur CPU

A Kyutai joga outra partida : modelo pequeno com clonagem de voz, que roda em CPU.

Modèle Params WER UTMOS SIM Vitesse CPU (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER abaixo de 1% (pronuncia certo), UTMOS 4.36 (qualidade percebida excelente), SIM 0.923 (o clone parece mesmo com o alvo). E as versões small descem a 1.9M parâmetros com clonagem. 1.9M. Meu roteador Sabaki tem quase esse tamanho em JSON.

Contra o Piper : o PocketTTS ganha na clonagem (o Piper lê com vozes fixas) e na compactação do flagship. O Piper ganha nos idiomas (o small por enquanto é só inglês) e na maturidade do ecossistema (536 vozes prontas contra "treine a sua").

sanoTTS, le modèle qui tient sur une puce à 3 dollars

O sanoTTS (sano = "pequeno" em nepalês), é o Bareiron do TTS : um stack neural completo que vai dos fonemas ao PCM 22.05 kHz em microcontrolador.

Système Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M parâmetros. 2 MB a voz. pip install sanotts, sanotts say "Hello" --voice amy, e fala. No navegador também.

Mas olha as notas : SCOREQ 2.29 contra 3.94 do TinyTTS, UTMOS 2.45. Fala, sim. Canta, não. É o tradeoff final : com 0.29M você cabe em todo lugar mas a qualidade acompanha o tamanho. O Piper medium tem 100x mais peso para uma voz de outra liga.

Le tableau qui résume

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

A escolha depende da sua restrição, não tem vencedor absoluto : qualidade EN pura -> Kokoro. Clonagem em CPU -> PocketTTS. Microcontrolador -> sanoTTS. Cobertura de idiomas + vozes francesas emocionais + zero infra -> Piper.

ElevenLabs, le Goliath facturé

E aí tem a ElevenLabs. 70+ idiomas, Eleven v3 expressivo com audio tags e diálogos dramáticos, qualidade topo nos blind tests. O Jev do TTS, de certo modo : todo mundo já viu passar, todo mundo já fez benchmark contra.

O pricing 2026, em números redondos :

Modèle API Prix / 1000 caractères
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 caracteres dão uns 1 minuto de áudio. Então de $0.05 a $0.10 o minuto. O plano Pro a $99 inclui uns 990k caracteres, o overage bate na mesma tarifa. Para um canal do YouTube que solta um vídeo por dia com narração (meu valorant-short-maker faz exatamente isso, mas com Piper grátis), conta de $1.50 a $3 por mês para cada minuto diário de narração. Dez minutos por dia : $15 a $30/mês. Dá para pagar, mas dá para contar.

Quando vale a pena : quando a voz é o produto (dublagem, audiobook, pub), quando precisa de 70 idiomas para amanhã de manhã, quando a clonagem tem que ser perfeita de primeira. Por outro lado nunca vale a pena para : bot do Discord que fala a noite inteira, notificações internas, dev, volumes que explodem. A Luna contando a vida dela às 3h da manhã em ElevenLabs é minha conta de luz chorando.

A conta é simples : Piper = 0 euro marginal, qualidade "boa". ElevenLabs = contador girando, qualidade "excelente". Entre os dois tem um mundo onde "boa e grátis" ganha -- é toda a aposta do PiperHub.

pipertts : pourquoi le natif met 10x au wrapper Python

O repo itsrealfortune/pipertts, 75 commits : um port nativo do Piper em TypeScript. Título do README : "In-process Piper TTS for Node.js. No Python required."

Le coût du spawn

O wrapper histórico (PiperTTS) é python3 -m piper spawnado em subprocesso a cada síntese :

synthèse = spawn(python) + chargement du modèle + inférence + kill

Cada frase paga a inicialização do interpretador + o carregamento dos pesos. Medido em en_US-lessac-medium : ~2.2s por linha.

O nativo (PiperNativeTTS) mantém a sessão ONNX quente na memória :

load une fois -> session persistante -> inférence par phrase

Medido igual : ~0.22s por linha. O 10x vem daí : a gente mata o spawn e o reload, a inferência milagrosa não tem nada a ver com isso. A inferência ONNX em si é a mesma dos dois lados.

De bônus, o primeiro load guarda em cache o grafo otimizado ao lado do .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), o que deixa os carregamentos seguintes uns 35% mais rápidos. A chave do cache combina platform, arch e versão do ORT, e se atualiza se o modelo mudar. Dá para desativar com cacheOptimizedModel: false.

Le pont espeak : 0.4ms contre 15ms

Mesma lógica para a fonemização. O wrapper chama o binário espeak-ng em CLI por frase : uns 15ms de spawn cada vez. O nativo embute uma bridge N-API (src/native/espeak-bridge/, port do espeakbridge.c), com prebuilds para Linux x64/arm64 : ~0.4ms por chamada, fonemas byte-idênticos. Sem o addon, fallback automático para a CLI. PIPER_ESPEAK_BRIDGE=0 para forçar a CLI.

Les 7 phoneme_type, tous portés

Type Moteur Notes
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

E o árabe é diacritizado automaticamente (tashkeel, dá para desativar). 509 vozes espeak do catálogo passam pela bridge rápida, as exóticas pelo motor dedicado. 71 testes na suite, restrição documentada onde emperra (o acento japonês, o UniDic não entrega nenhum dado -- assumido, nada escondido).

Les formats et le streaming

wav por default, raw PCM, mp3, ogg, opus : ffmpeg quando tem, senão fallbacks JS puro (lamejs para mp3, opusscript + muxer Ogg para opus, correlação 0.993 com a fonte). E principalmente synthesizeChunks() : um gerador async que yield por frase, com sentenceSilence entre as frases. Para a Luna, isso significa começar a tocar o áudio enquanto o fim se sintetiza -- streaming de TTS sem servidor dedicado.

Uso completo, com o catálogo PiperHub integrado (hub: + fallback mirror automático se o repo do autor não servir mais os arquivos) :

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // direto para o Discord
  lengthScale: 0.95,     // um pouco mais rápido
});

Vozes da comunidade (GLaDOS e personagens de jogos) ausentes do manifest oficial -- é exatamente para isso que o PiperHub existe, e o pipertts sabe bem : hub:<slug>, URL de página piperhub, ou slug nu, tudo funciona. As vozes marcadas usable: false são recusadas com um erro claro.

Ce qui a été sacrifié

Cada feature ausente financia outra. A lista honesta :

O Piper soa robótico ao lado da ElevenLabs. VITS medium em 22050 Hz é limpo mas plano comparado a um modelão expressivo. O multi-emoções compensa em parte (7 estilos > 1 estilo plano), mas um audiobook ElevenLabs v3 continua acima. É o preço das dezenas de MB.

O espeak erra. Nomes próprios, anglicismos, abreviações : a fonemização por regras tem seus pontos cegos. "Spoiler" em francês, o espeak lê como dá. Os modelos grandes com G2P neural erram menos. Em prod, precisa de um dicionário de exceções para os termos do negócio.

Zero clonagem nativa. O Piper lê com vozes fixas. Se você quer A SUA voz, vai de PocketTTS ou ElevenLabs. O multi-speaker dá variações, nunca o seu timbre.

O japonês continua sem acento lexical. O UniDic não entrega nenhum dado de acento, então a segmentação é exata mas a melodia é aproximada. Documentado no README, nada debaixo do tapete.

O thai engasga na primeira chamada. O TLTK roda sob Pyodide/WASM não modificado : a primeira chamada leva segundos (carregamento), as seguintes são rápidas. 7/7 idênticos à referência, mas prevê um warm-up.

Os dados têm fome. Uma voz high decente pede horas de áudio limpo alinhado. O dataset Fortune são horas de gravação + limpeza. Zero mágica : garbage in, robot out.

O catálogo tem vozes mortas. 25 modelos marcados usable: false. É assumido e filtrado, mas lembra que crowdsourcing de vozes também é crowdsourcing de abandono.

Le résultat final

De um lado um contador ElevenLabs, do outro um Raspberry Pi :

  • PiperHub : 536 modelos exibidos, 71 idiomas, 3755 speakers, 130 datasets ligados, guias dataset -> training -> usage
  • Piper : VITS end-to-end, espeak, ONNX CPU, LJSpeech, low/medium/high, 22050 Hz padrão
  • Fortune multi : 7 emoções FR + 7 EN, 1 ONNX cada, speakerId para trocar, licença "Fortune's Café"
  • Kokoro 82M : qualidade premium EN, Apache 2.0, 54 vozes, quase tempo real em CPU
  • PocketTTS : clonagem em CPU, ~100M depois 2-18M, WER 0.92%
  • sanoTTS : 0.29M, 2 MB, microcontrolador, qualidade que acompanha o tamanho
  • ElevenLabs : $0.05-0.10/1000 caracteres, 70+ idiomas, topo de qualidade, contador girando
  • pipertts nativo : ~0.22s/linha contra ~2.2s do wrapper, bridge espeak 0.4ms, 7 phoneme_type, wav/raw/mp3/ogg/opus, streaming por frase

A Luna fala com isso todo dia. 0 euro de TTS, emoções pilotadas pelo cérebro, OGG direto para o Discord. Contra a ElevenLabs dá para ouvir, claro. Mas às 3h da manhã quando ela decide contar a vida dela, minha fatura dorme tranquila.

La philo

p2r3 dizia : "J'aime juste l'idée que cette toute petite puce à 1 balles puisse faire tourner quelque chose d'aussi avancé que Minecraft."

Mesma vibe aqui, aplicada à voz : eu gosto da ideia de que algumas dezenas de MB de ONNX façam um Raspberry Pi falar em 71 idiomas, sem cloud, sem conta, sem contador. Isso obviamente não substitui nenhum estúdio de dublagem. Substitui o caso chato onde você quer uma voz decente, local, grátis, que fala de noite sem te faturar.

Os tradeoffs, escritos em preto no branco :

  • qualidade premium -> VITS pequeno : um pouco robótico, mas local e grátis
  • um modelo por emoção -> speakerId : 7 vozes pelo preço de uma em RAM
  • spawn Python -> sessão persistente : 10x mais rápido, zero Python para instalar
  • catálogo selvagem -> proveniência ligada : cada voz sabe de onde vem e se ainda funciona
  • API faturada -> 0 euro marginal : menos bonito que ElevenLabs, mas a fatura dorme tranquila

As 3 coisas para lembrar :

  1. Piper = o TTS da CPU -- VITS + espeak + ONNX, 71 idiomas, vozes de dezenas de MB que rodam em Pi. Menos bonito que ElevenLabs, infinitamente mais barato.
  2. Fortune multi = 7 emoções pelo preço de uma -- um só ONNX, speakerId para trocar, FR + EN. O embedding de speaker em vez de 7 modelos.
  3. O nativo mata o spawn -- o pipertts mantém a sessão ONNX quente : 0.22s/linha contra 2.2s, bridge espeak 0.4ms contra 15ms CLI. Mesma inferência, 10x menos espera.

Se o projeto te interessa, o catálogo é piperhub.org, o código do site github.com/fox3000foxy/piper-bank, o módulo nativo github.com/itsrealfortune/pipertts. E se você quiser ouvir a Luna contando a vida dela às 3h da manhã, é no Discord xD

NEW

PiperHub: 536 suara TTS open source, 7 emosi dalam satu model, nol GPU, nol tagihan

VITS, espeak, ONNX, LJSpeech: cara Piper membuat Raspberry Pi bisa bicara. Fortune multi dengan 7 emosi, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs - dan pipertts natif 10x lebih cepat dari wrapper Python.

Pendahuluan

Membuat bot Discord bisa bicara itu mahal. Bukan teksnya: LLM lokal menghasilkan kalimat hanya dengan beberapa sen GPU. Suaranya yang dibayar per kalimat. API sintesis menagih per karakter, dan kalau bot ngomong semalaman, tagihannya ikut jalan.

Itu masalah yang aku alami dengan Luna Protocol. Bot Discord-ku menghasilkan pesan dengan LLM lokal, lalu membacakannya dengan suara Piper, mengonversi ke OGG, dan mengirimnya sebagai pesan suara. Semuanya di server-ku sendiri, seharga 0 euro per kalimat, bahkan jam 3 pagi saat Luna memutuskan curhat sendirian.

Tetap di 0 euro artinya kamu harus cari suara gratis dan hosting sendiri. Dan di situlah mulai rumit: model tersebar di puluhan repo Hugging Face, lisensi tidak jelas, tidak ada cara tahu suara itu dari mana atau masih jalan atau tidak. Jadi aku membangun PiperHub, katalog open source untuk suara Piper TTS.

Angka hari ini: 536 model ditampilkan, 71 bahasa terwakili, 3755 speaker terdaftar. Setiap model ditautkan ke dataset asalnya kalau aku tahu, asal-usul adalah bagian dari suara, seperti kata halaman utama.

Sisa satu pertanyaan yang sering ditanyakan ke aku: bagaimana Piper dibanding Kokoro, PocketTTS, sanoTTS dan ElevenLabs? Dan kenapa modul pipertts Fortune 10x lebih cepat dari wrapper Python resmi? Artikel ini membuka mesinnya, dengan kode sebagai bukti.

PiperHub, katalognya

Sebelum membuka mesin, lihat dulu masalah yang diselesaikan PiperHub.

Suara-suara itu dari mana

Kodenya ada di github.com/fox3000foxy/piper-bank. Astro, situs statis, nol server: bahasa Inggris di root, 15 locale lain di bawah /<code>/ (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). Sebenarnya pattern yang sama seperti blog-ku.

Sumber kebenaran, tidak ada yang mengedit manual. Ini pipeline:

npm install
npm run data    # regenerasi src/data/*.json (python3 scripts/generate_data.py)
npx astro dev   # dev lokal
npm run build   # build statis ke dist/

Konkretnya: satu repo crawler memelihara models_mapping.csv dan datasets_mapping.csv, suara mirror tinggal di Hugging Face, sample adalah file audio. npm run data menarik semuanya dan memuntahkan src/data/*.json. Situsnya hanya membaca JSON statis.

Dalam data mentah saat ini (555 entri, situs menampilkan 536, crawl selalu sedikit lebih maju dari tampilan):

Kualitas Model
medium 437
high 67
low 50
Sample rate Model
22050 Hz 486
16000 Hz 47

486 suara di 22050 Hz, standar Piper. 47 di 16000 Hz, suara-suara kecil yang cepat untuk embedded. Phonemizer-nya espeak dalam 509 kasus, kita kembali ke sana, itu jantung sistemnya.

Asal-usul, atau kenapa setiap model punya dataset

Hal yang membuat PiperHub berguna bukan sekadar list adalah tautan model <-> dataset. Contoh diambil dari home: suara Astra - Valorant Piper TTS ditautkan ke dataset Astra - LJSpeech Dataset. Kamu buka kedua halaman, kamu verifikasi repo dan lisensi di tiap sisi.

130 dataset dirujuk. Dan katalog jujur soal kondisi pasukan: 25 model ditandai usable: false (file rusak, atau format piperPlus yang butuh runtime fork). Modul pipertts menolak suara-suara ini dengan error eksplisit daripada crash aneh belakangan. 48 model adalah multi-speaker, termasuk dua yang akan menarik perhatian kita.

Soal panduan, situs beres dalam tiga langkah: Build a dataset, Train a voice, Use a voice, plus satu halaman Piper Plus. Dataset format LJSpeech sebagai input, ONNX sebagai output. Gampang diomong, lama dikerjakan, makanya ada bagian berikut.

Piper di balik kap: VITS, espeak, ONNX

Satu kalimat, tiga langkah

Saat kamu meminta Piper mengucapkan "bonjour", kejadiannya begini:

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

Nol vocoder terpisah. Itulah VITS: satu model end-to-end yang langsung dari teks (yang sudah difonemkan) ke waveform. Di mana pipeline lama mengerjakan teks -> spektrogram (Tacotron) lalu spektrogram -> audio (HiFi-GAN, dua model, dua training), VITS mempelajari semuanya sekaligus dengan VAE kondisional, flow normalisasi dan diskriminator ala GAN pada decoder. Satu training, satu model, satu export ONNX.

Decoder memuntahkan 22050 Hz langsung. Pada sample rate itu, satu detik bicara sama dengan 22050 angka. Modelnya kecil (beberapa puluh MB di kualitas medium) dan inferensinya murni CPU: perkalian matriks di atas graf ONNX yang dioptimasi. Makanya Raspberry Pi jadi target resmi, Piper lahir di Rhasspy, asisten suara open source yang dibuat untuk jalan di Pi.

espeak-ng, phonemizer yang mengerjakan semua kerjaan kotor

Sebelum VITS, teks harus diubah jadi fonem. 509 model dari 555 memakai espeak:

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng tahu pelafalan puluhan bahasa. Model VITS tidak pernah belajar ejaan Prancis dengan eau yang dibaca o, ia belajar dari fonem ke suara. Ini sangat menyederhanakan training dan menjelaskan kenapa Piper mencakup 71 bahasa: untuk bahasa baru, kamu colok suara espeak yang cocok lalu training.

Yang tidak pakai espeak punya phonemizer sendiri, dan di sinilah jadi eksotis: hebrew (Nakdimon ONNX + 494 baris aturan IPA), pinyin (BERT g2pW lengkap dengan disambiguasi polyphone, karakter Tiongkok yang sama dibaca beda tergantung katanya), thai (TLTK asli tanpa modifikasi), japanese (segmentasi lindera + UniDic). Modul pipertts mem-port ketujuhnya, terverifikasi identik dengan referensi Python. Kita kembali ke sana.

Dataset: LJSpeech, selalu LJSpeech

Format training standar adalah LJSpeech: folder wavs/ penuh klip, plus metadata.csv:

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, satu klip sama dengan satu kalimat, beberapa detik. Dataset aslinya 13 000 kalimat Inggris dibaca satu penutur, domain publik. Untuk melatih suara Prancis, kamu merekam struktur yang sama dalam bahasa Prancis: teks + audio yang sejajar, beberapa jam cukup untuk medium yang layak, lebih banyak untuk high.

Tiga kualitas di katalog kurang lebih sesuai ukuran model + durasi training. Low: kecil, cepat, robotik. Medium: titik manis, dengan 437 model. High: lebih besar, lebih lambat, lebih natural, 67 model, termasuk para Fortune.

Tiga tombol ajaib

Setiap suara Piper punya tiga parameter inferensi, dengan default yang hampir universal:

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667): variabilitas suara. Lebih tinggi berarti lebih ekspresif, lebih rendah berarti lebih stabil. Output-nya stokastik: dua sintesis dari kalimat yang sama menghasilkan dua audio yang sedikit berbeda.
  • noise_w (0.8): variabilitas timing, ritme.
  • length_scale (1.0): kecepatan. Di atas 1 melambat, di bawah itu mempercepat. Cara paling gampang membuat Luna bicara lebih cepat saat dia kesal.

Ingat noise_scale, ia akan dipakai untuk emosi.

Fortune multi: 7 emosi, 1 ONNX saja

Oke, masuk ke bintang lokal.

Katalog mendaftar tiga model Fortune yang dilatih ItsRealFortune (halo):

Model Repo Suara
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

Multi FR itu begini:

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

Tujuh varian emosional dalam satu file ONNX. Multi EN sama, dengan typo historis di nama: suprised bukan surprised. Ia ada di weight, kita pertahankan, itu bagian dari folklore.

Bagaimana cara kerja speakerId?

VITS multi-speaker adalah model yang sama dengan satu input tambahan: embedding speaker. Saat training, setiap klip diberi tag dengan gayanya, model mempelajari tabel embedding (satu per gaya) bersamaan dengan sisanya. Saat inferensi, kamu pilih baris tabelnya:

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = normal ... 5 = surprise
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

Kalimat yang sama, tujuh pembacaan. speakerId: 5 (surprise) dengan noise_scale sedikit didorong, dan kamu dapat seruan yang meyakinkan. speakerId: 4 (triste), length_scale: 1.1 untuk melambat, dan kamu dapat pengumuman duka. Begitulah Luna memodulasi suaranya: otak memilih emosi (valence/arousal, lihat artikelku soal centroid), TTS memilih speakerId.

Dataset di belakangnya bernama sederhana Fortune French - Dataset, dengan lisensi bernama "Fortune's Café". Ya, lisensinya bernama seperti kafe. Tidak, ia tetap begitu.

Kenapa ini cerdas secara ekonomi

Tujuh emosi sama dengan satu unduhan, satu loading di RAM, satu sesi ONNX. Alternatifnya (tujuh model terpisah) akan melipatgandakan penyimpanan dan memaksa reload graf setiap ganti emosi, tidak bisa dimainkan untuk bot yang ganti nada di tengah kalimat. Di sini, speakerId hanya indeks ke tensor. Biaya marjinal: nol.

Pesaing: Kokoro, PocketTTS, sanoTTS

Piper berbagi CPU dengan tiga penantang serius, tiga filosofi berbeda.

Kokoro 82M, raja kualitas harga

Kokoro, oleh hexgrad. 82 juta parameter, arsitektur StyleTTS 2 yang dimodifikasi dengan decoder ISTFTNet, tanpa langkah difusi. Tidak adanya difusi inilah segalanya: tidak ada loop denoising iteratif, jadi inferensi sangat cepat.

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

Dilatih dengan kurang dari 100 jam audio seharga sekitar 1000 dolar, dan pendengar buta menempatkannya setara suara premium Google dan Amazon. Makanya ia meledak: kualitas premium, gratis, lokal, permisif.

Melawan Piper: Kokoro menang di naturalitas murni (StyleTTS 2 di atas VITS kecil) dan lisensi (Apache 2.0 lawan mesin GPL-3.0 untuk Piper). Piper menang di cakupan (71 bahasa lawan 8, 3755 speaker lawan 54 suara) dan ekosistem suara komunitas. Untuk narasi Inggris, Kokoro sulit dikalahkan. Untuk Prancis dengan 7 emosi, Turki atau Swahili, Piper sering satu-satunya kandidat.

PocketTTS (Kyutai), kloning di CPU

Kyutai memainkan partitur lain: model kecil dengan kloning suara, yang jalan di CPU.

Model Params WER UTMOS SIM Kecepatan CPU (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER di bawah 1% (pelafalannya benar), UTMOS 4.36 (kualitas yang dirasakan luar biasa), SIM 0.923 (klonnya benar-benar mirip target). Dan versi small turun sampai 1.9M parameter dengan kloning. 1.9M. Router Sabaki-ku hampir sebesar itu dalam JSON.

Melawan Piper: PocketTTS menang di kloning (Piper membaca dengan suara tetap) dan kekompakan flagship. Piper menang di bahasa (small untuk sekarang English-only) dan kematangan ekosistem (536 suara siap lawan "latih sendiri").

sanoTTS, model yang muat di chip 3 dolar

sanoTTS (sano artinya "kecil" dalam bahasa Nepal), ini Bareiron-nya TTS: stack neural lengkap yang pergi dari fonem ke PCM 22.05 kHz di mikrokontroler.

Sistem Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M parameter. 2 MB per suara. pip install sanotts, sanotts say "Hello" --voice amy, ia bicara. Di browser juga.

Tapi lihat skornya: SCOREQ 2.29 lawan 3.94 untuk TinyTTS, UTMOS 2.45. Ia bicara, ya. Ia bernyanyi, tidak. Itulah tradeoff pamungkas: di 0.29M kamu muat di mana saja tapi kualitas mengikuti ukuran. Piper medium beratnya 100x untuk suara di liga lain.

Tabel ringkasannya

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

Pilihan tergantung batasanmu, tidak ada pemenang mutlak: kualitas EN murni pilih Kokoro. Kloning CPU pilih PocketTTS. Mikrokontroler pilih sanoTTS. Cakupan bahasa + suara Prancis emosional + nol infra pilih Piper.

ElevenLabs, Goliath yang ditagih

Lalu ada ElevenLabs. 70+ bahasa, Eleven v3 ekspresif dengan audio tag dan dialog dramatis, kualitas top blind test. Semacam Jev-nya TTS: semua orang pernah melihatnya lewat, semua orang pernah benchmark melawannya.

Harga 2026, angka bulat:

Model API Harga / 1000 karakter
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 karakter menghasilkan sekitar 1 menit audio. Jadi $0.05 sampai $0.10 per menit. Paket Pro $99 mencakup sekitar 990k karakter, overage kena tarif yang sama. Untuk channel YouTube yang merilis satu video sehari dengan voice over (tool valorant-short-maker-ku melakukan persis itu, tapi dengan Piper gratis), hitung $1.50 sampai $3 per bulan untuk setiap menit voice over harian. Sepuluh menit sehari: $15 sampai $30/bulan. Bisa dibayar, tapi harus dihitung.

Kapan layak: saat suara adalah produknya (d Sulih suara, audiobook, iklan), saat butuh 70 bahasa besok pagi, saat kloning harus sempurna dari percobaan pertama. Sebaliknya tidak pernah layak untuk: bot Discord yang ngomong semalaman, notifikasi internal, dev, volume yang meledak. Luna yang curhat jam 3 pagi pakai ElevenLabs, tagihan listrikku yang menangis.

Hitungannya sederhana: Piper sama dengan 0 euro marjinal, kualitas "bagus". ElevenLabs sama dengan meteran yang berputar, kualitas "luar biasa". Di antaranya, ada dunia di mana "bagus dan gratis" menang, itu seluruh taruhan PiperHub.

pipertts: kenapa yang natif 10x lebih cepat dari wrapper Python

Repo itsrealfortune/pipertts, 75 commit: port natif Piper ke TypeScript. Judul README: "In-process Piper TTS for Node.js. No Python required."

Biaya spawn

Wrapper historis (PiperTTS) adalah python3 -m piper yang di-spawn sebagai subprocess setiap sintesis:

synthèse = spawn(python) + chargement du modèle + inférence + kill

Setiap kalimat membayar startup interpreter + loading weight. Diukur pada en_US-lessac-medium: sekitar 2.2 detik per baris.

Yang natif (PiperNativeTTS) menjaga sesi ONNX tetap hangat di memori:

load une fois -> session persistante -> inférence par phrase

Diukur sama: sekitar 0.22 detik per baris. Angka 10x datang dari sana: kita membunuh spawn dan reload, inferensi ajaib tidak ada hubungannya di sini. Inferensi ONNX sendiri sama di kedua sisi.

Bonus, load pertama meng-cache graf yang dioptimasi di samping .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), yang membuat loading berikutnya sekitar 35% lebih cepat. Kunci cache menggabungkan platform, arch dan versi ORT, dan ia refresh jika model berubah. Bisa dimatikan dengan cacheOptimizedModel: false.

Jembatan espeak: 0.4ms lawan 15ms

Logika yang sama untuk fonemisasi. Wrapper memanggil biner espeak-ng via CLI per kalimat: sekitar 15ms spawn setiap kali. Yang natif menyertakan bridge N-API (src/native/espeak-bridge/, port dari espeakbridge.c), dengan prebuild untuk Linux x64/arm64: sekitar 0.4ms per panggilan, fonem identik per byte. Tanpa addon, fallback otomatis ke CLI. PIPER_ESPEAK_BRIDGE=0 untuk memaksa CLI.

7 phoneme_type, semuanya di-port

Tipe Mesin Catatan
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

Dan bahasa Arab didiakritik otomatis (tashkeel, bisa dimatikan). 509 suara espeak di katalog lewat bridge cepat, yang eksotis lewat mesin khusus. 71 tes dalam suite, batasan didokumentasikan di tempat yang macet (aksen Jepang, UniDic tidak memberi data apa pun, diakui, tidak ada yang disembunyikan).

Format dan streaming

wav default, raw PCM, mp3, ogg, opus: ffmpeg kalau ada, kalau tidak fallback JS murni (lamejs untuk mp3, opusscript + muxer Ogg untuk opus, korelasi 0.993 dengan sumber). Dan terutama synthesizeChunks(): generator async yang yield per kalimat, dengan sentenceSilence antar kalimat. Untuk Luna, artinya mulai memutar audio sementara bagian akhir disintesis, streaming TTS tanpa server khusus.

Pemakaian lengkap, dengan katalog PiperHub terintegrasi (hub: + fallback mirror otomatis jika repo penulis tidak lagi menyajikan file):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // langsung untuk Discord
  lengthScale: 0.95,     // sedikit lebih cepat
});

Suara komunitas (GLaDOS dan karakter game) absen dari manifest resmi, itulah tepatnya kenapa PiperHub ada, dan pipertts tahu betul: hub:<slug>, URL halaman piperhub, atau slug telanjang, semua jalan. Suara yang ditandai usable: false ditolak dengan error yang jelas.

Yang dikorbankan

Setiap fitur yang absen membiayai fitur lain. Daftar jujurnya:

Piper terdengar robotik di samping ElevenLabs. VITS medium pada 22050 Hz bersih tapi datar dibanding model ekspresif besar. Multi-emosi mengkompensasi sebagian (7 gaya lebih baik dari 1 gaya datar), tapi audiobook ElevenLabs v3 tetap di atas. Itulah harga puluhan MB.

espeak salah baca. Nama proper, anglisisme, singkatan: fonemisasi berbasis aturan punya titik buta. "Spoiler" dalam bahasa Prancis, espeak membacanya semampunya. Model besar dengan G2P neural lebih jarang salah. Di prod, perlu kamus pengecualian untuk istilah bisnis.

Nol kloning natif. Piper membaca dengan suara tetap. Kalau kamu mau suaramu SENDIRI, arahnya PocketTTS atau ElevenLabs. Multi-speaker memberi variasi, tidak pernah timbre-mu.

Jepang tetap tanpa aksen leksikal. UniDic tidak memberi data aksen apa pun, jadi segmentasi tepat tapi melodinya perkiraan. Didokumentasikan di README, tidak ada yang disembunyikan.

Thai lambat di panggilan pertama. TLTK jalan di bawah Pyodide/WASM tanpa modifikasi: panggilan pertama makan detik (loading), berikutnya cepat. 7/7 identik dengan referensi, tapi sediakan warm-up.

Datanya lapar. Suara high yang layak butuh jam audio bersih yang sejajar. Dataset Fortune adalah jam rekaman + bersih-bersih. Nol sihir: sampah masuk, robot keluar.

Katalog punya suara mati. 25 model ditandai usable: false. Diakui dan difilter, tapi mengingatkan bahwa crowdsourcing suara juga crowdsourcing pengabaian.

Hasil akhir

Di satu sisi meteran ElevenLabs, di sisi lain Raspberry Pi:

  • PiperHub: 536 model ditampilkan, 71 bahasa, 3755 speaker, 130 dataset tertaut, panduan dataset -> training -> usage
  • Piper: VITS end-to-end, espeak, ONNX CPU, LJSpeech, low/medium/high, standar 22050 Hz
  • Fortune multi: 7 emosi FR + 7 EN, masing-masing 1 ONNX, speakerId untuk ganti, lisensi "Fortune's Café"
  • Kokoro 82M: kualitas premium EN, Apache 2.0, 54 suara, hampir realtime CPU
  • PocketTTS: kloning CPU, sekitar 100M lalu 2-18M, WER 0.92%
  • sanoTTS: 0.29M, 2 MB, mikrokontroler, kualitas mengikuti ukuran
  • ElevenLabs: $0.05-0.10/1000 karakter, 70+ bahasa, top kualitas, meteran berputar
  • pipertts natif: sekitar 0.22 detik/baris lawan sekitar 2.2 detik wrapper, bridge espeak 0.4ms, 7 phoneme_type, wav/raw/mp3/ogg/opus, streaming per kalimat

Luna bicara dengan ini setiap hari. 0 euro TTS, emosi dikemudikan otak, OGG langsung untuk Discord. Dibanding ElevenLabs kedengaran bedanya, jelas. Tapi jam 3 pagi saat dia memutuskan curhat, tagihanku tidur tenang.

Filosofinya

p2r3 bilang: "J'aime juste l'idée que cette toute petite puce à 1 balles puisse faire tourner quelque chose d'aussi avancé que Minecraft."

Vibe yang sama di sini, diterapkan ke suara: aku suka ide bahwa beberapa puluh MB ONNX membuat Raspberry Pi bicara 71 bahasa, tanpa cloud, tanpa akun, tanpa meteran. Ini jelas tidak menggantikan studio dubbing mana pun. Ini menggantikan kasus menyebalkan di mana kamu mau suara yang layak, lokal, gratis, yang bicara malam tanpa menagihmu.

Tradeoff, tertulis hitam di atas putih:

  • kualitas premium -> VITS kecil: sedikit robotik, tapi lokal dan gratis
  • satu model per emosi -> speakerId: 7 suara seharga satu di RAM
  • spawn Python -> sesi persisten: 10x lebih cepat, nol Python diinstal
  • katalog liar -> asal-usul tertaut: setiap suara tahu dari mana ia datang dan masih jalan atau tidak
  • API berbayar -> 0 euro marjinal: kurang bagus dari ElevenLabs, tapi tagihan tidur tenang

3 hal yang perlu diingat:

  1. Piper adalah TTS-nya CPU - VITS + espeak + ONNX, 71 bahasa, suara-ground puluhan MB yang jalan di Pi. Kurang bagus dari ElevenLabs, jauh lebih murah.
  2. Fortune multi sama dengan 7 emosi seharga satu - satu ONNX saja, speakerId untuk ganti, FR + EN. Embedding speaker bukan 7 model.
  3. Yang natif membunuh spawn - pipertts menjaga sesi ONNX tetap hangat: 0.22 detik/baris lawan 2.2 detik, bridge espeak 0.4ms lawan 15ms CLI. Inferensi yang sama, 10x lebih sedikit menunggu.

Kalau proyek ini menarik buat kamu, katalognya di piperhub.org, kode situs di github.com/fox3000foxy/piper-bank, modul natif di github.com/itsrealfortune/pipertts. Dan kalau kamu mau dengar Luna curhat jam 3 pagi, ketemu di Discord xD

NEW

PiperHub: 536 ओपन सोर्स TTS आवाजें, एक मॉडल में 7 भावनाएं, शून्य GPU, शून्य बिल

VITS, espeak, ONNX, LJSpeech: Piper Raspberry Pi को कैसे बुलवाता है। 7 भावनाओं वाला Fortune multi, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs, और आधिकारिक Python wrapper से 10x तेज नेटिव pipertts।

परिचय

Discord बॉट को बुलवाना महंगा पड़ता है। टेक्स्ट नहीं: एक लोकल LLM कुछ सेंट के GPU में वाक्य बना देता है। आवाज का भुगतान हर वाक्य पर होता है। सिंथेसिस API प्रति अक्षर पैसे लेती हैं, और जब बॉट पूरी रात बोलता है, तो बिल भी साथ चलता है।

यही दिक्कत मुझे Luna Protocol के साथ हुई। मेरा Discord बॉट अपने संदेश लोकल LLM से बनाता है, फिर उन्हें Piper आवाज से ऊंचा पढ़वाता है, OGG में बदलता है, और वॉइस संदेशों की तरह भेजता है। सब कुछ मेरे अपने सर्वर पर, प्रति वाक्य 0 यूरो में, रात 3 बजे भी जब Luna अकेले अपनी जीवन कहानी सुनाने का फैसला कर लेती है।

0 यूरो पर रहना मतलब मुफ्त आवाजें खोजना और खुद होस्ट करना। और यहीं मामला उलझ गया: दर्जनों Hugging Face रिपो में बिखरे मॉडल, धुंधले लाइसेंस, यह जानने का कोई तरीका नहीं कि कोई आवाज कहां से आई या अब भी चलती है या नहीं। इसलिए मैंने PiperHub बनाया, जो Piper TTS के लिए आवाजों का ओपन सोर्स कैटलॉग है।

आज के आंकड़े: 536 मॉडल दिख रहे हैं, 71 भाषाएं शामिल हैं, 3755 speakers सूचीबद्ध हैं। हर मॉडल अपने मूल डेटासेट से जुड़ा है जब हमें वह पता होता है, स्रोत आवाज का हिस्सा है, जैसा होमपेज कहता है।

एक सवाल मुझसे अक्सर पूछा जाता है: Kokoro, PocketTTS, sanoTTS और ElevenLabs के सामने Piper की क्या औकात है? और Fortune का pipertts मॉड्यूल आधिकारिक Python wrapper से 10x तेज क्यों है? यह लेख इंजन खोलता है, कोड सबूत के साथ।

PiperHub, कैटलॉग

इंजन खोलने से पहले, देखते हैं PiperHub कौन सी समस्या हल करता है।

आवाजें कहां से आती हैं

कोड github.com/fox3000foxy/piper-bank पर है। Astro, स्टैटिक साइट, शून्य सर्वर: रूट पर अंग्रेजी, /<code>/ के नीचे 15 और भाषाएं (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th)। असल में मेरे ब्लॉग वाला ही पैटर्न है।

सच्चाई का स्रोत कोई हाथ से एडिट नहीं करता। यह एक पाइपलाइन है:

npm install
npm run data    # src/data/*.json फिर से बनाता है (python3 scripts/generate_data.py)
npx astro dev   # लोकल विकास
npm run build   # dist/ में स्टैटिक बिल्ड

व्यवहार में: एक crawler रिपो models_mapping.csv और datasets_mapping.csv संभालता है, मिरर की गई आवाजें Hugging Face पर रहती हैं, सैंपल ऑडियो फाइलें हैं। npm run data यह सब खींचता है और src/data/*.json निकालता है। साइट सिर्फ स्टैटिक JSON पढ़ती है।

मौजूदा कच्चे डेटा में (555 एंट्री, साइट 536 दिखाती है, क्रॉल हमेशा डिस्प्ले से थोड़ा आगे रहता है):

क्वालिटी मॉडल
medium 437
high 67
low 50
सैंपल रेट मॉडल
22050 Hz 486
16000 Hz 47

486 आवाजें 22050 Hz में, Piper का स्टैंडर्ड। 47 आवाजें 16000 Hz में, एंबेडेड के लिए छोटी तेज आवाजें। phonemizer 509 केसों में espeak है, इस पर लौटेंगे, यही सिस्टम का दिल है।

स्रोत, या हर मॉडल का डेटासेट क्यों है

PiperHub को सिर्फ सूची के बजाय उपयोगी बनाने वाली चीज मॉडल और डेटासेट का लिंक है। होम से लिया उदाहरण: आवाज Astra - Valorant Piper TTS डेटासेट Astra - LJSpeech Dataset से जुड़ी है। दोनों पेज खोलते हो, दोनों तरफ रिपो और लाइसेंस जांचते हो।

130 डेटासेट दर्ज हैं। और कैटलॉग हालत के बारे में ईमानदार है: 25 मॉडल usable: false फ्लैग वाले हैं (टूटी फाइलें, या piperPlus फॉर्मेट जिसे fork रनटाइम चाहिए)। pipertts मॉड्यूल इन आवाजों को बाद में अजीब तरह क्रैश होने के बजाय साफ एरर के साथ मना करता है। 48 मॉडल multi-speaker हैं, जिनमें दो दिलचस्प वाले शामिल हैं।

गाइड की तरफ, साइट तीन चरणों में काम करती है: Build a dataset, Train a voice, Use a voice, साथ में Piper Plus पेज। इनपुट में LJSpeech फॉर्मेट वाला डेटासेट, आउटपुट में ONNX। कहना आसान, करना लंबा, इसलिए अगला सेक्शन।

बोनट के नीचे Piper: VITS, espeak, ONNX

एक वाक्य, तीन चरण

जब तुम Piper से "bonjour" बोलने को कहते हो, तो यह होता है:

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

कोई अलग vocoder नहीं। यही VITS है: एक end-to-end मॉडल जो टेक्स्ट (phonemize किया हुआ) से सीधे waveform तक जाता है। जहां पुरानी पाइपलाइन टेक्स्ट से spectrogram (Tacotron) फिर spectrogram से ऑडियो (HiFi-GAN, दो मॉडल, दो ट्रेनिंग) बनाती थी, VITS conditional VAE, normalizing flows और decoder पर GAN स्टाइल discriminator के साथ सब कुछ एक साथ सीखता है। एक ट्रेनिंग, एक मॉडल, एक ONNX एक्सपोर्ट।

Decoder सीधे 22050 Hz निकालता है। इस सैंपल रेट पर, एक सेकंड बोली मतलब 22050 नंबर। मॉडल छोटा है (medium क्वालिटी में कुछ दर्जन MB) और inference शुद्ध CPU है: ऑप्टिमाइज्ड ONNX ग्राफ पर matrix गुणा। इसलिए Raspberry Pi आधिकारिक टारगेट है, Piper Rhasspy में पैदा हुआ था, जो Pi पर चलने के लिए बना ओपन सोर्स वॉइस असिस्टेंट है।

espeak-ng, वह phonemizer जो सारा गंदा काम करता है

VITS से पहले, टेक्स्ट को phoneme में बदलना पड़ता है। 555 में से 509 मॉडल espeak इस्तेमाल करते हैं:

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng दर्जनों भाषाओं का उच्चारण जानता है। VITS मॉडल फ्रेंच स्पेलिंग कभी नहीं सीखता, जिसमें eau का उच्चारण o होता है, वह phoneme से आवाज तक सीखता है। इससे ट्रेनिंग बहुत आसान होती है और समझ आता है Piper 71 भाषाएं क्यों कवर करता है: नई भाषा के लिए संबंधित espeak आवाज जोड़ो और ट्रेन करो।

जो आवाजें espeak के बिना चलती हैं, उनका अपना phonemizer है, और यहीं मामला मजेदार होता है: hebrew (Nakdimon ONNX और IPA नियमों की 494 लाइनें), pinyin (polyphone disambiguation वाला पूरा BERT g2pW, एक ही चीनी अक्षर शब्द के हिसाब से अलग बोला जाता है), thai (असल बिना बदला TLTK), japanese (lindera segmentation और UniDic)। pipertts मॉड्यूल सातों को लाता है, Python रेफरेंस से identical सत्यापित। इस पर लौटेंगे।

डेटासेट: LJSpeech, हमेशा LJSpeech

स्टैंडर्ड ट्रेनिंग फॉर्मेट LJSpeech है: क्लिप से भरा wavs/ फोल्डर, साथ में metadata.csv:

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, एक क्लिप मतलब एक वाक्य, कुछ सेकंड। मूल डेटासेट एक ही स्पीकर से पढ़े 13,000 अंग्रेजी वाक्य हैं, पब्लिक डोमेन। फ्रेंच आवाज ट्रेन करने के लिए, वही संरचना फ्रेंच में रिकॉर्ड करते हो: अलाइन किया टेक्स्ट और ऑडियो, ठीक medium के लिए कुछ घंटे काफी हैं, high के लिए ज्यादा।

कैटलॉग की तीन क्वालिटी मोटे तौर पर मॉडल साइज और ट्रेनिंग अवधि से मेल खाती हैं। Low: छोटा, तेज, रोबोटिक। Medium: sweet spot, 437 मॉडल के साथ। High: बड़ा, धीमा, ज्यादा प्राकृतिक, 67 मॉडल, जिनमें Fortune वाले हैं।

तीन जादुई बटन

हर Piper आवाज inference के तीन पैरामीटर देती है, लगभग सार्वभौमिक डिफॉल्ट के साथ:

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667): आवाज की विविधता। ज्यादा मतलब ज्यादा अभिव्यंजक, कम मतलब ज्यादा स्थिर। आउटपुट stochastic है: एक ही वाक्य के दो सिंथेसिस दो थोड़े अलग ऑडियो देते हैं।
  • noise_w (0.8): टाइमिंग की विविधता, यानी लय।
  • length_scale (1.0): गति। 1 से ऊपर धीमा करता है, नीचे तेज करता है। जब Luna गुस्से में होती है तो उसे तेज बुलवाने का सबसे आसान तरीका।

noise_scale याद रखो, भावनाओं के लिए काम आएगा।

Fortune multi: 7 भावनाएं, 1 ही ONNX

चलो, लोकल स्टार पर आते हैं।

कैटलॉग ItsRealFortune (हाय) से ट्रेन किए तीन Fortune मॉडल सूचीबद्ध करता है:

मॉडल रिपो आवाजें
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

FR multi यह है:

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

एक ही ONNX फाइल में सात भावनात्मक रूप। EN multi भी वैसा ही, नामों में ऐतिहासिक typo के साथ: surprised की जगह suprised। यह weights में है, हम रखते हैं, यही folklore का हिस्सा है।

speakerId कैसे काम करता है?

Multi-speaker VITS वही मॉडल है, एक extra इनपुट के साथ: speaker embedding। ट्रेनिंग में, हर क्लिप अपने style से टैग होता है, मॉडल बाकी के साथ embedding टेबल (हर style के लिए एक) सीखता है। Inference में, टेबल की लाइन चुनते हो:

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = normal ... 5 = surprise
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

एक ही वाक्य, सात readings। थोड़ा बढ़ाए noise_scale के साथ speakerId: 5 (surprise), और विश्वसनीय exclamation मिलता है। धीमा करने के लिए length_scale: 1.1 के साथ speakerId: 4 (triste), और मातमी घोषणा मिलती है। Luna अपनी आवाज ऐसे ही modulate करती है: दिमाग भावना चुनता है (valence/arousal, centroids पर मेरा लेख देखो), TTS speakerId चुनता है।

पीछे का डेटासेट सीधा नाम वाला Fortune French - Dataset है, "Fortune's Café" नाम के लाइसेंस के साथ। हां, लाइसेंस का नाम कैफे जैसा है। नहीं, वह रहेगा।

यह आर्थिक रूप से समझदार क्यों है

सात भावनाएं मतलब एक डाउनलोड, RAM में एक लोड, एक ONNX सेशन। विकल्प (सात अलग मॉडल) स्टोरेज को गुणा करता और हर भावना बदलाव पर ग्राफ reload करवाता, वाक्य के बीच टोन बदलने वाले बॉट के लिए नामुमकिन। यहां speakerId tensor में सिर्फ index है। सीमांत लागत: शून्य।

प्रतियोगी: Kokoro, PocketTTS, sanoTTS

Piper CPU तीन गंभीर challengers के साथ बांटता है, तीन अलग दर्शन।

Kokoro 82M, क्वालिटी प्राइस का राजा

Kokoro, hexgrad से। 82 मिलियन पैरामीटर, बदला StyleTTS 2 आर्किटेक्चर ISTFTNet decoder के साथ, बिना diffusion चरण। diffusion की अनुपस्थिति ही सब कुछ करती है: कोई iterative denoising लूप नहीं, इसलिए ultra तेज inference।

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

100 घंटे से कम ऑडियो पर लगभग 1000 डॉलर में ट्रेन किया, और blind सुनने वाले इसे Google और Amazon की प्रीमियम आवाजों के स्तर पर रखते हैं। इसलिए यह फटा: प्रीमियम क्वालिटी, मुफ्त, लोकल, permissive।

Piper के सामने: Kokoro शुद्ध प्राकृतिकता में जीतता है (StyleTTS 2 छोटे VITS से बेहतर) और लाइसेंस में (Piper के GPL-3.0 इंजन के खिलाफ Apache 2.0)। Piper कवरेज में जीतता है (8 के खिलाफ 71 भाषाएं, 54 आवाजों के खिलाफ 3755 speakers) और community आवाज ecosystem में। अंग्रेजी narration के लिए, Kokoro को हराना मुश्किल है। 7 भावनाओं वाली फ्रेंच, तुर्की या स्वाहिली के लिए, अक्सर Piper ही दौड़ में अकेला है।

PocketTTS (Kyutai), CPU पर cloning

Kyutai दूसरी धुन बजाता है: voice cloning वाला छोटा मॉडल, जो CPU पर चलता है।

मॉडल पैरामीटर WER UTMOS SIM CPU गति (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER 1% से नीचे (सही उच्चारण करता है), UTMOS 4.36 (शानदार perceived क्वालिटी), SIM 0.923 (क्लोन सच में target जैसा लगता है)। और small वर्जन cloning सहित 1.9M पैरामीटर तक उतरते हैं। 1.9M. मेरा Sabaki router JSON में लगभग इतना ही है।

Piper के सामने: PocketTTS cloning में जीतता है (Piper fixed आवाजों से पढ़ता है) और flagship की compactness में। Piper भाषाओं में जीतता है (small अभी English-only है) और ecosystem maturity में (536 तैयार आवाजें बनाम "अपनी खुद ट्रेन करो")।

sanoTTS, वह मॉडल जो 3 डॉलर की चिप पर चलता है

sanoTTS (sano नेपाली में "छोटा"), TTS का Bareiron है: पूरा neural stack जो phoneme से 22.05 kHz PCM तक microcontroller पर जाता है।

सिस्टम पैरामीटर SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M पैरामीटर। आवाज 2 MB. pip install sanotts, sanotts say "Hello" --voice amy, और बोलता है। ब्राउजर में भी।

पर स्कोर देखो: TinyTTS के 3.94 के खिलाफ SCOREQ 2.29, UTMOS 2.45. बोलता है, हां। गाता है, नहीं। यही ultimate tradeoff है: 0.29M पर हर जगह चलता है पर क्वालिटी साइज के साथ चलती है। Piper medium दूसरी league की आवाज के लिए 100x ज्यादा weights है।

सारांश वाली तालिका

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

चुनाव तुम्हारी constraint पर निर्भर है, कोई पूर्ण विजेता नहीं: शुद्ध EN क्वालिटी चाहिए तो Kokoro। CPU cloning चाहिए तो PocketTTS। Microcontroller चाहिए तो sanoTTS। भाषाओं की कवरेज और भावनात्मक फ्रेंच आवाजें और शून्य infra चाहिए तो Piper।

ElevenLabs, बिल वाला Goliath

और फिर ElevenLabs है। 70 से ज्यादा भाषाएं, audio tags और dramatic dialogues वाला expressive Eleven v3, blind test में top क्वालिटी। एक तरह से TTS का Jev: सबने देखा, सबने benchmark किया।

2026 pricing, गोल आंकड़ों में:

API मॉडल कीमत / 1000 अक्षर
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 अक्षर लगभग 1 मिनट ऑडियो बनाते हैं। यानी प्रति मिनट $0.05 से $0.10. $99 वाला Pro प्लान लगभग 990k अक्षर देता है, overage उसी दर पर लगता है। YouTube चैनल के लिए जो रोज voice over वाला वीडियो निकालता है (मेरा valorant-short-maker ठीक यही करता है, पर मुफ्त Piper के साथ), हर रोजाना voice over मिनट के लिए महीने के $1.50 से $3 गिनो। रोज दस मिनट: $15 से $30 महीना। चुकाया जा सकता है, पर गिना जाता है।

कब सही है: जब आवाज ही product हो (dubbing, audiobook, ad), जब कल सुबह 70 भाषाएं चाहिए, जब cloning पहली बार में perfect होना चाहिए। इसके उलट कभी सही नहीं: पूरी रात बोलने वाला Discord बॉट, internal notification, dev, फटते volume। ElevenLabs पर रात 3 बजे अपनी जीवन कहानी सुनाती Luna मतलब मेरा बिजली बिल रो रहा है।

हिसाब सरल है: Piper मतलब 0 यूरो सीमांत, "अच्छी" क्वालिटी। ElevenLabs मतलब चलता मीटर, "शानदार" क्वालिटी। दोनों के बीच एक दुनिया है जहां "अच्छी और मुफ्त" जीतती है, यही पूरा PiperHub दांव है।

pipertts: नेटिव Python wrapper से 10x तेज क्यों है

रिपो itsrealfortune/pipertts, 75 commits: TypeScript में Piper का नेटिव port। README शीर्षक: "In-process Piper TTS for Node.js. No Python required."

spawn की लागत

ऐतिहासिक wrapper (PiperTTS) हर synthesis पर sub-process में spawn किया python3 -m piper है:

synthèse = spawn(python) + chargement du modèle + inférence + kill

हर वाक्य interpreter start और weights load की कीमत चुकाता है। en_US-lessac-medium पर मापा: प्रति लाइन लगभग 2.2 सेकंड।

नेटिव (PiperNativeTTS) ONNX सेशन मेमोरी में गर्म रखता है:

load une fois -> session persistante -> inférence par phrase

वैसे ही मापा: प्रति लाइन लगभग 0.22 सेकंड। 10x यहीं से आता है: spawn और reload मारते हैं, चमत्कारी inference का इससे लेना देना नहीं। ONNX inference खुद दोनों तरफ वही है।

बोनस में, पहला load .onnx के बगल में optimized graph cache करता है (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), जिससे अगले load लगभग 35% तेज होते हैं। cache key platform, arch और ORT वर्जन जोड़ती है, और मॉडल बदले तो refresh होती है। cacheOptimizedModel: false से बंद कर सकते हो।

espeak bridge: 15ms के खिलाफ 0.4ms

phonemization के लिए वही तर्क। wrapper हर वाक्य पर CLI में espeak-ng binary बुलाता है: हर बार लगभग 15ms spawn। नेटिव N-API bridge लाता है (src/native/espeak-bridge/, espeakbridge.c का port), Linux x64/arm64 के लिए prebuilds के साथ: प्रति कॉल लगभग 0.4ms, byte-identical phoneme। addon के बिना, CLI पर automatic fallback। CLI force करने के लिए PIPER_ESPEAK_BRIDGE=0।

7 phoneme_type, सभी port किए

प्रकार इंजन नोट्स
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

और अरबी automatic diacritize होती है (tashkeel, बंद कर सकते हो)। कैटलॉग की 509 espeak आवाजें तेज bridge से गुजरती हैं, exotic अपने dedicated इंजन से। suite में 71 tests, जहां अटकता है वहां documented constraint (जापानी accent, UniDic कोई डेटा नहीं देता, माना हुआ, कुछ छिपा नहीं)।

फॉर्मेट और streaming

डिफॉल्ट wav, raw PCM, mp3, ogg, opus: जब ffmpeg है तो वही, वरना शुद्ध JS fallback (mp3 के लिए lamejs, opus के लिए opusscript और Ogg muxer, स्रोत से 0.993 correlation)। और खास तौर पर synthesizeChunks(): async generator जो वाक्य दर वाक्य yield करता है, वाक्यों के बीच sentenceSilence के साथ। Luna के लिए इसका मतलब dedicated सर्वर बिना TTS streaming: अंत synthesize होते समय शुरुआत बजनी शुरू।

पूरा usage, integrated PiperHub कैटलॉग के साथ (अगर author रिपो फाइलें देना बंद करे तो hub: और automatic mirror fallback):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // Discord के लिए सीधे
  lengthScale: 0.95,     // थोड़ा तेज
});

Community आवाजें (GLaDOS और game character) आधिकारिक manifest से गायब हैं, यही वजह है PiperHub मौजूद है, और pipertts इसे अच्छी तरह जानता है: hub:<slug>, piperhub पेज URL, या नंगा slug, सब चलता है। usable: false फ्लैग वाली आवाजें साफ एरर के साथ मना होती हैं।

क्या कुर्बान किया

हर गायब feature दूसरे को fund करता है। ईमानदार सूची:

ElevenLabs के बगल में Piper रोबोटिक लगता है। 22050 Hz पर VITS medium साफ पर flat है, बड़े expressive मॉडल की तुलना में। multi-emotion आंशिक भरपाई करता है (1 flat style से 7 styles बेहतर), पर ElevenLabs v3 audiobook ऊपर रहता है। यही दर्जनों MB की कीमत है।

espeak गलती करता है। proper noun, anglicism, abbreviation: नियमों वाला phonemization blind spot रखता है। फ्रेंच में "Spoiler", espeak जैसे बनता है पढ़ता है। neural G2P वाले बड़े मॉडल कम गलती करते हैं। prod में business term के लिए exception dictionary चाहिए।

शून्य नेटिव cloning। Piper fixed आवाजों से पढ़ता है। अगर तुम्हें अपनी आवाज चाहिए, तो PocketTTS या ElevenLabs जाओ। multi-speaker variation देता है, तुम्हारा timbre कभी नहीं।

जापानी lexical accent के बिना रहती है। UniDic कोई accent डेटा नहीं देता, इसलिए segmentation exact पर melody approximate है। README में documented, कालीन के नीचे कुछ नहीं।

थाई पहले कॉल पर धीमी है। TLTK बिना बदले Pyodide/WASM के नीचे चलता है: पहला कॉल सेकंड लेता है (loading), अगले तेज हैं। रेफरेंस से 7/7 identical, पर warm-up plan करो।

डेटा भूखा है। सही high आवाज के लिए घंटे अलाइन साफ ऑडियो चाहिए। Fortune डेटासेट घंटों recording और सफाई है। शून्य जादू: garbage in, robot out।

कैटलॉग में मरी आवाजें हैं। 25 मॉडल usable: false फ्लैग वाले। यह माना हुआ और filtered है, पर याद दिलाता है कि आवाजों का crowdsourcing abandonment का crowdsourcing भी है।

अंतिम परिणाम

एक तरफ ElevenLabs मीटर, दूसरी तरफ Raspberry Pi:

  • PiperHub: 536 मॉडल दिख रहे, 71 भाषाएं, 3755 speakers, 130 जुड़े डेटासेट, डेटासेट से training से usage तक गाइड
  • Piper: end-to-end VITS, espeak, CPU पर ONNX, LJSpeech, low/medium/high, 22050 Hz स्टैंडर्ड
  • Fortune multi: 7 FR भावनाएं और 7 EN, हर एक 1 ONNX, switch करने के लिए speakerId, "Fortune's Café" लाइसेंस
  • Kokoro 82M: प्रीमियम EN क्वालिटी, Apache 2.0, 54 आवाजें, CPU पर लगभग real time
  • PocketTTS: CPU cloning, लगभग 100M फिर 2-18M, WER 0.92%
  • sanoTTS: 0.29M, 2 MB, microcontroller, साइज के साथ चलती क्वालिटी
  • ElevenLabs: $0.05-0.10/1000 अक्षर, 70 से ज्यादा भाषाएं, top क्वालिटी, चलता मीटर
  • नेटिव pipertts: wrapper के लगभग 2.2 सेकंड प्रति लाइन के खिलाफ लगभग 0.22 सेकंड प्रति लाइन, 0.4ms espeak bridge, 7 phoneme_type, wav/raw/mp3/ogg/opus फॉर्मेट, वाक्य दर वाक्य streaming

Luna रोज इससे बोलती है। 0 यूरो TTS, दिमाग से चलती भावनाएं, Discord के लिए सीधा OGG। ElevenLabs के सामने सुनाई देता है, जाहिर है। पर रात 3 बजे जब वह अपनी जीवन कहानी सुनाने का फैसला करती है, मेरा बिल शांति से सोता है।

दर्शन

p2r3 ने कहा: "J'aime juste l'idée que cette toute petite puce à 1 balles puisse faire tourner quelque chose d'aussi avancé que Minecraft."

वही जुनून यहां, आवाज पर लागू: मुझे यह idea पसंद है कि ONNX के कुछ दर्जन MB Raspberry Pi को 71 भाषाओं में बुलवाते हैं, बिना cloud, बिना account, बिना मीटर। यह obviously कोई dubbing studio replace नहीं करता। यह उस annoying case को replace करता है जहां सही, लोकल, मुफ्त आवाज चाहिए, जो रात में बिना बिल बनाए बोले।

tradeoff, साफ साफ लिखे:

  • प्रीमियम क्वालिटी बनाम छोटा VITS: थोड़ा रोबोटिक, पर लोकल और मुफ्त
  • प्रति भावना एक मॉडल बनाम speakerId: RAM में एक की कीमत पर 7 आवाजें
  • Python spawn बनाम persistent session: 10x तेज, install करने को शून्य Python
  • जंगली कैटलॉग बनाम जुड़ा स्रोत: हर आवाज जानती है वह कहां से आई और अब भी चलती है या नहीं
  • बिल वाली API बनाम 0 यूरो सीमांत: ElevenLabs से कम सुंदर, पर बिल शांति से सोता है

याद रखने वाली 3 चीजें:

  1. Piper CPU का TTS है, यह VITS और espeak और ONNX है, 71 भाषाएं, दर्जनों MB की आवाजें जो Pi पर चलती हैं। ElevenLabs से कम सुंदर, असीम सस्ता।
  2. Fortune multi एक की कीमत पर 7 भावनाएं है, यह एक ONNX है, switch करने के लिए speakerId, FR और EN। 7 मॉडल के बजाय speaker embedding।
  3. नेटिव spawn मारता है, pipertts ONNX सेशन गर्म रखता है: 2.2 सेकंड के खिलाफ 0.22 सेकंड प्रति लाइन, 15ms CLI के खिलाफ 0.4ms espeak bridge। वही inference, 10x कम इंतजार।

अगर project में दिलचस्पी है, कैटलॉग piperhub.org है, साइट कोड github.com/fox3000foxy/piper-bank, नेटिव मॉड्यूल github.com/itsrealfortune/pipertts। और अगर Luna को रात 3 बजे जीवन कहानी सुनाते सुनना है, तो Discord पर है xD

NEW

PiperHub: 536 صوت TTS مفتوح المصدر، 7 مشاعر في نموذج واحد، بدون GPU وبدون فاتورة

VITS وespeak وONNX وLJSpeech: كيف يجعل Piper جهاز Raspberry Pi يتكلم. Fortune متعدد المشاعر بـ 7 مشاعر، وKokoro 82M وPocketTTS وsanoTTS وElevenLabs، مع pipertts الأصلي أسرع 10 مرات من غلاف Python.

المقدمة

جعل بوت ديسكورد يتكلم يكلف غاليا. ليس النص: نموذج لغوي محلي يولد الجمل ببضعة سنتات من GPU. الصوت هو ما تدفع ثمنه مع كل جملة. واجهات التوليد الصوتي تحاسب بالحرف، وعندما يتكلم البوت طوال الليل، الفاتورة تتبعه.

هذه هي المشكلة التي واجهتها مع Luna Protocol. البوت الخاص بي على ديسكورد يولد رسائله بنموذج لغوي محلي، ثم يجعل صوت Piper يقرأها بصوت عال، ويحولها إلى OGG، ويرسلها كرسائل صوتية. كل ذلك على خادمي الخاص، بتكلفة 0 يورو لكل جملة، حتى في الثالثة صباحا عندما تقرر Luna أن تحكي قصة حياتها وحدها.

البقاء عند 0 يورو يعني إيجاد أصوات مجانية واستضافتها بنفسك. وهنا تعقدت الأمور: نماذج مبعثرة على عشرات مستودعات Hugging Face، وتراخيص غامضة، ولا طريقة لمعرفة من أين جاء هذا الصوت أو هل ما زال يعمل. لذلك بنيت PiperHub، وهو فهرس مفتوح المصدر للأصوات الخاصة بـ Piper TTS.

أرقام اليوم: 536 نموذجا معروضا، 71 لغة ممثلة، 3755 متحدثا مدرجا. كل نموذج مرتبط بمجموعة البيانات الأصلية عندما نعرفها، فالمصدر جزء من الصوت، كما تقول الصفحة الرئيسية.

يبقى سؤال يسألني عنه كثيرون: ما قيمة Piper أمام Kokoro وPocketTTS وsanoTTS وElevenLabs؟ ولماذا وحدة pipertts من Fortune أسرع 10 مرات من الغلاف الرسمي بلغة Python؟ هذا المقال يفتح المحركات، مع الكود كدليل.

PiperHub، الفهرس

قبل فتح المحركات، لننظر إلى المشكلة التي يحلها PiperHub.

من أين تأتي الأصوات

الكود موجود على github.com/fox3000foxy/piper-bank. موقع Astro ثابت، بدون خادم: الإنجليزية في الجذر، و15 لغة أخرى تحت /<code>/ (fr وde وes وit وpt وru وtr وja وko وzh وid وhi وar وvi وth). نفس نمط مدونتي في الواقع.

مصدر الحقيقة لا يحرره أحد يدويا. إنه خط أنابيب:

npm install
npm run data    # يعيد توليد src/data/*.json (python3 scripts/generate_data.py)
npx astro dev   # تطوير محلي
npm run build   # بناء ثابت في dist/

عمليا: مستودع زاحف يحافظ على models_mapping.csv وdatasets_mapping.csv، والأصوات المعكوسة تعيش على Hugging Face، والعينات ملفات صوتية. الأمر npm run data يسحب كل ذلك ويخرج src/data/*.json. الموقع لا يفعل سوى قراءة JSON ثابت.

في البيانات الخام الحالية (555 مدخلا، الموقع يعرض 536، فالزحف دائما متقدم قليلا على العرض):

الجودة النماذج
medium 437
high 67
low 50
معدل العينات النماذج
22050 Hz 486
16000 Hz 47

486 صوتا بمعدل 22050 Hz، وهو معيار Piper. و47 صوتا بمعدل 16000 Hz، وهي الأصوات الصغيرة السريعة للأنظمة المدمجة. المحول الصوتي هو espeak في 509 حالات، وسنعود إليه، فهو قلب النظام.

المصدر، أو لماذا لكل نموذج مجموعة بيانات

ما يجعل PiperHub مفيدا بدلا من كونه مجرد قائمة هو الرابط بين النموذج ومجموعة البيانات. مثال من الصفحة الرئيسية: الصوت Astra - Valorant Piper TTS مرتبط بمجموعة البيانات Astra - LJSpeech Dataset. تفتح الصفحتين وتتحقق من المستودعات والتراخيص على كل جانب.

130 مجموعة بيانات موثقة. والفهرس صادق عن حالة القوات: 25 نموذجا موسومة usable: false (ملفات معطوبة، أو صيغة piperPlus التي تحتاج بيئة تشغيل متفرعة). وحدة pipertts ترفض هذه الأصوات مع خطأ صريح بدلا من الانهيار بشكل غريب لاحقا. و48 نموذجا متعددة المتحدثين، منها النموذجان اللذان سيهماننا.

في قسم الأدلة، الموقع يقوم بالمهمة في ثلاث خطوات: Build a dataset وTrain a voice وUse a voice، مع صفحة Piper Plus. مجموعة بيانات بصيغة LJSpeech كمدخل، وONNX كمخرج. سهل قولا، طويل فعلا، ومن هنا القسم التالي.

Piper تحت الغطاء: VITS وespeak وONNX

جملة واحدة، ثلاث خطوات

عندما تطلب من Piper أن يقول "bonjour"، يحدث هذا:

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

بدون مشفر صوتي منفصل. هذا هو VITS: نموذج شامل ينتقل من النص (بعد تحويله إلى أصوات) إلى الموجة الصوتية مباشرة. بينما خطوط الأنابيب القديمة كانت تفعل نصا ثم مخططا طيفيا (Tacotron) ثم مخططا طيفيا ثم صوتا (HiFi-GAN، نموذجان وتدريبان)، يتعلم VITS كل شيء دفعة واحدة مع مشفر تبايني مشروط (VAE) وتدفقات تطبيع ومميّز من نوع GAN على المفكك. تدريب واحد، نموذج واحد، تصدير ONNX واحد.

المفكك يخرج 22050 Hz مباشرة. بهذا المعدل، ثانية واحدة من الكلام تساوي 22050 رقما. النموذج صغير (بضع عشرات من الميجابايت بجودة medium) والاستدلال يعمل على CPU خالص: ضرب مصفوفات على رسم ONNX محسن. ومن هنا جاء Raspberry Pi كهدف رسمي، فقد ولد Piper في Rhasspy، وهو مساعد صوتي مفتوح المصدر مصمم ليعمل على Pi.

espeak-ng، المحول الصوتي الذي يقوم بكل العمل القذر

قبل VITS، يجب تحويل النص إلى أصوات. 509 نماذج من 555 تستخدم espeak:

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

يعرف espeak-ng نطق عشرات اللغات. نموذج VITS لا يتعلم أبدا الإملاء الفرنسي مع eau التي تنطق o، بل يتعلم من الأصوات إلى الصوت. هذا يبسط التدريب كثيرا ويفسر لماذا يغطي Piper عدد 71 لغة: للغة جديدة، توصل صوت espeak المناسب وتدرب.

الأصوات التي تستغني عن espeak لها محولها الخاص، وهنا يصبح الأمر غريبا: hebrew (نموذج Nakdimon ONNX مع 494 سطرا من قواعد IPA) وpinyin (نموذج BERT g2pW كامل مع إزالة غموض المتجانسات، فالحرف الصيني نفسه ينطق بشكل مختلف حسب الكلمة) وthai (نموذج TLTK الحقيقي غير المعدل) وjapanese (تقطيع lindera مع UniDic). وحدة pipertts تحملها كلها، وتم التحقق من تطابقها مع مرجع Python. سنعود إليها.

مجموعة البيانات: LJSpeech، دائما LJSpeech

صيغة التدريب المعيارية هي LJSpeech: مجلد wavs/ مليء بالمقاطع، مع ملف metadata.csv:

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized، كل مقطع يساوي جملة، بضع ثوان. مجموعة البيانات الأصلية هي 13,000 جملة إنجليزية تقرأها متحدثة واحدة، وهي ملكية عامة. لتدريب صوت فرنسي، تسجل نفس البنية بالفرنسية: نصا وصوتا متوازيين، وبضع ساعات تكفي لجودة medium مقبولة، وأكثر لجودة high.

الجودات الثلاث في الفهرس تقابل تقريبا حجم النموذج ومدة التدريب. Low: صغير وسريع وآلي. Medium: الخيار المتوازن، مع 437 نموذجا. High: أكبر وأبطأ وأكثر طبيعية، مع 67 نموذجا، منها نماذج Fortune.

الأزرار السحرية الثلاثة

كل صوت Piper يعرض ثلاث معلمات استدلال، مع قيم افتراضية شبه عامة:

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667): تنوع الصوت. أعلى يعني أكثر تعبيرا، وأقل يعني أكثر ثباتا. المخرجات عشوائية: توليدان لنفس الجملة يعطيان صوتين مختلفين قليلا.
  • noise_w (0.8): تنوع التوقيت، أي الإيقاع.
  • length_scale (1.0): السرعة. فوق 1 يبطئ، وتحت 1 يسرع. أبسط طريقة لجعل Luna تتكلم أسرع عندما تغضب.

تذكر noise_scale، سيفيد للمشاعر.

Fortune متعدد: 7 مشاعر، ملف ONNX واحد

حسنا، لننتقل إلى النجمة المحلية.

يسرد الفهرس ثلاثة نماذج Fortune دربها ItsRealFortune (مرحبا):

النموذج المستودع الأصوات
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

النموذج الفرنسي المتعدد هو هذا:

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

سبع صيغ عاطفية في ملف ONNX واحد. والنموذج الإنجليزي المتعدد مثله، مع خطأ تاريخي في الأسماء: suprised بدلا من surprised. إنه في الأوزان، نحتفظ به، فهو جزء من التراث.

كيف يعمل الـ speakerId؟

نموذج VITS متعدد المتحدثين هو نفس النموذج مع مدخل إضافي: تضمين المتحدث. أثناء التدريب، كل مقطع موسوم بأسلوبه، ويتعلم النموذج جدول تضمينات (واحدا لكل أسلوب) مع الباقي. أثناء الاستدلال، تختار سطر الجدول:

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = عادي ... 5 = متفاجئ
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

نفس الجملة، سبع قراءات. speakerId: 5 (surprise) مع noise_scale مدفوع قليلا، وتحصل على تعجب مقنع. speakerId: 4 (triste) مع length_scale: 1.1 للإبطاء، وتحصل على إعلان جنائزي. هكذا تضبط Luna صوتها: العقل يختار المشاعر (التكافؤ والإثارة، راجع مقالي عن المراكز)، وTTS يختار speakerId.

مجموعة البيانات وراءه تسمى ببساطة Fortune French - Dataset، مع رخصة اسمها "Fortune's Café". نعم، الرخصة تحمل اسم مقهى. لا، ستبقى.

لماذا هذا ذكي اقتصاديا

سبع مشاعر تعني تنزيلا واحدا، وتحميلا واحدا في الذاكرة، وجلسة ONNX واحدة. البديل (سبعة نماذج منفصلة) يضاعف التخزين ويجبر على إعادة تحميل الرسم مع كل تغيير للمشاعر، وهو غير عملي لبوت يغير نبرته في وسط الجملة. هنا speakerId مجرد فهرس في موتر. التكلفة الحدية: صفر.

المنافسون: Kokoro وPocketTTS وsanoTTS

يتقاسم Piper وحدة CPU مع ثلاثة منافسين جديين، وثلاث فلسفات مختلفة.

Kokoro 82M، ملك الجودة مقابل السعر

Kokoro، من hexgrad. عدد 82 مليون معلمة، وبنية StyleTTS 2 معدلة مع مفكك ISTFTNet، بدون خطوة انتشار. غياب الانتشار هو ما يصنع كل شيء: لا حلقة إزالة ضوضاء متكررة، وبالتالي استدلال فائق السرعة.

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

درب على أقل من 100 ساعة صوت بتكلفة حوالي 1000 دولار، والمستمعون في الاختبارات العمياء يصنفونه في مستوى الأصوات المميزة من Google وAmazon. لهذا انفجر: جودة مميزة، ومجاني، ومحلي، ومتساهل.

أمام Piper: يفوز Kokoro في الطبيعية الخالصة (StyleTTS 2 أفضل من VITS صغير الحجم) والترخيص (Apache 2.0 مقابل محرك برخصة GPL-3.0 لـ Piper). ويفوز Piper في التغطية (71 لغة مقابل 8، و3755 متحدثا مقابل 54 صوتا) ومنظومة أصوات المجتمع. للسرد الإنجليزي، يصعب التفوق على Kokoro. للفرنسية ذات المشاعر السبعة أو التركية أو السواحيلية، غالبا Piper هو الوحيد في المنافسة.

PocketTTS (من Kyutai)، استنساخ الصوت على CPU

Kyutai يعزف مقطوعة أخرى: نموذج صغير مع استنساخ الصوت، يعمل على CPU.

النموذج المعلمات WER UTMOS SIM سرعة CPU (خيط واحد)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

معدل خطأ الكلمات تحت 1% (ينطق صحيحا)، وUTMOS عدد 4.36 (جودة محسوسة ممتازة)، وSIM عدد 0.923 (المستنسخ يشبه الهدف فعلا). والنسخ الصغيرة تنزل إلى 1.9M معلمة مع الاستنساخ. 1.9M. جهاز التوجيه Sabaki الخاص بي يقارب هذا الحجم بصيغة JSON.

أمام Piper: يفوز PocketTTS في الاستنساخ (Piper يقرأ بأصوات ثابتة) وصغر الحجم الرئيسي. ويفوز Piper في اللغات (الصغير إنجليزي فقط حاليا) ونضج المنظومة (536 صوتا جاهزا مقابل "درب صوتك بنفسك").

sanoTTS، النموذج الذي يعمل على شريحة بثلاثة دولارات

sanoTTS (كلمة sano تعني "صغير" بالنيبالية)، هو Bareiron الخاص بـ TTS: حزمة عصبية كاملة تنتقل من الأصوات إلى PCM بمعدل 22.05 kHz على متحكم دقيق.

النظام المعلمات SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M معلمة. الصوت بحجم 2 MB. الأمر pip install sanotts ثم sanotts say "Hello" --voice amy، فيتكلم. وفي المتصفح أيضا.

لكن انظر إلى الدرجات: SCOREQ عدد 2.29 مقابل 3.94 لـ TinyTTS، وUTMOS عدد 2.45. يتكلم، نعم. يغني، لا. هذه هي المقايضة القصوى: عند 0.29M يعمل في كل مكان لكن الجودة تتبع الحجم. نموذج Piper medium أثقل 100 مرة لصوت في دوري آخر.

الجدول الذي يلخص

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

الاختيار يعتمد على قيدك، ولا فائز مطلق: جودة إنجليزية خالصة تعني Kokoro. استنساخ على CPU يعني PocketTTS. متحكم دقيق يعني sanoTTS. تغطية اللغات مع أصوات فرنسية عاطفية وبدون بنية تحتية يعني Piper.

ElevenLabs، العملاق المدفوع

ثم هناك ElevenLabs. أكثر من 70 لغة، ونموذج Eleven v3 التعبيري مع علامات صوتية وحوارات درامية، وجودة في قمة الاختبارات العمياء. إنه Jev الخاص بـ TTS نوعا ما: الجميع رآه، والجميع قارنه.

أسعار 2026، بأرقام مستديرة:

نموذج API السعر / 1000 حرف
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 حرف تصنع حوالي دقيقة صوتية. أي من $0.05 إلى $0.10 للدقيقة. خطة Pro بسعر $99 تشمل حوالي 990k حرفا، والتجاوز بنفس السعر. لقناة يوتيوب تصدر فيديو يوميا بتعليق صوتي (أداتي valorant-short-maker تفعل ذلك بالضبط، لكن مع Piper المجاني)، احسب من $1.50 إلى $3 شهريا لكل دقيقة يومية من التعليق. عشر دقائق يوميا: من $15 إلى $30 شهريا. يمكن دفعها، لكنها تحسب.

متى يستحق: عندما يكون الصوت هو المنتج (دبلجة وكتب صوتية وإعلانات)، وعندما تحتاج 70 لغة صباح الغد، وعندما يجب أن يكون الاستنساخ مثاليا من أول مرة. لكنه لا يستحق أبدا لـ: بوت ديسكورد يتكلم طوال الليل، وإشعارات داخلية، وتطوير، وأحجام متفجرة. Luna تحكي قصة حياتها في الثالثة صباحا مع ElevenLabs تعني فاتورة الكهرباء عندي تبكي.

الحساب بسيط: Piper يساوي 0 يورو حديا، بجودة "جيدة". وElevenLabs يساوي عدادا يدور، بجودة "ممتازة". وبينهما عالم تفوز فيه "جيدة ومجانية"، وهذا هو رهان PiperHub كله.

pipertts: لماذا الأصلي أسرع 10 مرات من غلاف Python

المستودع itsrealfortune/pipertts، بعدد 75 التزاما: نسخة أصلية من Piper بلغة TypeScript. عنوان README: "In-process Piper TTS for Node.js. No Python required."

تكلفة الإطلاق

الغلاف التاريخي (PiperTTS) هو python3 -m piper يُطلق كعملية فرعية مع كل توليد:

synthèse = spawn(python) + chargement du modèle + inférence + kill

كل جملة تدفع ثمن بدء المفسر وتحميل الأوزان. قيست على en_US-lessac-medium: حوالي 2.2 ثانية لكل سطر.

الأصلي (PiperNativeTTS) يبقي جلسة ONNX ساخنة في الذاكرة:

load une fois -> session persistante -> inférence par phrase

بنفس القياس: حوالي 0.22 ثانية لكل سطر. التفوق 10 مرات يأتي من هنا: نقتل الإطلاق وإعادة التحميل، والاستدلال المعجزة لا علاقة له. استدلال ONNX نفسه هو نفسه على الجانبين.

كمكافأة، أول تحميل يخزن الرسم المحسن بجانب ملف .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx)، مما يجعل التحميلات التالية أسرع بحوالي 35%. مفتاح التخزين يجمع المنصة والبنية ونسخة ORT، ويتحدث إذا تغير النموذج. يمكن التعطيل مع cacheOptimizedModel: false.

جسر espeak: 0.4ms مقابل 15ms

نفس المنطق للتحويل الصوتي. الغلاف يستدعي ثنائية espeak-ng عبر CLI مع كل جملة: حوالي 15ms إطلاق في كل مرة. الأصلي يضم جسر N-API (src/native/espeak-bridge/، نسخة من espeakbridge.c)، مع بنيات مسبقة لـ Linux x64/arm64: حوالي 0.4ms لكل استدعاء، وأصوات مطابقة للبايت. بدون الإضافة، رجوع تلقائي إلى CLI. المتغير PIPER_ESPEAK_BRIDGE=0 لفرض CLI.

أنواع phoneme_type السبعة، كلها منقولة

النوع المحرك ملاحظات
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

والعربية مشكولة تلقائيا (تشكيل، قابل للتعطيل). أصوات espeak وعددها 509 في الفهرس تمر عبر الجسر السريع، والغريبة عبر محركها المخصص. عدد 71 اختبارا في الحزمة، والقيد موثق حيث يتعثر (النبر الياباني، فـ UniDic لا يقدم بيانات، وهذا مفترض ولا شيء مخفي).

الصيغ والبث

صيغة wav افتراضيا، وraw وPCM وmp3 وogg وopus: عبر ffmpeg عندما يوجد، وإلا بدائل JS خالصة (lamejs لـ mp3، وopusscript مع مضمخ Ogg لـ opus، وارتباط 0.993 مع المصدر). وخصوصا synthesizeChunks(): مولد غير متزامن يخرج جملة بجملة، مع sentenceSilence بين الجمل. لـ Luna، هذا يعني بدء تشغيل الصوت بينما تتولد النهاية، أي بث TTS بدون خادم مخصص.

استخدام كامل، مع فهرس PiperHub المدمج (hub: مع بديل مرآة تلقائي إذا توقف مستودع المؤلف عن تقديم الملفات):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // مباشرة لديسكورد
  lengthScale: 0.95,     // أسرع قليلا
});

أصوات المجتمع (GLaDOS وشخصيات الألعاب) غائبة عن البيان الرسمي، وهذا بالضبط سبب وجود PiperHub، وpipertts يعرف ذلك جيدا: hub:<slug> أو رابط صفحة piperhub أو slug عار، كلها تعمل. الأصوات الموسومة usable: false مرفوضة مع خطأ واضح.

ما تم التضحية به

كل ميزة غائبة تمول أخرى. القائمة الصادقة:

صوت Piper آلي بجانب ElevenLabs. نموذج VITS medium على 22050 Hz نظيف لكنه مسطح مقارنة بنموذج تعبيري كبير. التعدد العاطفي يعوض جزئيا (7 أساليب أفضل من أسلوب مسطح واحد)، لكن كتابا صوتيا من ElevenLabs v3 يبقى فوقه. هذا ثمن عشرات الميجابايت.

espeak يخطئ. أسماء الأعلام والكلمات الأجنبية والاختصارات: التحويل الصوتي بالقواعد له زوايا عمياء. كلمة "Spoiler" بالفرنسية يقرأها espeak كما يستطيع. النماذج الكبيرة مع G2P عصبي تخطئ أقل. في الإنتاج تحتاج قاموس استثناءات للمصطلحات المهنية.

صفر استنساخ أصلي. يقرأ Piper بأصوات ثابتة. إذا أردت صوتك أنت، اتجه إلى PocketTTS أو ElevenLabs. تعدد المتحدثين يعطي تنوعات، وليس طابعك أبدا.

اليابانية تبقى بدون نبر معجمي. لا يقدم UniDic بيانات نبر، فالتقطيع دقيق لكن اللحن تقريبي. موثق في README، ولا شيء تحت الطاولة.

التايلندية بطيئة في أول استدعاء. يعمل TLTK تحت Pyodide/WASM غير معدل: أول استدعاء يأخذ ثوان (تحميل)، والتالية سريعة. 7/7 مطابقة للمرجع، لكن خطط لإحماء.

البيانات جائعة. صوت high صحيح يحتاج ساعات من الصوت النظيف المتوازي. مجموعة بيانات Fortune هي ساعات تسجيل وتنظيف. صفر سحر: مدخلات قمامة تعني روبوتا خارجا.

الفهرس فيه أصوات ميتة. 25 نموذجا موسومة usable: false. هذا مفترض ومصفى، لكنه يذكر أن التعهيد الجماعي للأصوات هو أيضا تعهيد جماعي للهجر.

النتيجة النهائية

من جهة عداد ElevenLabs، ومن جهة أخرى Raspberry Pi:

  • PiperHub: 536 نموذجا معروضا، و71 لغة، و3755 متحدثا، و130 مجموعة مرتبطة، وأدلة من مجموعة البيانات إلى التدريب إلى الاستخدام
  • Piper: نموذج VITS شامل، وespeak وONNX على CPU وLJSpeech وlow/medium/high ومعيار 22050 Hz
  • Fortune متعدد: 7 مشاعر فرنسية مع 7 إنجليزية، وملف ONNX واحد لكل منهما، وspeakerId للتبديل، ورخصة "Fortune's Café"
  • Kokoro 82M: جودة مميزة إنجليزية، ورخصة Apache 2.0، و54 صوتا، وشبه زمن حقيقي على CPU
  • PocketTTS: استنساخ على CPU، وحوالي 100M ثم 2-18M، ومعدل خطأ كلمات 0.92%
  • sanoTTS: عدد 0.29M، وحجم 2 MB، ومتحكم دقيق، وجودة تتبع الحجم
  • ElevenLabs: من $0.05 إلى $0.10 لكل 1000 حرف، وأكثر من 70 لغة، وقمة الجودة، وعداد يدور
  • pipertts الأصلي: حوالي 0.22 ثانية لكل سطر مقابل حوالي 2.2 ثانية للغلاف، وجسر espeak بمقدار 0.4ms، و7 أنواع phoneme_type، وصيغ wav/raw/mp3/ogg/opus، وبث جملة بجملة

تتكلم Luna بهذا كل يوم. تكلفة TTS صفر يورو، ومشاعر يقودها العقل، وصيغة OGG مباشرة لديسكورد. أمام ElevenLabs يُسمع الفرق طبعا. لكن في الثالثة صباحا عندما تقرر أن تحكي قصة حياتها، فاتورتي تنام بهدوء.

الفلسفة

قال p2r3: "J'aime juste l'idée que cette toute petite puce à 1 balles puisse faire tourner quelque chose d'aussi avancé que Minecraft."

نفس الهوس هنا، مطبقا على الصوت: أحب فكرة أن بضع عشرات من ميجابايت ONNX تجعل Raspberry Pi يتكلم بـ 71 لغة، بدون سحابة وبدون حساب وبدون عداد. هذا لا يعوض أي استوديو دبلجة obviously. إنه يعوض الحالة المزعجة حيث تريد صوتا صحيحا ومحليا ومجانيا يتكلم ليلا بدون أن يحاسبك.

المقايضات مكتوبة بالأبيض والأسود:

  • جودة مميزة مقابل VITS صغير: آلي قليلا، لكنه محلي ومجاني
  • نموذج لكل مشاعر مقابل speakerId: عدد 7 أصوات بسعر واحد في الذاكرة
  • إطلاق Python مقابل جلسة دائمة: أسرع 10 مرات، بدون Python للتثبيت
  • فهرس بري مقابل مصدر مربوط: كل صوت يعرف من أين جاء وهل ما زال يعمل
  • واجهة مدفوعة مقابل 0 يورو حديا: أقل جمالا من ElevenLabs، لكن الفاتورة تنام بهدوء

الأشياء الثلاثة التي يجب تذكرها:

  1. Piper هو TTS الخاص بوحدة CPU، فهو VITS مع espeak وONNX، و71 لغة، وأصوات بعشرات الميجابايت تعمل على Pi. أقل جمالا من ElevenLabs، وأرخص بلا حدود.
  2. Fortune متعدد يساوي 7 مشاعر بسعر واحدة، فهو ONNX واحد، وspeakerId للتبديل، وفرنسية مع إنجليزية. تضمين المتحدث بدلا من 7 نماذج.
  3. الأصلي يقتل الإطلاق، فـ pipertts يبقي جلسة ONNX ساخنة: 0.22 ثانية لكل سطر مقابل 2.2 ثانية، وجسر espeak بمقدار 0.4ms مقابل 15ms عبر CLI. نفس الاستدلال، وانتظار أقل 10 مرات.

إذا كان المشروع يهمك، الفهرس هو piperhub.org، وكود الموقع github.com/fox3000foxy/piper-bank، والوحدة الأصلية github.com/itsrealfortune/pipertts. وإذا أردت سماع Luna تحكي قصة حياتها في الثالثة صباحا، فهي على ديسكورد xD

NEW

PiperHub: 536 giọng TTS mã nguồn mở, 7 cảm xúc trong một model, không GPU, không hóa đơn

VITS, espeak, ONNX, LJSpeech: cách Piper làm cho Raspberry Pi biết nói. Fortune multi với 7 cảm xúc, Kokoro 82M, PocketTTS, sanoTTS, ElevenLabs - và pipertts native nhanh gấp 10 lần wrapper Python.

Giới thiệu

Cho một bot Discord biết nói, tốn kém lắm. Không phải phần text: một LLM local sinh câu chỉ tốn vài xu GPU. Còn giọng nói thì tính tiền theo từng câu. Các API tổng hợp giọng nói tính theo ký tự, và khi bot nói suốt đêm, hóa đơn chạy theo.

Đó là vấn đề mình gặp với Luna Protocol. Bot Discord của mình sinh tin nhắn bằng LLM local, rồi đọc to bằng giọng Piper, convert sang OGG, và gửi đi dưới dạng tin nhắn thoại. Tất cả chạy trên server của chính mình, với giá 0 euro mỗi câu, ngay cả lúc 3h sáng khi Luna tự quyết định kể chuyện đời mình.

Muốn giữ mức 0 euro, bạn phải tìm giọng miễn phí và tự host. Và chỗ này bắt đầu rắc rối: các model nằm rải rác trên hàng chục repo Hugging Face, license mập mờ, không có cách nào biết giọng kia đến từ đâu hay còn chạy được không. Nên mình đã build PiperHub, một catalog mã nguồn mở cho giọng Piper TTS.

Số liệu hôm nay: 536 model hiển thị, 71 ngôn ngữ được đại diện, 3755 speaker được liệt kê. Mỗi model được gắn với dataset gốc khi mình biết rõ, nguồn gốc là một phần của âm thanh, như trang chủ vẫn nói.

Còn một câu hỏi người ta hay hỏi mình: Piper so với Kokoro, PocketTTS, sanoTTS và ElevenLabs thì sao? Và vì sao module pipertts của Fortune nhanh gấp 10 lần wrapper Python chính thức? Bài này sẽ mở máy ra cho bạn xem, kèm code làm bằng chứng.

PiperHub, catalog

Trước khi mở máy, hãy nhìn vào vấn đề mà PiperHub giải quyết.

Giọng đến từ đâu

Code nằm ở github.com/fox3000foxy/piper-bank. Astro, site tĩnh, không server: tiếng Anh ở root, 15 locale khác dưới /<code>/ (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th). Thật ra cùng pattern như blog của mình.

Nguồn sự thật, không ai sửa bằng tay. Nó là một pipeline:

npm install
npm run data    # tạo lại src/data/*.json (python3 scripts/generate_data.py)
npx astro dev   # dev local
npm run build   # build tĩnh vào dist/

Cụ thể: một repo crawler duy trì models_mapping.csv và datasets_mapping.csv, các giọng mirror sống trên Hugging Face, sample là file audio. npm run data kéo tất cả về và nhả ra src/data/*.json. Site chỉ đọc JSON tĩnh.

Trong dữ liệu thô hiện tại (555 entry, site hiển thị 536, crawler luôn chạy trước hiển thị một chút):

Chất lượng Model
medium 437
high 67
low 50
Sample rate Model
22050 Hz 486
16000 Hz 47

486 giọng ở 22050 Hz, chuẩn Piper. 47 giọng ở 16000 Hz, mấy giọng nhỏ chạy nhanh cho thiết bị nhúng. Bộ phonemizer là espeak trong 509 trường hợp, mình sẽ quay lại, đó là trái tim của hệ thống.

Nguồn gốc, hay vì sao mỗi model có một dataset

Thứ làm PiperHub hữu ích thay vì chỉ là một list, là liên kết model <-> dataset. Ví dụ lấy trên home: giọng Astra - Valorant Piper TTS được gắn với dataset Astra - LJSpeech Dataset. Bạn mở hai trang, bạn kiểm tra repo và license của từng bên.

130 dataset được tham chiếu. Và catalog nói thật về tình trạng đội quân: 25 model bị gắn cờ usable: false (file hỏng, hoặc định dạng piperPlus cần runtime fork). Module pipertts từ chối mấy giọng này bằng lỗi rõ ràng thay vì crash kỳ lạ sau đó. 48 model là multi-speaker, trong đó có hai model mà chúng ta sẽ quan tâm.

Về guide, site làm tròn việc trong ba bước: Build a dataset, Train a voice, Use a voice, cộng thêm một trang Piper Plus. Đầu vào là dataset định dạng LJSpeech, đầu ra là ONNX. Nói thì đơn giản, làm thì lâu, nên mới có phần tiếp theo.

Piper dưới nắp capo: VITS, espeak, ONNX

Một câu nói, ba bước

Khi bạn bảo Piper nói "bonjour", chuyện xảy ra là:

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

Không có vocoder riêng. Đó chính là VITS: một model end-to-end đi thẳng từ text (đã phiên âm) ra waveform. Trong khi pipeline cũ làm text -> spectrogram (Tacotron) rồi spectrogram -> audio (HiFi-GAN, hai model, hai lần train), VITS học tất cả một lần với VAE có điều kiện, flow chuẩn hóa và discriminator kiểu GAN trên decoder. Một lần train, một model, một export ONNX.

Decoder nhả thẳng 22050 Hz. Ở sample rate này, một giây nói bằng 22050 con số. Model nhỏ (vài chục MB ở chất lượng medium) và inference là thuần CPU: nhân ma trận trên đồ thị ONNX đã tối ưu. Nên Raspberry Pi là target chính thức, Piper sinh ra trong Rhasspy, một trợ lý giọng nói mã nguồn mở làm để chạy trên Pi.

espeak-ng, bộ phiên âm làm hết việc bẩn

Trước VITS, phải biến text thành phoneme. 509 model trên 555 dùng espeak:

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng biết phát âm của hàng chục ngôn ngữ. Model VITS không bao giờ học chính tả tiếng Pháp với mấy chữ eau phát âm thành o, nó học từ phoneme ra âm thanh. Việc này đơn giản hóa training cực nhiều và giải thích vì sao Piper phủ 71 ngôn ngữ: với ngôn ngữ mới, bạn cắm giọng espeak tương ứng vào rồi train.

Mấy model không dùng espeak thì có phonemizer riêng, và chỗ này bắt đầu lạ: hebrew (Nakdimon ONNX + 494 dòng rule IPA), pinyin (BERT g2pW đầy đủ với giải nghĩa polyphone, cùng một ký tự Trung Quốc phát âm khác nhau tùy từ), thai (TLTK thật không chỉnh sửa), japanese (tách từ lindera + UniDic). Module pipertts port cả bảy, đã verify giống hệt bản Python tham chiếu. Mình sẽ quay lại.

Dataset: toàn LJSpeech

Định dạng train chuẩn là LJSpeech: một thư mục wavs/ đầy clip, cộng một file metadata.csv:

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized, một clip bằng một câu, vài giây. Dataset gốc là 13 000 câu tiếng Anh đọc bởi một speaker nữ duy nhất, public domain. Để train giọng Pháp, bạn thu cùng cấu trúc bằng tiếng Pháp: text + audio đã align, vài giờ là đủ cho medium ổn, nhiều hơn cho high.

Ba chất lượng trong catalog tương ứng đại khái với kích thước model + thời gian train. Low: nhỏ, nhanh, robotic. Medium: điểm ngọt, với 437 model. High: to hơn, chậm hơn, tự nhiên hơn, 67 model, trong đó có các Fortune.

Ba nút thần kỳ

Mỗi giọng Piper có ba tham số inference, với default gần như phổ quát:

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667): độ biến thiên của giọng. Cao hơn thì biểu cảm hơn, thấp hơn thì ổn định hơn. Đầu ra là ngẫu nhiên: hai lần tổng hợp cùng một câu cho hai audio hơi khác nhau.
  • noise_w (0.8): độ biến thiên của timing, nhịp điệu.
  • length_scale (1.0): tốc độ. Trên 1 thì chậm lại, dưới 1 thì nhanh lên. Cách đơn giản nhất để Luna nói nhanh hơn khi cô ấy nổi giận.

Giữ lại noise_scale, nó sẽ dùng cho phần cảm xúc.

Fortune multi: 7 cảm xúc, 1 file ONNX duy nhất

Rồi, sang ngôi sao địa phương.

Catalog liệt kê ba model Fortune train bởi ItsRealFortune (chào bạn):

Model Repo Giọng
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

Bản multi FR là thế này:

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

Bảy biến thể cảm xúc trong một file ONNX duy nhất. Bản multi EN cũng vậy, với một typo lịch sử trong tên: suprised thay vì surprised. Nó nằm trong weight, mình giữ lại, đó là một phần folklore.

SpeakerId hoạt động thế nào?

VITS multi-speaker là cùng một model với thêm một đầu vào: một embedding speaker. Khi train, mỗi clip được gắn tag với style của nó, model học một bảng embedding (một dòng mỗi style) cùng lúc với phần còn lại. Khi inference, bạn chọn dòng trong bảng:

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = normal ... 5 = surprise
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

Cùng một câu, bảy cách đọc. speakerId: 5 (surprise) với noise_scale đẩy lên một chút, và bạn có một câu cảm thán đáng tin. speakerId: 4 (triste), length_scale: 1.1 để nói chậm lại, và bạn có một thông báo buồn. Luna điều chỉnh giọng kiểu đó: bộ não chọn cảm xúc (valence/arousal, xem bài của mình về centroid), TTS chọn speakerId.

Dataset đằng sau có tên đơn giản là Fortune French - Dataset, với license tên là "Fortune's Café". Đúng, license tên như một quán cà phê. Không, nó vẫn giữ nguyên.

Vì sao cách này khôn ngoan về kinh tế

Bảy cảm xúc bằng một lần tải, một lần nạp RAM, một session ONNX. Lựa chọn khác (bảy model riêng) sẽ nhân dung lượng lưu trữ và buộc reload đồ thị mỗi lần đổi cảm xúc, không chơi được với bot đổi tông giữa câu. Còn đây, speakerId chỉ là một index trong tensor. Chi phí biên: bằng không.

Đối thủ: Kokoro, PocketTTS, sanoTTS

Piper chia sẻ CPU với ba đối thủ nghiêm túc, ba triết lý khác nhau.

Kokoro 82M, vua hiệu năng giá

Kokoro, của hexgrad. 82 triệu tham số, kiến trúc StyleTTS 2 chỉnh sửa với decoder ISTFTNet, không có bước diffusion. Không có diffusion làm nên tất cả: không vòng lặp khử nhiễu lặp đi lặp lại, nên inference cực nhanh.

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

Train trên chưa tới 100h audio với khoảng 1000 đô, và người nghe mù xếp nó ngang giọng premium của Google và Amazon. Đó là lý do nó bùng nổ: chất lượng premium, miễn phí, local, permissif.

So với Piper: Kokoro thắng về độ tự nhiên thuần (StyleTTS 2 hơn VITS cỡ nhỏ) và license (Apache 2.0 so với engine GPL-3.0 của Piper). Piper thắng về độ phủ (71 ngôn ngữ so với 8, 3755 speaker so với 54 giọng) và hệ sinh thái giọng cộng đồng. Với narration tiếng Anh, Kokoro khó bị đánh bại. Với tiếng Pháp 7 cảm xúc, tiếng Thổ hay tiếng Swahili, Piper thường là lựa chọn duy nhất.

PocketTTS (Kyutai), nhân bản giọng trên CPU

Kyutai chơi một bản nhạc khác: model nhỏ có nhân bản giọng, chạy trên CPU.

Model Params WER UTMOS SIM Tốc độ CPU (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER dưới 1% (phát âm chuẩn), UTMOS 4.36 (chất lượng cảm nhận xuất sắc), SIM 0.923 (bản clone giống target thật). Và bản small xuống tới 1.9M tham số kèm nhân bản. 1.9M. Router Sabaki của mình làm file JSON gần bằng cỡ đó.

So với Piper: PocketTTS thắng về nhân bản (Piper đọc bằng giọng cố định) và độ gọn của flagship. Piper thắng về ngôn ngữ (bản small hiện tại chỉ tiếng Anh) và độ chín của hệ sinh thái (536 giọng sẵn sàng so với "tự train đi").

sanoTTS, model chạy trên chip 3 đô

sanoTTS (sano nghĩa là "nhỏ" trong tiếng Nepal), là Bareiron của TTS: một stack neural đầy đủ đi từ phoneme ra PCM 22.05 kHz trên vi điều khiển.

Hệ thống Params SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M tham số. 2 MB một giọng. pip install sanotts, sanotts say "Hello" --voice amy, nó nói. Trên trình duyệt cũng vậy.

Nhưng nhìn điểm đi: SCOREQ 2.29 so với 3.94 của TinyTTS, UTMOS 2.45. Nó nói được, đúng. Nó hát được, không. Đó là tradeoff cuối cùng: ở 0.29M bạn chạy được mọi nơi nhưng chất lượng đi theo kích thước. Piper medium nặng gấp 100 lần để có giọng ở đẳng cấp khác.

Bảng tóm tắt

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

Lựa chọn tùy vào ràng buộc của bạn, không có người thắng tuyệt đối: chất lượng EN thuần thì Kokoro. Nhân bản CPU thì PocketTTS. Vi điều khiển thì sanoTTS. Phủ ngôn ngữ + giọng Pháp cảm xúc + không infra thì Piper.

ElevenLabs, gã khổng lồ tính phí

Rồi còn ElevenLabs. Hơn 70 ngôn ngữ, Eleven v3 biểu cảm với audio tag và hội thoại kịch tính, chất lượng top blind test. Kiểu Jev của TTS: ai cũng từng thấy qua, ai cũng từng benchmark so với nó.

Giá 2026, số tròn:

Model API Giá / 1000 ký tự
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 ký tự làm được khoảng 1 phút audio. Nên $0.05 đến $0.10 một phút. Gói Pro $99 gồm khoảng 990k ký tự, phí vượt gói cũng tính vậy. Với một kênh YouTube ra một video mỗi ngày có voice off (tool valorant-short-maker của mình làm đúng việc đó, nhưng bằng Piper miễn phí), tính $1.50 đến $3 mỗi tháng cho mỗi phút voice off hằng ngày. Mười phút mỗi ngày: $15 đến $30/tháng. Trả được, nhưng phải đếm.

Khi nào đáng tiền: khi giọng nói là sản phẩm (lồng tiếng, audiobook, quảng cáo), khi cần 70 ngôn ngữ vào sáng mai, khi nhân bản phải chuẩn ngay lần đầu. Còn không bao giờ đáng cho: bot Discord nói suốt đêm, thông báo nội bộ, dev, volume bùng nổ. Luna kể chuyện đời lúc 3h sáng bằng ElevenLabs thì hóa đơn điện nhà mình khóc luôn.

Phép tính đơn giản: Piper bằng 0 euro biên, chất lượng "tốt". ElevenLabs bằng đồng hồ chạy tiền, chất lượng "xuất sắc". Ở giữa, có một thế giới nơi "tốt và miễn phí" thắng, đó là toàn bộ cược của PiperHub.

pipertts: vì sao bản native nhanh gấp 10 lần wrapper Python

Repo itsrealfortune/pipertts, 75 commit: một port native của Piper sang TypeScript. Tiêu đề README: "In-process Piper TTS for Node.js. No Python required."

Cái giá của spawn

Wrapper lịch sử (PiperTTS) là python3 -m piper spawn trong subprocess mỗi lần tổng hợp:

synthèse = spawn(python) + chargement du modèle + inférence + kill

Mỗi câu trả tiền khởi động interpreter + nạp weight. Đo trên en_US-lessac-medium: khoảng 2.2s mỗi dòng.

Bản native (PiperNativeTTS) giữ session ONNX nóng trong bộ nhớ:

load une fois -> session persistante -> inférence par phrase

Đo y hệt: khoảng 0.22s mỗi dòng. Con số 10x đến từ đó: mình giết spawn và reload, inference thần kỳ chẳng liên quan gì ở đây. Bản thân inference ONNX giống nhau ở cả hai bên.

Bonus, lần load đầu cache đồ thị đã tối ưu cạnh file .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx), giúp các lần nạp sau nhanh hơn khoảng 35%. Khóa cache kết hợp platform, arch và phiên bản ORT, và tự refresh nếu model đổi. Tắt được bằng cacheOptimizedModel: false.

Cầu espeak: 0.4ms so với 15ms

Cùng logic cho phiên âm. Wrapper gọi binary espeak-ng qua CLI mỗi câu: khoảng 15ms spawn mỗi lần. Bản native nhúng bridge N-API (src/native/espeak-bridge/, port của espeakbridge.c), với prebuild cho Linux x64/arm64: khoảng 0.4ms mỗi gọi, phoneme giống từng byte. Không có addon thì tự rơi về CLI. PIPER_ESPEAK_BRIDGE=0 để ép dùng CLI.

7 phoneme_type, port hết

Kiểu Engine Ghi chú
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

Và tiếng Ả Rập được gắn dấu tự động (tashkeel, tắt được). 509 giọng espeak trong catalog chạy qua bridge nhanh, mấy giọng lạ chạy qua engine riêng. 71 test trong suite, ràng buộc ghi rõ ở chỗ kẹt (giọng Nhật, UniDic không đưa dữ liệu, đã thừa nhận, không giấu gì).

Định dạng và streaming

wav mặc định, raw PCM, mp3, ogg, opus: ffmpeg khi có, nếu không thì fallback thuần JS (lamejs cho mp3, opusscript + muxer Ogg cho opus, tương quan 0.993 với nguồn). Và nhất là synthesizeChunks(): một generator async nhả theo câu, với sentenceSilence giữa các câu. Với Luna, nghĩa là bắt đầu phát audio trong khi phần cuối đang tổng hợp, streaming TTS mà không cần server riêng.

Dùng đầy đủ, với catalog PiperHub tích hợp (hub: + fallback mirror tự động nếu repo tác giả không còn phục vụ file):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // thẳng cho Discord
  lengthScale: 0.95,     // nhanh hơn một chút
});

Giọng cộng đồng (GLaDOS và nhân vật game) vắng mặt trong manifest chính thức, đó đúng là lý do PiperHub tồn tại, và pipertts biết rõ: hub:<slug>, URL trang piperhub, hay slug trần, cái nào cũng chạy. Giọng bị gắn usable: false thì bị từ chối với lỗi rõ ràng.

Những gì đã hy sinh

Mỗi tính năng vắng mặt nuôi một tính năng khác. List trung thực:

Piper nghe robotic cạnh ElevenLabs. VITS medium trên 22050 Hz thì sạch nhưng phẳng so với model biểu cảm lớn. Bản multi cảm xúc bù lại một phần (7 style hơn 1 style phẳng), nhưng audiobook ElevenLabs v3 vẫn ở trên. Đó là giá của vài chục MB.

espeak đọc sai. Tên riêng, từ mượn tiếng Anh, viết tắt: phiên âm bằng rule có điểm mù. "Spoiler" trong tiếng Pháp, espeak đọc theo kiểu của nó. Model lớn có G2P neural thì sai ít hơn. Chạy prod thì cần từ điển ngoại lệ cho thuật ngữ chuyên môn.

Không nhân bản native. Piper đọc bằng giọng cố định. Nếu bạn muốn giọng CỦA BẠN, đi về phía PocketTTS hay ElevenLabs. Multi-speaker cho biến thể, không bao giờ cho timbre của bạn.

Tiếng Nhật vẫn không có trọng âm từ vựng. UniDic không đưa dữ liệu trọng âm, nên tách từ chính xác nhưng giai điệu chỉ tương đối. Ghi trong README, không giấu gì dưới thảm.

Tiếng Thái chậm ở gọi đầu. TLTK chạy dưới Pyodide/WASM không chỉnh sửa: gọi đầu mất vài giây (nạp), các gọi sau nhanh. 7/7 giống bản tham chiếu, nhưng nhớ warm-up.

Dữ liệu thì đói. Một giọng high đúng nghĩa cần hàng giờ audio sạch đã align. Dataset Fortune là hàng giờ thu âm + làm sạch. Không có phép màu: rác vào, robot ra.

Catalog có giọng chết. 25 model gắn cờ usable: false. Đã thừa nhận và lọc, nhưng nó nhắc rằng crowdsourcing giọng cũng là crowdsourcing bỏ dở.

Kết quả cuối cùng

Một bên là đồng hồ ElevenLabs, một bên là Raspberry Pi:

  • PiperHub: 536 model hiển thị, 71 ngôn ngữ, 3755 speaker, 130 dataset liên kết, guide dataset -> training -> usage
  • Piper: VITS end-to-end, espeak, ONNX CPU, LJSpeech, low/medium/high, chuẩn 22050 Hz
  • Fortune multi: 7 cảm xúc FR + 7 EN, mỗi bản 1 ONNX, speakerId để chuyển, license "Fortune's Café"
  • Kokoro 82M: chất lượng premium EN, Apache 2.0, 54 giọng, gần như realtime CPU
  • PocketTTS: nhân bản CPU, khoảng 100M rồi 2-18M, WER 0.92%
  • sanoTTS: 0.29M, 2 MB, vi điều khiển, chất lượng đi theo kích thước
  • ElevenLabs: $0.05-0.10/1000 ký tự, hơn 70 ngôn ngữ, top chất lượng, đồng hồ chạy tiền
  • pipertts native: khoảng 0.22s/dòng so với khoảng 2.2s wrapper, bridge espeak 0.4ms, 7 phoneme_type, wav/raw/mp3/ogg/opus, streaming theo câu

Luna nói bằng đống này mỗi ngày. 0 euro TTS, cảm xúc lái bởi bộ não, OGG thẳng cho Discord. So với ElevenLabs thì nghe ra khác, rõ ràng. Nhưng lúc 3h sáng khi cô ấy quyết định kể chuyện đời, hóa đơn nhà mình ngủ yên.

Triết lý

p2r3 nói: "J'aime juste l'idée que cette toute petite puce à 1 balles puisse faire tourner quelque chose d'aussi avancé que Minecraft."

Cùng vibe ở đây, áp cho giọng nói: mình thích ý tưởng vài chục MB ONNX làm một con Raspberry Pi nói 71 ngôn ngữ, không cloud, không tài khoản, không đồng hồ tiền. Nó rõ ràng không thay studio lồng tiếng nào. Nó thay chỗ khó chịu khi bạn muốn một giọng đúng mức, local, miễn phí, nói ban đêm mà không tính tiền bạn.

Tradeoff, viết đen trên trắng:

  • chất lượng premium -> VITS nhỏ: hơi robotic, nhưng local và miễn phí
  • một model mỗi cảm xúc -> speakerId: 7 giọng với giá RAM của một
  • spawn Python -> session bền: nhanh gấp 10 lần, không cần cài Python
  • catalog hoang dã -> nguồn gốc gắn kèm: mỗi giọng biết nó từ đâu đến và còn chạy không
  • API tính phí -> 0 euro biên: kém đẹp hơn ElevenLabs, nhưng hóa đơn ngủ yên

3 điều cần nhớ:

  1. Piper là TTS của CPU - VITS + espeak + ONNX, 71 ngôn ngữ, giọng nặng vài chục MB chạy trên Pi. Kém đẹp hơn ElevenLabs, rẻ hơn vô hạn.
  2. Fortune multi bằng 7 cảm xúc với giá một - một ONNX duy nhất, speakerId để chuyển, FR + EN. Embedding speaker thay vì 7 model.
  3. Bản native giết spawn - pipertts giữ session ONNX nóng: 0.22s/dòng so với 2.2s, bridge espeak 0.4ms so với 15ms CLI. Cùng inference, bớt chờ 10 lần.

Nếu dự án làm bạn quan tâm, catalog ở piperhub.org, code site ở github.com/fox3000foxy/piper-bank, module native ở github.com/itsrealfortune/pipertts. Và nếu bạn muốn nghe Luna kể chuyện đời lúc 3h sáng, gặp trên Discord xD

NEW

>-

>-

บทนำ

การทำให้บอท Discord พูดได้มันแพง ไม่ใช่ข้อความ: LLM ในเครื่องสร้างประโยคด้วย GPU แค่ไม่กี่เซนต์ แต่เสียงต้องจ่ายทุกประโยค API สังเคราะห์เสียงคิดเงินตามตัวอักษร และเมื่อบอทพูดทั้งคืน บิลก็ตามมาด้วย

นั่นคือปัญหาที่ผมเจอกับ Luna Protocol บอท Discord ของผมสร้างข้อความด้วย LLM ในเครื่อง แล้วให้เสียง Piper อ่านออกเสียง แปลงเป็น OGG แล้วส่งเป็นข้อความเสียง ทั้งหมดบนเซิร์ฟเวอร์ของผมเอง 0 ยูโรต่อประโยค แม้ตอนตี 3 ที่ Luna ตัดสินใจเล่าชีวิตของตัวเองคนเดียว

การอยู่ที่ 0 ยูโรหมายถึงการหาเสียงฟรีและโฮสต์เอง และตรงนี้แหละที่เรื่องยุ่ง: โมเดลกระจัดกระจายอยู่บน repo Hugging Face หลายสิบแห่ง ไลเซนส์คลุมเครือ ไม่มีทางรู้ว่าเสียงไหนมาจากไหนหรือยังใช้งานได้ไหม ผมเลยสร้าง PiperHub ซึ่งเป็นแค็ตตาล็อกโอเพนซอร์สของเสียงสำหรับ Piper TTS

ตัวเลขวันนี้: 536 โมเดลที่แสดง 71 ภาษาที่มี 3755 speakersที่ลิสต์ไว้ แต่ละโมเดลเชื่อมกับ dataset ต้นฉบับเมื่อเรารู้จัก ที่มาของเสียงเป็นส่วนหนึ่งของเสียง ตามที่หน้าแรกบอก

เหลือคำถามที่คนถามผมบ่อย: Piper สู้ Kokoro, PocketTTS, sanoTTS กับ ElevenLabs ได้แค่ไหน และทำไมโมดูล pipertts ของ Fortune ถึงเร็วกว่า wrapper Python ทางการ 10 เท่า บทความนี้เปิดเครื่องยนต์ พร้อมโค้ดเป็นหลักฐาน

PiperHub แค็ตตาล็อก

ก่อนเปิดเครื่องยนต์ มาดูปัญหาที่ PiperHub แก้กันก่อน

เสียงมาจากไหน

โค้ดอยู่ที่ github.com/fox3000foxy/piper-bank Astro เว็บสแตติก ไม่มีเซิร์ฟเวอร์: ภาษาอังกฤษที่ root อีก 15 ภาษาใต้ /<code>/ (fr, de, es, it, pt, ru, tr, ja, ko, zh, id, hi, ar, vi, th) แพทเทิร์นเดียวกับบล็อกของผมเลย

แหล่งความจริงไม่มีใครแก้ด้วยมือ มันคือ pipeline:

npm install
npm run data    # สร้าง src/data/*.json ใหม่ (python3 scripts/generate_data.py)
npx astro dev   # dev ในเครื่อง
npm run build   # บิลด์สแตติกลง dist/

จริงๆ แล้ว: repo crawler ดูแล models_mapping.csv กับ datasets_mapping.csv เสียง mirror อยู่บน Hugging Face ตัวอย่างคือไฟล์เสียง npm run data ดึงทั้งหมดแล้วคายออกมาเป็น src/data/*.json เว็บแค่อ่าน JSON สแตติก

ในข้อมูลดิบตอนนี้ (555 รายการ เว็บแสดง 536 งานครอวล์นำหน้าการแสดงผลนิดหน่อยเสมอ):

คุณภาพ โมเดล
medium 437
high 67
low 50
Sample rate โมเดล
22050 Hz 486
16000 Hz 47

486 เสียงที่ 22050 Hz มาตรฐาน Piper 47 เสียงที่ 16000 Hz เสียงเล็กเร็วสำหรับงานฝังตัว phonemizer คือ espeak ใน 509 เคส เดี๋ยวกลับมา มันคือหัวใจของระบบ

ที่มา หรือทำไมทุกโมเดลต้องมี dataset

สิ่งที่ทำให้ PiperHub มีประโยชน์แทนที่จะเป็นแค่ลิสต์ คือลิงก์โมเดลกับ dataset ตัวอย่างจากหน้าแรก: เสียง Astra - Valorant Piper TTS เชื่อมกับ dataset Astra - LJSpeech Dataset เปิดทั้งสองหน้า ตรวจ repo กับไลเซนส์ทั้งสองฝั่ง

130 dataset ที่อ้างอิงไว้ และแค็ตตาล็อกซื่อสัตย์เรื่องสภาพกองทัพ: 25 โมเดลถูกติดธง usable: false (ไฟล์พัง หรือฟอร์แมต piperPlus ที่ต้องใช้ runtime สาย fork) โมดูล pipertts ปฏิเสธเสียงพวกนี้ด้วย error ชัดเจนแทนที่จะแครชแปลกๆ ทีหลัง 48 โมเดลเป็น multi-speaker รวมถึงสองตัวที่เราจะสนใจ

ฝั่งไกด์ เว็บทำงานสามขั้น: Build a dataset, Train a voice, Use a voice บวกหน้า Piper Plus dataset ฟอร์แมต LJSpeech เป็นอินพุต ONNX เป็นเอาต์พุต พูดง่าย ทำยาว เลยต้องมีส่วนถัดไป

Piper ใต้ฝากระโปรง: VITS, espeak, ONNX

หนึ่งประโยค สามขั้นตอน

เมื่อคุณขอให้ Piper พูดคำว่า "bonjour" จะเกิดสิ่งนี้:

"bonjour"
  -> [phonémisation espeak-ng] -> b O~ Z u R
  -> [encodeur VITS + flow + décodeur] -> waveform 22050 Hz

ไม่มี vocoder แยก นั่นคือ VITS: โมเดล end-to-end จากข้อความ (ที่แปลงเป็นเสียงพูดแล้ว) ไป waveform ตรงๆ ในขณะที่ pipeline เก่าทำข้อความแล้ว spectrogram (Tacotron) แล้ว spectrogram แล้วเสียง (HiFi-GAN สองโมเดล สองรอบเทรน) VITS เรียนทุกอย่างทีเดียวด้วย conditional VAE, normalizing flows และ discriminator แบบ GAN บน decoder เทรนครั้งเดียว โมเดลเดียว export ONNX ครั้งเดียว

decoder คาย 22050 Hz ตรงๆ ที่ sample rate นี้ หนึ่งวินาทีของเสียงเท่ากับ 22050 ตัวเลข โมเดลเล็ก (หลักสิบ MB ในคุณภาพ medium) และ inference เป็น CPU ล้วน: คูณเมทริกซ์บนกราฟ ONNX ที่ optimize แล้ว นั่นคือเหตุผลที่ Raspberry Pi เป็นเป้าหมายทางการ Piper เกิดใน Rhasspy ผู้ช่วยเสียงโอเพนซอร์สที่ออกแบบมาให้รันบน Pi

espeak-ng ตัวแปลงเสียงที่ทำงานสกปรกทั้งหมด

ก่อน VITS ต้องแปลงข้อความเป็น phoneme 509 โมเดลจาก 555 ใช้ espeak:

echo "bonjour" | espeak-ng --ipa -v fr
# bO~'ZuR

espeak-ng รู้การออกเสียงหลายสิบภาษา โมเดล VITS ไม่เคยเรียนสะกดฝรั่งเศสที่ eau ออกเสียง o มันเรียนจาก phoneme ไปเสียง ทำให้เทรนง่ายมาก และอธิบายว่าทำไม Piper ครอบคลุม 71 ภาษา: ภาษาใหม่ก็ต่อเสียง espeak ที่ตรงกันแล้วเทรน

เสียงที่ไม่ใช้ espeak มี phonemizer ของตัวเอง และตรงนี้เริ่มแปลก: hebrew (โมเดล Nakdimon ONNX กับกฎ IPA 494 บรรทัด), pinyin (BERT g2pW เต็มตัวพร้อมแก้ polyphone อักษรจีนตัวเดียวกันออกเสียงต่างกันตามคำ), thai (TLTK จริงไม่ดัดแปลง), japanese (ตัดคำ lindera กับ UniDic) โมดูล pipertts พกมาทั้งหมด ตรวจแล้วว่าตรงกับ Python อ้างอิง เดี๋ยวกลับมา

dataset: LJSpeech เสมอ LJSpeech

ฟอร์แมตเทรนมาตรฐานคือ LJSpeech: โฟลเดอร์ wavs/ เต็มไปด้วยคลิป กับ metadata.csv:

LJ001-0001|Printing, in the only sense with which we are at present concerned...|printing in the only sense...
LJ001-0002|in being comparatively modern...|in being comparatively modern...

fileid|transcription|normalized หนึ่งคลิปเท่ากับหนึ่งประโยค ไม่กี่วินาที dataset ต้นฉบับคือประโยคอังกฤษ 13,000 ประโยคอ่านโดยผู้พูดคนเดียว public domain จะเทรนเสียงฝรั่งเศสก็อัดโครงสร้างเดียวกันเป็นฝรั่งเศส: ข้อความกับเสียงที่ align กัน ไม่กี่ชั่วโมงพอสำหรับ medium ที่ใช้ได้ มากกว่านั้นสำหรับ high

สามคุณภาพในแค็ตตาล็อกตรงกับขนาดโมเดลกับระยะเวลาเทรนคร่าวๆ Low: เล็ก เร็ว หุ่นยนต์ Medium: จุดลงตัว มี 437 โมเดล High: ใหญ่กว่า ช้ากว่า ธรรมชาติ กว่า มี 67 โมเดล รวมถึง Fortune

ปุ่มวิเศษสามปุ่ม

ทุกเสียง Piper มีพารามิเตอร์ inference สามตัว ค่าเริ่มต้นแทบสากล:

"inference": { "noise_scale": 0.667, "length_scale": 1, "noise_w": 0.8 }
  • noise_scale (0.667): ความหลากหลายของเสียง สูงกว่าหมายถึงแสดงอารมณ์มาก ต่ำกว่าหมายถึงนิ่งกว่า เอาต์พุตสุ่ม: สังเคราะห์ประโยคเดียวกันสองครั้งได้เสียงต่างกันเล็กน้อย
  • noise_w (0.8): ความหลากหลายของจังหวะ
  • length_scale (1.0): ความเร็ว เกิน 1 ช้าลง ต่ำกว่า 1 เร็วขึ้น วิธีง่ายสุดที่จะทำให้ Luna พูดเร็วขึ้นตอนโมโห

จำ noise_scale ไว้ เดี๋ยวใช้กับอารมณ์

Fortune multi: 7 อารมณ์ 1 ONNX

เอาล่ะ มาดาวเด่นท้องถิ่นกัน

แค็ตตาล็อกลิสต์สามโมเดล Fortune ที่เทรนโดย ItsRealFortune (หวัดดี):

โมเดล Repo เสียง
ItsRealFortune - French (FR) High fox3000foxy/piper-fortune-v6 1 (voice-0)
ItsRealFortune - French (FR) Multi High fox3000foxy/piper-checkpoints-fortune-fr-multi 7
ItsRealFortune - English (EN) Multi High fox3000foxy/piper-checkpoints-fortune-en-multi 7

ตัว FR multi คือแบบนี้:

normal, joyeuse, storytelling, colere, triste, surprise, interrogative

เจ็ดอารมณ์ในไฟล์ ONNX เดียว ตัว EN multi เหมือนกัน มี typo ประวัติศาสตร์ในชื่อ: suprised แทน surprised มันอยู่ใน weight เราเก็บไว้ เป็นส่วนหนึ่งของตำนาน

speakerId ทำงานอย่างไร

VITS multi-speaker คือโมเดลเดียวกันที่มีอินพุตเพิ่ม: speaker embedding ตอนเทรน แต่ละคลิปแท็กสไตล์ โมเดลเรียนตาราง embedding (สไตล์ละหนึ่ง) พร้อมส่วนที่เหลือ ตอน inference เลือกแถวของตาราง:

import { PiperNativeTTS } from "pipertts";

const tts = await PiperNativeTTS.load({
  model: "hub:itsrealfortune-french-fr-multi-high",
  modelsDir: "./models",
});

const styles = [
  "normal", "joyeuse", "storytelling",
  "colere", "triste", "surprise", "interrogative",
];

for (let i = 0; i < styles.length; i++) {
  const r = await tts.synthesize("Quelle merveilleuse nouvelle !", {
    speakerId: i,           // 0 = ปกติ ... 5 = ประหลาดใจ
    outputFile: `./fortune-${styles[i]}.wav`,
  });
}

ประโยคเดียว อ่านเจ็ดแบบ speakerId: 5 (surprise) กับ noise_scale ที่ดันขึ้นนิดหน่อย ได้อุทานที่น่าเชื่อ speakerId: 4 (triste) กับ length_scale: 1.1 ให้ช้าลง ได้ประกาศงานศพ นี่คือวิธีที่ Luna ปรับเสียง: สมองเลือกอารมณ์ (valence/arousal ดูบทความ centroids ของผม) TTS เลือก speakerId

dataset เบื้องหลังชื่อเรียบๆ ว่า Fortune French - Dataset กับไลเซนส์ชื่อ "Fortune's Café" ใช่ ไลเซนส์ชื่อเหมือนคาเฟ่ ไม่ มันอยู่ต่อ

ทำไมมันฉลาดเชิงเศรษฐศาสตร์

เจ็ดอารมณ์หมายถึงดาวน์โหลดครั้งเดียว โหลดเข้า RAM ครั้งเดียว session ONNX เดียว ทางเลือก (เจ็ดโมเดลแยก) คูณพื้นที่เก็บข้อมูลและบังคับ reload กราฟทุกครั้งที่เปลี่ยนอารมณ์ ใช้ไม่ได้กับบอทที่เปลี่ยนโทนกลางประโยค ตรงนี้ speakerId แค่ index ใน tensor ต้นทุนเพิ่ม: ศูนย์

คู่แข่ง: Kokoro, PocketTTS, sanoTTS

Piper แบ่ง CPU กับคู่แข่งจริงจังสามราย สามปรัชญาต่างกัน

Kokoro 82M ราชาคุณภาพต่อราคา

Kokoro โดย hexgrad 82 ล้านพารามิเตอร์ สถาปัตย์ StyleTTS 2 ดัดแปลงพร้อม decoder ISTFTNet ไม่มีขั้น diffusion การไม่มี diffusion คือทุกอย่าง: ไม่มีลูป denoise ซ้ำ inference เร็วสุดๆ

Kokoro-82M
Params 82M
Poids ~300 Mo (164 Mo en FP16)
Licence Apache 2.0 (commercial OK)
Langues 8, 54 voix preset + blending
CPU quasi temps réel
GPU 36 à 96x temps réel
Tests aveugles au niveau de Google et Amazon

เทรนด้วยเสียงไม่ถึง 100 ชั่วโมงราคาแค่ประมาณ 1000 ดอลลาร์ และผู้ฟัง blind test จัดให้อยู่ระดับเสียงพรีเมียมของ Google กับ Amazon นั่นคือเหตุผลที่มันระเบิด: คุณภาพพรีเมียม ฟรี ในเครื่อง permissive

เจอ Piper: Kokoro ชนะเรื่องธรรมชาติล้วนๆ (StyleTTS 2 ดีกว่า VITS ตัวเล็ก) กับไลเซนส์ (Apache 2.0 เทียบกับเอนจิน GPL-3.0 ของ Piper) Piper ชนะเรื่องครอบคลุม (71 ภาษาเทียบ 8, 3755 speakers เทียบ 54 เสียง) กับระบบนิเวศเสียงชุมชน งานบรรยายอังกฤษ Kokoro เอาชนะยาก งานฝรั่งเศส 7 อารมณ์ ตุรกี หรือสวาฮิลี Piper มักเป็นตัวเดียวในสนาม

PocketTTS (Kyutai) โคลนเสียงบน CPU

Kyutai เล่นอีกเพลง: โมเดลเล็กพร้อมโคลนเสียง รันบน CPU

โมเดล พารามิเตอร์ WER UTMOS SIM ความเร็ว CPU (1 thread)
Pocket TTS ~100M 0.92% 4.364 0.923 2.8x temps réel
Pocket TTS small 1.9M / 6.9M / 18.2M - - - CPU, clonage inclus

WER ต่ำกว่า 1% (ออกเสียงถูก) UTMOS 4.36 (คุณภาพที่รู้สึกว่ายอดเยี่ยม) SIM 0.923 (โคลนเหมือนเป้าหมายจริง) และรุ่น small ลงถึง 1.9M พารามิเตอร์พร้อมโคลน 1.9M เราเตอร์ Sabaki ของผมขนาด JSON เกือบเท่านี้

เจอ Piper: PocketTTS ชนะเรื่องโคลน (Piper อ่านด้วยเสียงตายตัว) กับความกะทัดรัดของ flagship Piper ชนะเรื่องภาษา (ตัว small ตอนนี้อังกฤษอย่างเดียว) กับความสุกของระบบนิเวศ (536 เสียงพร้อมใช้เทียบกับ "เทรนของตัวเอง")

sanoTTS โมเดลที่อยู่บนชิป 3 ดอลลาร์

sanoTTS (sano แปลว่า "เล็ก" ในภาษาเนปาล) คือ Bareiron ของ TTS: stack neural ทั้งชุดจาก phoneme ไป PCM 22.05 kHz บนไมโครคอนโทรลเลอร์

ระบบ พารามิเตอร์ SCOREQ UTMOS DNS-SIG
sanoTTS (heart-nano) 0.29M 2.29 2.45 3.35
TinyTTS 1.62M 3.94 3.65 3.62

0.29M พารามิเตอร์ เสียง 2 MB pip install sanotts แล้ว sanotts say "Hello" --voice amy มันพูด ในเบราว์เซอร์ด้วย

แต่ดูคะแนน: SCOREQ 2.29 เทียบกับ 3.94 ของ TinyTTS UTMOS 2.45 พูดได้ ใช่ ร้องเพลง ไม่ มันคือ tradeoff สุดขั้ว: ที่ 0.29M อยู่ได้ทุกที่แต่คุณภาพตามขนาด Piper medium หนักกว่า 100 เท่าเพื่อเสียงคนละลีก

ตารางสรุป

Piper Kokoro 82M PocketTTS sanoTTS
Params typiques ~10-60M 82M ~100M / 2-18M 0.3-1.4M
Langues 71 8 EN (+ smalls EN) multilingue, petit
Voix dispos 3755 speakers 54 + blending clonage (la tienne) quelques voix
Clonage non non (natif) oui, CPU non
Licence moteur GPL-3.0 Apache 2.0 open (Kyutai) open
Entraînable maison oui (LJSpeech) oui (plus lourd) oui oui
Qualité FR bonne (multi émotions) correcte (EN-first) EN-only faible

ตัวเลือกขึ้นกับข้อจำกัด ไม่มีผู้ชนะสัมบูรณ์: คุณภาพ EN ล้วนไป Kokoro โคลนบน CPU ไป PocketTTS ไมโครคอนโทรลเลอร์ไป sanoTTS ครอบคลุมภาษากับเสียงฝรั่งเศสมีอารมณ์กับศูนย์ infra ไป Piper

ElevenLabs ยักษ์ที่เก็บเงิน

แล้วก็มี ElevenLabs 70 กว่าภาษา Eleven v3 แสดงอารมณ์พร้อม audio tag กับบทสนทนาดราม่า คุณภาพ top ของ blind test เป็น Jev ของ TTS ประมาณนั้น: ทุกคนเคยเห็น ทุกคนเคยเทียบ

ราคา 2026 ตัวเลขกลมๆ:

โมเดล API ราคา / 1000 ตัวอักษร
Flash / Turbo, v3 Conversational $0.05
Multilingual v2, Eleven v3 $0.10

1000 ตัวอักษรได้เสียงประมาณ 1 นาที ดังนั้นนาทีละ $0.05 ถึง $0.10 แผน Pro $99 รวมประมาณ 990k ตัวอักษร overage คิดอัตราเดียวกัน สำหรับช่อง YouTube ที่ออกวิดีโอพากย์เสียงวันละคลิป (ผมทำแบบนั้นกับ valorant-short-maker แต่ใช้ Piper ฟรี) นับ $1.50 ถึง $3 ต่อเดือนสำหรับทุกนาทีพากย์รายวัน วันละสิบนาที: $15 ถึง $30 ต่อเดือน จ่ายได้ แต่นับ

เมื่อไหร่คุ้ม: เมื่อเสียงคือสินค้า (พากย์ หนังสือเสียง โฆษณา) เมื่อต้องได้ 70 ภาษาพรุ่งนี้เช้า เมื่อโคลนต้องเป๊ะตั้งแต่ครั้งแรก แต่ไม่คุ้มเลยสำหรับ: บอท Discord ที่พูดทั้งคืน notification ภายใน dev ปริมาณระเบิด Luna เล่าชีวิตตอนตี 3 บน ElevenLabs คือบิลไฟผมร้องไห้

คณิตง่าย: Piper เท่ากับ 0 ยูโรเพิ่ม คุณภาพ "ดี" ElevenLabs เท่ากับมิเตอร์วิ่ง คุณภาพ "ยอดเยี่ยม" ระหว่างกันมีโลกที่ "ดีและฟรี" ชนะ นั่นคือเดิมพันทั้งหมดของ PiperHub

pipertts: ทำไมเนทีฟเร็วกว่า wrapper Python 10 เท่า

repo itsrealfortune/pipertts 75 commit: พอร์ต Piper เนทีฟเป็น TypeScript ชื่อ README: "In-process Piper TTS for Node.js. No Python required."

ต้นทุนของ spawn

wrapper ดั้งเดิม (PiperTTS) คือ python3 -m piper ที่ spawn เป็น sub-process ทุกครั้งที่สังเคราะห์:

synthèse = spawn(python) + chargement du modèle + inférence + kill

ทุกประโยคจ่ายค่าสตาร์ท interpreter กับโหลด weight วัดบน en_US-lessac-medium: ประมาณ 2.2 วินาทีต่อบรรทัด

เนทีฟ (PiperNativeTTS) เก็บ session ONNX อุ่นในหน่วยความจำ:

load une fois -> session persistante -> inférence par phrase

วัดแบบเดียวกัน: ประมาณ 0.22 วินาทีต่อบรรทัด 10 เท่ามาจากตรงนี้: ฆ่า spawn กับ reload ส่วน inference มหัศจรรย์ไม่เกี่ยวตรงนี้ inference ONNX เองเหมือนกันทั้งสองฝั่ง

เป็นโบนัส load ครั้งแรก cache กราฟที่ optimize ไว้ข้าง .onnx (<model>.ortopt-<platform>-<arch>-v<ort>.onnx) ทำให้ load ครั้งต่อไปเร็วขึ้นประมาณ 35% คีย์ cache รวม platform, arch กับเวอร์ชัน ORT และ refresh ถ้าโมเดลเปลี่ยน ปิดได้ด้วย cacheOptimizedModel: false

สะพาน espeak: 0.4ms เทียบ 15ms

ตรรกะเดียวกันสำหรับ phonemization wrapper เรียกไบนารี espeak-ng ผ่าน CLI ทุกประโยค: spawn ประมาณ 15ms ทุกครั้ง เนทีฟฝัง bridge N-API (src/native/espeak-bridge/ พอร์ตจาก espeakbridge.c) พร้อม prebuild สำหรับ Linux x64/arm64: ประมาณ 0.4ms ต่อครั้ง phoneme ตรงระดับ byte ไม่มี addon ถอยอัตโนมัติไป CLI PIPER_ESPEAK_BRIDGE=0 เพื่อบังคับ CLI

phoneme_type ทั้ง 7 พอร์ตหมดแล้ว

ชนิด เอนจิน หมายเหตุ
espeak bridge N-API ou CLI clauses byte-identiques
text passthrough codepoints trivial
hebrew Nakdimon ONNX + règles IPA sorties identiques
lithuanian espeak + dico de stress 189k entrées sorties identiques
pinyin BERT g2pW ONNX + WordPiece désambiguïsation des polyphones OK
thai vrai TLTK non modifié sous Pyodide/WASM 7/7 identiques (1er appel lent, suite rapide)
japanese lindera-wasm + UniDic + moteur de mores TS segmentation exacte, pas d'accent lexical

และภาษาอาหรับใส่สระอัตโนมัติ (tashkeel ปิดได้) 509 เสียง espeak ในแค็ตตาล็อกผ่าน bridge เร็ว ตัวแปลกผ่านเอนจินเฉพาะ 71 เทสต์ในชุด ข้อจำกัดเอกสารไว้ตรงที่ติด (สำเนียงญี่ปุ่น UniDic ไม่ให้ข้อมูลมา ยอมรับ ไม่มีซ่อน)

ฟอร์แมตกับ streaming

wav ค่าเริ่มต้น raw PCM mp3 ogg opus: ใช้ ffmpeg เมื่อมี ไม่งั้น fallback JS ล้วน (lamejs สำหรับ mp3, opusscript กับ Ogg muxer สำหรับ opus สหสัมพันธ์ 0.993 กับต้นฉบับ) และสำคัญสุด synthesizeChunks(): async generator ที่ yield ทีละประโยค มี sentenceSilence ระหว่างประโยค สำหรับ Luna หมายถึงเริ่มเล่นเสียงขณะที่ตอนท้ายกำลังสังเคราะห์ สตรีม TTS โดยไม่ต้องมีเซิร์ฟเวอร์เฉพาะ

การใช้งานเต็ม พร้อมแค็ตตาล็อก PiperHub ในตัว (hub: กับ mirror fallback อัตโนมัติถ้า repo ผู้แต่งเลิกเสิร์ฟไฟล์):

const tts = await PiperNativeTTS.load({
  model: "hub:glados-french-medium-voice",
  modelsDir: "./models",
});

const r = await tts.synthesize("Bonjour, je suis GLaDOS.", {
  outputFormat: "ogg",   // ตรงสำหรับ Discord
  lengthScale: 0.95,     // เร็วขึ้นนิด
});

เสียงชุมชน (GLaDOS กับตัวละครเกม) ไม่อยู่ใน manifest ทางการ นั่นคือเหตุผลที่ PiperHub มีอยู่พอดี และ pipertts รู้ดี: hub:<slug> URL หน้า piperhub หรือ slug เปล่า ใช้ได้หมด เสียงที่ติดธง usable: false ถูกปฏิเสธด้วย error ชัดเจน

สิ่งที่สละไป

ทุกฟีเจอร์ที่หายไปจ่ายให้อีกอัน รายการซื่อสัตย์:

Piper ฟังดูหุ่นยนต์ข้าง ElevenLabs VITS medium บน 22050 Hz สะอาดแต่แบนเทียบกับโมเดลใหญ่แสดงอารมณ์ multi-emotion ชดเชยบางส่วน (7 สไตล์ดีกว่า 1 สไตล์แบน) แต่หนังสือเสียง ElevenLabs v3 ยังเหนือกว่า นั่นคือราคาของหลักสิบ MB

espeak ผิด ชื่อเฉพาะ คำยืม ตัวย่อ: phonemization ด้วยกฎมีจุดบอด "Spoiler" ในภาษาฝรั่งเศส espeak อ่านเท่าที่ทำได้ โมเดลใหญ่ที่มี G2P neural พลาดน้อยกว่า ใน prod ต้องมีพจนานุกรมข้อยกเว้นสำหรับศัพท์งาน

โคลนเนทีฟศูนย์ Piper อ่านด้วยเสียงตายตัว ถ้าอยากได้เสียงตัวเอง ไป PocketTTS หรือ ElevenLabs multi-speaker ให้ความหลากหลาย ไม่เคยให้ timbre ของคุณ

ญี่ปุ่นยังไม่มี lexical accent UniDic ไม่ให้ข้อมูล accent การตัดคำถูกต้องแต่ทำนองประมาณ เอกสารไว้ใน README ไม่มีหมก

ไทยช้าครั้งแรก TLTK รันใต้ Pyodide/WASM ไม่ดัดแปลง: ครั้งแรกใช้เวลาหลายวินาที (โหลด) ครั้งต่อไปเร็ว 7/7 ตรงกับอ้างอิง แต่เผื่อ warm-up

ข้อมูลหิว เสียง high ที่ดีต้องใช้เสียงสะอาด align กันหลายชั่วโมง dataset Fortune คือชั่วโมงอัดกับทำความสะอาด ศูนย์เวทมนตร์: garbage in, robot out

แค็ตตาล็อกมีเสียงตาย 25 โมเดลติดธง usable: false ยอมรับและกรองแล้ว แต่เตือนว่าการคราวด์ซอร์สเสียงก็คือคราวด์ซอร์สการทิ้งด้วย

ผลลัพธ์สุดท้าย

ฝั่งหนึ่งมิเตอร์ ElevenLabs อีกฝั่ง Raspberry Pi:

  • PiperHub: 536 โมเดลที่แสดง 71 ภาษา 3755 speakers 130 dataset ที่เชื่อม ไกด์ dataset แล้ว training แล้ว usage
  • Piper: VITS end-to-end, espeak, ONNX บน CPU, LJSpeech, low/medium/high, มาตรฐาน 22050 Hz
  • Fortune multi: 7 อารมณ์ FR กับ 7 EN, อย่างละ 1 ONNX, speakerId สำหรับสลับ, ไลเซนส์ "Fortune's Café"
  • Kokoro 82M: คุณภาพพรีเมียม EN, Apache 2.0, 54 เสียง, เกือบ real time บน CPU
  • PocketTTS: โคลนบน CPU, ประมาณ 100M แล้ว 2-18M, WER 0.92%
  • sanoTTS: 0.29M, 2 MB, ไมโครคอนโทรลเลอร์, คุณภาพตามขนาด
  • ElevenLabs: $0.05-0.10/1000 ตัวอักษร, 70 กว่าภาษา, คุณภาพ top, มิเตอร์วิ่ง
  • pipertts เนทีฟ: ประมาณ 0.22 วินาทีต่อบรรทัดเทียบกับ wrapper ประมาณ 2.2 วินาที, bridge espeak 0.4ms, phoneme_type 7 แบบ, wav/raw/mp3/ogg/opus, สตรีมทีละประโยค

Luna พูดด้วยสิ่งนี้ทุกวัน TTS 0 ยูโร อารมณ์ขับโดยสมอง OGG ตรงสำหรับ Discord เจอ ElevenLabs ได้ยินความต่าง แน่นอน แต่ตอนตี 3 ที่เธอตัดสินใจเล่าชีวิต บิลของผมนอนหลับสนิท

ปรัชญา

p2r3 พูดว่า: "J'aime juste l'idée que cette toute petite puce à 1 balles puisse faire tourner quelque chose d'aussi avancé que Minecraft."

ฟีลเดียวกันตรงนี้ ใช้กับเสียง: ผมชอบไอเดียที่ ONNX หลักสิบ MB ทำให้ Raspberry Pi พูด 71 ภาษา โดยไม่มีคลาวด์ ไม่มีบัญชี ไม่มีมิเตอร์ มันแทนสตูดิโอพากย์ไม่ได้แน่นอน แต่มันแทนเคสน่ารำคาญที่อยากได้เสียงที่ใช้ได้ ในเครื่อง ฟรี พูดกลางคืนโดยไม่เก็บเงิน

tradeoff เขียนขาวดำ:

  • คุณภาพพรีเมียมแลก VITS เล็ก: หุ่นยนต์นิดหน่อย แต่ในเครื่องและฟรี
  • โมเดลละอารมณ์แลก speakerId: 7 เสียงในราคาเดียวใน RAM
  • spawn Python แลก session ค้าง: เร็วขึ้น 10 เท่า ไม่ต้องติดตั้ง Python
  • แค็ตตาล็อกป่าแลกที่มาผูก: ทุกเสียงรู้ว่ามาจากไหนและยังใช้ได้ไหม
  • API เก็บเงินแลก 0 ยูโรเพิ่ม: สวยน้อยกว่า ElevenLabs แต่บิลนอนหลับสนิท

3 อย่างที่ต้องจำ:

  1. Piper คือ TTS ของ CPU คือ VITS กับ espeak กับ ONNX 71 ภาษา เสียงหลักสิบ MB ที่รันบน Pi สวยน้อยกว่า ElevenLabs ถูกกว่าไม่รู้กี่เท่า
  2. Fortune multi คือ 7 อารมณ์ในราคาหนึ่ง คือ ONNX เดียว speakerId สำหรับสลับ FR กับ EN speaker embedding แทน 7 โมเดล
  3. เนทีฟฆ่า spawn pipertts เก็บ session ONNX อุ่นไว้: 0.22 วินาทีต่อบรรทัดเทียบกับ 2.2 วินาที bridge espeak 0.4ms เทียบกับ CLI 15ms inference เดียวกัน รอน้อยลง 10 เท่า

ถ้าโปรเจกต์น่าสนใจ แค็ตตาล็อกคือ piperhub.org โค้ดเว็บ github.com/fox3000foxy/piper-bank โมดูลเนทีฟ github.com/itsrealfortune/pipertts และถ้าอยากฟัง Luna เล่าชีวิตตอนตี 3 อยู่บน Discord xD

Related Articles