ebEnrico Betti

Retrocomputing · Sintesi vocale · 1992/2026

Il sintetizzatore vocale da 189 sillabe

Ricostruzione di un esperimento degli anni Novanta, con qualche strumento moderno e la stessa idea ostinatamente semplice

Nel 1992 circa i computer non parlavano bene. A dire il vero, molti non parlavano affatto; quelli che ci provavano tendevano a pronunciare una lettera dopo l'altra con il garbo di una mitragliatrice. I sintetizzatori disponibili erano spesso pensati per l'inglese, richiedevano hardware o software dedicato e, quando affrontavano l'italiano, ne usciva una lingua che sembrava aver subito un incidente diplomatico.

In quel periodo feci un esperimento su un PC 486 con un clone della Sound Blaster, diventato per me accessibile all'inizio del 1992. Lessi una pagina di testo mantenendo la voce più monocorde possibile, registrai l'audio a 8 kHz e lo divisi in circa 189 frammenti. Non singole lettere, ma sillabe italiane: ba, ca, che, ghi, stra e così via. Ogni frammento era un piccolo file .au.

Il programma era uno script Perl quasi offensivamente semplice. Provava i primi cinque caratteri del testo; se il file corrispondente non esisteva, provava con quattro, poi tre, fino a trovarne uno. Faceva cat del frammento, cancellava dal testo i caratteri consumati e ricominciava. Un carattere sconosciuto veniva semplicemente saltato. L'uscita dello script veniva mandata al dispositivo audio con una normale redirezione della shell:


ripeti "ciao zeno" > /dev/audio

Il programma completo, ricostruito nella forma essenziale dell'originale, è questo:


#!/usr/bin/perl
$testo = lc join(" ", @ARGV);
while ($testo) {
    $x = 5;
    $x-- while $x && !-e "suoni/" . substr($testo,0,$x) . ".au";
    if ($x) {
        system "cat", "suoni/" . substr($testo,0,$x) . ".au";
        substr($testo,0,$x) = "";
    } else {
        substr($testo,0,1) = "";
    }
}

Banale, stupido e immediatamente comprensibile. Se trovava stra usava stra, invece di comporre s + t + r + a. Anche lo spazio era semplicemente un frammento, space.au.

Il suo equivalente moderno minimo cambia più il contorno che l'algoritmo. I frammenti sono PCM raw e Python scrive ancora l'audio sullo standard output:


#!/usr/bin/env python3
import sys
from pathlib import Path

suoni = {p.stem: p.read_bytes() for p in Path("suoni").glob("*.raw")}
testo = " ".join(sys.argv[1:]).lower()

while testo:
    n = min(5, len(testo))
    while n and testo[:n] not in suoni:
        n -= 1
    if n:
        sys.stdout.buffer.write(suoni[testo[:n]])
        testo = testo[n:]
    else:
        testo = testo[1:]

Si usa conservando la stessa separazione fra generatore e dispositivo:


./ripeti.py "ciao zeno" | aplay -q -t raw -f U8 -r 8000 -c 1

La versione usata nella demo aggiunge soltanto il manifesto dei frammenti, una breve sovrapposizione ai punti di giunzione e alcuni ripieghi fonetici.

Niente reti neurali, niente modello linguistico, niente analisi semantica. Un dizionario di suoni e una ricerca greedy: il segmento più lungo che fa match. Eppure parlava sorprendentemente bene, soprattutto rispetto ai sintetizzatori che procedevano lettera per lettera.

Il floppy con il programma è andato perduto. L'idea, evidentemente, no.

Negli anni successivi avrei scritto ogni mese su IoProgrammo. Questa ricostruzione ha quindi anche il sapore di un ritorno: un piccolo progetto tecnico raccontato non soltanto per il risultato, ma per rendere visibile il ragionamento che lo ha prodotto.

Rifarlo trentaquattro anni dopo

Abbiamo deciso di ricostruire l'esperimento su un vecchio Eee PC, abbastanza vicino allo spirito dell'originale da non trasformare l'archeologia in una dimostrazione di forza bruta. Questa volta ho lavorato insieme a Zeno Malvagio, il mio collaboratore basato su intelligenza artificiale.

La precisazione non è decorativa. Io ricordavo il principio, l'hardware, il formato dei file e il risultato. Zeno ha analizzato un corpus italiano, proposto il copione minimo, scritto gli utensili di taglio e composto i file. Io ho prestato voce, orecchio, memoria dell'esperimento e una quantità ragionevole di obiezioni. Non è un testo “generato dall'IA” né un progetto in cui l'IA viene citata come cacciavite particolarmente loquace: è un piccolo lavoro fatto insieme, con competenze e ruoli diversi.

