
Negli ultimi mesi sto facendo un giro d’orizzonte sui Text-to-Speech locali, per capire cosa abbia senso usare “on-prem” quando privacy, latenza e costi ricorrenti contano più di tutto. In questo post metto sotto la lente Chatterbox TTS, un progetto open che espone un’API stile OpenAI e supporta la clonazione voce e lo streaming.
Sul mio hardware (Windows 11 + Docker Desktop/WSL2, RTX 3050 Ti 4 GB e 64 GB RAM) Chatterbox TTS gira bene in locale. Con GPU l’inferenza è nettamente più vivace rispetto alla CPU (che per una frase breve arrivava anche a ~60s), ma per l’italiano oggi sento ancora un accento inglese e un timbro un po’ metallico anche clonando la mia voce. Non è “pronto per doppiaggio”, però è promettente e lo terrò in test continuo.
Perché TTS locale
- Privacy: gli audio non escono dalla tua macchina.
- Predictable cost: niente token/overage.
- Bassa latenza (specie su GPU) + controllo totale del deployment.
Cosa è Chatterbox TTS (in 20 secondi)

- Server FastAPI con documentazione /docs e schema /openapi.json.
- Endpoint compatibili con OpenAI per /v1/audio/speech e vari alias (/audio/speech, /audio/speech/stream).
- Modello multilingua con 20+ lingue (italiano incluso).
- Voice library integrata: puoi caricare campioni WAV/MP3, rinominare, impostare una default voice, gestire alias, scaricare/eliminare.
- Modalità streaming per sentire l’audio mentre viene generato.
La mia postazione di test
- OS: Windows 11 + Docker Desktop (con WSL2)
- GPU: NVIDIA RTX 3050 Ti 4 GB
- RAM: 64 GB
- Container:
- chatterbox-tts-api-cpu (porta 4123)
- chatterbox-tts-api-gpu (sempre 4123, device
cuda
) - Frontend statico su Nginx (porta 8080) con
VITE_API_BASE
puntato all’API
Nota: per chi usa Docker su Windows, l’API gira dentro docker-desktop (WSL2). Il processo VmmemWSL può salire di RAM: è normale durante il primo warm-up modello.
Cosa ho testato
- Lingue disponibili via
GET /languages
→ 22 (italiano incluso). - Voci personali caricate via
POST /voices
(es.italian_sample.wav
) e verificate conGET /voices
. - Generazione non-streaming su
/audio/speech
e streaming su/audio/speech/stream
con chunking per frase. - Frontend Docker con Nginx davanti: pulsante “Generate”, pannello “Advanced Settings”.
Tuning che mi ha convinto di più (italiano)
Nella UI, questi range hanno dato i risultati più naturali:
- Exaggeration (emotività): 0.35–0.50
- Pace / CFG Weight (ritmo/pausa): 0.25–0.40
- Temperature (variazione/creatività): 0.60–0.80
- Streaming: strategy sentence, chunk ~frase
Se senti “sibilanti” o cantilena inglese, abbassa un filo Exaggeration e Temperature, e tieni CFG sotto 0.4.
Qualità della voce in italiano
La parte più importante. Con campioni voce personali puliti (WAV 16-48 kHz, 16-bit, senza musica/riverbero, 30–60 s), la somiglianza timbrica c’è, ma:
- rimane una patina anglofona (intonazione e /r/ /t/ /s/ spesso “anglicizzate”);
- in certe frasi si sente una leggerissima metallicità;
- la prosodia non sempre “aggancia” le virgole.
Non è un problema di pipeline: è proprio lo stato attuale del modello multilingua. Se il tuo target è assistenti vocali, demo interne o prototipi va già bene; per voice-over pubblicitari o doppiaggio serve ancora un salto.
Come migliorare la resa (consigli pratici)
- Campione voce: 30–60 s, ambiente trattato, microfono decente, ritmo naturale.
- Testo campione: includi accenti, apostrofi, numeri, nomi propri italiani.
- Pronuncia: evita inflessioni teatrali; meglio neutra e scorrevole.
- Parametri: parti dai range sopra, poi micro-tuning su frasi difficili.
- Warm-up: la prima generazione scalda cache e può essere lenta; le successive migliorano.
Se ti serve, ho preparato un copione sample per caricare una voce “ricca” di fonemi italiani (lo trovi nella guida).
Performance: CPU vs GPU
- CPU: una frase breve può arrivare anche a ~60 secondi la prima volta (poi migliora, ma resta poco reattivo).
- GPU (RTX 3050 Ti 4 GB): inferenza molto più fluida; non è “istantanea” come i TTS nativi-inglese più ottimizzati, ma è usabile e con streaming l’effetto percepito è decisamente migliore.
- RAM: tener d’occhio VmmemWSL durante i primi minuti; poi si stabilizza.
Frontend & Nginx: due note che ti risparmiano ore
- Se avvii il frontend in Docker, non usare nomi di servizio hard-codati (
chatterbox-tts
) nell’upstream Nginx.
Punta l’API ahttp://host.docker.internal:4123
(Windows/Mac) o all’IP della macchina. - Nel container del frontend, passa
-e VITE_API_BASE=http://host.docker.internal:4123
e usa l’Nginx già configurato per servire gli asset statici.
Problemi incontrati (e soluzioni lampo)
- PowerShell vs bash: i comandi
curl
con-H
e-d
danno errore in PS se copiati “alla Linux”.
Usacurl.exe
e json con--data
/-d
tra apici doppi, oppure fai le chiamate con Python/requests. - Frontend che “loopa” sulle voci: era l’upstream sbagliato in Nginx. Sistemato puntando a
host.docker.internal:4123
. - /docs ok ma frontend vuoto: quasi sempre è CORS/endpoint o variabile
VITE_API_BASE
mancante.
In sintesi
Chatterbox TTS è già oggi un buon candidato per chi vuole un TTS locale multilingua con una vera voice library ed endpoint comodi.
Nel mio uso reale:
- Pro: facile da containerizzare, API chiare, streaming, voice management, gira su GPU “consumer”.
- Contro: italiano ancora con accento straniero e prosodia da limare; CPU troppo lenta per scenari “live”.
Io lo terrò in produzione “di prova” su GPU e aggiornerò questo post man mano che escono nuovi pesi/preset.
Adesso passiamo al pezzo forte, ovvero la guida completa per rifare esattamente i miei test (CPU e GPU), con frontend Docker e Nginx già pronti, script per upload voce e chiamate streaming. Buon divertimento!
Guida pratica
Intanto se vuoi avere maggiori informazioni ti invito ad accedere alla Repo GitHub del progetto.
Iniziamo con il vero setup:
1) Prerequisiti
- Windows 10/11 con WSL2 abilitato.
- Docker Desktop (usa sempre la distro
docker-desktop
come VM dei container). - GPU (opzionale): driver NVIDIA recenti + “Use the WSL 2 based engine” abilitato in Docker Desktop → Settings → Resources → WSL Integration e Settings → Resources → GPU.
- curl.exe (già presente su Windows 11), Python 3 (per gli esempi), ffmpeg opzionale se vuoi ascoltare stream al volo.
🔎 Nota WSL: le distro “Ubuntu”/“Ubuntu-22.04” sono facoltative per te. I container girano in
docker-desktop
.
2) Clona il progetto
cd $HOME
git clone https://github.com/travisvn/chatterbox-tts-api.git
cd chatterbox-tts-api
Struttura che ci interessa:
chatterbox-tts-api/
docker/
docker-compose.cpu.yml
docker-compose.gpu.yml
frontend/
Dockerfile
nginx.conf
3) Avviare il backend API
3.1 CPU (semplice e universale)
cd docker
docker compose -f docker-compose.cpu.yml up -d --build
- Espone l’API su http://localhost:4123
- Contenitore tipico:
chatterbox-tts-api-cpu
3.2 GPU (più veloce)
cd docker
# se il container CPU è acceso, spegnilo PRIMA (stesso port 4123)
docker compose -f docker-compose.cpu.yml down
# poi avvia la versione GPU
docker compose -f docker-compose.gpu.yml up -d --build
- Espone sempre su http://localhost:4123
- Contenitore tipico:
chatterbox-tts-api-gpu
- Nei log vedrai
Device: cuda
. Puoi verificare:
docker exec -it chatterbox-tts-api-gpu bash -lc "nvidia-smi"
3.3 Verifiche veloci
curl.exe -s http://127.0.0.1:4123/health
curl.exe -s http://127.0.0.1:4123/openapi.json > NUL
start http://127.0.0.1:4123/docs
Se /docs
si apre, l’API è pronta.
3.4 Script in Python per caricare la tua voce
import requests
files = {
"voice_file": ("italian_sample.wav", open("C:/Users/Francesco/Desktop/tts/italian_sample.wav", "rb"), "audio/wav")
}
data = {
"voice_name": "italian_sample",
"language": "it"
}
response = requests.post("http://localhost:4123/voices", data=data, files=files)
print(f"Upload status: {response.status_code}, response: {response.text}")
3.5 Script in Python per generare l’audio e salvarlo
import requests
url = "http://localhost:4123/audio/speech"
headers = {"accept": "audio/wav", "Content-Type": "application/json"}
data = {
"input": "Ciao, come stai? Tutto bene? Questo è un test di generazione vocale.",
"voice": "italian_sample",
"response_format": "wav",
"speed": 1.0,
"exaggeration": 0.5,
"cfg_weight": 0.5,
"temperature": 0.75
}
r = requests.post(url, headers=headers, json=data)
if r.ok:
with open("output_italian.wav", "wb") as f:
f.write(r.content)
print("Audio salvato come output_italian.wav")
else:
print(r.status_code, r.text)
4) Avviare il frontend con Docker
Costruiamo l’immagine e lanciamo un container Nginx statico.
cd ..\frontend
docker build -t chatterbox-tts-frontend .
4.1 Modalità “standalone” (frontend separato dall’API)
In Windows, il frontend deve parlare con l’API host usando host.docker.internal
:
docker run --name tts-frontend --rm -p 8080:80 `
-e VITE_API_BASE=http://host.docker.internal:4123 `
chatterbox-tts-frontend
Apri http://localhost:8080 → ora il frontend chiama correttamente l’API.
💡 Se preferisci 127.0.0.1 al posto di
host.docker.internal
, modifica direttamentenginx.conf
(vedi §5).
4.2 Modalità “tutto in Compose” (frontend + API nella stessa rete)
Se vuoi far parlare Nginx con l’API usando il nome del servizio (chatterbox-tts
), serve un compose unico. Esempio minimale:
# docker-compose.app.yml
services:
chatterbox-tts:
build:
context: ..
dockerfile: docker/Dockerfile.cpu # o .gpu per GPU
image: docker-chatterbox-tts
ports:
- "4123:4123"
tts-frontend:
build:
context: ../frontend
image: chatterbox-tts-frontend
ports:
- "8080:80"
environment:
- VITE_API_BASE=http://chatterbox-tts:4123
depends_on:
- chatterbox-tts
Poi:
docker compose -f docker-compose.app.yml up -d --build
5) Nginx: configurazione pronta
5.1 Se il frontend è standalone (API fuori dal compose)
Usa questa nginx.conf
(è quella che ci ha sbloccato tutto):
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
gzip on; gzip_vary on; gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# API Proxy → backend locale su Windows/Mac:
set $api http://host.docker.internal:4123;
location /v1/ { proxy_pass $api; }
location /health { proxy_pass $api; }
location /config { proxy_pass $api; }
location /models { proxy_pass $api; }
location /memory { proxy_pass $api; }
location /docs { proxy_pass $api; }
location /redoc { proxy_pass $api; }
location /openapi.json { proxy_pass $api; }
# Static cache
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# SPA routing
location / {
try_files $uri $uri/ /index.html;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
}
# Health del frontend
location /frontend-health { access_log off; return 200 "frontend healthy\n"; }
# Sicurezza di base
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' http://host.docker.internal:4123 ws://localhost:*";
server_tokens off;
client_max_body_size 10M;
}
Se preferisci 127.0.0.1, cambia
set $api http://127.0.0.1:4123;
.
5.2 Se usi compose per entrambi
Nel nginx.conf
metti:
set $api http://chatterbox-tts:4123;
perché i servizi condividono la rete Compose.
6) Primi test API (PowerShell-friendly)
⚠️ In PowerShell usa
curl.exe
(non l’aliascurl
) per evitare i classici errori di parametri.
6.1 Lingue e voci
curl.exe -s http://127.0.0.1:4123/languages
curl.exe -s http://127.0.0.1:4123/voices
6.2 Carica una voce (clonazione semplice)
File di esempio: C:\Users\Francesco\Desktop\tts\italian_sample.wav
(16 kHz/24 kHz, mono, 5–20 s, pulito).
curl.exe -s -X POST http://127.0.0.1:4123/voices `
--form "voice_name=italian_sample" `
--form "language=it" `
--form "voice_file=@C:\Users\Francesco\Desktop\tts\italian_sample.wav"
Verifica:
curl.exe -s http://127.0.0.1:4123/voices
6.3 Genera audio (WAV) – endpoint compatto
curl.exe -s -X POST http://127.0.0.1:4123/audio/speech `
--json "{`"input`": `"Ciao, come stai? Tutto bene?`", `"voice`": `"italian_sample`", `"language_id`": `"it`", `"exaggeration`": 0.4, `"cfg_weight`": 0.3, `"temperature`": 0.6}" `
--output out.wav
Apri out.wav
e ascolta.
6.4 Streaming “a spezzoni” (Python, salva mentre arriva)
import requests
URL = "http://127.0.0.1:4123/audio/speech/stream"
payload = {
"input": "Ciao! Prova di streaming in tempo reale.",
"voice": "italian_sample",
"language_id": "it",
"streaming_strategy": "sentence",
"exaggeration": 0.4,
"cfg_weight": 0.3,
"temperature": 0.6
}
with requests.post(URL, json=payload, stream=True, timeout=120) as r:
r.raise_for_status()
with open("stream.wav", "wb") as f:
for chunk in r.iter_content(8192):
if chunk:
f.write(chunk)
print("OK -> stream.wav")
7) Impostazioni consigliate (italiano)
- Campione voce: 5–20 s, mono, 16 kHz o 24 kHz, senza rumore/sottofondo, tono colloquiale.
- Parametri “Advanced”
language_id
: “it”cfg_weight
(Pace): 0.3–0.45exaggeration
(Emozione): 0.35–0.6temperature
(creatività): 0.55–0.8
- Per voci “piatte” o con accento inglese: carica un campione più pulito e rallenta un filo il pace (
cfg_weight ~0.35
).
8) Operazioni utili
- Imposta la voce di default
curl.exe -s -X POST http://127.0.0.1:4123/voices/default -d "voice_name=italian_sample" curl.exe -s http://127.0.0.1:4123/voices/default
- Pulizia/diagnostica memoria
curl.exe -s "http://127.0.0.1:4123/memory?cleanup=true&force_cuda_clear=true"
- Stato/Statistiche
curl.exe -s http://127.0.0.1:4123/status curl.exe -s http://127.0.0.1:4123/status/statistics
9) Troubleshooting (problemi visti davvero)

- Frontend “non trova le voci” / loop richieste
→ Manca/è sbagliato l’endpoint API.
Se è standalone: lancia con-e VITE_API_BASE=http://host.docker.internal:4123
oppure modificanginx.conf
come in §5.1.
In alternativa puoi cambiarlo facilmente dal frontend. - Errore Nginx “host not found in upstream ‘chatterbox-tts’”
→ Capitava quando il frontend era standalone ma il proxy puntava al nome compose.
Usahost.docker.internal
(Windows/Mac) o127.0.0.1
. - PowerShell e
curl
che “non capisce”-H
o-d
→ In PS l’aliascurl
èInvoke-WebRequest
. Usacurl.exe
. - CPU e GPU insieme sullo stesso 4123
→ Non puoi pubblicare lo stesso port due volte. Spegni l’altro:docker compose -f docker-compose.cpu.yml down
- GPU non usata
→ Controlla i log dell’API (Device: cuda
) edocker exec ... nvidia-smi
. Abilita GPU in Docker Desktop. - IPv6/localhost “muto”
→ Usahttp://127.0.0.1:4123
per i test curl.
10) Pulizia/Disinstallazione
Spegni i container:
# backend (CPU o GPU)
cd chatterbox-tts-api\docker
docker compose -f docker-compose.cpu.yml down
docker compose -f docker-compose.gpu.yml down
# frontend
docker rm -f tts-frontend
Rimuovi immagini (facoltativo):
docker rmi docker-chatterbox-tts chatterbox-tts-frontend
Volumi (attenzione: perdi voci caricate e cache modelli):
docker volume rm docker_chatterbox-voices docker_chatterbox-models
11) Esempio “ready-to-run” (CPU) con tutto in Compose
Salva come docker-compose.all.yml
nella cartella docker/
:
services:
chatterbox-tts:
build:
context: ..
dockerfile: docker/Dockerfile.cpu
image: docker-chatterbox-tts
ports:
- "4123:4123"
volumes:
- chatterbox-models:/cache
- chatterbox-voices:/voices
tts-frontend:
build:
context: ../frontend
image: chatterbox-tts-frontend
ports:
- "8080:80"
environment:
- VITE_API_BASE=http://chatterbox-tts:4123
depends_on:
- chatterbox-tts
volumes:
chatterbox-models:
chatterbox-voices:
Lancio:
cd chatterbox-tts-api\docker
docker compose -f docker-compose.all.yml up -d --build
start http://localhost:8080
12) FAQ lampo
- Posso usare solo l’API senza frontend? Certo, lavori da
/docs
o con script. - Perché vedo tanta RAM su “VmmemWSL”? È la VM WSL che contiene
docker-desktop
; è normale (cache).
Puoi limitare risorse con un.wslconfig
se vuoi.
- In locale Chatterbox TTS è tra i motori più semplici da mettere in piedi e integrare via API. Per assistenti vocali, demo e POC è già usabile (soprattutto su GPU). In italiano resta un po’ di accento/prosodia da rifinire per voice-over pro. Continuerò i test e aggiornerò il post quando usciranno preset/modelli migliori.
Conclusione
In locale Chatterbox TTS è tra i motori più semplici da mettere in piedi e integrare via API. Per assistenti vocali, demo e POC è già usabile (soprattutto su GPU). In italiano resta un po’ di accento/prosodia da rifinire per voice-over pro. Continuerò i test e aggiornerò il post quando usciranno preset/modelli migliori.
Spero che questa guida ti sia stata utile, se hai dubbi e domande o semplicemente vuoi condividere il tuo feedback ti invito a scrivere un commento qui sotto.