Logo
Kiedy AI czyta moim głosem — i dlaczego to wcale nie jest proste. Preprocessing i walka z artefaktami

Kiedy AI czyta moim głosem — i dlaczego to wcale nie jest proste. Preprocessing i walka z artefaktami

Zdjęcie: Stas Knop

W poprzednim wpisie opisałam, jak sklonowałam swój głos i wygenerowałam audiobooka dla babci przy użyciu XTTS v2.
Działało.
Do momentu, kiedy zmieniłam rodzaj książki i wydawnictwo.

Biografia Wisławy Szymborskiej szybko zweryfikowała pierwszą wersję skryptu. Inna struktura EPUB-a, dużo cytatów, długie zdania, przypisy, ozdobne inicjały.
Model „działał", ale efekt był daleki od naturalności. W dodatku, przesłuchując kolejne rodziały audiobooków, zebrałam sporą listę "poprawek".

Lista problemów była konkretna:

  • czytanie zaczynało się od rozdziału 6, bo wcześniejsze nie miały odpowiednich oznaczeń,
  • „Rozdział 3" brzmiał jak „Rozdział trzy", nie „trzeci",
  • model czytał noty redakcyjne, ISBN i informacje od wydawcy,
  • inicjały były rozdzielone („W isława"),
  • długie cytaty zlewały się z narracją,
  • brakowało pauz między rozdziałami,
  • chunki po 3000 znaków powodowały artefakty i błędy tokenów,
  • przypisy typu [1], [2] były czytane na głos.

Wniosek był prosty: model TTS to najmniejsza część problemu. Prawdziwa praca zaczyna się przed i po generowaniu audio.

Preprocessing — przygotowanie tekstu

Chunking: 3000 znaków to za dużo

Pierwsza wersja dzieliła tekst mechanicznie co 3000 znaków. XTTS ma limit około 400 tokenów na wywołanie — przy polskim tekście oznaczało to regularne błędy i ucięcia.

Zaczęłam od okodowania możliwych przypadków. Ale szybko okazało się, że reguły oparte wyłącznie na regexpach nie wystarczały. Kod nie rozumie kontekstu. Spróbowałam użyć lokalnego modelu językowego, który miał segmentować tekst i zwracać uporządkowane fragmenty w formacie JSON. W teorii — inteligentne cięcie zdań. W praktyce:

  • model w wersji skwantyzowanej był niestabilny — generował niepoprawny JSON, gubił strukturę, dopisywał komentarze,
  • większe modele działały lepiej, ale były zbyt ciężkie obliczeniowo, aby sensownie przetwarzać całą książkę lokalnie.

Rozwiązaniem okazała się biblioteka pySBD — regułowa segmentacja zdań. Nie wymaga GPU ani modelu ML. Rozumie wielokropki i większość skrótów. Dla języka polskiego musiałam dodać własną listę skrótów (prof., dr, nr, im., ul., tzn., tzw.), aby uniknąć fałszywych podziałów.

Finalnie:

  • domyślny chunk: 300 znaków,
  • limit 150 słów,
  • cięcie wyłącznie na granicach zdań.

Model dostaje więc fragmenty, które są naturalnie podzielone — tak, jak zrobiłby to lektor, a nie licznik znaków.

Liczby i fleksja

XTTS czyta cyfry dosłownie. „Rozdział 3" brzmi technicznie poprawnie jako „Rozdział trzy", ale w mowie używamy formy „Rozdział trzeci".

Dodałam konwersję liczb na liczebniki porządkowe z odmianą przez rodzaj: rozdział trzeci, strona piąta, część druga.

Obsługa działa do 999, z prawidłowym budowaniem form złożonych (np. „dwudziesty pierwszy").

Czyszczenie EPUB-a

Polskie EPUB-y mają specyficzną strukturę. Oprócz treści zawierają: noty redakcyjne, informacje wydawnicze, przypisy (<sup>, [1]),ozdobne inicjały w osobnych znacznikach HTML, obrazy i podpisy.

Rozbudowałam filtr o:

  • usuwanie przypisów i markerów dolnych,
  • usuwanie obrazów i podpisów,
  • sklejanie inicjałów z resztą słowa (np. <span class="dropcap">W</span>isława),
  • czytanie rozdziałów zgodnie z EPUB spine,
  • zachowanie wstępów i przedmów jako części książki.

Każdy wydawca strukturyzuje EPUB trochę inaczej. Parser musi to uwzględniać.

Jako wisienkę na torcie dodałam na początku książki - mocno zaakcentowany tytuł i autora (filtry go wycinały).

Cytaty i pauzy

Długie cytaty powodowały dezorientację. Słuchacz nie wiedział, gdzie kończy się narracja.

Dla cytatów powyżej 50 znaków dodaję:

  • „Cytat:" na początku,
  • „Koniec cytatu." na końcu,
  • pauzy dźwiękowe wokół.

Po nagłówkach wstrzykuję w tekście specjalne markery (np. [PAUZA_CHAPTER], [PAUZA_SECTION]). Podczas składania audio są one zamieniane na ciszę:

  • 2–2.5 sekundy po tytule rozdziału — wyraźne wejście w nową część,
  • 2 sekundy po podrozdziale — strukturalne oddzielenie treści,
  • 1–1.5 sekundy przy dłuższych cytatach — subtelna zmiana rytmu.

W wersji 1.0 tekst był poprawny, ale „płaski". Po wprowadzeniu kontrolowanych pauz narracja zaczęła oddychać.

Mechanizm retry

Nawet przy krótkich chunkach XTTS czasem zgłasza błąd limitu tokenów.

Dodałam rekurencyjny mechanizm retry:

  • jeśli fragment jest za długi, dzielony jest na pół,
  • każda połowa przetwarzana osobno,
  • proces powtarza się aż do skutku.

Każdy wygenerowany fragment przechodzi mikro-postprocessing:

  • 3 ms fade-in (eliminuje „pop" na początku),
  • przycięcie nadmiarowej ciszy,
  • 50 ms bezpiecznego ogona pod crossfade.

Postprocessing — FFmpeg

Surowe audio z XTTS ma charakterystyczne artefakty: szum wokodera, kliknięcia i nierówną głośność.

Wokoder to komponent modelu TTS, który przekształca reprezentację akustyczną (np. mel-spektrogram) w końcowy sygnał audio. To on „składa" dźwięk. W zależności od jakości modelu może wprowadzać delikatny szum tła, metaliczne brzmienie lub mikro-zniekształcenia.

Pipeline postprocessingu przez FFmpeg obejmuje:

  • Denoising (afftdn) — redukcję szumu bez agresywnego wygładzania mowy,
  • Filtr pasmowy (40 Hz – 15 kHz) — usunięcie skrajnych częstotliwości i DC offsetu,
  • Declick (adeclick) — automatyczne usuwanie impulsów powstających przy łączeniu chunków,
  • Normalizację EBU R128 do -16 LUFS — standardową głośność audiobooków.

To nie jest „upiększanie" dźwięku. To usuwanie sygnałów, które zdradzają jego syntetyczne pochodzenie.

Konsola miksująca i oprogramowanie do obróbki audio

Zdjęcie: cottonbro studio

Optymalizacja parametrów TTS

Claude podczas jednej z sesji "review kodu" zasugerował aby zróżnicować czytanie w zależności od typu tekstu. Jednorodne parametry powodowały, że dialog, opis i pytanie brzmiały identycznie.

Powstał moduł TTSOptimizer, który analizuje fragment i dynamicznie dostosowuje parametry generowania:

  • dialogi — wyższa temperatura i top_p (więcej ekspresji),
  • pytania — lekko wolniejsze tempo,
  • listy i wyliczenia — spokojniejsze tempo i niższa temperatura.

Parametry są więc dopasowywane do rodzaju treści. Efekt to naturalniejsze odczucie czytania.

Dodatkowo parametr --pause-stretch wydłuża naturalne pauzy wykrywane w wygenerowanym audio (cisza poniżej -35 dBFS, minimum 150 ms).

Ten parametr powstał specjalnie z myślą o babci. Przy współczynniku 1.5 pauzy między frazami są o 50% dłuższe. Tempo pozostaje bez zmian, ale narracja staje się spokojniejsza i łatwiejsza w odbiorze.

TL;DR — jeśli chcesz zrobić własny audiobook z TTS

Jeśli przymierzasz się do podobnego rozwiązania, pamiętaj:

  • Model to nie wszystko. Model czyta, ale to Ty musisz przygotować tekst. Najwięcej pracy jest w preprocessingu i postprocessingu.
  • Chunking (cięcie na kawałki) musi być semantyczny, nie znakowy. Cięcie po 3000 znaków to proszenie się o artefakty i nienaturalne brzmienie.
  • Polska fleksja ma znaczenie. Liczby, skróty, odmiana — to właśnie te detale budują naturalność.
  • EPUB to nie czysty tekst. Spine, przypisy, inicjały, metadane — wszystko trzeba świadomie przefiltrować, zanim trafi do modelu.
  • Pauzy budują narrację. Bez nich audiobook brzmi jak ciągły strumień tekstu. Liczy się też prędkość, intonacja i rytm — dialog, cytat, wyliczenie czy opis nie powinny być czytane w ten sam sposób. Długie cytaty warto wyraźnie oznaczać i oddzielać.
  • Retry i fallback są konieczne. Model wcześniej czy później zwróci błąd — pipeline musi być na to przygotowany.
  • Postprocessing robi ogromną różnicę. Denoising, declick, filtracja pasma i normalizacja to standard, nie luksus.
  • Parametry TTS warto różnicować. Dialog nie powinien brzmieć jak opis, a pytanie jak przypis.
  • Testuj na realnym słuchaczu. Zwłaszcza jeśli tworzysz to dla konkretnej osoby — odbiór praktyczny weryfikuje techniczne założenia.

Technicznie to projekt TTS. W praktyce — to inżynieria czytania.

Próbka nagrania

Ponżej możesz posłuchać próbki książki: "Pamiątkowe rupiecie, biografia Wisławy Szymborskiej", autorki: Anna Bikont, Joanna Szczęsna.

Co z tego wynika

Model TTS nie „czyta książki". On przetwarza ciąg znaków.

To preprocessing decyduje, czy tekst stanie się narracją, czy zlepkiem artefaktów.
To postprocessing decyduje, czy dźwięk brzmi jak głos, czy jak synteza.

Każda książka jest inna. Każdy EPUB ma własne pułapki. Ale każda z nich była dla mnie okazją do nauczenia się czegoś nowego (lub przypomnienia sobie materiału ze studiów i przedmiotu "Cyfrowe Przetwarzanie Sygnałów").

Babcia słucha i mówi, że brzmi prawie jak ja.

Prawie. I właśnie to „prawie" jest najbardziej wymagającą częścią całego projektu.

Kod źródłowy całego pipeline'u jest dostępny na GitHubie — jeśli chcesz wypróbować rozwiązanie lub dostosować je do własnych potrzeb.

Stay tuned, coś czuje, że to jeszcze nie koniec historii...