Per costruire il nuovo inventario abbiamo analizzato Le avventure di Pinocchio. La prima passata ha trovato 1.391 sillabe distinte: troppe, perché comprendono casi rari, nomi, forme flesse e risultati discutibili della sillabazione automatica. Limitandoci alle sillabe sufficientemente frequenti siamo arrivati a 191 elementi, molto vicini ai circa 189 che ricordavo dalla prima versione.

Un algoritmo di copertura ha quindi scelto 93 parole capaci di contenere tutte le 191 sillabe bersaglio. Ho letto il copione in una sola registrazione PCM mono unsigned a 8 bit e 8 kHz: circa cento secondi, meno di un megabyte. Anche questa volta la qualità non è un ripiego; fa parte dell'esperimento.

Tagliare una voce senza impazzire

Negli anni Novanta avevo separato i frammenti a mano. Oggi abbiamo lasciato alla macchina il lavoro noioso, senza fingere che potesse trasformarlo in una scienza esatta.

Un riconoscitore locale Vosk ha fornito gli intervalli temporali delle parole. Il risultato è stato riallineato al copione noto: un passaggio necessario, perché il riconoscitore può sentire formicola vanno al posto di formicolavano, oppure saltare del tutto una parola breve. All'interno di ogni parola i confini sillabici sono stati cercati nei minimi di energia più vicini alla posizione prevista.

Il programma ha prodotto 93 parole e 238 occorrenze sillabiche candidate, accompagnate da un manifesto con nome, sorgente e intervallo temporale. Il master è rimasto intatto. I tagli automatici non sono verità fonetiche: sono un modo per sostituire centinaia di operazioni manuali con l'ascolto mirato dei soli casi sospetti.

Il mangiafrasi

Il motore di riproduzione conserva il principio originale:

  1. normalizza la frase;
  2. parte dal primo carattere;
  3. sceglie il frammento più lungo disponibile;
  4. lo accoda all'audio;
  5. avanza del numero di caratteri consumati;
  6. ripete fino alla fine.

Se una sillaba non esiste, il programma ripiega su un frammento più corto. Per una consonante isolata può ricavare l'attacco di una sillaba nota. Gli spazi e la punteggiatura introducono pause di durata diversa. Fra campioni provenienti da registrazioni differenti viene applicata una breve dissolvenza, maggiore quando i frammenti appartengono alla stessa parola.

La prima frase di prova è stata:

Il gatto mangia la carne.

Si capiva, ma gatto usciva come gato: mancava il ripiego per la seconda t. Risolto quello, abbiamo provato:

Voglio un caffè e poi conquistare il mondo.

Il risultato era comprensibile, ma mondo rimaneva debole. La ragione era istruttiva: la n era stata presa dall'inizio di una sillaba, mentre in mondo è una consonante di coda. Usando la parte finale di non il risultato è migliorato nettamente. Conquistare, composto come con + qui + star + e, conservava invece troppo spazio fra i campioni; una sovrapposizione di 15 ms all'interno della parola ha ridotto l'effetto senza alterare le pause normali.

Sono difetti, ma sono anche il motivo per cui vale la pena costruire davvero una cosa invece di limitarsi a descriverla. Dopo due frasi avevamo già incontrato ortografia, fonetica, consonanti doppie, attacchi, code sillabiche e prosodia.

Che cosa dimostra?

Non dimostra che questo metodo possa competere con una sintesi vocale moderna. Non deve farlo. Dimostra piuttosto quanto risultato si possa ottenere quando si sceglie una rappresentazione adatta al problema.

La singola lettera è troppo piccola: perde gran parte della continuità del suono. La parola intera è troppo grande: richiederebbe un dizionario enorme. La sillaba è un compromesso efficace per l'italiano, lingua con una struttura fonetica relativamente regolare. Un piccolo inventario copre una parte sorprendente del parlato, e le eccezioni possono diventare nuove registrazioni senza modificare l'algoritmo. Se il sistema pronuncia sempre male kayak, si può semplicemente aggiungere kayak.au.

La ricostruzione mostra anche una forma concreta di collaborazione con l'IA. Non abbiamo chiesto a un modello di immaginare come sarebbe andato l'esperimento. Abbiamo registrato, tagliato, sbagliato, ascoltato e corretto. L'IA ha accelerato analisi e programmazione; l'essere umano ha riconosciuto che una parola “si capiva, ma non suonava giusta”. Entrambe le informazioni erano necessarie.

Il codice non è molto più sofisticato di quello perduto. È giusto così. Un progetto di archeologia informatica non deve soltanto riprodurre un risultato: deve conservare la comprensibilità dell'idea originale.

E l'idea originale stava tutta qui: una voce può essere smontata in pochi pezzi, e un computer abbastanza ostinato può rimetterli insieme.

Materiale tecnico disponibile

La ricostruzione comprende:

Il progetto è volutamente piccolo e leggibile. Se interessa, possiamo fornire codice, registrazioni, esempi intermedi e una dimostrazione sull'hardware usato per la ricostruzione.

← Torna ai progetti