faster-tts 0.0.1__tar.gz
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.
- faster_tts-0.0.1/LICENSE +21 -0
- faster_tts-0.0.1/PKG-INFO +199 -0
- faster_tts-0.0.1/README.md +173 -0
- faster_tts-0.0.1/faster_tts/__init__.py +9 -0
- faster_tts-0.0.1/faster_tts/__main__.py +121 -0
- faster_tts-0.0.1/faster_tts/base.py +82 -0
- faster_tts-0.0.1/faster_tts/cache.py +123 -0
- faster_tts-0.0.1/faster_tts/chunking.py +252 -0
- faster_tts-0.0.1/faster_tts/engines/__init__.py +1 -0
- faster_tts-0.0.1/faster_tts/engines/drama3.py +435 -0
- faster_tts-0.0.1/faster_tts/memory.py +147 -0
- faster_tts-0.0.1/faster_tts/model.py +285 -0
- faster_tts-0.0.1/faster_tts/prompt.py +256 -0
- faster_tts-0.0.1/faster_tts.egg-info/PKG-INFO +199 -0
- faster_tts-0.0.1/faster_tts.egg-info/SOURCES.txt +26 -0
- faster_tts-0.0.1/faster_tts.egg-info/dependency_links.txt +1 -0
- faster_tts-0.0.1/faster_tts.egg-info/entry_points.txt +2 -0
- faster_tts-0.0.1/faster_tts.egg-info/requires.txt +7 -0
- faster_tts-0.0.1/faster_tts.egg-info/top_level.txt +1 -0
- faster_tts-0.0.1/pyproject.toml +49 -0
- faster_tts-0.0.1/setup.cfg +4 -0
- faster_tts-0.0.1/tests/test_cache.py +178 -0
- faster_tts-0.0.1/tests/test_chunking.py +213 -0
- faster_tts-0.0.1/tests/test_docs.py +58 -0
- faster_tts-0.0.1/tests/test_drama3_prompt.py +194 -0
- faster_tts-0.0.1/tests/test_drama3_python_api.py +1222 -0
- faster_tts-0.0.1/tests/test_memory.py +148 -0
- faster_tts-0.0.1/tests/test_model_dispatch.py +266 -0
faster_tts-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jordi Mas i Hernàndez
|
|
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,199 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: faster-tts
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Text-to-speech library on CPU and GPU using CTranslate2
|
|
5
|
+
Author-email: Jordi Mas <jmas@softcatala.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jordimas/faster_tts
|
|
8
|
+
Project-URL: Repository, https://github.com/jordimas/faster_tts
|
|
9
|
+
Project-URL: Documentation, https://github.com/jordimas/faster_tts/blob/main/docs/api.md
|
|
10
|
+
Project-URL: Issues, https://github.com/jordimas/faster_tts/issues
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: ctranslate2>=4.8.4
|
|
20
|
+
Requires-Dist: numpy>=1.23
|
|
21
|
+
Requires-Dist: tokenizers>=0.20
|
|
22
|
+
Requires-Dist: huggingface-hub>=0.24
|
|
23
|
+
Provides-Extra: docs
|
|
24
|
+
Requires-Dist: pre-commit>=4; extra == "docs"
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# faster_tts
|
|
28
|
+
|
|
29
|
+
Text-to-speech with **Drama 3 on CPU and GPU**, powered by CTranslate2.
|
|
30
|
+
Provides a Python library API and the `faster-tts` command-line interface (CLI).
|
|
31
|
+
Includes model downloading and caching, sentence splitting, speaker tags, and WAV output.
|
|
32
|
+
Requires CTranslate2 **4.8.4+**.
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
Python API:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from faster_tts import Model
|
|
40
|
+
|
|
41
|
+
model = Model()
|
|
42
|
+
result = model.synthesize(
|
|
43
|
+
"Hello! Welcome to our application.",
|
|
44
|
+
output="hello.wav", # Save the audio as a WAV file.
|
|
45
|
+
)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`Model()` downloads and caches
|
|
49
|
+
[`jordimas/drama-3-ct2`](https://huggingface.co/jordimas/drama-3-ct2).
|
|
50
|
+
Reuse it across calls.
|
|
51
|
+
|
|
52
|
+
CLI (also available as `python -m faster_tts`):
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
faster-tts --text "Hello!" --output hello.wav
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Generate emotion, dialogue, and multilingual audio with the
|
|
59
|
+
[sample scripts](https://github.com/jordimas/faster_tts/blob/dev/samples/README.md), or narrate a UTF-8 article:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
python samples/article_voice.py article.txt --output article.wav
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The article sample selects the device automatically. Use `--device cpu` or
|
|
66
|
+
`--device cuda` to override it. Its default audio budget is 120 seconds
|
|
67
|
+
(`--max-duration-s`); reaching it saves partial narration.
|
|
68
|
+
|
|
69
|
+
## Models and devices
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
model = Model("drama-3") # Default checkpoint
|
|
73
|
+
model = Model("jordimas/drama-3-ct2", revision="main", cache_dir="./models")
|
|
74
|
+
model = Model("/path/to/converted-model")
|
|
75
|
+
model = Model(local_files_only=True) # Cached assets only
|
|
76
|
+
|
|
77
|
+
model = Model(device="cuda", device_index=0, compute_type="float16")
|
|
78
|
+
model = Model(device="cpu", compute_type="int8")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The default `device="auto"` selects GPU when available, otherwise CPU.
|
|
82
|
+
CUDA and ROCm both use `"cuda"` with a matching CT2 build. Compute types depend
|
|
83
|
+
on the build; `"default"` lets CT2 choose. Inspect settings with `model.info()`.
|
|
84
|
+
Set `CT2_DRAMA_GPU_WAVEFORM=0` to decode waveforms on CPU.
|
|
85
|
+
|
|
86
|
+
The CLI accepts the same model and cache options. For local models, use explicit
|
|
87
|
+
paths such as `./model` to avoid interpreting missing directories as Hub IDs.
|
|
88
|
+
Custom checkpoints must match the engine's layout.
|
|
89
|
+
|
|
90
|
+
## Memory
|
|
91
|
+
|
|
92
|
+
GPU expert caches size automatically. Set a total budget in decimal GB:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
model = Model(
|
|
96
|
+
device="cuda", num_workers=2,
|
|
97
|
+
engine_options={"gpu_expert_cache_gb": 12},
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This gives each worker a 6 GB cache. Use `"auto"` or `"uncapped"` instead of a
|
|
102
|
+
number, or CLI `--gpu-expert-cache-gb`. Without an explicit budget,
|
|
103
|
+
`CT2_DRAMA3_GPU_EXPERT_CACHE_GB` sets the per-worker limit (`0` means uncapped).
|
|
104
|
+
Automatic sizing reserves at least 2 GB and 30% of free memory and fails if
|
|
105
|
+
memory cannot be queried or is insufficient. Cache budgets do not cap total VRAM.
|
|
106
|
+
|
|
107
|
+
When `device="auto"` selects CPU, large checkpoints can use expert memory mapping
|
|
108
|
+
on Linux/macOS with `compute_type="default"` or `"float32"`. Override with
|
|
109
|
+
`CT2_DRAMA3_CPU_MMAP=0` or `1`. Explicit `device="cpu"` leaves it off by default.
|
|
110
|
+
Keep the checkpoint unchanged while loaded; mapping does not cap RAM usage.
|
|
111
|
+
Both memory features require a supporting CT2 build.
|
|
112
|
+
|
|
113
|
+
## Synthesis options and results
|
|
114
|
+
|
|
115
|
+
Pass engine options through `engine_options`. Experimental `directed_spans`
|
|
116
|
+
splits explicit breathing events from speech:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
result = model.synthesize(
|
|
120
|
+
"Set the bag down. [breathing] We can continue now.",
|
|
121
|
+
engine_options={"directed_spans": True, "max_frames": 534},
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Speech spans are capped at 256 frames and breathing events at 22, within the
|
|
126
|
+
overall budget. Events use empty history and do not condition later speech.
|
|
127
|
+
Listen to check voice, prosody, and event quality. CLI: `--directed-spans`.
|
|
128
|
+
|
|
129
|
+
[`SynthesisResult`](https://github.com/jordimas/faster_tts/blob/dev/docs/api.md#synthesisresult) includes audio, sample rate, and
|
|
130
|
+
`finish_reason`. A `"length"` result may be truncated; the CLI saves it and exits
|
|
131
|
+
with status 1. `"eos"` does not guarantee complete speech.
|
|
132
|
+
Enable INFO logging for `faster_tts.engines.drama3` for device and segment details.
|
|
133
|
+
|
|
134
|
+
See the [API reference](https://github.com/jordimas/faster_tts/blob/dev/docs/api.md), `python -m faster_tts --help`, and
|
|
135
|
+
[CT2 Drama 3 guide](https://github.com/OpenNMT/CTranslate2/blob/master/docs/guides/drama3.md)
|
|
136
|
+
for options and conversion instructions.
|
|
137
|
+
|
|
138
|
+
## Benchmark
|
|
139
|
+
|
|
140
|
+
Drama 3 only. See [benchmark instructions](https://github.com/jordimas/faster_tts/blob/dev/benchmark/README.md) to run your own measurements.
|
|
141
|
+
|
|
142
|
+
<!-- current-benchmarks -->
|
|
143
|
+
### Benchmark results (2026-10-24)
|
|
144
|
+
|
|
145
|
+
#### CPU — AMD EPYC 7B13 (62 GiB RAM)
|
|
146
|
+
|
|
147
|
+
Test text: `Hello! Welcome to our application.`
|
|
148
|
+
|
|
149
|
+
Times in the CPU table are means. RTF is compute time divided by generated audio duration; lower is better, and 1 means real-time synthesis. Loading and warmup are excluded.
|
|
150
|
+
|
|
151
|
+
| Implementation | Precision | Time (s) | Audio duration (s) | RTF | Peak RAM (GiB RSS) | Status |
|
|
152
|
+
| --- | --- | ---: | ---: | ---: | ---: | --- |
|
|
153
|
+
| HF / PyTorch | bfloat16 | 300.9 | 2.8 | 106.2 | 44.5 | success |
|
|
154
|
+
| CT2 | bfloat16 | — | — | — | — | unsupported (no matching precision) |
|
|
155
|
+
| HF / PyTorch | float16 | 351.5 | 3.2 | 109.7 | 44.7 | success |
|
|
156
|
+
| CT2 | float16 | — | — | — | — | unsupported (no matching precision) |
|
|
157
|
+
| HF / PyTorch | int8 | — | — | — | — | unsupported (no matching precision) |
|
|
158
|
+
| CT2 | int8 | 18.4 | 2.4 | 7.6 | 44.7 | success |
|
|
159
|
+
| HF / PyTorch | int8_float32 | — | — | — | — | unsupported (no matching precision) |
|
|
160
|
+
| CT2 | int8_float32 | 17.9 | 2.4 | 7.4 | 44.7 | success |
|
|
161
|
+
| HF / PyTorch | float32 | 155.2 | 3.0 | 52.2 | 51.3 | success |
|
|
162
|
+
| CT2 | float32 | 37.7 | 2.5 | 15.0 | 45.5 | success |
|
|
163
|
+
|
|
164
|
+
<!-- gpu-benchmarks-ci40090377176 -->
|
|
165
|
+
#### GPU — NVIDIA Tesla T4 (15 GiB VRAM)
|
|
166
|
+
|
|
167
|
+
CT2 4.8.4, 8 CPU threads, one warmup and three timed runs per successful configuration.
|
|
168
|
+
Same test text as above. Times are means; peak RAM is process RSS and excludes VRAM.
|
|
169
|
+
Expert cache: auto (10.874 GB), except the successful float16 run (8 GB).
|
|
170
|
+
|
|
171
|
+
| Implementation | Precision | Time (s) | Audio duration (s) | RTF | Peak RAM (GiB RSS) | Status |
|
|
172
|
+
| --- | --- | ---: | ---: | ---: | ---: | --- |
|
|
173
|
+
| CT2 | float16 | — | — | — | — | CUDA OOM during warmup |
|
|
174
|
+
| CT2 | float32 | 34.5 | 2.5 | 13.8 | 34.5 | success |
|
|
175
|
+
| CT2 | int8 | 27.3 | 4.3 | 6.3 | 36.6 | success |
|
|
176
|
+
| CT2 | int8_float16 | 26.9 | 3.0 | 9.0 | 35.9 | success |
|
|
177
|
+
| CT2 | int8_float32 | 27.2 | 4.3 | 6.3 | 36.6 | success |
|
|
178
|
+
| CT2 | float16 | 37.9 | 3.7 | 10.3 | 36.0 | success |
|
|
179
|
+
<!-- /gpu-benchmarks-ci40090377176 -->
|
|
180
|
+
|
|
181
|
+
<!-- gpu-benchmarks-l4-ci40090377176 -->
|
|
182
|
+
#### GPU — NVIDIA L4
|
|
183
|
+
|
|
184
|
+
CT2 4.8.4, 8 CPU threads, a 12 GB expert cache, one warmup and one timed run per configuration.
|
|
185
|
+
Same test text as above. Loading and warmup are excluded; peak RAM is process RSS and excludes VRAM.
|
|
186
|
+
Single runs do not measure timing variability.
|
|
187
|
+
|
|
188
|
+
| Implementation | Precision | Time (s) | Audio duration (s) | RTF | Peak RAM (GiB RSS) | Status |
|
|
189
|
+
| --- | --- | ---: | ---: | ---: | ---: | --- |
|
|
190
|
+
| CT2 | bfloat16 | 26.5 | 2.8 | 9.5 | 35.5 | success |
|
|
191
|
+
| CT2 | float16 | 27.6 | 3.1 | 8.9 | 35.6 | success |
|
|
192
|
+
| CT2 | float32 | 29.4 | 2.5 | 11.7 | 34.6 | success |
|
|
193
|
+
| CT2 | int8 | 22.1 | 4.3 | 5.1 | 36.6 | success |
|
|
194
|
+
| CT2 | int8_bfloat16 | 21.4 | 2.8 | 7.6 | 35.1 | success |
|
|
195
|
+
| CT2 | int8_float16 | 22.8 | 3.0 | 7.7 | 35.9 | success |
|
|
196
|
+
| CT2 | int8_float32 | 22.1 | 4.3 | 5.1 | 36.7 | success |
|
|
197
|
+
<!-- /gpu-benchmarks-l4-ci40090377176 -->
|
|
198
|
+
|
|
199
|
+
Contact: Jordi Mas <jmas@softcatala.org>
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# faster_tts
|
|
2
|
+
|
|
3
|
+
Text-to-speech with **Drama 3 on CPU and GPU**, powered by CTranslate2.
|
|
4
|
+
Provides a Python library API and the `faster-tts` command-line interface (CLI).
|
|
5
|
+
Includes model downloading and caching, sentence splitting, speaker tags, and WAV output.
|
|
6
|
+
Requires CTranslate2 **4.8.4+**.
|
|
7
|
+
|
|
8
|
+
## Quick start
|
|
9
|
+
|
|
10
|
+
Python API:
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from faster_tts import Model
|
|
14
|
+
|
|
15
|
+
model = Model()
|
|
16
|
+
result = model.synthesize(
|
|
17
|
+
"Hello! Welcome to our application.",
|
|
18
|
+
output="hello.wav", # Save the audio as a WAV file.
|
|
19
|
+
)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`Model()` downloads and caches
|
|
23
|
+
[`jordimas/drama-3-ct2`](https://huggingface.co/jordimas/drama-3-ct2).
|
|
24
|
+
Reuse it across calls.
|
|
25
|
+
|
|
26
|
+
CLI (also available as `python -m faster_tts`):
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
faster-tts --text "Hello!" --output hello.wav
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Generate emotion, dialogue, and multilingual audio with the
|
|
33
|
+
[sample scripts](https://github.com/jordimas/faster_tts/blob/dev/samples/README.md), or narrate a UTF-8 article:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python samples/article_voice.py article.txt --output article.wav
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The article sample selects the device automatically. Use `--device cpu` or
|
|
40
|
+
`--device cuda` to override it. Its default audio budget is 120 seconds
|
|
41
|
+
(`--max-duration-s`); reaching it saves partial narration.
|
|
42
|
+
|
|
43
|
+
## Models and devices
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
model = Model("drama-3") # Default checkpoint
|
|
47
|
+
model = Model("jordimas/drama-3-ct2", revision="main", cache_dir="./models")
|
|
48
|
+
model = Model("/path/to/converted-model")
|
|
49
|
+
model = Model(local_files_only=True) # Cached assets only
|
|
50
|
+
|
|
51
|
+
model = Model(device="cuda", device_index=0, compute_type="float16")
|
|
52
|
+
model = Model(device="cpu", compute_type="int8")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The default `device="auto"` selects GPU when available, otherwise CPU.
|
|
56
|
+
CUDA and ROCm both use `"cuda"` with a matching CT2 build. Compute types depend
|
|
57
|
+
on the build; `"default"` lets CT2 choose. Inspect settings with `model.info()`.
|
|
58
|
+
Set `CT2_DRAMA_GPU_WAVEFORM=0` to decode waveforms on CPU.
|
|
59
|
+
|
|
60
|
+
The CLI accepts the same model and cache options. For local models, use explicit
|
|
61
|
+
paths such as `./model` to avoid interpreting missing directories as Hub IDs.
|
|
62
|
+
Custom checkpoints must match the engine's layout.
|
|
63
|
+
|
|
64
|
+
## Memory
|
|
65
|
+
|
|
66
|
+
GPU expert caches size automatically. Set a total budget in decimal GB:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
model = Model(
|
|
70
|
+
device="cuda", num_workers=2,
|
|
71
|
+
engine_options={"gpu_expert_cache_gb": 12},
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This gives each worker a 6 GB cache. Use `"auto"` or `"uncapped"` instead of a
|
|
76
|
+
number, or CLI `--gpu-expert-cache-gb`. Without an explicit budget,
|
|
77
|
+
`CT2_DRAMA3_GPU_EXPERT_CACHE_GB` sets the per-worker limit (`0` means uncapped).
|
|
78
|
+
Automatic sizing reserves at least 2 GB and 30% of free memory and fails if
|
|
79
|
+
memory cannot be queried or is insufficient. Cache budgets do not cap total VRAM.
|
|
80
|
+
|
|
81
|
+
When `device="auto"` selects CPU, large checkpoints can use expert memory mapping
|
|
82
|
+
on Linux/macOS with `compute_type="default"` or `"float32"`. Override with
|
|
83
|
+
`CT2_DRAMA3_CPU_MMAP=0` or `1`. Explicit `device="cpu"` leaves it off by default.
|
|
84
|
+
Keep the checkpoint unchanged while loaded; mapping does not cap RAM usage.
|
|
85
|
+
Both memory features require a supporting CT2 build.
|
|
86
|
+
|
|
87
|
+
## Synthesis options and results
|
|
88
|
+
|
|
89
|
+
Pass engine options through `engine_options`. Experimental `directed_spans`
|
|
90
|
+
splits explicit breathing events from speech:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
result = model.synthesize(
|
|
94
|
+
"Set the bag down. [breathing] We can continue now.",
|
|
95
|
+
engine_options={"directed_spans": True, "max_frames": 534},
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Speech spans are capped at 256 frames and breathing events at 22, within the
|
|
100
|
+
overall budget. Events use empty history and do not condition later speech.
|
|
101
|
+
Listen to check voice, prosody, and event quality. CLI: `--directed-spans`.
|
|
102
|
+
|
|
103
|
+
[`SynthesisResult`](https://github.com/jordimas/faster_tts/blob/dev/docs/api.md#synthesisresult) includes audio, sample rate, and
|
|
104
|
+
`finish_reason`. A `"length"` result may be truncated; the CLI saves it and exits
|
|
105
|
+
with status 1. `"eos"` does not guarantee complete speech.
|
|
106
|
+
Enable INFO logging for `faster_tts.engines.drama3` for device and segment details.
|
|
107
|
+
|
|
108
|
+
See the [API reference](https://github.com/jordimas/faster_tts/blob/dev/docs/api.md), `python -m faster_tts --help`, and
|
|
109
|
+
[CT2 Drama 3 guide](https://github.com/OpenNMT/CTranslate2/blob/master/docs/guides/drama3.md)
|
|
110
|
+
for options and conversion instructions.
|
|
111
|
+
|
|
112
|
+
## Benchmark
|
|
113
|
+
|
|
114
|
+
Drama 3 only. See [benchmark instructions](https://github.com/jordimas/faster_tts/blob/dev/benchmark/README.md) to run your own measurements.
|
|
115
|
+
|
|
116
|
+
<!-- current-benchmarks -->
|
|
117
|
+
### Benchmark results (2026-10-24)
|
|
118
|
+
|
|
119
|
+
#### CPU — AMD EPYC 7B13 (62 GiB RAM)
|
|
120
|
+
|
|
121
|
+
Test text: `Hello! Welcome to our application.`
|
|
122
|
+
|
|
123
|
+
Times in the CPU table are means. RTF is compute time divided by generated audio duration; lower is better, and 1 means real-time synthesis. Loading and warmup are excluded.
|
|
124
|
+
|
|
125
|
+
| Implementation | Precision | Time (s) | Audio duration (s) | RTF | Peak RAM (GiB RSS) | Status |
|
|
126
|
+
| --- | --- | ---: | ---: | ---: | ---: | --- |
|
|
127
|
+
| HF / PyTorch | bfloat16 | 300.9 | 2.8 | 106.2 | 44.5 | success |
|
|
128
|
+
| CT2 | bfloat16 | — | — | — | — | unsupported (no matching precision) |
|
|
129
|
+
| HF / PyTorch | float16 | 351.5 | 3.2 | 109.7 | 44.7 | success |
|
|
130
|
+
| CT2 | float16 | — | — | — | — | unsupported (no matching precision) |
|
|
131
|
+
| HF / PyTorch | int8 | — | — | — | — | unsupported (no matching precision) |
|
|
132
|
+
| CT2 | int8 | 18.4 | 2.4 | 7.6 | 44.7 | success |
|
|
133
|
+
| HF / PyTorch | int8_float32 | — | — | — | — | unsupported (no matching precision) |
|
|
134
|
+
| CT2 | int8_float32 | 17.9 | 2.4 | 7.4 | 44.7 | success |
|
|
135
|
+
| HF / PyTorch | float32 | 155.2 | 3.0 | 52.2 | 51.3 | success |
|
|
136
|
+
| CT2 | float32 | 37.7 | 2.5 | 15.0 | 45.5 | success |
|
|
137
|
+
|
|
138
|
+
<!-- gpu-benchmarks-ci40090377176 -->
|
|
139
|
+
#### GPU — NVIDIA Tesla T4 (15 GiB VRAM)
|
|
140
|
+
|
|
141
|
+
CT2 4.8.4, 8 CPU threads, one warmup and three timed runs per successful configuration.
|
|
142
|
+
Same test text as above. Times are means; peak RAM is process RSS and excludes VRAM.
|
|
143
|
+
Expert cache: auto (10.874 GB), except the successful float16 run (8 GB).
|
|
144
|
+
|
|
145
|
+
| Implementation | Precision | Time (s) | Audio duration (s) | RTF | Peak RAM (GiB RSS) | Status |
|
|
146
|
+
| --- | --- | ---: | ---: | ---: | ---: | --- |
|
|
147
|
+
| CT2 | float16 | — | — | — | — | CUDA OOM during warmup |
|
|
148
|
+
| CT2 | float32 | 34.5 | 2.5 | 13.8 | 34.5 | success |
|
|
149
|
+
| CT2 | int8 | 27.3 | 4.3 | 6.3 | 36.6 | success |
|
|
150
|
+
| CT2 | int8_float16 | 26.9 | 3.0 | 9.0 | 35.9 | success |
|
|
151
|
+
| CT2 | int8_float32 | 27.2 | 4.3 | 6.3 | 36.6 | success |
|
|
152
|
+
| CT2 | float16 | 37.9 | 3.7 | 10.3 | 36.0 | success |
|
|
153
|
+
<!-- /gpu-benchmarks-ci40090377176 -->
|
|
154
|
+
|
|
155
|
+
<!-- gpu-benchmarks-l4-ci40090377176 -->
|
|
156
|
+
#### GPU — NVIDIA L4
|
|
157
|
+
|
|
158
|
+
CT2 4.8.4, 8 CPU threads, a 12 GB expert cache, one warmup and one timed run per configuration.
|
|
159
|
+
Same test text as above. Loading and warmup are excluded; peak RAM is process RSS and excludes VRAM.
|
|
160
|
+
Single runs do not measure timing variability.
|
|
161
|
+
|
|
162
|
+
| Implementation | Precision | Time (s) | Audio duration (s) | RTF | Peak RAM (GiB RSS) | Status |
|
|
163
|
+
| --- | --- | ---: | ---: | ---: | ---: | --- |
|
|
164
|
+
| CT2 | bfloat16 | 26.5 | 2.8 | 9.5 | 35.5 | success |
|
|
165
|
+
| CT2 | float16 | 27.6 | 3.1 | 8.9 | 35.6 | success |
|
|
166
|
+
| CT2 | float32 | 29.4 | 2.5 | 11.7 | 34.6 | success |
|
|
167
|
+
| CT2 | int8 | 22.1 | 4.3 | 5.1 | 36.6 | success |
|
|
168
|
+
| CT2 | int8_bfloat16 | 21.4 | 2.8 | 7.6 | 35.1 | success |
|
|
169
|
+
| CT2 | int8_float16 | 22.8 | 3.0 | 7.7 | 35.9 | success |
|
|
170
|
+
| CT2 | int8_float32 | 22.1 | 4.3 | 5.1 | 36.7 | success |
|
|
171
|
+
<!-- /gpu-benchmarks-l4-ci40090377176 -->
|
|
172
|
+
|
|
173
|
+
Contact: Jordi Mas <jmas@softcatala.org>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""CTranslate2 text-to-speech through the public Model and SynthesisResult API.
|
|
2
|
+
|
|
3
|
+
See README.md for setup and examples. Engine-specific helpers are internal.
|
|
4
|
+
Importing the package does not download checkpoints or initialize a model.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .model import Model, SynthesisResult
|
|
8
|
+
|
|
9
|
+
__all__ = ["Model", "SynthesisResult"]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Speech synthesis: python -m faster_tts [MODEL] --text TEXT --output out.wav.
|
|
2
|
+
|
|
3
|
+
MODEL defaults to jordimas/drama-3-ct2. Accepts drama-3, a converted Hub repository
|
|
4
|
+
ID (optionally prefixed with hf://), or a local converted directory.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from .cache import DEFAULT_MODEL
|
|
12
|
+
from .model import Model, _save_wav
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _cache_budget(value):
|
|
16
|
+
if value in ("auto", "uncapped"):
|
|
17
|
+
return value
|
|
18
|
+
try:
|
|
19
|
+
from .memory import validate_cache_budget
|
|
20
|
+
|
|
21
|
+
result = float(value)
|
|
22
|
+
validate_cache_budget(result)
|
|
23
|
+
return result
|
|
24
|
+
except ValueError as exc:
|
|
25
|
+
raise argparse.ArgumentTypeError(
|
|
26
|
+
'expected "auto", "uncapped", or a finite positive GB budget'
|
|
27
|
+
) from exc
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main():
|
|
31
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"model",
|
|
34
|
+
nargs="?",
|
|
35
|
+
default=DEFAULT_MODEL,
|
|
36
|
+
help="model name, Hub repository ID, or local directory (default: %(default)s)",
|
|
37
|
+
)
|
|
38
|
+
parser.add_argument("--revision", help="Hub revision; prefer a pinned commit")
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"--cache-dir", type=Path, help="Hugging Face snapshot cache directory"
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--local-files-only",
|
|
44
|
+
action="store_true",
|
|
45
|
+
help="resolve Hub assets only from the local cache",
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument("--text", required=True)
|
|
48
|
+
parser.add_argument("--output", required=True, type=Path)
|
|
49
|
+
parser.add_argument("--max-duration-s", type=float, default=24.0)
|
|
50
|
+
parser.add_argument("--threads", type=int, default=8)
|
|
51
|
+
parser.add_argument("--device", choices=("cpu", "cuda", "auto"), default="cpu")
|
|
52
|
+
parser.add_argument("--device-index", type=int, default=0)
|
|
53
|
+
parser.add_argument("--compute-type", default="default")
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"--gpu-expert-cache-gb",
|
|
56
|
+
type=_cache_budget,
|
|
57
|
+
help='GPU expert cache: decimal GB, "auto", or "uncapped"; defaults to environment or auto',
|
|
58
|
+
)
|
|
59
|
+
parser.add_argument("--temperature", type=float, default=0.7)
|
|
60
|
+
parser.add_argument("--top-k", type=int, default=30)
|
|
61
|
+
parser.add_argument("--top-p", type=float, default=0.7)
|
|
62
|
+
parser.add_argument("--seed", type=int, default=0)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--no-split-sentences",
|
|
65
|
+
action="store_true",
|
|
66
|
+
help="diagnostic single-pass generation",
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"--greedy",
|
|
70
|
+
action="store_true",
|
|
71
|
+
help="diagnostic greedy decoding instead of sampling",
|
|
72
|
+
)
|
|
73
|
+
parser.add_argument(
|
|
74
|
+
"--directed-spans",
|
|
75
|
+
action="store_true",
|
|
76
|
+
help="opt-in Drama 3 cue/turn splitting and bounded breathing events",
|
|
77
|
+
)
|
|
78
|
+
args = parser.parse_args()
|
|
79
|
+
if args.directed_spans and args.no_split_sentences:
|
|
80
|
+
parser.error("--directed-spans requires sentence splitting")
|
|
81
|
+
engine_options = {}
|
|
82
|
+
if args.gpu_expert_cache_gb is not None:
|
|
83
|
+
engine_options["gpu_expert_cache_gb"] = args.gpu_expert_cache_gb
|
|
84
|
+
model = Model(
|
|
85
|
+
args.model,
|
|
86
|
+
intra_threads=args.threads,
|
|
87
|
+
compute_type=args.compute_type,
|
|
88
|
+
device=args.device,
|
|
89
|
+
device_index=args.device_index,
|
|
90
|
+
revision=args.revision,
|
|
91
|
+
cache_dir=args.cache_dir,
|
|
92
|
+
local_files_only=args.local_files_only,
|
|
93
|
+
engine_options=engine_options or None,
|
|
94
|
+
)
|
|
95
|
+
started = time.perf_counter()
|
|
96
|
+
result = model.synthesize(
|
|
97
|
+
args.text,
|
|
98
|
+
max_duration_s=args.max_duration_s,
|
|
99
|
+
temperature=args.temperature,
|
|
100
|
+
top_k=args.top_k,
|
|
101
|
+
top_p=args.top_p,
|
|
102
|
+
seed=args.seed,
|
|
103
|
+
greedy=args.greedy,
|
|
104
|
+
split_sentences=not args.no_split_sentences,
|
|
105
|
+
**({"engine_options": {"directed_spans": True}} if args.directed_spans else {}),
|
|
106
|
+
)
|
|
107
|
+
inference_seconds = time.perf_counter() - started
|
|
108
|
+
_save_wav(result, args.output)
|
|
109
|
+
print(
|
|
110
|
+
f"Saved {result.audio.size / result.sample_rate:.3f}s to {args.output}; "
|
|
111
|
+
f"finish_reason={result.finish_reason}; decoding={'greedy' if args.greedy else 'sampled'}"
|
|
112
|
+
)
|
|
113
|
+
print(f"Inference time (excluding model loading): {inference_seconds:.3f}s")
|
|
114
|
+
if result.finish_reason != "eos":
|
|
115
|
+
raise SystemExit(
|
|
116
|
+
"Incomplete synthesis: frame limit reached; the saved audio may be truncated."
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == "__main__":
|
|
121
|
+
main()
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Engine registration and shared helpers; engine-specific code lives in engines/."""
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from importlib import import_module
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
_ENGINE_REGISTRY: dict[str, type] = {}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def register_engine(model_type):
|
|
12
|
+
"""Decorator that registers a :class:`BaseModel` subclass by ``model_type``.
|
|
13
|
+
|
|
14
|
+
``model_type`` is matched against the ``model_type`` field of the converted
|
|
15
|
+
checkpoint's ``config.json``. Re-registering an existing key replaces the
|
|
16
|
+
previous binding so engines can be reloaded during tests.
|
|
17
|
+
"""
|
|
18
|
+
if not isinstance(model_type, str) or not model_type:
|
|
19
|
+
raise ValueError("model_type must be a nonempty string")
|
|
20
|
+
|
|
21
|
+
def decorator(cls):
|
|
22
|
+
_ENGINE_REGISTRY[model_type] = cls
|
|
23
|
+
cls.engine_model_type = model_type
|
|
24
|
+
return cls
|
|
25
|
+
|
|
26
|
+
return decorator
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_engine_class(model_type):
|
|
30
|
+
"""Look up the engine class previously registered for ``model_type``."""
|
|
31
|
+
import_module(".engines.drama3", __package__)
|
|
32
|
+
if not isinstance(model_type, str):
|
|
33
|
+
raise ValueError("Converted checkpoint must specify a string model_type") # noqa: TRY004 - invalid checkpoint data
|
|
34
|
+
try:
|
|
35
|
+
return _ENGINE_REGISTRY[model_type]
|
|
36
|
+
except KeyError:
|
|
37
|
+
raise ValueError(
|
|
38
|
+
f"No CT2 TTS engine registered for model_type={model_type!r}; "
|
|
39
|
+
f"known engines: {sorted(_ENGINE_REGISTRY)}"
|
|
40
|
+
) from None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _positive_int(value, name):
|
|
44
|
+
if (
|
|
45
|
+
not isinstance(value, int)
|
|
46
|
+
or isinstance(value, bool)
|
|
47
|
+
or not 1 <= value <= np.iinfo(np.int32).max
|
|
48
|
+
):
|
|
49
|
+
raise ValueError(f"{name} must be a positive int32 integer")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class BaseModel:
|
|
53
|
+
"""Shared surface across CT2 TTS engines.
|
|
54
|
+
|
|
55
|
+
Subclasses set ``self._info`` in their constructor and implement
|
|
56
|
+
``validate_checkpoint(directory, config)`` and ``synthesize``. Constructors
|
|
57
|
+
receive a resolved directory, checkpoint metadata, and the public loader's
|
|
58
|
+
settings. Engine-specific knobs are declared in ``init_options`` and
|
|
59
|
+
``synthesize_options`` as frozensets of accepted keys; the dispatcher
|
|
60
|
+
validates ``engine_options`` against these and raises ``ValueError`` on
|
|
61
|
+
unknown keys. Return a SynthesisResult with engine-specific codec
|
|
62
|
+
dimensions. Declare supported features in ``capabilities``. The base class
|
|
63
|
+
adds engine identity and capabilities to an independent copy of ``_info``.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
engine_model_type: str
|
|
67
|
+
capabilities = ("synthesis",)
|
|
68
|
+
init_options: frozenset = frozenset()
|
|
69
|
+
synthesize_options: frozenset = frozenset()
|
|
70
|
+
|
|
71
|
+
def info(self):
|
|
72
|
+
"""Copy engine metadata and add its registered identity and capabilities.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
dict: Independent deep copy of `_info`, plus `engine` and
|
|
76
|
+
`capabilities`. Public fields are documented in `Model.info`.
|
|
77
|
+
"""
|
|
78
|
+
return dict(
|
|
79
|
+
deepcopy(self._info),
|
|
80
|
+
engine=self.engine_model_type,
|
|
81
|
+
capabilities=self.capabilities,
|
|
82
|
+
)
|