superclonica-cpu 0.6.25__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.
Files changed (62) hide show
  1. superclonica/__init__.py +5 -0
  2. superclonica/__main__.py +59 -0
  3. superclonica/_supertonic/LICENSE +21 -0
  4. superclonica/_supertonic/__init__.py +3 -0
  5. superclonica/_supertonic/config.py +247 -0
  6. superclonica/_supertonic/core.py +531 -0
  7. superclonica/_supertonic/loader.py +462 -0
  8. superclonica/_supertonic/pipeline.py +415 -0
  9. superclonica/_supertonic/utils.py +217 -0
  10. superclonica/assets/confirmed_runtime_heads.onnx +0 -0
  11. superclonica/assets/confirmed_runtime_parent.onnx +0 -0
  12. superclonica/assets/confirmed_runtime_window.npz +0 -0
  13. superclonica/assets/confirmed_v15.npz +0 -0
  14. superclonica/assets/confirmed_v16.npz +0 -0
  15. superclonica/assets/confirmed_v21.json +46 -0
  16. superclonica/assets/confirmed_v21.npz +0 -0
  17. superclonica/assets/edge_denoiser_multivoice_v5.npz +0 -0
  18. superclonica/assets/fade_spectral_v8.npz +0 -0
  19. superclonica/assets/prefix_noise_v1.npz +0 -0
  20. superclonica/audio_io.py +18 -0
  21. superclonica/batching.py +137 -0
  22. superclonica/chunker.py +203 -0
  23. superclonica/confirmed/__init__.py +1 -0
  24. superclonica/confirmed/boundary.py +43 -0
  25. superclonica/confirmed/effects.py +83 -0
  26. superclonica/confirmed/ending_effect_v17.py +56 -0
  27. superclonica/confirmed/ending_effect_v18.py +63 -0
  28. superclonica/confirmed/ending_effect_v18b.py +11 -0
  29. superclonica/confirmed/ending_effect_v19.py +49 -0
  30. superclonica/confirmed/learned_repair_v15.py +124 -0
  31. superclonica/confirmed/onnx_zero_ending.py +118 -0
  32. superclonica/confirmed/terminal_declick_v16.py +65 -0
  33. superclonica/cpu_graph.py +305 -0
  34. superclonica/cuda_graph.py +439 -0
  35. superclonica/denoise.py +181 -0
  36. superclonica/devices.py +278 -0
  37. superclonica/duration_guard.py +37 -0
  38. superclonica/edge_denoiser.py +258 -0
  39. superclonica/ending.py +1558 -0
  40. superclonica/ending_effect_gate.py +140 -0
  41. superclonica/ending_reverb.py +92 -0
  42. superclonica/engine.py +140 -0
  43. superclonica/fade_selector.py +47 -0
  44. superclonica/finisher.py +96 -0
  45. superclonica/lanes.py +282 -0
  46. superclonica/loudness.py +139 -0
  47. superclonica/model_store.py +79 -0
  48. superclonica/prefix_denoiser.py +109 -0
  49. superclonica/resources.py +11 -0
  50. superclonica/slang.py +53 -0
  51. superclonica/spectral_fade.py +156 -0
  52. superclonica/synthesis.py +387 -0
  53. superclonica/text.py +41 -0
  54. superclonica_cpu-0.6.25.dist-info/METADATA +243 -0
  55. superclonica_cpu-0.6.25.dist-info/RECORD +62 -0
  56. superclonica_cpu-0.6.25.dist-info/WHEEL +5 -0
  57. superclonica_cpu-0.6.25.dist-info/entry_points.txt +2 -0
  58. superclonica_cpu-0.6.25.dist-info/licenses/LICENSE.txt +201 -0
  59. superclonica_cpu-0.6.25.dist-info/licenses/NOTICE.txt +29 -0
  60. superclonica_cpu-0.6.25.dist-info/licenses/SUPERTONIC_CODE_LICENSE.txt +21 -0
  61. superclonica_cpu-0.6.25.dist-info/licenses/SUPERTONIC_MODEL_LICENSE.txt +209 -0
  62. superclonica_cpu-0.6.25.dist-info/top_level.txt +1 -0
