soprosynth 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.
- soprosynth/__init__.py +103 -0
- soprosynth/__main__.py +178 -0
- soprosynth/_onnxvoice.py +609 -0
- soprosynth/_version.py +24 -0
- soprosynth/api.py +817 -0
- soprosynth/api_contract.py +67 -0
- soprosynth/audio.py +106 -0
- soprosynth/calibration.py +364 -0
- soprosynth/calibration_builder.py +323 -0
- soprosynth/config.py +134 -0
- soprosynth/data/voice_level_calibration.json +4 -0
- soprosynth/discovery.py +134 -0
- soprosynth/errors.py +87 -0
- soprosynth/postprocess.py +433 -0
- soprosynth/py.typed +0 -0
- soprosynth/types.py +356 -0
- soprosynth/voice.py +130 -0
- soprosynth-0.1.1.dist-info/METADATA +200 -0
- soprosynth-0.1.1.dist-info/RECORD +23 -0
- soprosynth-0.1.1.dist-info/WHEEL +5 -0
- soprosynth-0.1.1.dist-info/entry_points.txt +3 -0
- soprosynth-0.1.1.dist-info/licenses/LICENSE +201 -0
- soprosynth-0.1.1.dist-info/top_level.txt +1 -0
soprosynth/__init__.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Dependency-light public API for the Sopro speech-synthesis runtime."""
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
from ._version import version as __version__
|
|
5
|
+
except ImportError:
|
|
6
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
__version__ = version("soprosynth")
|
|
10
|
+
except PackageNotFoundError:
|
|
11
|
+
__version__ = "0.0.0"
|
|
12
|
+
|
|
13
|
+
from .api import SoproRuntime
|
|
14
|
+
from .api_contract import REQUEST_API_VERSION, RequestApiContract, request_api_contract
|
|
15
|
+
from .config import (
|
|
16
|
+
GenerationConfig,
|
|
17
|
+
SoproOutputConfig,
|
|
18
|
+
SoproOutputMode,
|
|
19
|
+
VoiceLevelApplication,
|
|
20
|
+
VoiceLevelConfig,
|
|
21
|
+
VoiceLevelMode,
|
|
22
|
+
VoiceLevelSource,
|
|
23
|
+
)
|
|
24
|
+
from .discovery import discover_bundles, runtime_identity
|
|
25
|
+
from .errors import (
|
|
26
|
+
AssetAccessError,
|
|
27
|
+
AssetCacheError,
|
|
28
|
+
AssetDownloadError,
|
|
29
|
+
AssetError,
|
|
30
|
+
BundleNotFoundError,
|
|
31
|
+
CatalogUnavailableError,
|
|
32
|
+
EmptyTextError,
|
|
33
|
+
InvalidGenerationConfigError,
|
|
34
|
+
InvalidLanguageError,
|
|
35
|
+
InvalidRequestError,
|
|
36
|
+
InvalidVoiceError,
|
|
37
|
+
ModelInferenceError,
|
|
38
|
+
OfflineAssetError,
|
|
39
|
+
OptionalDependencyError,
|
|
40
|
+
RuntimeCapabilityError,
|
|
41
|
+
RuntimeClosedError,
|
|
42
|
+
SessionCreationError,
|
|
43
|
+
SoproSynthError,
|
|
44
|
+
UnsupportedBundleError,
|
|
45
|
+
UnsupportedFeatureError,
|
|
46
|
+
)
|
|
47
|
+
from .types import (
|
|
48
|
+
DiscoveredBundle,
|
|
49
|
+
PreparedVoice,
|
|
50
|
+
RequestMeasure,
|
|
51
|
+
RuntimeCapabilities,
|
|
52
|
+
RuntimeDiagnostics,
|
|
53
|
+
SynthesisChunk,
|
|
54
|
+
SynthesisRequest,
|
|
55
|
+
SynthesisResult,
|
|
56
|
+
WordTiming,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
__all__ = [
|
|
60
|
+
"REQUEST_API_VERSION",
|
|
61
|
+
"AssetAccessError",
|
|
62
|
+
"AssetCacheError",
|
|
63
|
+
"AssetDownloadError",
|
|
64
|
+
"AssetError",
|
|
65
|
+
"BundleNotFoundError",
|
|
66
|
+
"CatalogUnavailableError",
|
|
67
|
+
"DiscoveredBundle",
|
|
68
|
+
"EmptyTextError",
|
|
69
|
+
"GenerationConfig",
|
|
70
|
+
"InvalidGenerationConfigError",
|
|
71
|
+
"InvalidLanguageError",
|
|
72
|
+
"InvalidRequestError",
|
|
73
|
+
"InvalidVoiceError",
|
|
74
|
+
"ModelInferenceError",
|
|
75
|
+
"OfflineAssetError",
|
|
76
|
+
"OptionalDependencyError",
|
|
77
|
+
"PreparedVoice",
|
|
78
|
+
"RequestApiContract",
|
|
79
|
+
"RequestMeasure",
|
|
80
|
+
"RuntimeCapabilities",
|
|
81
|
+
"RuntimeCapabilityError",
|
|
82
|
+
"RuntimeClosedError",
|
|
83
|
+
"RuntimeDiagnostics",
|
|
84
|
+
"SessionCreationError",
|
|
85
|
+
"SoproOutputConfig",
|
|
86
|
+
"SoproOutputMode",
|
|
87
|
+
"SoproRuntime",
|
|
88
|
+
"SoproSynthError",
|
|
89
|
+
"SynthesisChunk",
|
|
90
|
+
"SynthesisRequest",
|
|
91
|
+
"SynthesisResult",
|
|
92
|
+
"UnsupportedBundleError",
|
|
93
|
+
"UnsupportedFeatureError",
|
|
94
|
+
"VoiceLevelApplication",
|
|
95
|
+
"VoiceLevelConfig",
|
|
96
|
+
"VoiceLevelMode",
|
|
97
|
+
"VoiceLevelSource",
|
|
98
|
+
"WordTiming",
|
|
99
|
+
"__version__",
|
|
100
|
+
"discover_bundles",
|
|
101
|
+
"request_api_contract",
|
|
102
|
+
"runtime_identity",
|
|
103
|
+
]
|
soprosynth/__main__.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""SoproSynth command-line interface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
from collections.abc import Mapping, Sequence
|
|
8
|
+
from dataclasses import fields, is_dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from . import __version__, discover_bundles, request_api_contract, runtime_identity
|
|
13
|
+
from .api import SoproRuntime
|
|
14
|
+
from .config import GenerationConfig, SoproOutputConfig, VoiceLevelConfig
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _json_value(value: Any) -> Any:
|
|
18
|
+
if is_dataclass(value) and not isinstance(value, type):
|
|
19
|
+
return {field.name: _json_value(getattr(value, field.name)) for field in fields(value)}
|
|
20
|
+
if isinstance(value, Mapping):
|
|
21
|
+
return {str(key): _json_value(item) for key, item in value.items()}
|
|
22
|
+
if isinstance(value, (tuple, list)):
|
|
23
|
+
return [_json_value(item) for item in value]
|
|
24
|
+
if isinstance(value, Path):
|
|
25
|
+
return str(value)
|
|
26
|
+
if value is None or isinstance(value, (str, int, float, bool)):
|
|
27
|
+
return value
|
|
28
|
+
return str(value)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _print_json(value: Any) -> None:
|
|
32
|
+
print(json.dumps(_json_value(value), indent=2, sort_keys=True))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _contract_summary() -> dict[str, Any]:
|
|
36
|
+
contract = request_api_contract()
|
|
37
|
+
return {
|
|
38
|
+
"version": contract.version,
|
|
39
|
+
"request_type": contract.request_type.__name__,
|
|
40
|
+
"result_type": contract.result_type.__name__,
|
|
41
|
+
"synthesis_method": contract.synthesis_method,
|
|
42
|
+
"measurement_method": contract.measurement_method,
|
|
43
|
+
"supports_reference_voice": contract.supports_reference_voice,
|
|
44
|
+
"supports_voice_level": contract.supports_voice_level,
|
|
45
|
+
"supports_sopro_output_policy": contract.supports_sopro_output_policy,
|
|
46
|
+
"caller_owns_text_boundaries": contract.caller_owns_text_boundaries,
|
|
47
|
+
"supports_request_measurement": contract.supports_request_measurement,
|
|
48
|
+
"supports_streaming": contract.supports_streaming,
|
|
49
|
+
"supports_word_timings": contract.supports_word_timings,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
54
|
+
parser = argparse.ArgumentParser(
|
|
55
|
+
prog="soprosynth",
|
|
56
|
+
description="SoproSynth speech synthesis using ONNXVoice's Sopro runtime",
|
|
57
|
+
)
|
|
58
|
+
parser.add_argument("--version", action="version", version=__version__)
|
|
59
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
60
|
+
|
|
61
|
+
speak = subparsers.add_parser("speak", help="synthesize speech")
|
|
62
|
+
speak.add_argument("text", help="text to synthesize")
|
|
63
|
+
speak.add_argument("--ref", required=True, help="reference voice WAV path")
|
|
64
|
+
speak.add_argument("--out", default="out.wav", help="output mono PCM16 WAV path")
|
|
65
|
+
speak.add_argument("--language", choices=("en", "pt", "fr", "de", "auto"))
|
|
66
|
+
speak.add_argument("--model", default="sopro:v2-turbo")
|
|
67
|
+
speak.add_argument("--profile", default="wasm-uint8")
|
|
68
|
+
speak.add_argument("--provider", choices=("cpu",), default="cpu")
|
|
69
|
+
speak.add_argument(
|
|
70
|
+
"--output-mode",
|
|
71
|
+
choices=("native", "raw"),
|
|
72
|
+
default="native",
|
|
73
|
+
help="Sopro-compatible output conditioning or raw ONNX waveform",
|
|
74
|
+
)
|
|
75
|
+
speak.add_argument("--temperature", type=float)
|
|
76
|
+
speak.add_argument("--top-p", type=float)
|
|
77
|
+
speak.add_argument("--top-k", type=int)
|
|
78
|
+
speak.add_argument("--max-seconds", type=float)
|
|
79
|
+
speak.add_argument("--min-seconds", type=float)
|
|
80
|
+
speak.add_argument("--voice-level", choices=("off", "calibrated"), default="off")
|
|
81
|
+
speak.add_argument("--gain-db", type=float, help="explicit static gain override")
|
|
82
|
+
|
|
83
|
+
subparsers.add_parser(
|
|
84
|
+
"info", help="show API contract and runtime provenance without loading a model"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
discover = subparsers.add_parser("discover", help="list ONNXVoice Sopro bundle metadata")
|
|
88
|
+
discover.add_argument(
|
|
89
|
+
"--offline", action="store_true", help="use catalog data available offline"
|
|
90
|
+
)
|
|
91
|
+
discover.add_argument(
|
|
92
|
+
"--refresh", action="store_true", help="refresh ONNXVoice catalog metadata"
|
|
93
|
+
)
|
|
94
|
+
discover.add_argument("--cache-dir", type=Path)
|
|
95
|
+
|
|
96
|
+
inspect = subparsers.add_parser(
|
|
97
|
+
"inspect",
|
|
98
|
+
help="open a local ONNXVoice session and print diagnostics; does not synthesize",
|
|
99
|
+
)
|
|
100
|
+
inspect.add_argument("directory", type=Path)
|
|
101
|
+
inspect.add_argument("--profile", default="wasm-uint8")
|
|
102
|
+
return parser
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _speak(args: argparse.Namespace, parser: argparse.ArgumentParser) -> int:
|
|
106
|
+
if args.gain_db is not None and args.voice_level != "calibrated":
|
|
107
|
+
parser.error("--gain-db requires --voice-level calibrated")
|
|
108
|
+
config = GenerationConfig(
|
|
109
|
+
temperature=args.temperature,
|
|
110
|
+
top_p=args.top_p,
|
|
111
|
+
top_k=args.top_k,
|
|
112
|
+
max_seconds=args.max_seconds,
|
|
113
|
+
min_seconds=args.min_seconds,
|
|
114
|
+
)
|
|
115
|
+
voice_level = VoiceLevelConfig(mode=args.voice_level, gain_db=args.gain_db)
|
|
116
|
+
output_config = SoproOutputConfig(mode=args.output_mode)
|
|
117
|
+
with SoproRuntime.from_pretrained(
|
|
118
|
+
model=args.model,
|
|
119
|
+
profile=args.profile,
|
|
120
|
+
providers=args.provider,
|
|
121
|
+
) as runtime:
|
|
122
|
+
result = runtime.synthesize_text(
|
|
123
|
+
args.text,
|
|
124
|
+
id="cli-speak",
|
|
125
|
+
voice=args.ref,
|
|
126
|
+
language=args.language,
|
|
127
|
+
config=config,
|
|
128
|
+
voice_level=voice_level,
|
|
129
|
+
output=output_config,
|
|
130
|
+
)
|
|
131
|
+
output_path = result.save_wav(args.out)
|
|
132
|
+
print(output_path)
|
|
133
|
+
_print_json(
|
|
134
|
+
{
|
|
135
|
+
"sopro_output": result.metadata.get("sopro_output"),
|
|
136
|
+
"voice_level_application": result.metadata.get("voice_level_application"),
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
return 0
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _discover(args: argparse.Namespace) -> int:
|
|
143
|
+
bundles = discover_bundles(
|
|
144
|
+
offline=args.offline,
|
|
145
|
+
refresh=args.refresh,
|
|
146
|
+
cache_dir=args.cache_dir,
|
|
147
|
+
)
|
|
148
|
+
_print_json(bundles)
|
|
149
|
+
return 0
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _inspect(args: argparse.Namespace) -> int:
|
|
153
|
+
print("Developer inspection only; this command does not run synthesis.")
|
|
154
|
+
with SoproRuntime.from_onnx_local(args.directory, profile=args.profile) as runtime:
|
|
155
|
+
_print_json(runtime.diagnostics)
|
|
156
|
+
return 0
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
160
|
+
parser = _build_parser()
|
|
161
|
+
args = parser.parse_args(argv)
|
|
162
|
+
if args.command == "speak":
|
|
163
|
+
return _speak(args, parser)
|
|
164
|
+
if args.command == "info":
|
|
165
|
+
_print_json(
|
|
166
|
+
{"version": __version__, "api": _contract_summary(), "runtime": runtime_identity()}
|
|
167
|
+
)
|
|
168
|
+
return 0
|
|
169
|
+
if args.command == "discover":
|
|
170
|
+
return _discover(args)
|
|
171
|
+
if args.command == "inspect":
|
|
172
|
+
return _inspect(args)
|
|
173
|
+
parser.error(f"unknown command: {args.command}")
|
|
174
|
+
return 2
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == "__main__":
|
|
178
|
+
raise SystemExit(main())
|