fix: Update Piper TTS to support voice preview and improve setup

- Fixed preview endpoint to work with both Kokoro and Piper engines
- Improved voice model download with curl fallback and SSL handling
- Added better error messages when models are missing
- Created PIPER_SETUP.md with manual download instructions
- Added test_piper_direct.py for testing Piper installation
- Updated README with voice model download requirements

Note: Piper voice models require manual download from GitHub releases
due to HuggingFace URL structure. See PIPER_SETUP.md for instructions.
This commit is contained in:
2025-12-09 14:56:48 +00:00
parent 6f7330c491
commit d3e72edd7f
6 changed files with 300 additions and 26 deletions

View File

@@ -385,26 +385,42 @@ async def preview_voice(request: VoicePreviewRequest):
preview_text = request.text or "Hello! This is a preview of this voice. How does it sound?"
try:
# Get pipeline for language
pipeline = tts_service.get_pipeline(request.lang)
# Generate preview audio
audio = tts_service.synthesize_segment(
pipeline=pipeline,
text=preview_text,
voice=request.voice,
speed=request.speed
)
# Convert to WAV bytes
import io
import soundfile as sf
import numpy as np
# Check which engine is active
if tts_service.engine_name == 'kokoro':
# Use Kokoro service
pipeline = tts_service.kokoro_service.get_pipeline(request.lang)
audio = tts_service.kokoro_service.synthesize_segment(
pipeline=pipeline,
text=preview_text,
voice=request.voice,
speed=request.speed
)
sample_rate = tts_service.kokoro_service.SAMPLE_RATE
elif tts_service.engine_name == 'piper':
# Use Piper service with pitch control
audio = tts_service.piper_service.synthesize_with_pitch(
text=preview_text,
voice=request.voice,
speed=request.speed,
pitch_shift=0 # No pitch shift for preview
)
sample_rate = tts_service.piper_service.SAMPLE_RATE
else:
raise ValueError(f"Unknown engine: {tts_service.engine_name}")
# Convert to WAV bytes
buffer = io.BytesIO()
sf.write(buffer, audio, tts_service.SAMPLE_RATE, format='WAV')
sf.write(buffer, audio, sample_rate, format='WAV')
buffer.seek(0)
audio_bytes = buffer.read()
logger.info(f"Generated voice preview: {len(audio_bytes)} bytes")
logger.info(f"Generated voice preview ({tts_service.engine_name}): {len(audio_bytes)} bytes")
return StreamingResponse(
iter([audio_bytes]),