@@ -0,0 +1,5 @@
1
+ """Superclonica: Supertonic 3 speech with chat-ready text cleanup, learned finishing and fast inference."""
2
+ from .engine import Superclonica
3
+
4
+ __all__ = ["Superclonica", "__version__"]
5
+ __version__ = "0.6.25"
@@ -0,0 +1,59 @@
1
+ """Command line: superclonica --text "Hello." --voice F3 --lang en --output hello.wav"""
2
+ import argparse
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ def main(argv=None):
8
+ from . import __version__
9
+ p = argparse.ArgumentParser(prog='superclonica', description='Superclonica text to speech (Supertonic 3)')
10
+ p.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
11
+ source = p.add_mutually_exclusive_group(required=True)
12
+ source.add_argument('--text')
13
+ source.add_argument('--text-file', type=Path)
14
+ p.add_argument('--output', type=Path, required=True)
15
+ p.add_argument('--voice', default='M1', help='M1..M5, F1..F5 or a voice-style JSON file')
16
+ p.add_argument('--lang', default='en')
17
+ p.add_argument('--seed', type=int)
18
+ p.add_argument('--model-dir', type=Path, help='an existing Supertonic 3 copy (default: download once)')
19
+ p.add_argument('--steps', type=int, help='model steps (default 8)')
20
+ p.add_argument('--speed', type=float, default=1.05)
21
+ p.add_argument('--device', choices=['auto', 'cpu', 'cuda'], default='auto')
22
+ p.add_argument('--device-id', type=int, default=0)
23
+ p.add_argument('--threads', type=int, help='CPU threads (default: one per physical core)')
24
+ p.add_argument('--lanes', type=int, default=1, help='parallel model copies for the pieces of a long text')
25
+ p.add_argument('--gpu-memory-mb', type=int, default=1024, help='CUDA arena limit per model session')
26
+ p.add_argument('--cuda-graph', choices=('auto', 'on', 'off'), default='auto',
27
+ help='auto = when there is GPU memory and a start-up check shows they are faster here')
28
+ p.add_argument('--effect', choices=['auto', 'off'], default='auto', help='ending effects (cleanup always runs)')
29
+ p.add_argument('--normalize', action='store_true', help='loudness -18 LUFS')
30
+ p.add_argument('--no-denoise', action='store_true', help='keep the model hiss and pause residue')
31
+ p.add_argument('--no-chunking', action='store_true')
32
+ p.add_argument('--no-numbers', action='store_true', help='do not spell digits as words')
33
+ p.add_argument('--no-slang', action='store_true', help='do not write out chat shorthand ("гг вп", "pls", "xD")')
34
+ p.add_argument('--keep-silence', action='store_true', help="keep the model's silence before and after the phrase")
35
+ p.add_argument('--report', type=Path, help='write a JSON report of the synthesis')
36
+ a = p.parse_args(argv)
37
+ if a.threads is not None and a.threads < 1:
38
+ p.error('--threads must be positive')
39
+ if not 1 <= a.lanes <= 8:
40
+ p.error('--lanes must be from 1 to 8')
41
+ text = a.text if a.text is not None else a.text_file.read_text('utf-8-sig')
42
+ from . import Superclonica
43
+ tts = Superclonica(model_dir=a.model_dir, total_steps=a.steps, speed=a.speed, device=a.device,
44
+ device_id=a.device_id, threads=a.threads, lanes=a.lanes, gpu_memory_mb=a.gpu_memory_mb,
45
+ cuda_graph={'auto': 'auto', 'on': True, 'off': False}[a.cuda_graph],
46
+ chunking=not a.no_chunking, numbers=not a.no_numbers, denoise=not a.no_denoise,
47
+ trim_silence=not a.keep_silence, slang=not a.no_slang)
48
+ wave, sr, report = tts.synthesize(text, a.voice, a.lang, seed=a.seed, effect=a.effect,
49
+ normalize=a.normalize, return_report=True)
50
+ a.output.parent.mkdir(parents=True, exist_ok=True)
51
+ tts.save(a.output, wave)
52
+ if a.report:
53
+ a.report.parent.mkdir(parents=True, exist_ok=True)
54
+ a.report.write_text(json.dumps(report, ensure_ascii=False, indent=2, default=str), 'utf-8')
55
+ print(str(a.output))
56
+
57
+
58
+ if __name__ == '__main__':
59
+ main()
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Supertone Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ from .pipeline import TTS
2
+ from .core import Style, UnicodeProcessor
3
+ from .config import *
@@ -0,0 +1,247 @@
1
+ """Configuration and constants for Supertonic TTS package.
2
+
3
+ This module centralizes all configuration values, magic numbers, and default
4
+ settings used throughout the package.
5
+ """
6
+
7
+ import logging
8
+ import os
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # Available models
15
+ AVAILABLE_MODELS = ["supertonic", "supertonic-2", "supertonic-3"]
16
+ DEFAULT_MODEL = "supertonic-3"
17
+
18
+ # Model configuration mapping.
19
+ #
20
+ # `revision` pins the HuggingFace Hub commit each Supertonic release was
21
+ # tested against. Pinning by SHA (instead of `"main"`) keeps a `pip install`
22
+ # of a given package version reproducible even if the HF repo later
23
+ # changes. To upgrade the model, bump the SHA here and ship a new package
24
+ # release. Power users can still override with the
25
+ # `SUPERTONIC_MODEL_REVISION` environment variable.
26
+ MODEL_CONFIGS = {
27
+ "supertonic": {
28
+ "repo": "Supertone/supertonic",
29
+ "cache_dir": "supertonic",
30
+ "multilingual": False,
31
+ "revision": "b6856d033f622c63ea29441795be266a1133e227",
32
+ },
33
+ "supertonic-2": {
34
+ "repo": "Supertone/supertonic-2",
35
+ "cache_dir": "supertonic2",
36
+ "multilingual": True,
37
+ "revision": "75e6727618a02f323c720cba9478152d4bc16ca4",
38
+ },
39
+ "supertonic-3": {
40
+ "repo": "Supertone/supertonic-3",
41
+ "cache_dir": "supertonic3",
42
+ "multilingual": True,
43
+ "revision": "724fb5abbf5502583fb520898d45929e62f02c0b",
44
+ },
45
+ }
46
+
47
+ # Default model settings (can be overridden by environment variables)
48
+ DEFAULT_MODEL_REPO = os.getenv("SUPERTONIC_MODEL_REPO", MODEL_CONFIGS[DEFAULT_MODEL]["repo"])
49
+ # ``SUPERTONIC_CACHE_DIR`` is intentionally not snapshotted to a module-level
50
+ # constant here — it is consulted at call time inside
51
+ # :func:`get_model_cache_dir` so the override is honored on every code path
52
+ # (notably ``TTS(model="…")``, the default and most common entry) and
53
+ # late-set env vars (test fixtures, late configuration) take effect.
54
+ # Optional revision override. When unset (the common case), each model is
55
+ # downloaded at its pinned SHA from `MODEL_CONFIGS`. When set, the override
56
+ # applies to every model — primarily a debugging / development knob.
57
+ MODEL_REVISION_ENV_OVERRIDE = os.getenv("SUPERTONIC_MODEL_REVISION")
58
+
59
+
60
+ def get_model_config(model_name: str) -> dict:
61
+ """Get configuration for a specific model.
62
+
63
+ Args:
64
+ model_name: Model name (one of ``AVAILABLE_MODELS``)
65
+
66
+ Returns:
67
+ Dictionary with model configuration (repo, cache_dir, multilingual)
68
+
69
+ Raises:
70
+ ValueError: If model_name is not valid
71
+ """
72
+ if model_name not in MODEL_CONFIGS:
73
+ raise ValueError(
74
+ f"Invalid model: '{model_name}'. " f"Available models: {', '.join(AVAILABLE_MODELS)}"
75
+ )
76
+ return MODEL_CONFIGS[model_name]
77
+
78
+
79
+ def get_model_cache_dir(model_name: str) -> Path:
80
+ """Get cache directory for a specific model.
81
+
82
+ ``$SUPERTONIC_CACHE_DIR`` overrides the directory for *every* model when
83
+ set — matching the documented contract and the historical
84
+ ``DEFAULT_CACHE_DIR`` spelling. Users running two models from the same
85
+ process and wanting them separated should leave the env var unset (each
86
+ model then falls back to its own ``~/.cache/<cache_dir>``).
87
+
88
+ Args:
89
+ model_name: Model name (one of ``AVAILABLE_MODELS``)
90
+
91
+ Returns:
92
+ Path to the model's cache directory
93
+ """
94
+ env = os.environ.get("SUPERTONIC_CACHE_DIR")
95
+ if env:
96
+ return Path(env).expanduser()
97
+ config = get_model_config(model_name)
98
+ return Path.home() / ".cache" / config["cache_dir"]
99
+
100
+
101
+ def get_model_repo(model_name: str) -> str:
102
+ """Get HuggingFace repo ID for a specific model.
103
+
104
+ Args:
105
+ model_name: Model name (one of ``AVAILABLE_MODELS``)
106
+
107
+ Returns:
108
+ HuggingFace repository ID
109
+ """
110
+ config = get_model_config(model_name)
111
+ return config["repo"]
112
+
113
+
114
+ def get_model_revision(model_name: str) -> str:
115
+ """Get the HuggingFace Hub revision to download for a model.
116
+
117
+ Priority: ``SUPERTONIC_MODEL_REVISION`` env var (if set) → the
118
+ per-model pinned SHA in ``MODEL_CONFIGS``. Pinning by SHA keeps a
119
+ given package version reproducible across HF Hub updates.
120
+
121
+ Args:
122
+ model_name: Model name (one of ``AVAILABLE_MODELS``)
123
+
124
+ Returns:
125
+ Commit SHA (or whatever ref the env override points at).
126
+ """
127
+ if MODEL_REVISION_ENV_OVERRIDE:
128
+ return MODEL_REVISION_ENV_OVERRIDE
129
+ return get_model_config(model_name)["revision"]
130
+
131
+
132
+ def is_multilingual_model(model_name: str) -> bool:
133
+ """Check if a model supports multilingual synthesis.
134
+
135
+ Args:
136
+ model_name: Model name (one of ``AVAILABLE_MODELS``)
137
+
138
+ Returns:
139
+ True if model supports multiple languages
140
+ """
141
+ config = get_model_config(model_name)
142
+ return config["multilingual"]
143
+
144
+
145
+ # Model paths
146
+ ONNX_DIR = Path("onnx")
147
+ VOICE_STYLES_DIR = Path("voice_styles")
148
+
149
+ CFG_REL_PATH = ONNX_DIR / "tts.json"
150
+ UNICODE_INDEXER_REL_PATH = ONNX_DIR / "unicode_indexer.json"
151
+ DP_ONNX_REL_PATH = ONNX_DIR / "duration_predictor.onnx"
152
+ TEXT_ENC_ONNX_REL_PATH = ONNX_DIR / "text_encoder.onnx"
153
+ VECTOR_EST_ONNX_REL_PATH = ONNX_DIR / "vector_estimator.onnx"
154
+ VOCODER_ONNX_REL_PATH = ONNX_DIR / "vocoder.onnx"
155
+
156
+ # Language configuration (multilingual support)
157
+ # supertonic-3 supports 31 languages plus the special "na" fallback for unknown.
158
+ # supertonic-2 supports a subset (en, ko, es, pt, fr) — language validation
159
+ # only enforces membership in this list; mismatches between a model and a
160
+ # language outside its training set are not blocked here.
161
+ SUPPORTED_LANGUAGES = [
162
+ "en", # English
163
+ "ko", # Korean
164
+ "ja", # Japanese
165
+ "ar", # Arabic
166
+ "bg", # Bulgarian
167
+ "cs", # Czech
168
+ "da", # Danish
169
+ "de", # German
170
+ "el", # Greek
171
+ "es", # Spanish
172
+ "et", # Estonian
173
+ "fi", # Finnish
174
+ "fr", # French
175
+ "hi", # Hindi
176
+ "hr", # Croatian
177
+ "hu", # Hungarian
178
+ "id", # Indonesian
179
+ "it", # Italian
180
+ "lt", # Lithuanian
181
+ "lv", # Latvian
182
+ "nl", # Dutch
183
+ "pl", # Polish
184
+ "pt", # Portuguese
185
+ "ro", # Romanian
186
+ "ru", # Russian
187
+ "sk", # Slovak
188
+ "sl", # Slovenian
189
+ "sv", # Swedish
190
+ "tr", # Turkish
191
+ "uk", # Ukrainian
192
+ "vi", # Vietnamese
193
+ ]
194
+ # Special fallback code for unknown / unsupported language.
195
+ # Wraps text with the <na>...</na> token so the model can still synthesize.
196
+ UNKNOWN_LANGUAGE = "na"
197
+ AVAILABLE_LANGUAGES = SUPPORTED_LANGUAGES + [UNKNOWN_LANGUAGE]
198
+ DEFAULT_LANGUAGE = "en"
199
+
200
+ # TTS parameters - defaults
201
+ DEFAULT_TOTAL_STEPS = 8
202
+ DEFAULT_SPEED = 1.05
203
+ DEFAULT_MAX_CHUNK_LENGTH = 300
204
+ DEFAULT_MAX_CHUNK_LENGTH_KO = 120 # Korean requires shorter chunks
205
+ DEFAULT_SILENCE_DURATION = 0.3 # seconds
206
+
207
+ # TTS parameters - constraints
208
+ MIN_SPEED = 0.7
209
+ MAX_SPEED = 2.0
210
+ MIN_TOTAL_STEPS = 1
211
+ MAX_TOTAL_STEPS = 100
212
+
213
+ # ONNX Runtime configuration
214
+ # TODO: Add parsing of SUPERTONIC_ONNX_PROVIDERS environment variable
215
+ DEFAULT_ONNX_PROVIDERS = ["CPUExecutionProvider"] # GPU support can be added by extending this list
216
+
217
+
218
+ def _parse_env_int(env_var: str, default: Optional[int] = None) -> Optional[int]:
219
+ """Parse integer from environment variable with validation.
220
+
221
+ Args:
222
+ env_var: Environment variable name
223
+ default: Default value if not set or invalid
224
+
225
+ Returns:
226
+ Parsed integer or default value
227
+ """
228
+ value = os.getenv(env_var)
229
+ if value is None:
230
+ return default
231
+ try:
232
+ return int(value)
233
+ except ValueError:
234
+ logger.warning(f"Invalid value for {env_var}: '{value}'. Using default: {default}")
235
+ return default
236
+
237
+
238
+ # Thread configuration - None means let ONNX Runtime decide automatically
239
+ DEFAULT_INTRA_OP_NUM_THREADS = _parse_env_int("SUPERTONIC_INTRA_OP_THREADS")
240
+ DEFAULT_INTER_OP_NUM_THREADS = _parse_env_int("SUPERTONIC_INTER_OP_THREADS")
241
+
242
+ # Text processing
243
+ MAX_TEXT_LENGTH = 100_000 # Maximum characters per single synthesis call
244
+
245
+ # Logging configuration
246
+ LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
247
+ LOG_LEVEL = os.getenv("SUPERTONIC_LOG_LEVEL", "INFO")