Files
voicecoach/piper-service/main.py
T
jeremy 59856bd4fc feat: initial commit — VoiceCoach real-time voice coaching pipeline
Services: Pipecat agent (LiveKit + Claude + Whisper STT + Piper TTS),
SpeechBrain analysis service, Piper TTS service, web client (nginx).
Includes LiveKit server config and docker-compose orchestration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 01:15:24 -05:00

73 lines
1.8 KiB
Python
Executable File

"""
Piper TTS HTTP service.
Self-hosted text-to-speech, internal network only.
"""
import io
import logging
import os
import wave
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from fastapi.responses import Response
from pydantic import BaseModel
import piper
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
MODEL_PATH = os.getenv("PIPER_MODEL", "/app/models/en_US-lessac-medium.onnx")
voice = None
@asynccontextmanager
async def lifespan(app: FastAPI):
global voice
logger.info(f"Loading Piper model: {MODEL_PATH}")
voice = piper.PiperVoice.load(MODEL_PATH)
logger.info("Piper TTS ready.")
yield
logger.info("Shutting down.")
app = FastAPI(title="Piper TTS Service", lifespan=lifespan)
class TTSRequest(BaseModel):
text: str
speaker_id: int = 0
length_scale: float = 1.0
sentence_silence: float = 0.2
@app.get("/health")
async def health():
if voice is None:
raise HTTPException(status_code=503, detail="Model not loaded")
return {"status": "ok", "model": os.path.basename(MODEL_PATH)}
@app.post("/synthesize")
async def synthesize(req: TTSRequest):
if voice is None:
raise HTTPException(status_code=503, detail="Model not loaded")
if not req.text.strip():
raise HTTPException(status_code=400, detail="Empty text")
buf = io.BytesIO()
with wave.open(buf, "wb") as wf:
voice.synthesize(
req.text,
wf,
speaker_id=req.speaker_id,
length_scale=req.length_scale,
sentence_silence=req.sentence_silence,
)
return Response(
content=buf.getvalue(),
media_type="audio/wav",
headers={"Content-Disposition": "inline; filename=speech.wav"},
)