voicerender 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- voicerender/__init__.py +127 -0
- voicerender/__main__.py +3 -0
- voicerender/_version.py +24 -0
- voicerender/capacity.py +678 -0
- voicerender/cli.py +345 -0
- voicerender/engines/__init__.py +68 -0
- voicerender/engines/api_probe.py +495 -0
- voicerender/engines/base.py +425 -0
- voicerender/engines/catalog.py +83 -0
- voicerender/engines/inflectsynth.py +920 -0
- voicerender/engines/kittensynth.py +511 -0
- voicerender/engines/kokoro.py +725 -0
- voicerender/engines/metadata.py +58 -0
- voicerender/engines/piper.py +173 -0
- voicerender/engines/pipersynth.py +570 -0
- voicerender/engines/pocket.py +169 -0
- voicerender/engines/pocketsynth.py +1234 -0
- voicerender/engines/registry.py +320 -0
- voicerender/engines/selection.py +61 -0
- voicerender/engines/supertonicsynth.py +707 -0
- voicerender/errors.py +161 -0
- voicerender/events.py +43 -0
- voicerender/handoff.py +62 -0
- voicerender/inputs/__init__.py +21 -0
- voicerender/inputs/utterplan_v5.py +897 -0
- voicerender/lexicons.py +42 -0
- voicerender/manifest.py +750 -0
- voicerender/models.py +499 -0
- voicerender/py.typed +0 -0
- voicerender/render.py +365 -0
- voicerender/render_v5.py +1193 -0
- voicerender/schemas/render-manifest-v2.schema.json +415 -0
- voicerender/storage.py +113 -0
- voicerender-0.1.1.dist-info/METADATA +254 -0
- voicerender-0.1.1.dist-info/RECORD +39 -0
- voicerender-0.1.1.dist-info/WHEEL +5 -0
- voicerender-0.1.1.dist-info/entry_points.txt +2 -0
- voicerender-0.1.1.dist-info/licenses/LICENSE +201 -0
- voicerender-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
"""Request-centric adapter for the public KittenSynth API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from contextlib import AbstractContextManager, contextmanager
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from ..errors import (
|
|
11
|
+
EmptySpeechTextError,
|
|
12
|
+
EngineBackendError,
|
|
13
|
+
EngineSynthesisError,
|
|
14
|
+
InvalidEngineModelError,
|
|
15
|
+
InvalidEngineOptionError,
|
|
16
|
+
InvalidEngineVoiceError,
|
|
17
|
+
)
|
|
18
|
+
from .api_probe import SUPPORTED_REQUEST_API_VERSION, EngineApiProbe, probe_public_api
|
|
19
|
+
from .base import (
|
|
20
|
+
EngineCapabilities,
|
|
21
|
+
EngineSelection,
|
|
22
|
+
RenderedSpeech,
|
|
23
|
+
RequestMeasure,
|
|
24
|
+
SpeechRequest,
|
|
25
|
+
distribution_version,
|
|
26
|
+
)
|
|
27
|
+
from .catalog import CatalogRequest, SynthesisTarget, TargetVoice
|
|
28
|
+
from .metadata import (
|
|
29
|
+
language_base,
|
|
30
|
+
normalize_gender,
|
|
31
|
+
normalize_language_key,
|
|
32
|
+
normalize_locale_tag,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
DEFAULT_KITTEN_MODEL = "nano-0.8-int8"
|
|
36
|
+
DEFAULT_KITTEN_VOICE = "Jasper"
|
|
37
|
+
KITTEN_OPTION_NAMES = frozenset(
|
|
38
|
+
{
|
|
39
|
+
"speed",
|
|
40
|
+
"quality",
|
|
41
|
+
"cache_dir",
|
|
42
|
+
"force_download",
|
|
43
|
+
"providers",
|
|
44
|
+
"provider_options",
|
|
45
|
+
"session_options",
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class KittenSelectionError(ValueError):
|
|
51
|
+
"""A Kitten selection error with a stable plan diagnostic."""
|
|
52
|
+
|
|
53
|
+
def __init__(self, code: str, message: str, field: str) -> None:
|
|
54
|
+
super().__init__(message)
|
|
55
|
+
self.diagnostic_code = code
|
|
56
|
+
self.diagnostic_field = field
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _is_english(language: str) -> bool:
|
|
60
|
+
normalized = normalize_language_key(language)
|
|
61
|
+
return normalized == "english" or language_base(normalized) == "en"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _language_values(value: Any) -> tuple[str, str, str]:
|
|
65
|
+
raw = str(value or "en")
|
|
66
|
+
normalized = normalize_locale_tag(raw)
|
|
67
|
+
base = language_base(normalized) or normalize_language_key(raw)
|
|
68
|
+
if base == "english":
|
|
69
|
+
base = "en"
|
|
70
|
+
normalized = "en"
|
|
71
|
+
return base or "unknown", normalized or base or "unknown", raw
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _target_from_model(model: Any) -> SynthesisTarget:
|
|
75
|
+
language, locale, raw_language = _language_values(model.language)
|
|
76
|
+
quality = model.quality
|
|
77
|
+
voices = tuple(str(voice) for voice in model.voice_ids)
|
|
78
|
+
display_name = str(model.display_name)
|
|
79
|
+
if model.version:
|
|
80
|
+
display_name = f"{display_name} {model.version}"
|
|
81
|
+
normalized_metadata = {
|
|
82
|
+
**dict(model.metadata),
|
|
83
|
+
"language": language,
|
|
84
|
+
"locale": locale,
|
|
85
|
+
"language_label": raw_language
|
|
86
|
+
if raw_language.casefold() != "en"
|
|
87
|
+
else "English",
|
|
88
|
+
"quality": quality,
|
|
89
|
+
"source": "kittensynth",
|
|
90
|
+
}
|
|
91
|
+
return SynthesisTarget(
|
|
92
|
+
engine="kitten",
|
|
93
|
+
id=str(model.id),
|
|
94
|
+
display_name=display_name,
|
|
95
|
+
languages=(locale,) if locale and locale != "unknown" else (),
|
|
96
|
+
status="ready",
|
|
97
|
+
runtime_available=bool(model.runtime_available),
|
|
98
|
+
sample_rate=model.sample_rate,
|
|
99
|
+
voices=voices,
|
|
100
|
+
voice_details=tuple(
|
|
101
|
+
TargetVoice(
|
|
102
|
+
id=str(voice.id),
|
|
103
|
+
gender=normalize_gender(voice.gender),
|
|
104
|
+
language=language_base(voice.language) or language,
|
|
105
|
+
locale=normalize_locale_tag(voice.locale) or locale,
|
|
106
|
+
language_label=str(voice.language_label or raw_language),
|
|
107
|
+
)
|
|
108
|
+
for voice in model.voices
|
|
109
|
+
),
|
|
110
|
+
default_voice=model.default_voice,
|
|
111
|
+
qualities=(str(quality),) if isinstance(quality, str) and quality else (),
|
|
112
|
+
aliases=tuple(str(alias) for alias in model.aliases),
|
|
113
|
+
capabilities=frozenset({"named_voices"}),
|
|
114
|
+
metadata=normalized_metadata,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _catalog_targets(request: CatalogRequest) -> tuple[SynthesisTarget, ...]:
|
|
119
|
+
import kittensynth
|
|
120
|
+
|
|
121
|
+
models = kittensynth.discover_models(
|
|
122
|
+
language=request.language,
|
|
123
|
+
offline=request.offline,
|
|
124
|
+
refresh=request.refresh,
|
|
125
|
+
)
|
|
126
|
+
return tuple(_target_from_model(model) for model in models)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _translate_kitten_error(
|
|
130
|
+
error: Exception,
|
|
131
|
+
selection: EngineSelection,
|
|
132
|
+
request: SpeechRequest | None = None,
|
|
133
|
+
*,
|
|
134
|
+
opening: bool = False,
|
|
135
|
+
) -> EngineSynthesisError:
|
|
136
|
+
try:
|
|
137
|
+
import kittensynth
|
|
138
|
+
except ImportError:
|
|
139
|
+
kittensynth = None
|
|
140
|
+
|
|
141
|
+
error_map: tuple[tuple[str, type[EngineSynthesisError], str | None], ...] = (
|
|
142
|
+
("EmptyTextError", EmptySpeechTextError, None),
|
|
143
|
+
("InvalidSpeedError", InvalidEngineOptionError, "kitten.invalid_speed"),
|
|
144
|
+
("InvalidVoiceError", InvalidEngineVoiceError, "kitten.voice_unavailable"),
|
|
145
|
+
("UnsupportedModelError", InvalidEngineModelError, "kitten.model_unsupported"),
|
|
146
|
+
("OnnxVoiceContractError", EngineBackendError, "kitten.runtime_contract_error"),
|
|
147
|
+
)
|
|
148
|
+
error_type: type[EngineSynthesisError] = EngineBackendError
|
|
149
|
+
code: str | None = "kitten.engine_error"
|
|
150
|
+
if kittensynth is not None:
|
|
151
|
+
for native_name, mapped_type, mapped_code in error_map:
|
|
152
|
+
native_type = getattr(kittensynth, native_name, None)
|
|
153
|
+
if isinstance(native_type, type) and isinstance(error, native_type):
|
|
154
|
+
error_type = mapped_type
|
|
155
|
+
code = mapped_code
|
|
156
|
+
break
|
|
157
|
+
else:
|
|
158
|
+
if opening and isinstance(error, (TypeError, ValueError)):
|
|
159
|
+
error_type = InvalidEngineOptionError
|
|
160
|
+
code = "kitten.invalid_options"
|
|
161
|
+
context: dict[str, Any] = {
|
|
162
|
+
"engine": "kitten",
|
|
163
|
+
"engine_version": distribution_version("kittensynth"),
|
|
164
|
+
"target_id": selection.target_id,
|
|
165
|
+
"language": request.language if request is not None else selection.language,
|
|
166
|
+
"voice": request.voice if request is not None else selection.voice,
|
|
167
|
+
"request_id": request.id if request is not None else None,
|
|
168
|
+
"native_error_type": type(error).__name__,
|
|
169
|
+
}
|
|
170
|
+
return error_type(str(error), **context, code=code)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class KittenSynthEngineSession:
|
|
174
|
+
"""Adapt prepared VoiceRender requests to one open ``KittenVoice``."""
|
|
175
|
+
|
|
176
|
+
def __init__(self, voice: Any, selection: EngineSelection) -> None:
|
|
177
|
+
self._voice = voice
|
|
178
|
+
self._selection = selection
|
|
179
|
+
|
|
180
|
+
def measure(self, request: SpeechRequest) -> RequestMeasure:
|
|
181
|
+
return RequestMeasure(
|
|
182
|
+
fits=None,
|
|
183
|
+
amount=None,
|
|
184
|
+
maximum=None,
|
|
185
|
+
unit="unknown",
|
|
186
|
+
source="kittensynth.no_capacity_measurement",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def synthesize(self, request: SpeechRequest) -> RenderedSpeech:
|
|
190
|
+
speed = float(self._selection.options.get("speed", 1.0))
|
|
191
|
+
try:
|
|
192
|
+
result = self._voice.synthesize_prepared(
|
|
193
|
+
request.text,
|
|
194
|
+
voice=request.voice or self._selection.voice or DEFAULT_KITTEN_VOICE,
|
|
195
|
+
speed=speed,
|
|
196
|
+
)
|
|
197
|
+
except EngineSynthesisError:
|
|
198
|
+
raise
|
|
199
|
+
except Exception as exc:
|
|
200
|
+
raise _translate_kitten_error(exc, self._selection, request) from exc
|
|
201
|
+
metadata = dict(result.metadata or {})
|
|
202
|
+
metadata.update(
|
|
203
|
+
{
|
|
204
|
+
"voice": result.voice,
|
|
205
|
+
"model_ref": result.model_ref,
|
|
206
|
+
"effective_speed": result.speed,
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
return RenderedSpeech(
|
|
210
|
+
id=request.id,
|
|
211
|
+
audio=result.audio,
|
|
212
|
+
sample_rate=result.sample_rate,
|
|
213
|
+
warnings=(),
|
|
214
|
+
word_timings=(),
|
|
215
|
+
metadata=metadata,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class KittenSynthEngineAdapter:
|
|
220
|
+
"""Kitten model discovery, selection, runtime and request adaptation."""
|
|
221
|
+
|
|
222
|
+
id = "kitten"
|
|
223
|
+
package_name = "kittensynth"
|
|
224
|
+
|
|
225
|
+
def version(self) -> str | None:
|
|
226
|
+
return distribution_version(self.package_name)
|
|
227
|
+
|
|
228
|
+
def probe_api(self) -> EngineApiProbe:
|
|
229
|
+
return probe_public_api(
|
|
230
|
+
engine=self.id,
|
|
231
|
+
package=self.package_name,
|
|
232
|
+
required_symbols=(
|
|
233
|
+
"KittenVoice",
|
|
234
|
+
"SynthesisConfig",
|
|
235
|
+
"SynthesisResult",
|
|
236
|
+
"discover_models",
|
|
237
|
+
"runtime_identity",
|
|
238
|
+
),
|
|
239
|
+
required_methods={
|
|
240
|
+
"KittenVoice": (
|
|
241
|
+
"synthesize_prepared",
|
|
242
|
+
"from_pretrained",
|
|
243
|
+
"from_local",
|
|
244
|
+
"close",
|
|
245
|
+
),
|
|
246
|
+
"__module__": ("discover_models", "runtime_identity"),
|
|
247
|
+
},
|
|
248
|
+
expected_api_version=SUPPORTED_REQUEST_API_VERSION,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
def compatible_api(self) -> bool:
|
|
252
|
+
return self.probe_api().compatible
|
|
253
|
+
|
|
254
|
+
def capabilities(self) -> EngineCapabilities:
|
|
255
|
+
return EngineCapabilities(
|
|
256
|
+
id=self.id,
|
|
257
|
+
voice_binding_namespace="kitten",
|
|
258
|
+
voice_binding_scope="request",
|
|
259
|
+
option_names=KITTEN_OPTION_NAMES,
|
|
260
|
+
supports_named_voices=True,
|
|
261
|
+
supports_reference_voice=False,
|
|
262
|
+
supports_speakers=False,
|
|
263
|
+
supports_pronunciation_overrides=False,
|
|
264
|
+
supports_linguistic_tokens=False,
|
|
265
|
+
supports_whole_request_phonemes=False,
|
|
266
|
+
supports_lexicons=False,
|
|
267
|
+
supports_model_sources=False,
|
|
268
|
+
supports_qualities=True,
|
|
269
|
+
supports_live=True,
|
|
270
|
+
supports_timestamps=False,
|
|
271
|
+
supports_voice_level_calibration=False,
|
|
272
|
+
supports_request_measurement=False,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
def discover(self, request: CatalogRequest) -> tuple[SynthesisTarget, ...]:
|
|
276
|
+
try:
|
|
277
|
+
return _catalog_targets(request)
|
|
278
|
+
except ImportError:
|
|
279
|
+
return ()
|
|
280
|
+
|
|
281
|
+
def resolve(self, request: Any) -> tuple[EngineSelection, tuple[Any, ...]]:
|
|
282
|
+
language = normalize_language_key(getattr(request, "language", None) or "en-us")
|
|
283
|
+
if not _is_english(language):
|
|
284
|
+
raise KittenSelectionError(
|
|
285
|
+
"kitten.language_incompatible",
|
|
286
|
+
f"KittenSynth supports English only, not {language!r}.",
|
|
287
|
+
"synthesis.language",
|
|
288
|
+
)
|
|
289
|
+
options = dict(getattr(request, "options", {}) or {})
|
|
290
|
+
options.update(dict(getattr(request, "engine_options", {}) or {}))
|
|
291
|
+
speed = options.get("speed", 1.0)
|
|
292
|
+
try:
|
|
293
|
+
speed = float(speed)
|
|
294
|
+
except (TypeError, ValueError) as exc:
|
|
295
|
+
raise KittenSelectionError(
|
|
296
|
+
"kitten.invalid_speed",
|
|
297
|
+
"Kitten speed must be finite and greater than zero.",
|
|
298
|
+
"synthesis.speed",
|
|
299
|
+
) from exc
|
|
300
|
+
if not math.isfinite(speed) or speed <= 0:
|
|
301
|
+
raise KittenSelectionError(
|
|
302
|
+
"kitten.invalid_speed",
|
|
303
|
+
"Kitten speed must be finite and greater than zero.",
|
|
304
|
+
"synthesis.speed",
|
|
305
|
+
)
|
|
306
|
+
options["speed"] = speed
|
|
307
|
+
target_id = (
|
|
308
|
+
getattr(request, "target_id", None)
|
|
309
|
+
or options.pop("model", None)
|
|
310
|
+
or DEFAULT_KITTEN_MODEL
|
|
311
|
+
)
|
|
312
|
+
voice = (
|
|
313
|
+
getattr(request, "voice", None)
|
|
314
|
+
or options.pop("voice", None)
|
|
315
|
+
or DEFAULT_KITTEN_VOICE
|
|
316
|
+
)
|
|
317
|
+
return (
|
|
318
|
+
EngineSelection(
|
|
319
|
+
engine=self.id,
|
|
320
|
+
target_id=str(target_id),
|
|
321
|
+
language=language,
|
|
322
|
+
voice=str(voice),
|
|
323
|
+
speaker=getattr(request, "speaker", None),
|
|
324
|
+
options={
|
|
325
|
+
key: value
|
|
326
|
+
for key, value in options.items()
|
|
327
|
+
if key in KITTEN_OPTION_NAMES
|
|
328
|
+
},
|
|
329
|
+
offline=bool(getattr(request, "offline", False)),
|
|
330
|
+
refresh=bool(getattr(request, "refresh", False)),
|
|
331
|
+
),
|
|
332
|
+
(),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
def validate_selection(self, selection: EngineSelection) -> tuple[Any, ...]:
|
|
336
|
+
from .base import EngineDiagnostic
|
|
337
|
+
|
|
338
|
+
if not _is_english(selection.language):
|
|
339
|
+
return (
|
|
340
|
+
EngineDiagnostic(
|
|
341
|
+
code="kitten.language_incompatible",
|
|
342
|
+
severity="error",
|
|
343
|
+
message=f"KittenSynth supports English only, not {selection.language!r}.",
|
|
344
|
+
field="synthesis.language",
|
|
345
|
+
),
|
|
346
|
+
)
|
|
347
|
+
try:
|
|
348
|
+
targets = self.discover(
|
|
349
|
+
CatalogRequest(
|
|
350
|
+
engine=self.id,
|
|
351
|
+
language=selection.language,
|
|
352
|
+
offline=selection.offline,
|
|
353
|
+
refresh=selection.refresh,
|
|
354
|
+
)
|
|
355
|
+
)
|
|
356
|
+
except (ImportError, OSError, RuntimeError, TypeError, ValueError) as exc:
|
|
357
|
+
return (
|
|
358
|
+
EngineDiagnostic(
|
|
359
|
+
code="kitten.engine_unavailable",
|
|
360
|
+
severity="error",
|
|
361
|
+
message=f"Kitten model catalog is unavailable: {exc}",
|
|
362
|
+
field="synthesis.engine",
|
|
363
|
+
),
|
|
364
|
+
)
|
|
365
|
+
target = next(
|
|
366
|
+
(
|
|
367
|
+
item
|
|
368
|
+
for item in targets
|
|
369
|
+
if item.id == selection.target_id or selection.target_id in item.aliases
|
|
370
|
+
),
|
|
371
|
+
None,
|
|
372
|
+
)
|
|
373
|
+
if target is None:
|
|
374
|
+
return (
|
|
375
|
+
EngineDiagnostic(
|
|
376
|
+
code="kitten.model_not_found",
|
|
377
|
+
severity="error",
|
|
378
|
+
message=f"Kitten model {selection.target_id!r} was not found in the Kitten catalog.",
|
|
379
|
+
field="render.target.id",
|
|
380
|
+
),
|
|
381
|
+
)
|
|
382
|
+
if target.engine != "kitten":
|
|
383
|
+
return (
|
|
384
|
+
EngineDiagnostic(
|
|
385
|
+
code="kitten.model_not_found",
|
|
386
|
+
severity="error",
|
|
387
|
+
message=f"Catalog target {target.id!r} does not belong to the Kitten system.",
|
|
388
|
+
field="render.target.id",
|
|
389
|
+
),
|
|
390
|
+
)
|
|
391
|
+
if not target.sample_rate or target.sample_rate <= 0:
|
|
392
|
+
return (
|
|
393
|
+
EngineDiagnostic(
|
|
394
|
+
code="kitten.model_not_found",
|
|
395
|
+
severity="error",
|
|
396
|
+
message=f"Kitten model {target.id!r} has no valid sample rate.",
|
|
397
|
+
field="render.target.id",
|
|
398
|
+
),
|
|
399
|
+
)
|
|
400
|
+
if selection.voice not in target.voices:
|
|
401
|
+
return (
|
|
402
|
+
EngineDiagnostic(
|
|
403
|
+
code="kitten.voice_unavailable",
|
|
404
|
+
severity="error",
|
|
405
|
+
message=f"Voice {selection.voice!r} is not available for Kitten model {target.id!r}.",
|
|
406
|
+
field="render.target.voice",
|
|
407
|
+
),
|
|
408
|
+
)
|
|
409
|
+
quality = selection.options.get("quality")
|
|
410
|
+
if quality is not None and quality not in target.qualities:
|
|
411
|
+
return (
|
|
412
|
+
EngineDiagnostic(
|
|
413
|
+
code="kitten.quality_unavailable",
|
|
414
|
+
severity="error",
|
|
415
|
+
message=f"Quality {quality!r} is not available for Kitten model {target.id!r}.",
|
|
416
|
+
field="render.options.quality",
|
|
417
|
+
),
|
|
418
|
+
)
|
|
419
|
+
return ()
|
|
420
|
+
|
|
421
|
+
def target_metadata(self, selection: EngineSelection) -> Mapping[str, Any]:
|
|
422
|
+
targets = self.discover(
|
|
423
|
+
CatalogRequest(
|
|
424
|
+
engine=self.id,
|
|
425
|
+
language=selection.language,
|
|
426
|
+
offline=selection.offline,
|
|
427
|
+
refresh=selection.refresh,
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
target = next(
|
|
431
|
+
(
|
|
432
|
+
item
|
|
433
|
+
for item in targets
|
|
434
|
+
if item.id == selection.target_id or selection.target_id in item.aliases
|
|
435
|
+
),
|
|
436
|
+
None,
|
|
437
|
+
)
|
|
438
|
+
if target is None:
|
|
439
|
+
return {}
|
|
440
|
+
return {
|
|
441
|
+
**dict(target.metadata),
|
|
442
|
+
"sample_rate": target.sample_rate,
|
|
443
|
+
"voices": target.voices,
|
|
444
|
+
"qualities": target.qualities,
|
|
445
|
+
"source_revision": target.metadata.get("source_revision"),
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
def canonical_synthesis_identity(
|
|
449
|
+
self, selection: EngineSelection
|
|
450
|
+
) -> Mapping[str, Any]:
|
|
451
|
+
import kittensynth
|
|
452
|
+
|
|
453
|
+
pcm_options = {
|
|
454
|
+
key: selection.options[key]
|
|
455
|
+
for key in (
|
|
456
|
+
"quality",
|
|
457
|
+
"speed",
|
|
458
|
+
"providers",
|
|
459
|
+
"provider_options",
|
|
460
|
+
"session_options",
|
|
461
|
+
)
|
|
462
|
+
if key in selection.options
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
"engine": self.id,
|
|
466
|
+
"engine_version": self.version(),
|
|
467
|
+
"engine_identity": dict(kittensynth.runtime_identity()),
|
|
468
|
+
"target_id": selection.target_id,
|
|
469
|
+
"voice": selection.voice,
|
|
470
|
+
"language": selection.language,
|
|
471
|
+
"options": pcm_options,
|
|
472
|
+
"model_revision": selection.metadata.get("source_revision"),
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
def open(
|
|
476
|
+
self, selection: EngineSelection
|
|
477
|
+
) -> AbstractContextManager[KittenSynthEngineSession]:
|
|
478
|
+
import kittensynth
|
|
479
|
+
|
|
480
|
+
options = dict(selection.options)
|
|
481
|
+
try:
|
|
482
|
+
voice = kittensynth.KittenVoice.from_pretrained(
|
|
483
|
+
selection.target_id,
|
|
484
|
+
quality=options.get("quality"),
|
|
485
|
+
cache_dir=options.get("cache_dir"),
|
|
486
|
+
offline=selection.offline,
|
|
487
|
+
refresh_catalog=selection.refresh,
|
|
488
|
+
force_download=bool(options.get("force_download", False)),
|
|
489
|
+
providers=options.get("providers"),
|
|
490
|
+
provider_options=options.get("provider_options"),
|
|
491
|
+
session_options=options.get("session_options"),
|
|
492
|
+
)
|
|
493
|
+
except Exception as exc:
|
|
494
|
+
raise _translate_kitten_error(exc, selection, opening=True) from exc
|
|
495
|
+
|
|
496
|
+
@contextmanager
|
|
497
|
+
def session() -> Any:
|
|
498
|
+
try:
|
|
499
|
+
yield KittenSynthEngineSession(voice, selection)
|
|
500
|
+
finally:
|
|
501
|
+
voice.close()
|
|
502
|
+
|
|
503
|
+
return session()
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
__all__ = [
|
|
507
|
+
"DEFAULT_KITTEN_MODEL",
|
|
508
|
+
"DEFAULT_KITTEN_VOICE",
|
|
509
|
+
"KittenSynthEngineAdapter",
|
|
510
|
+
"KittenSynthEngineSession",
|
|
511
|
+
]
|