feat: Kokoro voice blending — blend builder UI + weighted tensor synthesis

Add voice blend support to TTS pipeline and settings UI. Users can mix
2–5 Kokoro voices with per-voice weight sliders; the blended style tensor
replaces the single voice when enabled. Settings persist as JSON and auto-
load on synthesis when no explicit voice is supplied in the request.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-30 19:10:22 -04:00
parent 98b3cdb593
commit 1460863e82
4 changed files with 237 additions and 7 deletions
+20 -1
View File
@@ -126,8 +126,27 @@ async def synthesise_speech():
except (TypeError, ValueError):
speed = 1.0
voice_blend = data.get("voice_blend")
if not isinstance(voice_blend, list) or len(voice_blend) < 2:
voice_blend = None
# Auto-load blend from user settings when no explicit voice/blend provided
if voice_blend is None and "voice" not in data and "voice_blend" not in data:
from fabledassistant.services.settings import get_setting
from fabledassistant.auth import get_current_user_id
import json as _json
try:
uid = get_current_user_id()
stored = await get_setting(uid, "voice_tts_blend", "")
if stored:
parsed = _json.loads(stored)
if isinstance(parsed, list) and len(parsed) >= 2:
voice_blend = parsed
except Exception:
pass
try:
wav_bytes = await synthesise(text, voice=voice, speed=speed)
wav_bytes = await synthesise(text, voice=voice, speed=speed, voice_blend=voice_blend)
except Exception:
logger.exception("TTS synthesis failed")
return jsonify({"error": "Synthesis failed"}), 500