Crear contenidor entrada text de html-sortida text a Peper

Aquí tens l’entrada de blog completa, llesta per publicar (en Markdown):


---
title: "Text del navegador a veu amb Piper TTS en Docker"
date: "2026-09-20"
tags: ["docker", "piper", "tts", "python", "flask"]
---

# Text del navegador a veu amb Piper TTS en Docker

Vols escriure text en una pàgina web i que es converteixi en àudio (WAV) amb una veu sintètica? En aquest post et mostro com muntar un **contenidor Docker** que fa exactament això: un HTML envia el text, i el contenidor el passa al **Piper** (motor de text-a-veu) per retornar-te el fitxer WAV directament al navegador.

## Arquitectura

El flux és senzill:

1. L'usuari escriu text a un formulari HTML.
2. JavaScript envia el text a una API (Flask) mitjançant `POST /api/tts`.
3. Flask executa `piper` dins del contenidor amb el text rebut.
4. Piper genera un fitxer `.wav`.
5. L'àudio es retorna al navegador i es reprodueix automàticament.

Navegador (HTML) ──POST text──▶ Flask API ──▶ Piper CLI ──▶ WAV ──▶ Navegador

## Requisits

- Docker i Docker Compose instal·lats.
- Connexió a internet per baixar les imatges i el model de veu.

## Estructura de fitxers

piper-tts/
├── app.py
├── Dockerfile
├── docker-compose.yml
└── templates/
└── index.html

## Pas 1: l'API amb Flask (`app.py`)

L'aplicació exposa la pàgina principal i l'endpoint `/api/tts`, que rep el text, l'executa amb Piper i retorna el WAV:

```python
from flask import Flask, request, render_template, send_file
import subprocess

app = Flask(__name__)

MODEL = "/models/zh_CN-huayan-medium.onnx"
OUTPUT = "/app/output/resultat.wav"

@app.route("/")
def index():
    return render_template("index.html")

@app.route("/api/tts", methods=["POST"])
def tts():
    data = request.get_json()
    text = data.get("text", "")
    if not text:
        return {"error": "text buit"}, 400

    subprocess.run(
        ["piper", "--model", MODEL, "--output_file", OUTPUT],
        input=text.encode("utf-8")
    )
    return send_file(OUTPUT, mimetype="audio/wav")

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Pas 2: la pàgina HTML (templates/index.html)

Un textarea per escriure, un botó per sintetitzar i un reproductor d’àudio:

<!DOCTYPE html>
<html lang="ca">
<head>
    <meta charset="UTF-8">
    <title>Piper TTS</title>
</head>
<body>
    <h1>Piper TTS — text a veu</h1>
    <textarea id="text" rows="4" cols="60">Hola, benvingut al servei de veu!</textarea>
    <br><br>
    <button onclick="sintetitza()">Sintetitza</button>
    <br><br>
    <audio id="audio" controls style="display:none"></audio>

    <script>
        async function sintetitza() {
            const text = document.getElementById("text").value;
            const res = await fetch("/api/tts", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ text: text })
            });
            const blob = await res.blob();
            const url = URL.createObjectURL(blob);
            const audio = document.getElementById("audio");
            audio.src = url;
            audio.style.display = "block";
            audio.play();
        }
    </script>
</body>
</html>

Pas 3: el Dockerfile

Instal·la Flask i Piper, baixa el model de veu i defineix l’arrencada:

FROM python:3.11-slim

# Instal·la dependències
RUN apt-get update && apt-get install -y wget espeak-ng && \
    pip install flask piper-tts

# Baixa el model de veu (exemple: xinès huayan; canvia'l pel que vulguis)
RUN mkdir -p /models && cd /models && \
    wget https://huggingface.co/rhasspy/piper-voices/resolve/main/zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx && \
    wget https://huggingface.co/rhasspy/piper-voices/resolve/main/zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx.json

WORKDIR /app
COPY app.py .
COPY templates ./templates
RUN mkdir -p output

EXPOSE 5000
CMD ["python", "app.py"]

💡 Per a veus en català o castellà, consulta els models a huggingface.co/rhasspy/piper-voices i canvia les URLs del Dockerfile.

Pas 4: docker-compose.yml

services:
  piper-tts:
    build: .
    container_name: piper-tts
    ports:
      - "5000:5000"
    volumes:
      - ./models:/models
      - ./output:/app/output
    restart: unless-stopped

Pas 5: posada en marxa

cd piper-tts
docker compose up -d --build

Obre http://localhost:5000 al navegador i:

  1. Escriu el text al quadre HTML.
  2. Prem Sintetitza.
  3. Rep el WAV i el reprodueix automàticament.

Prova ràpida amb curl

curl -X POST http://localhost:5000/api/tts \
  -H "Content-Type: application/json" \
  -d '{"text": "Hola, això és una prova"}' \
  --output prova.wav

Alternativa: imatge ja preparada

Si no vols construir res, existeixen imatges com loderunner84/piper-rest (Flask + Piper). Ara bé, muntar-ho tu mateix et dona control total sobre el model, els endpoints i la interfície.

Conclusió

Amb quatre fitxers tens un servei de text-a-veu complet dins d’un contenidor Docker: text des del navegador entra, WAV en surt. Pots ampliar-lo fàcilment: suport de múltiples idiomes, cua de peticions o desar historial d’àudios. Bon blog i bona sintetització! 🎙️

---

Aquest és el post complet en Markdown. Si vols, puc:

1. **Adaptar-lo al teu blog** (canviar títol, tags, to, afegir captures).
2. **Traduir-lo al castellà** o a l'anglès.
3. **Crear la carpeta del post** perquè el desis directament (recorda que per crear fitxers necessito que em diguis com vols fer-ho, o t'obro l'editor).

Què prefereixes?