mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 05:40:26 +02:00
feat: Implement voice profile resolution in debug TTS and add corresponding tests
This commit is contained in:
@@ -28,6 +28,18 @@ class DebugWavArtifact:
|
|||||||
code: Optional[str] = None
|
code: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_voice_setting(value: str) -> tuple[str, Optional[str], Optional[str]]:
|
||||||
|
"""Resolve settings voice strings into a pipeline-ready voice spec.
|
||||||
|
|
||||||
|
Supports "profile:<name>" by converting it into a concrete voice formula.
|
||||||
|
Returns (resolved_voice_spec, profile_name, profile_language).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from abogen.web.routes.utils.voice import resolve_voice_setting
|
||||||
|
|
||||||
|
return resolve_voice_setting(value)
|
||||||
|
|
||||||
|
|
||||||
def _load_pipeline(language: str, use_gpu: bool) -> Any:
|
def _load_pipeline(language: str, use_gpu: bool) -> Any:
|
||||||
device = "cpu"
|
device = "cpu"
|
||||||
if use_gpu:
|
if use_gpu:
|
||||||
@@ -117,6 +129,19 @@ def run_debug_tts_wavs(
|
|||||||
use_gpu = bool(settings.get("use_gpu", False))
|
use_gpu = bool(settings.get("use_gpu", False))
|
||||||
speed = float(settings.get("default_speed", 1.0) or 1.0)
|
speed = float(settings.get("default_speed", 1.0) or 1.0)
|
||||||
|
|
||||||
|
# Settings may store "profile:<name>" which is not a Kokoro voice ID.
|
||||||
|
# Resolve it to a concrete voice formula (e.g. "af_heart*0.5+...") so Kokoro
|
||||||
|
# doesn't attempt to download a non-existent "voices/profile:<name>.pt".
|
||||||
|
try:
|
||||||
|
resolved_voice, _profile_name, profile_language = _resolve_voice_setting(voice_spec)
|
||||||
|
if resolved_voice:
|
||||||
|
voice_spec = resolved_voice
|
||||||
|
if profile_language:
|
||||||
|
language = str(profile_language).strip() or language
|
||||||
|
except Exception:
|
||||||
|
# Voice profile resolution is best-effort; fall back to raw voice_spec.
|
||||||
|
pass
|
||||||
|
|
||||||
# Best-effort voice caching (only for known Kokoro internal voices).
|
# Best-effort voice caching (only for known Kokoro internal voices).
|
||||||
voice_ids = _spec_to_voice_ids(voice_spec)
|
voice_ids = _spec_to_voice_ids(voice_spec)
|
||||||
if voice_ids:
|
if voice_ids:
|
||||||
|
|||||||
@@ -119,3 +119,37 @@ def test_debug_samples_have_minimum_per_category():
|
|||||||
|
|
||||||
for prefix, minimum in prefixes.items():
|
for prefix, minimum in prefixes.items():
|
||||||
assert counts[prefix] >= minimum
|
assert counts[prefix] >= minimum
|
||||||
|
|
||||||
|
|
||||||
|
def test_debug_runner_resolves_profile_voice_before_pipeline(tmp_path, monkeypatch):
|
||||||
|
from abogen.web import debug_tts_runner as runner
|
||||||
|
|
||||||
|
# Stub voice setting resolution so we don't depend on the user's profile file.
|
||||||
|
monkeypatch.setattr(runner, "_resolve_voice_setting", lambda value: ("af_heart", "AM HQ Alt", None))
|
||||||
|
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
class _Seg:
|
||||||
|
def __init__(self, audio):
|
||||||
|
self.audio = audio
|
||||||
|
|
||||||
|
class DummyPipeline:
|
||||||
|
def __call__(self, text, **kwargs):
|
||||||
|
calls.append(kwargs.get("voice"))
|
||||||
|
audio = np.zeros(int(0.05 * runner.SAMPLE_RATE), dtype="float32")
|
||||||
|
yield _Seg(audio)
|
||||||
|
|
||||||
|
monkeypatch.setattr(runner, "_load_pipeline", lambda language, use_gpu: DummyPipeline())
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
"language": "en",
|
||||||
|
"default_voice": "profile:AM HQ Alt",
|
||||||
|
"use_gpu": False,
|
||||||
|
"default_speed": 1.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest = runner.run_debug_tts_wavs(output_root=tmp_path, settings=settings)
|
||||||
|
assert manifest.get("run_id")
|
||||||
|
assert calls
|
||||||
|
# Must not pass through the profile:* string.
|
||||||
|
assert all(isinstance(v, str) and not v.lower().startswith("profile:") for v in calls)
|
||||||
|
|||||||
Reference in New Issue
Block a user