> Három nyílt TTS-modell egy laptopon: hogyan lett 95 cikkből 153 perc felolvasás — a SQLite-soroktól a tagolt szintézisen és a 192 kbit/s AAC-en át az oldal mellett maradó lejátszóig.
>
> Web page: https://balazscsorba.com/hu/blog/local-text-to-speech-pipeline · Language: Magyar · Also available in: [English](https://balazscsorba.com/blog/local-text-to-speech-pipeline.md) · [Deutsch](https://balazscsorba.com/de/blog/local-text-to-speech-pipeline.md)
> Author: Balázs Csorba · Published: 2026-10-01 · Keywords: local text-to-speech, text-to-speech pipeline, kokoro tts, piper tts, bark tts comparison, narrate blog posts, wav to m4a ffmpeg, aac 192 kbit/s faststart, vue audio player, onnx tts mac

[Blog](https://balazscsorba.com/hu/blog)/LLMOps és értékelés

# Helyi felolvasás nagyban: 95 cikk narrálása nyílt modellekkel

Három nyílt TTS-modell egy laptopon: hogyan lett 95 cikkből 153 perc felolvasás — a SQLite-soroktól a tagolt szintézisen és a 192 kbit/s AAC-en át az oldal mellett maradó lejátszóig.

[Balázs Csorba](https://balazscsorba.com/hu/about)·2026\. október 1.·11 perc olvasás

-   Text-to-speech
-   Audio
-   Kokoro
-   FFmpeg
-   Vue

![Hullámdiagram a narrációs pipeline-ról: az adatbázsissorok mondatokra hullanak, egy helyi modell szintetizálja őket, AAC-kódolásra kerülnek, és a képernyő szélén ragadó fülről indulnak.](https://balazscsorba.com/images/blog/local-text-to-speech-pipeline/cover.webp?v=8aa3b62c5b)

## A lényeg röviden

-   A helyi TTS megfordítja a narrálás gazdaságát: 95 cikk és 153 perc hang körülbelül 35 perc számítási időbe és nulla API-díjba kerül — egy plusz cikk határköltsége az áram.
-   Ilyen méretben a modellválasztás sebesség-minőség Pareto, nem benchmark-harc: a Piper volt a leggyorsabb, a Bark sosem fejezett be, a Kokoro v1.0 (82M, ONNX) pedig a proszódiát nyerte nagyjából négyszeres valós idejű sebességgel.
-   Mondatoknál tagolj, 400 karakterig, 80 ms csenddel a darabok között; az okosabb vágópontok szinte semmit nem adnak, amit a hallgató észrevenne.
-   A WAV munkaformátum, nem kézbesítési formátum: a 192 kbit/s-os AAC faststarttel 406 MB-ról 181 MB-ra vágta a méretet, és a letöltés befejezése előtt teszi használhatóvá a hosszt és a pörgetést.
-   A prerenderelt lejátszóban az események verik a feltételezéseket: a loadedmetadata lefuthat a hidratálás előtt, ezért a hosszt durationchange-en, canplay-en és mountkor újra kell olvasni — az állapotot iratkozz fel, ne az értesítésre.

Ezen az oldalon

1.  [Miért helyi a text-to-speech](https://balazscsorba.com/#why-local)
2.  [Három modell, egy laptop](https://balazscsorba.com/#model-shootout)
3.  [A generálási pipeline](https://balazscsorba.com/#the-pipeline)
4.  [A WAV munkaformátum, nem kézbesítési formátum](https://balazscsorba.com/#wav-to-aac)
5.  [A lejátszó](https://balazscsorba.com/#the-player)
6.  [Mi tört össze menet közben](https://balazscsorba.com/#what-broke)
7.  [A számok](https://balazscsorba.com/#the-numbers)
8.  [Mit csinálnék másképp](https://balazscsorba.com/#verdict)
9.  [Források](https://balazscsorba.com/#sources)

Cikk meghallgatása

0:000:00

Mostantól minden cikkhez tartozik felolvasás: 95 bejegyzés, 153 perc hang, egyetlen laptopon generálva, egyetlen API-hívás nélkül. Ez a poszt a teljes beszámoló — hogyan hasonlítottunk össze három nyílt text-to-speech modellt, hogyan alakítja a pipeline a SQLite-sorokat mondatnyi szintézis-darabokká, miért AAC a kézbesített formátum 192 kbit/s-on, és hogyan épül a lejátszó, hogy a hang együtt fusson az oldallal.

Először a számok, mert ezek keretezik az alábbi minden döntést: 95 cikk (45 blogbejegyzés és 50 eszközismertető), 153 perc kész narráció, körülbelül 35 perc generálási idő, 406 MB köztes WAV 181 MB kézbesített AAC-ra csökkentve, és nulla euró API-díj. Minden egyetlen, 16 GB memóriájú Apple Silicon laptopon futott.

## Miért helyi a text-to-speech

A felhőben futó text-to-speech kiváló és negyedévről negyedévre jobb lesz — de mérik. ilyen volumenben a számla minden szóval, minden szerkesztés utáni újraépítéssel, minden hang- vagy sebességkísérlettel nő. A helyi pipeline megfordítja a gazdaságot: egy plusz cikk határköltsége néhány cent áram és két perc várakozás. Ráadásul reprodukálható — ugyanaz a szöveg, ugyanaz a modellverzió és ugyanaz a hang jövőre ugyanazt a fájlt adja, és ez számít, amikor egy szerkesztett cikket újra kell narrálni, hogy a hang ne csússzon el.

Van egy másik ok is: a szöveg nem hagyja el a gépet. Egy még publikálatlan cikk vázlatát ugyanaz a folyamat olvassa, amely közzéteszi, ugyanazon a gépen — és ha egy bekezdés változik, egy slug újrafuttatása egy fájlt állít elő, nem kilencvenötöt.

**A keretfeltétel**

Nincs GPU-klaszter és nincs felhős batch: egy 16 GB-os Apple Silicon laptop, amit minden más is használ. Ez a választást a memóriában kényelesen elférő, CPU-n közel valós idejű modellekre szűkítette — néhány száz millió paraméterű modellre, nem néhány milliárdosra.

## Három modell, egy laptop

A Piper indult, mert a legjobb értelemben unalmas: kis ONNX-modell (a LibriTTS high hang körülbelül 137 MB), espeak-ng fonémázás, 22,05 kHz kimenet, generálás nagyjából ötszörös valós idejű sebességgel. Az eredmény tiszta és konsztisztens — egy kompetens hírolvasó —, de egyben egyenletes. A hosszú cikkek kissé laposak.

Az első akadály a csomagolás volt, nem a minőség: a prebuilt macOS bináris csak x86\_64-re készült, és Apple Siliconon megtagadta a linkelést az arm64-es espeak-ng könyvtárral architektúra-ütközés miatt. A Python csomag teljesen kikerüli a binárist, és percek alatt futott. Hasznos emlékeztető: az „nem fut" gyakran eszközlánc-probléma, nem modellprobléma.

A Bark volt a három legcsábítóbb: expresszív, képes nevetésre és sóhajtásra, publikált mintákkal, amelyek bármelyik másik modellt metronómnak hangoltatnak. Ez volt az is, amelyik sosem produkált használható mondatot. A jelenlegi PyTorch a torch.load alapértelmezését weights\_only-ra állította, így a checkpoint kicsomagolása addig bukott, amíg nem patchelték; amikor a betöltés működött, a CPU-s inferencia elég lassú volt, hogy 95 cikk a nap nagy részét vigye. Az expresszivitás nem érte meg ezt az árat — a Bark erre a munkára kutatási játék marad.

A Kokoro v1.0 a kokoro-onnx kötéseken keresztül volt a megtalálás: 82 millió paraméter, körülbelül 325 MB ONNX, 24 kHz kimenet, 54 hang, és generálás nagyjából négyszeres valós idejű sebességgel, érezhetően jobb proszódiával, mint a Piper — a mondatok leteszik a hangsúlyukat, a számok és rövidítések pedig nem a kisebb modellre jellemző akadozással szólnak. Így szól most minden cikk ezen az oldalon.

Modell

Súlyok

Kimenet

Sebesség ezen a gépen

Ítélet

Piper, LibriTTS high

körülbelül 137 MB, ONNX

22,05 kHz WAV

körülbelül 5x valós idő

Gyors és konsztisztens, kissé lapos

Bark a Sunótól

körülbelül 2 GB, PyTorch

24 kHz WAV

a költségkereten belül sosem készült el

Expresszív, itt gyakorlatlan

Kokoro v1.0

82M, körülbelül 325 MB, ONNX

24 kHz WAV

körülbelül 4x valós idő

Természetes proszódia; a kiszállított hang

## A generálási pipeline

A tartalmi adatbázis az egyetlen igazságforrás, ezért a pipeline közvetlenül onnan olvas: cím, leírás, kulcsmondatok és GYIK egyik oldalon, a cikk blokklistája a másikon. Semmit nem kapar le a renderelt oldalról. Ha egy bekezdés a posztban van, narrálja — és ha tábla, callout vagy kódblokk, akkor úgy bontja szét, hogy a hallgató követni tudja: a táblasorok vesszővel elválasztott sorokká válnak, a callout-címeket fejezetként mondják, a kód úgy hangzik, ahogy íródott.

Két keretfeltétel formálja a pipeline középső részét. A modellek levágják a hosszú bemenetet, ezért a cikkeket bontani kell; a proszódiát nem szabad félbevágni, ezért a vágások mondatvégeken landolnak. A chunker szándékosan egyszerű: mondatok gyűjtése 400 karakterig, kiürítés a határon, a darabok 80 milliszekundum csenddel fűzve. Az okosabb vágópontok — bekezdésvégek, fejezetek mint intonációs újraindítás — szinte semmit nem hoztak; a mondat az a mértékegység, amit a hallgató valóban észrevesz.

### A chunker

```
def chunks(text, limit=400):
    """Accumulate sentences up to limit characters; never split mid-sentence."""
    out, buf = [], ""
    for sentence in text.split(". "):
        cand = (buf + " " + sentence).strip()
        if len(cand) > limit and buf:
            out.append(buf + ".")
            buf = sentence
        else:
            buf = cand
    if buf:
        out.append(buf)
    return out

pieces = []
for part in chunks(article_text):
    audio, _ = model.create(part, voice="af_bella")
    pieces.append(audio)
    pieces.append(np.zeros(int(24000 * 0.08)))   # 80 ms between chunks
sf.write(tmp_wav, np.concatenate(pieces), 24000)
```

**Egy hang olvassa fel mind a 95 cikket.** Ez af\_bella, miután ugyanazt a bekezdést a 54 hang többjével is lejátszottuk. A konzisztencia a lényeg: az archívumnak egy kiadványnak kell hangzania, nem lottónak. A narráció minden cikk angol szövegét olvassa fel — a német és magyar oldalakról is —, és a cikk angol felolvasásaként van megjelölve, nem fordításaként.

**Csak a változottat újítsd fel**

A generátor kihagy minden slugot, amelyiknek a kimeneti fájlja már létezik, így egy cikk szerkesztése egy újragenerálást kerül, nem teljes futást. Egy teljes, frisss futás mind a 95 cikkre körülbelül 35 perc — elég olcsó ahhoz, hogy a teljes újraépítés is jó alapértelmezés legyen.

## A WAV munkaformátum, nem kézbesítési formátum

A szintézis lépés 24 kHz, 16 bites PCM-et ír: helyes, pörgethető, tömörítetlen — és körülbelül 406 MB 153 perc hangért. A lemezen ártalmatlan, a hálózaton hiba, és mindent prerenderelő oldalon egy nagyságrenddel a legnagyobb asset-osztály lenne.

A kézbesített formátum AAC M4A-konténerben, 192 kbit/s, mono. Ez a bitsebesség bőkezű beszédre — 96 és 128 kbit/s 24 kHz-en már átlátszóan hangzik —, de a 192 tartalékot hagy, és cikkenként körülbelül egy megabájtba kerül. A konténer ugyanannyit számít, mint a kodek: a faststart a moov atomot a fájl elejére teszi, így a böngésző letöltés vége előtt ismeri a hosszt és pörgetni tud. Nélküle a lejátszók 0:00-n állnak, és az utolsó bájtig nem hajlandóak tekerni.

```
ffmpeg -i article.wav -c:a aac -b:a 192k -ac 1 -movflags +faststart article.m4a
```

406 MB lett 181 MB — 55 százalék csökkenés, minden fájl 1,5 MB alatt, sima range-kérésekkel és cache-headerekkel kiszállítva. A WAV-fájlok sosem érik el a build kimenetét: a kódoló sikerét követően töröljük őket.

## A lejátszó

A kései hangot senki sem hallja, ezért a lejátszó csak a metadatokat tölti le: a header lejön, a hossz kiderül, és egyetlen minta sem töltődik le, amíg az olvasó meg nem nyomja a Playt. Semmi sem indul automatikusan — az az oldal, amelyik megszólal, olyan oldal, amit bezárnak.

Minden cikk egy soros lejátszót kap a tartalomjegyzék alatt: play és pause, pörgethető haladásvonal aktuális és teljes idővel, és 1x, 1,25x, 1,5x és 2x között váltó sebességkapcsoló. A pörgető natív range input az oldal stílusában — a billentyűzetes navigáció és a képernyőolvasó-szemantika örökölve, nem újraimplementálva.

A soros lejátszó hasztalan, amint alá görget az ember, ezért átadja a helyét a nézet jobb szélére rögzített ragadós fülnek. Egy IntersectionObserver figyeli a sort: amint eltűnik a képernyőről, a fül megjelenik; visszagörgetve félreáll. A fül alulról töltődik a haladással — a fejlédés egy pillantással olvasható —, és tiszteletben tartja a prefers-reduced-motiont, akárcsak a felület többi része.

**A 0:00-hiba**

Az első kiadott verzió minden oldalon 0:00-t mutatott teljes hosszként. Az audio elem loadedmetadata eseményt küld az oldalbetöltéskor, és gyors kapcsolaton faststart fájlnál ez még azelőtt megtörténik, hogy a JavaScript hidratált és csatlakoztatta volna a kezelőit — így az egyetlen esemény, amely a hosszt hozta volna, elveszett. A megoldás: nem bízni meg egyetlen eseményben — a hosszat most durationchange-en és canplay-en olvassuk újra, és mountkor egyszer ellenőrizzük. A lejátszóban a médiaesemények folyam, nem üzenet — az állapotra iratkozz fel, nem az értesítésre.

## Mi tört össze menet közben

-   **A csomagolás kétszer verte a modelleket.** A Piper csak x86\_64-es binárist szállított arm64-es gépre, a Bark checkpoint-betöltése pedig akkor bukott, amikor a PyTorch a weights\_only alapértelmezést fordította. Egyik hibának sem volt köze a beszédhez.
-   **A verzió-eltolódás valós.** A kokoro-onnx csomag int32-ként adta át a speed paramétert, miközben a kiszállított modell floatet vár — egy soros javítás, két soros stacktrace olvasásával megtalálva, nem találgatva.
-   **A hangot ellenőrizd, nem csak a fájlt.**
-   Az ffprobe minden kimeneten fogta ki a hossz-problémákat, mielőtt böngészőhöz értek volna; a létező fájl nem játszó fájl.
-   **A régi formátumok nehezen halnak meg.** Az első kör WAV-okat hagyott a build kimenetén, és csak azért derült ki, mert egy kérés rossz bájtszámot adott vissza.

## A számok

Mutasató

Érték

Narrált cikkek

95 (45 blogbejegyzés, 50 eszközismertető)

Kész hang

153 perc

Generálási falidő

körülbelül 35 perc, 4,5x valós idő

Csúcsfoglalás

2 GB alatt

Köztes WAV

406 MB

Kiszállított AAC

181 MB, körülbelül 1 MB cikkenként

API-költség

0 EUR

Együtt olvasva a számok azt mondják, hogy ennek a pipeline-nak a költsége a türelem, nem a pénz. Egy teljes újraépítés — hangcsere után vagy olyan szerkesztés után, ami minden cikket érint — bőven egy óra alatt megvan egy olyan gépen, ami közben tovább használható. Ez az érv a helyi text-to-speech mellett ekkora léptékben: nem az, hogy felülmúl egy felhős Frontier-hangot expresszivitásban, hanem az, hogy a narrációt alapértelmezéssé teszi, nem költségrovattá.

## Mit csinálnék másképp

A Kokoroval kezdeni. A shoot-out nem volt kidobott idő — modelleket összehasonlítani az, ami a választást védhetővé teszi —, de a kiszállított pipeline a Kokoróval egyetlen jelöltként is azonos lett volna. Másodszor: közvetlenül a szintézisből AAC-be kódolni, WAV-okat nem tárolni; a köztes formátum egy takarító lépést és 406 MB sosem szükséges fájlt adott hozzá. Harmadszor: a lejátszó médiaeseményeit feliratkozandó állapotként kezelni, nem elkapandó értesítésként — és a hossz-hiba sosem történik meg.

Ami marad, az a hatókör. A narráció egyelőre angol nyelvű — egy hang, egy nyelv, 95 cikk —, és a nyelvenkénti hangok az egyértelmű következő lépés, ha a német és magyar olvasóközönség kéri. Addig az angol narráció az oldal minden terméknevének kiejtési útmutatója is — ez maga egy csendes hasznosság.

## Források

1.  [Kokoro onnx: futtatókötések](https://github.com/thewh1teagle/kokoro-onnx)
2.  [Kokoro-82M modellkártya](https://huggingface.co/hexgrad/Kokoro-82M)
3.  [Piper text-to-speech](https://github.com/rhasspy/piper)
4.  [Bark a Sunótól](https://github.com/suno-ai/bark)
5.  [FFmpeg AAC-encoder dokumentáció](https://ffmpeg.org/ffmpeg-codecs.html#aac)
6.  [torch.load weights\_only dokumentáció](https://pytorch.org/docs/stable/generated/torch.load.html)
7.  [ESpeak NG fonémázó](https://github.com/espeak-ng/espeak-ng)

## Gyakori kérdések

Miért nem felhő alapú text-to-speech API-t használsz?

Mert 95 cikknél a mért díjak minden szerkesztésnél és újraépítésnél számítanak, és mert a reprodukálhatóság fontosabb: ugyanaz a szöveg ugyanazzal a modellverzióval jövőre ugyanazt a fájlt állítja elő. A felhőben futó hangok továbbra is az expresszív csúcson győznek, de nem egy teljes archívum gazdaságosságában.

Melyik modell hangzik a legjobban?

A Kokoro v1.0 — és ez szállít ki: 82 millió paraméter, természetes hangsúlyozás, tiszta szám- és terméknevek. A Piper a LibriTTS hanggal szorosan mögötte, érezhetően gyorsabban. A Bark a három legexpresszívebb, de túl lassú volt ehhez a volumenhez.

Mennyi idő egy teljes futás?

Körülbelül 35 perc mind a 95 cikkre egy Apple Silicon laptopon — nagyjából 4,5-szörös valós idő —, 2 GB alatti csúcsfoglalással. Egyetlen cikk körülbelül 20 másodperc alatt áll újra elő.

Miért AAC (M4A) és nem MP3 vagy Opus?

192 kbit/s-on a kodekkülönbségek beszédnél hallhatatlanok, szólt a döntés a csomagolásról: az AAC natívan fut mindenhol, Safari alatt is, a faststart a letöltés vége előtt megmutatja a hosszt, az Opus kisebb lenne, de a böngészőtámogatása egyenetlen WebM-en kívül.

Lézik német és magyar narráció is?

Egyelőre nem. A narráció minden cikk angol eredetét olvassa fel; maga a lejátszó felülete teljesen lokalizált angolul, németül és magyarul.

Írta: Csorba Balázs

Senior fullstack és AI mérnök Stájerországban – több mint 10 év Vue, Nuxt, Node.js és PHP, ma AI-ügynököknek építek eszközöket.

[AI fejlesztés és MCP szerverek →](https://balazscsorba.com/hu/expertise/ai-engineer)[Rólam →](https://balazscsorba.com/hu/about)

## További cikkek

-   [Claude Opus 5.5 az Artificial Analysis élén, de az igazi hír a medium effort](https://balazscsorba.com/hu/blog/artificial-analysis-leaderboard-claude-opus-5-5)
-   [LLM-ügynökök megfigyelhetősége OpenTelemetryvel: trace-ek, tokenek, PII és eval](https://balazscsorba.com/hu/blog/agent-observability-opentelemetry)
-   [Prompt caching és modell-routing: LLM költség és késleltetés csökkentése](https://balazscsorba.com/hu/blog/llm-cost-latency-prompt-caching-routing)
-   [LLM evals termékfunkciókhoz: a valódi trace-ektől a CI kapuig](https://balazscsorba.com/hu/blog/llm-evals-for-product-features)

## Pont erre van szükséged?

Írj a projektedről vagy a pozícióról – szívesen hallok felőled.

[Beszélgetés kérése](mailto:contact@balazscsorba.com) [Kapcsolódjunk LinkedInen](https://www.linkedin.com/in/balazs-csorba)
