""" Voice Server - Local HTTP service for text-to-speech playback. This module provides the FastAPI application with endpoints for: - POST /notify: Submit text for TTS playback - GET /health: Health check endpoint - GET /voices: List available voice models """ import logging import time from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.audio_player import SounddevicePlayer from app.config import get_settings from app.queue_manager import TTSQueueManager from app.tts_engine import PiperTTSEngine logger = logging.getLogger(__name__) # Track server start time for uptime calculation _start_time: float = 0.0 # Global instances (initialized in lifespan) tts_engine: PiperTTSEngine | None = None audio_player: SounddevicePlayer | None = None queue_manager: TTSQueueManager | None = None @asynccontextmanager async def lifespan(app: FastAPI): """ Application lifespan handler. Handles startup and shutdown events: - Startup: Initialize TTS engine, audio player, queue processor - Shutdown: Stop audio playback, drain queue """ global _start_time, tts_engine, audio_player, queue_manager settings = get_settings() _start_time = time.time() # Initialize TTS engine logger.info("Initializing TTS engine...") tts_engine = PiperTTSEngine( model_dir=settings.model_dir, default_voice=settings.default_voice, ) # Initialize audio player logger.info("Initializing audio player...") audio_player = SounddevicePlayer( default_sample_rate=tts_engine.get_sample_rate(), ) # Initialize and start queue manager logger.info("Starting queue manager...") queue_manager = TTSQueueManager( tts_engine=tts_engine, audio_player=audio_player, max_size=settings.queue_max_size, request_timeout=settings.request_timeout_seconds, ) await queue_manager.start() logger.info("Voice server started successfully") yield # Shutdown cleanup logger.info("Shutting down voice server...") if queue_manager: await queue_manager.stop() if audio_player: audio_player.stop() logger.info("Voice server stopped") def create_app() -> FastAPI: """ Create and configure the FastAPI application. Returns a configured FastAPI instance with all routes and middleware. """ settings = get_settings() app = FastAPI( title="Voice Server", description="Local HTTP service for text-to-speech playback using Piper TTS", version="1.0.0", lifespan=lifespan, ) # Configure CORS app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Register routes from app.routes import router app.include_router(router) return app def get_uptime_seconds() -> int: """Get server uptime in seconds.""" if _start_time == 0.0: return 0 return int(time.time() - _start_time) # Create the application instance app = create_app() def run(): """Run the server using uvicorn (for CLI entry point).""" import uvicorn settings = get_settings() uvicorn.run( "app.main:app", host=settings.host, port=settings.port, reload=True, ) if __name__ == "__main__": run()