fix(worker): preserve isolated OmniVoice synthesis settings
This commit is contained in:
@@ -208,9 +208,11 @@ class TaskExecutor:
|
||||
from services.text_normalization import normalize_for_tts
|
||||
|
||||
text = normalize_for_tts(row.get("text") or "", row.get("language"))
|
||||
seed = None
|
||||
if row.get("seed") is not None:
|
||||
import torch
|
||||
torch.manual_seed(int(row["seed"]))
|
||||
seed = int(row["seed"])
|
||||
torch.manual_seed(seed)
|
||||
kwargs = {
|
||||
"language": row.get("language") if row.get("language") != "Auto" else None,
|
||||
"ref_audio": row.get("ref_audio"), "ref_text": row.get("ref_text"),
|
||||
@@ -221,6 +223,11 @@ class TaskExecutor:
|
||||
"speed": float(row.get("speed") or 1.0), "denoise": True,
|
||||
"postprocess_output": True,
|
||||
}
|
||||
if (
|
||||
getattr(backend, "supports_native_omnivoice_controls", False)
|
||||
and seed is not None
|
||||
):
|
||||
kwargs["seed"] = seed
|
||||
audio = backend.generate(text=text, **kwargs)
|
||||
preset = row.get("effect_preset") or "broadcast"
|
||||
if preset != "raw":
|
||||
@@ -332,7 +339,10 @@ class TaskExecutor:
|
||||
key: value for key, value in opts.to_manifest().items()
|
||||
if value is not None and key not in ("seed", "vary_repeats")
|
||||
}
|
||||
if isinstance(backend, OmniVoiceBackend):
|
||||
native_proxy = bool(
|
||||
getattr(backend, "supports_native_omnivoice_controls", False)
|
||||
)
|
||||
if isinstance(backend, OmniVoiceBackend) or native_proxy:
|
||||
extra.setdefault("num_step", 32)
|
||||
extra.setdefault("guidance_scale", 2.0)
|
||||
for key in ("emo_vector", "emo_text", "emo_alpha"):
|
||||
@@ -341,11 +351,13 @@ class TaskExecutor:
|
||||
def synth(text, index, speed=None):
|
||||
voice = voices[int(index)]
|
||||
base_seed = opts.seed if opts.seed is not None else voice.get("seed")
|
||||
seed = None
|
||||
if base_seed is not None:
|
||||
import torch
|
||||
nonce = occurrence["value"] if opts.vary_repeats else 0
|
||||
occurrence["value"] += 1
|
||||
torch.manual_seed(segment_seed(base_seed, text, nonce))
|
||||
seed = segment_seed(base_seed, text, nonce)
|
||||
torch.manual_seed(seed)
|
||||
kwargs = {
|
||||
"language": language,
|
||||
"ref_audio": voice.get("ref_audio"),
|
||||
@@ -354,6 +366,8 @@ class TaskExecutor:
|
||||
"speed": float(speed) if speed else 1.0,
|
||||
**extra,
|
||||
}
|
||||
if native_proxy and seed is not None:
|
||||
kwargs["seed"] = seed
|
||||
return backend.generate(text, **kwargs)
|
||||
|
||||
spans = [Span(voice_id=str(i), text=row.get("text", ""),
|
||||
|
||||
@@ -32,7 +32,7 @@ lower call overhead.
|
||||
|
||||
## Selecting it
|
||||
|
||||
- **Settings -> Engines**, or
|
||||
- **Model Catalogue → Engines**, or
|
||||
- `OMNIVOICE_TTS_BACKEND=omnivoice-subprocess`
|
||||
|
||||
The explicit engine is opt-in on CUDA, ROCm, and CPU. Apple Silicon gets the
|
||||
|
||||
@@ -54,3 +54,30 @@ def test_audiobook_worker_marks_and_encodes_chapter(monkeypatch):
|
||||
)
|
||||
assert len(audio) == 20
|
||||
assert marked == [(100, "worker.executor.tts")]
|
||||
|
||||
|
||||
def test_audiobook_worker_forwards_mps_proxy_quality_and_seed():
|
||||
import numpy as np
|
||||
from services.audiobook import segment_seed
|
||||
from worker.executor import TaskExecutor
|
||||
|
||||
calls = []
|
||||
|
||||
class Backend:
|
||||
sample_rate = 100
|
||||
supports_native_omnivoice_controls = True
|
||||
|
||||
def generate(self, text, **kwargs):
|
||||
calls.append((text, kwargs))
|
||||
return np.ones(20, dtype=np.float32)
|
||||
|
||||
TaskExecutor._synthesize_audiobook(
|
||||
Backend(), [{"text": "hello", "pause_ms_after": 0}],
|
||||
[{"ref_text": None, "instruct": None, "seed": 42}],
|
||||
{"ref_audio": [None], "expressive": {}, "watermark": False},
|
||||
)
|
||||
|
||||
_text, kwargs = calls[0]
|
||||
assert kwargs["num_step"] == 32
|
||||
assert kwargs["guidance_scale"] == 2.0
|
||||
assert kwargs["seed"] == segment_seed(42, "hello")
|
||||
|
||||
@@ -83,6 +83,27 @@ class _FakeBackend:
|
||||
type(self).unloaded += 1
|
||||
|
||||
|
||||
def test_dub_worker_forwards_seed_into_mps_proxy():
|
||||
calls = []
|
||||
|
||||
class Proxy:
|
||||
sample_rate = 24_000
|
||||
applies_own_mastering = False
|
||||
supports_native_omnivoice_controls = True
|
||||
|
||||
def generate(self, text, **kwargs):
|
||||
calls.append((text, kwargs))
|
||||
import torch
|
||||
|
||||
return torch.zeros(1, 240)
|
||||
|
||||
TaskExecutor._synthesize_dub_segment(Proxy(), {
|
||||
"text": "hello", "seed": 123, "effect_preset": "raw",
|
||||
})
|
||||
|
||||
assert calls[0][1]["seed"] == 123
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cancelling_execution_drains_the_blocking_engine_thread(monkeypatch):
|
||||
started = threading.Event()
|
||||
|
||||
Reference in New Issue
Block a user