chatterbox-light 1.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.
- chatterbox_light-1.0.1/LICENSE +21 -0
- chatterbox_light-1.0.1/PKG-INFO +141 -0
- chatterbox_light-1.0.1/README.md +109 -0
- chatterbox_light-1.0.1/pyproject.toml +48 -0
- chatterbox_light-1.0.1/setup.cfg +4 -0
- chatterbox_light-1.0.1/src/chatterbox/__init__.py +11 -0
- chatterbox_light-1.0.1/src/chatterbox/models/__init__.py +0 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/__init__.py +2 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/configs.py +10 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/const.py +2 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/decoder.py +292 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/f0_predictor.py +42 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/flow.py +163 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/flow_matching.py +140 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/hifigan.py +350 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/matcha/conformer_block.py +249 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/matcha/decoder.py +412 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/matcha/flow_matching.py +76 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/matcha/transformer.py +235 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/s3gen.py +293 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/transformer/__init__.py +0 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/transformer/activation.py +35 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/transformer/attention.py +153 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/transformer/convolution.py +97 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/transformer/embedding.py +170 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/transformer/encoder_layer.py +135 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/transformer/positionwise_feed_forward.py +56 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/transformer/subsampling.py +209 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/transformer/upsample_encoder.py +219 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/utils/class_utils.py +57 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/utils/intmeanflow.py +33 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/utils/mask.py +75 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/utils/mel.py +76 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3gen/xvector.py +399 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3tokenizer/__init__.py +30 -0
- chatterbox_light-1.0.1/src/chatterbox/models/s3tokenizer/s3tokenizer.py +140 -0
- chatterbox_light-1.0.1/src/chatterbox/models/t3/__init__.py +1 -0
- chatterbox_light-1.0.1/src/chatterbox/models/t3/inference/t3_hf_backend.py +86 -0
- chatterbox_light-1.0.1/src/chatterbox/models/t3/llama_configs.py +106 -0
- chatterbox_light-1.0.1/src/chatterbox/models/t3/modules/cond_enc.py +80 -0
- chatterbox_light-1.0.1/src/chatterbox/models/t3/modules/learned_pos_emb.py +22 -0
- chatterbox_light-1.0.1/src/chatterbox/models/t3/modules/perceiver.py +169 -0
- chatterbox_light-1.0.1/src/chatterbox/models/t3/modules/t3_config.py +39 -0
- chatterbox_light-1.0.1/src/chatterbox/models/t3/t3.py +468 -0
- chatterbox_light-1.0.1/src/chatterbox/models/tokenizers/__init__.py +1 -0
- chatterbox_light-1.0.1/src/chatterbox/models/tokenizers/tokenizer.py +286 -0
- chatterbox_light-1.0.1/src/chatterbox/models/utils.py +4 -0
- chatterbox_light-1.0.1/src/chatterbox/models/voice_encoder/__init__.py +1 -0
- chatterbox_light-1.0.1/src/chatterbox/models/voice_encoder/config.py +18 -0
- chatterbox_light-1.0.1/src/chatterbox/models/voice_encoder/melspec.py +84 -0
- chatterbox_light-1.0.1/src/chatterbox/models/voice_encoder/voice_encoder.py +222 -0
- chatterbox_light-1.0.1/src/chatterbox/mtl_tts.py +331 -0
- chatterbox_light-1.0.1/src/chatterbox/tts.py +252 -0
- chatterbox_light-1.0.1/src/chatterbox/tts_turbo.py +283 -0
- chatterbox_light-1.0.1/src/chatterbox/vc.py +111 -0
- chatterbox_light-1.0.1/src/chatterbox_light.egg-info/PKG-INFO +141 -0
- chatterbox_light-1.0.1/src/chatterbox_light.egg-info/SOURCES.txt +58 -0
- chatterbox_light-1.0.1/src/chatterbox_light.egg-info/dependency_links.txt +1 -0
- chatterbox_light-1.0.1/src/chatterbox_light.egg-info/requires.txt +18 -0
- chatterbox_light-1.0.1/src/chatterbox_light.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Resemble AI
|
|
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,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chatterbox-light
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Lightweight fork of Resemble AI's Chatterbox text-to-speech and voice conversion, with fewer dependencies and no watermarking
|
|
5
|
+
Author: Resemble AI
|
|
6
|
+
Maintainer: Blair Chintella
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/BBC-Esq/chatterbox-light
|
|
9
|
+
Project-URL: Repository, https://github.com/BBC-Esq/chatterbox-light
|
|
10
|
+
Project-URL: Upstream, https://github.com/resemble-ai/chatterbox
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: numpy
|
|
15
|
+
Requires-Dist: torch
|
|
16
|
+
Requires-Dist: torchaudio
|
|
17
|
+
Requires-Dist: transformers>=4.46.0
|
|
18
|
+
Requires-Dist: diffusers
|
|
19
|
+
Requires-Dist: safetensors
|
|
20
|
+
Requires-Dist: s3tokenizer
|
|
21
|
+
Requires-Dist: einops
|
|
22
|
+
Requires-Dist: scipy
|
|
23
|
+
Requires-Dist: pyloudnorm
|
|
24
|
+
Requires-Dist: soundfile
|
|
25
|
+
Requires-Dist: tokenizers
|
|
26
|
+
Requires-Dist: tqdm
|
|
27
|
+
Requires-Dist: huggingface-hub
|
|
28
|
+
Provides-Extra: multilingual
|
|
29
|
+
Requires-Dist: pykakasi; extra == "multilingual"
|
|
30
|
+
Requires-Dist: spacy-pkuseg; extra == "multilingual"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
## This is a light version of chatterbox that removes:
|
|
34
|
+
* ```librosa``` and all its required dependencies and instead uses ```torchaudio``` and ```scipy```
|
|
35
|
+
* ```perth``` because who the heck needs watermarking anyways
|
|
36
|
+
* ```omegaconf``` and instead use standard python
|
|
37
|
+
* others I forget, but everything works
|
|
38
|
+
|
|
39
|
+
However, I did add ```soundfile``` because I like it.
|
|
40
|
+
|
|
41
|
+
# Installation
|
|
42
|
+
>Go through these steps in order.
|
|
43
|
+
```
|
|
44
|
+
python -m venv .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
.\Scripts\activate
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
python.exe -m pip install --upgrade pip
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
pip install uv
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Next, make sure appropriate versions of torch, torchaudio, and CUDA are installed.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
uv pip install chatterbox-light
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
To install the latest code from GitHub instead:
|
|
66
|
+
```
|
|
67
|
+
uv pip install -r requirements.txt
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
pip install git+https://github.com/BBC-Esq/chatterbox-light.git --no-deps
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This package installs the `chatterbox` module, just like Resemble AI's `chatterbox-tts`, so don't install both in one environment. Installs of this fork made from GitHub before version 1.0.1 are also named `chatterbox-tts`; run `pip uninstall chatterbox-tts` before installing this.
|
|
75
|
+
|
|
76
|
+
Optional: if you'll use the multilingual model with Japanese or Chinese text, also install these (or install `"chatterbox-light[multilingual]"`). Without them, Japanese kanji are dropped and Chinese text isn't split into words. The first time Chinese text is used, spacy-pkuseg downloads a ~35 MB model to `~/.pkuseg`.
|
|
77
|
+
```
|
|
78
|
+
uv pip install pykakasi spacy-pkuseg
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
# Usage
|
|
82
|
+
Each model's `generate()` returns a `(1, samples)` tensor at `model.sr` (24 kHz). Models download from Hugging Face the first time they're used. Save the audio with `soundfile`:
|
|
83
|
+
```python
|
|
84
|
+
import soundfile as sf
|
|
85
|
+
sf.write("output.wav", wav.squeeze(0).numpy(), model.sr)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Chatterbox (English)
|
|
89
|
+
```python
|
|
90
|
+
from chatterbox.tts import ChatterboxTTS
|
|
91
|
+
|
|
92
|
+
model = ChatterboxTTS.from_pretrained(device="cuda") # or "cpu" / "mps"
|
|
93
|
+
wav = model.generate("Hello there, this is Chatterbox.")
|
|
94
|
+
|
|
95
|
+
# clone a voice from a reference clip
|
|
96
|
+
wav = model.generate("Hello there, this is Chatterbox.", audio_prompt_path="reference.wav")
|
|
97
|
+
|
|
98
|
+
# more expressive: raise exaggeration (default 0.5) and lower cfg_weight (default 0.5) for slower pacing
|
|
99
|
+
wav = model.generate("Hello there, this is Chatterbox.", exaggeration=0.7, cfg_weight=0.3)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Chatterbox Turbo (English)
|
|
103
|
+
Faster, and understands paralinguistic tags such as `[laugh]`, `[chuckle]` and `[cough]`. A reference clip must be longer than 5 seconds. `cfg_weight`, `exaggeration` and `min_p` are ignored.
|
|
104
|
+
```python
|
|
105
|
+
from chatterbox.tts_turbo import ChatterboxTurboTTS
|
|
106
|
+
|
|
107
|
+
model = ChatterboxTurboTTS.from_pretrained(device="cuda")
|
|
108
|
+
wav = model.generate("Hi there [chuckle], have you got a minute to chat?", audio_prompt_path="reference.wav")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Chatterbox Nano (English)
|
|
112
|
+
A smaller version of Turbo for CPU and tight memory budgets, loaded through the same class with `nano=True`:
|
|
113
|
+
```python
|
|
114
|
+
from chatterbox.tts_turbo import ChatterboxTurboTTS
|
|
115
|
+
|
|
116
|
+
model = ChatterboxTurboTTS.from_pretrained(device="cpu", nano=True)
|
|
117
|
+
wav = model.generate("Hi there [chuckle], have you got a minute to chat?")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Chatterbox Multilingual (23 languages)
|
|
121
|
+
The v2 checkpoint loads by default. Pass `t3_model="v3"` for the newer v3 checkpoint, which upstream recommends (fewer hallucinations, better speaker similarity). For Japanese or Chinese text, install the optional packages above.
|
|
122
|
+
```python
|
|
123
|
+
from chatterbox.mtl_tts import ChatterboxMultilingualTTS, SUPPORTED_LANGUAGES
|
|
124
|
+
|
|
125
|
+
print(SUPPORTED_LANGUAGES) # language codes such as "fr", "de", "ja", "zh"
|
|
126
|
+
model = ChatterboxMultilingualTTS.from_pretrained(device="cuda", t3_model="v3")
|
|
127
|
+
wav = model.generate("Bonjour, comment ça va ?", language_id="fr")
|
|
128
|
+
wav = model.generate("Hola, ¿cómo estás?", language_id="es", audio_prompt_path="reference.wav")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Voice conversion
|
|
132
|
+
```python
|
|
133
|
+
from chatterbox.vc import ChatterboxVC
|
|
134
|
+
|
|
135
|
+
model = ChatterboxVC.from_pretrained(device="cuda")
|
|
136
|
+
wav = model.generate("input.wav", target_voice_path="target_voice.wav")
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
See the `example_*.py` scripts for complete runs.
|
|
140
|
+
|
|
141
|
+
Enjoy!
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
## This is a light version of chatterbox that removes:
|
|
2
|
+
* ```librosa``` and all its required dependencies and instead uses ```torchaudio``` and ```scipy```
|
|
3
|
+
* ```perth``` because who the heck needs watermarking anyways
|
|
4
|
+
* ```omegaconf``` and instead use standard python
|
|
5
|
+
* others I forget, but everything works
|
|
6
|
+
|
|
7
|
+
However, I did add ```soundfile``` because I like it.
|
|
8
|
+
|
|
9
|
+
# Installation
|
|
10
|
+
>Go through these steps in order.
|
|
11
|
+
```
|
|
12
|
+
python -m venv .
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
.\Scripts\activate
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
python.exe -m pip install --upgrade pip
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
pip install uv
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Next, make sure appropriate versions of torch, torchaudio, and CUDA are installed.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
uv pip install chatterbox-light
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
To install the latest code from GitHub instead:
|
|
34
|
+
```
|
|
35
|
+
uv pip install -r requirements.txt
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
pip install git+https://github.com/BBC-Esq/chatterbox-light.git --no-deps
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This package installs the `chatterbox` module, just like Resemble AI's `chatterbox-tts`, so don't install both in one environment. Installs of this fork made from GitHub before version 1.0.1 are also named `chatterbox-tts`; run `pip uninstall chatterbox-tts` before installing this.
|
|
43
|
+
|
|
44
|
+
Optional: if you'll use the multilingual model with Japanese or Chinese text, also install these (or install `"chatterbox-light[multilingual]"`). Without them, Japanese kanji are dropped and Chinese text isn't split into words. The first time Chinese text is used, spacy-pkuseg downloads a ~35 MB model to `~/.pkuseg`.
|
|
45
|
+
```
|
|
46
|
+
uv pip install pykakasi spacy-pkuseg
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
# Usage
|
|
50
|
+
Each model's `generate()` returns a `(1, samples)` tensor at `model.sr` (24 kHz). Models download from Hugging Face the first time they're used. Save the audio with `soundfile`:
|
|
51
|
+
```python
|
|
52
|
+
import soundfile as sf
|
|
53
|
+
sf.write("output.wav", wav.squeeze(0).numpy(), model.sr)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Chatterbox (English)
|
|
57
|
+
```python
|
|
58
|
+
from chatterbox.tts import ChatterboxTTS
|
|
59
|
+
|
|
60
|
+
model = ChatterboxTTS.from_pretrained(device="cuda") # or "cpu" / "mps"
|
|
61
|
+
wav = model.generate("Hello there, this is Chatterbox.")
|
|
62
|
+
|
|
63
|
+
# clone a voice from a reference clip
|
|
64
|
+
wav = model.generate("Hello there, this is Chatterbox.", audio_prompt_path="reference.wav")
|
|
65
|
+
|
|
66
|
+
# more expressive: raise exaggeration (default 0.5) and lower cfg_weight (default 0.5) for slower pacing
|
|
67
|
+
wav = model.generate("Hello there, this is Chatterbox.", exaggeration=0.7, cfg_weight=0.3)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Chatterbox Turbo (English)
|
|
71
|
+
Faster, and understands paralinguistic tags such as `[laugh]`, `[chuckle]` and `[cough]`. A reference clip must be longer than 5 seconds. `cfg_weight`, `exaggeration` and `min_p` are ignored.
|
|
72
|
+
```python
|
|
73
|
+
from chatterbox.tts_turbo import ChatterboxTurboTTS
|
|
74
|
+
|
|
75
|
+
model = ChatterboxTurboTTS.from_pretrained(device="cuda")
|
|
76
|
+
wav = model.generate("Hi there [chuckle], have you got a minute to chat?", audio_prompt_path="reference.wav")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Chatterbox Nano (English)
|
|
80
|
+
A smaller version of Turbo for CPU and tight memory budgets, loaded through the same class with `nano=True`:
|
|
81
|
+
```python
|
|
82
|
+
from chatterbox.tts_turbo import ChatterboxTurboTTS
|
|
83
|
+
|
|
84
|
+
model = ChatterboxTurboTTS.from_pretrained(device="cpu", nano=True)
|
|
85
|
+
wav = model.generate("Hi there [chuckle], have you got a minute to chat?")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Chatterbox Multilingual (23 languages)
|
|
89
|
+
The v2 checkpoint loads by default. Pass `t3_model="v3"` for the newer v3 checkpoint, which upstream recommends (fewer hallucinations, better speaker similarity). For Japanese or Chinese text, install the optional packages above.
|
|
90
|
+
```python
|
|
91
|
+
from chatterbox.mtl_tts import ChatterboxMultilingualTTS, SUPPORTED_LANGUAGES
|
|
92
|
+
|
|
93
|
+
print(SUPPORTED_LANGUAGES) # language codes such as "fr", "de", "ja", "zh"
|
|
94
|
+
model = ChatterboxMultilingualTTS.from_pretrained(device="cuda", t3_model="v3")
|
|
95
|
+
wav = model.generate("Bonjour, comment ça va ?", language_id="fr")
|
|
96
|
+
wav = model.generate("Hola, ¿cómo estás?", language_id="es", audio_prompt_path="reference.wav")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Voice conversion
|
|
100
|
+
```python
|
|
101
|
+
from chatterbox.vc import ChatterboxVC
|
|
102
|
+
|
|
103
|
+
model = ChatterboxVC.from_pretrained(device="cuda")
|
|
104
|
+
wav = model.generate("input.wav", target_voice_path="target_voice.wav")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
See the `example_*.py` scripts for complete runs.
|
|
108
|
+
|
|
109
|
+
Enjoy!
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "chatterbox-light"
|
|
3
|
+
version = "1.0.1"
|
|
4
|
+
description = "Lightweight fork of Resemble AI's Chatterbox text-to-speech and voice conversion, with fewer dependencies and no watermarking"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [
|
|
10
|
+
{name = "Resemble AI"},
|
|
11
|
+
]
|
|
12
|
+
maintainers = [
|
|
13
|
+
{name = "Blair Chintella"},
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"numpy",
|
|
17
|
+
"torch",
|
|
18
|
+
"torchaudio",
|
|
19
|
+
"transformers>=4.46.0",
|
|
20
|
+
"diffusers",
|
|
21
|
+
"safetensors",
|
|
22
|
+
"s3tokenizer",
|
|
23
|
+
"einops",
|
|
24
|
+
"scipy",
|
|
25
|
+
"pyloudnorm",
|
|
26
|
+
"soundfile",
|
|
27
|
+
"tokenizers",
|
|
28
|
+
"tqdm",
|
|
29
|
+
"huggingface-hub",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
multilingual = [
|
|
34
|
+
"pykakasi",
|
|
35
|
+
"spacy-pkuseg",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/BBC-Esq/chatterbox-light"
|
|
40
|
+
Repository = "https://github.com/BBC-Esq/chatterbox-light"
|
|
41
|
+
Upstream = "https://github.com/resemble-ai/chatterbox"
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["setuptools>=77.0.0"]
|
|
45
|
+
build-backend = "setuptools.build_meta"
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.packages.find]
|
|
48
|
+
where = ["src"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from importlib.metadata import version
|
|
3
|
+
except ImportError:
|
|
4
|
+
from importlib_metadata import version # For Python <3.8
|
|
5
|
+
|
|
6
|
+
__version__ = version("chatterbox-light")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from .tts import ChatterboxTTS
|
|
10
|
+
from .vc import ChatterboxVC
|
|
11
|
+
from .mtl_tts import ChatterboxMultilingualTTS, SUPPORTED_LANGUAGES
|
|
File without changes
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
from einops import pack, rearrange, repeat
|
|
5
|
+
|
|
6
|
+
from .utils.mask import add_optional_chunk_mask
|
|
7
|
+
from .matcha.decoder import SinusoidalPosEmb, Block1D, ResnetBlock1D, Downsample1D, \
|
|
8
|
+
TimestepEmbedding, Upsample1D
|
|
9
|
+
from .matcha.transformer import BasicTransformerBlock
|
|
10
|
+
from .utils.intmeanflow import get_intmeanflow_time_mixer
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def mask_to_bias(mask: torch.Tensor, dtype: torch.dtype) -> torch.Tensor:
|
|
14
|
+
assert mask.dtype == torch.bool
|
|
15
|
+
assert dtype in [torch.float32, torch.bfloat16, torch.float16]
|
|
16
|
+
mask = mask.to(dtype)
|
|
17
|
+
mask = (1.0 - mask) * -1.0e+10
|
|
18
|
+
return mask
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Transpose(torch.nn.Module):
|
|
23
|
+
def __init__(self, dim0: int, dim1: int):
|
|
24
|
+
super().__init__()
|
|
25
|
+
self.dim0 = dim0
|
|
26
|
+
self.dim1 = dim1
|
|
27
|
+
|
|
28
|
+
def forward(self, x: torch.Tensor):
|
|
29
|
+
x = torch.transpose(x, self.dim0, self.dim1)
|
|
30
|
+
return x
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CausalBlock1D(Block1D):
|
|
34
|
+
def __init__(self, dim: int, dim_out: int):
|
|
35
|
+
super(CausalBlock1D, self).__init__(dim, dim_out)
|
|
36
|
+
self.block = torch.nn.Sequential(
|
|
37
|
+
CausalConv1d(dim, dim_out, 3),
|
|
38
|
+
Transpose(1, 2),
|
|
39
|
+
nn.LayerNorm(dim_out),
|
|
40
|
+
Transpose(1, 2),
|
|
41
|
+
nn.Mish(),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def forward(self, x: torch.Tensor, mask: torch.Tensor):
|
|
45
|
+
output = self.block(x * mask)
|
|
46
|
+
return output * mask
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class CausalResnetBlock1D(ResnetBlock1D):
|
|
50
|
+
def __init__(self, dim: int, dim_out: int, time_emb_dim: int, groups: int = 8):
|
|
51
|
+
super(CausalResnetBlock1D, self).__init__(dim, dim_out, time_emb_dim, groups)
|
|
52
|
+
self.block1 = CausalBlock1D(dim, dim_out)
|
|
53
|
+
self.block2 = CausalBlock1D(dim_out, dim_out)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class CausalConv1d(torch.nn.Conv1d):
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
in_channels: int,
|
|
60
|
+
out_channels: int,
|
|
61
|
+
kernel_size: int,
|
|
62
|
+
stride: int = 1,
|
|
63
|
+
dilation: int = 1,
|
|
64
|
+
groups: int = 1,
|
|
65
|
+
bias: bool = True,
|
|
66
|
+
padding_mode: str = 'zeros',
|
|
67
|
+
device=None,
|
|
68
|
+
dtype=None
|
|
69
|
+
) -> None:
|
|
70
|
+
super(CausalConv1d, self).__init__(in_channels, out_channels,
|
|
71
|
+
kernel_size, stride,
|
|
72
|
+
padding=0, dilation=dilation,
|
|
73
|
+
groups=groups, bias=bias,
|
|
74
|
+
padding_mode=padding_mode,
|
|
75
|
+
device=device, dtype=dtype)
|
|
76
|
+
assert stride == 1
|
|
77
|
+
self.causal_padding = (kernel_size - 1, 0)
|
|
78
|
+
|
|
79
|
+
def forward(self, x: torch.Tensor):
|
|
80
|
+
x = F.pad(x, self.causal_padding)
|
|
81
|
+
x = super(CausalConv1d, self).forward(x)
|
|
82
|
+
return x
|
|
83
|
+
class ConditionalDecoder(nn.Module):
|
|
84
|
+
def __init__(
|
|
85
|
+
self,
|
|
86
|
+
in_channels=320,
|
|
87
|
+
out_channels=80,
|
|
88
|
+
causal=True,
|
|
89
|
+
channels=[256],
|
|
90
|
+
dropout=0.0,
|
|
91
|
+
attention_head_dim=64,
|
|
92
|
+
n_blocks=4,
|
|
93
|
+
num_mid_blocks=12,
|
|
94
|
+
num_heads=8,
|
|
95
|
+
act_fn="gelu",
|
|
96
|
+
meanflow=False,
|
|
97
|
+
):
|
|
98
|
+
super().__init__()
|
|
99
|
+
channels = tuple(channels)
|
|
100
|
+
self.meanflow = meanflow
|
|
101
|
+
self.in_channels = in_channels
|
|
102
|
+
self.out_channels = out_channels
|
|
103
|
+
self.causal = causal
|
|
104
|
+
self.time_embeddings = SinusoidalPosEmb(in_channels)
|
|
105
|
+
time_embed_dim = channels[0] * 4
|
|
106
|
+
self.time_mlp = TimestepEmbedding(
|
|
107
|
+
in_channels=in_channels,
|
|
108
|
+
time_embed_dim=time_embed_dim,
|
|
109
|
+
act_fn="silu",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
self.down_blocks = nn.ModuleList([])
|
|
113
|
+
self.mid_blocks = nn.ModuleList([])
|
|
114
|
+
self.up_blocks = nn.ModuleList([])
|
|
115
|
+
|
|
116
|
+
self.static_chunk_size = 0
|
|
117
|
+
|
|
118
|
+
output_channel = in_channels
|
|
119
|
+
for i in range(len(channels)):
|
|
120
|
+
input_channel = output_channel
|
|
121
|
+
output_channel = channels[i]
|
|
122
|
+
is_last = i == len(channels) - 1
|
|
123
|
+
resnet = CausalResnetBlock1D(dim=input_channel, dim_out=output_channel, time_emb_dim=time_embed_dim) if self.causal else \
|
|
124
|
+
ResnetBlock1D(dim=input_channel, dim_out=output_channel, time_emb_dim=time_embed_dim)
|
|
125
|
+
transformer_blocks = nn.ModuleList(
|
|
126
|
+
[
|
|
127
|
+
BasicTransformerBlock(
|
|
128
|
+
dim=output_channel,
|
|
129
|
+
num_attention_heads=num_heads,
|
|
130
|
+
attention_head_dim=attention_head_dim,
|
|
131
|
+
dropout=dropout,
|
|
132
|
+
activation_fn=act_fn,
|
|
133
|
+
)
|
|
134
|
+
for _ in range(n_blocks)
|
|
135
|
+
]
|
|
136
|
+
)
|
|
137
|
+
downsample = (
|
|
138
|
+
Downsample1D(output_channel) if not is_last else
|
|
139
|
+
CausalConv1d(output_channel, output_channel, 3) if self.causal else nn.Conv1d(output_channel, output_channel, 3, padding=1)
|
|
140
|
+
)
|
|
141
|
+
self.down_blocks.append(nn.ModuleList([resnet, transformer_blocks, downsample]))
|
|
142
|
+
|
|
143
|
+
for _ in range(num_mid_blocks):
|
|
144
|
+
input_channel = channels[-1]
|
|
145
|
+
out_channels = channels[-1]
|
|
146
|
+
resnet = CausalResnetBlock1D(dim=input_channel, dim_out=output_channel, time_emb_dim=time_embed_dim) if self.causal else \
|
|
147
|
+
ResnetBlock1D(dim=input_channel, dim_out=output_channel, time_emb_dim=time_embed_dim)
|
|
148
|
+
|
|
149
|
+
transformer_blocks = nn.ModuleList(
|
|
150
|
+
[
|
|
151
|
+
BasicTransformerBlock(
|
|
152
|
+
dim=output_channel,
|
|
153
|
+
num_attention_heads=num_heads,
|
|
154
|
+
attention_head_dim=attention_head_dim,
|
|
155
|
+
dropout=dropout,
|
|
156
|
+
activation_fn=act_fn,
|
|
157
|
+
)
|
|
158
|
+
for _ in range(n_blocks)
|
|
159
|
+
]
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
self.mid_blocks.append(nn.ModuleList([resnet, transformer_blocks]))
|
|
163
|
+
|
|
164
|
+
channels = channels[::-1] + (channels[0],)
|
|
165
|
+
for i in range(len(channels) - 1):
|
|
166
|
+
input_channel = channels[i] * 2
|
|
167
|
+
output_channel = channels[i + 1]
|
|
168
|
+
is_last = i == len(channels) - 2
|
|
169
|
+
resnet = CausalResnetBlock1D(
|
|
170
|
+
dim=input_channel,
|
|
171
|
+
dim_out=output_channel,
|
|
172
|
+
time_emb_dim=time_embed_dim,
|
|
173
|
+
) if self.causal else ResnetBlock1D(
|
|
174
|
+
dim=input_channel,
|
|
175
|
+
dim_out=output_channel,
|
|
176
|
+
time_emb_dim=time_embed_dim,
|
|
177
|
+
)
|
|
178
|
+
transformer_blocks = nn.ModuleList(
|
|
179
|
+
[
|
|
180
|
+
BasicTransformerBlock(
|
|
181
|
+
dim=output_channel,
|
|
182
|
+
num_attention_heads=num_heads,
|
|
183
|
+
attention_head_dim=attention_head_dim,
|
|
184
|
+
dropout=dropout,
|
|
185
|
+
activation_fn=act_fn,
|
|
186
|
+
)
|
|
187
|
+
for _ in range(n_blocks)
|
|
188
|
+
]
|
|
189
|
+
)
|
|
190
|
+
upsample = (
|
|
191
|
+
Upsample1D(output_channel, use_conv_transpose=True)
|
|
192
|
+
if not is_last
|
|
193
|
+
else CausalConv1d(output_channel, output_channel, 3) if self.causal else nn.Conv1d(output_channel, output_channel, 3, padding=1)
|
|
194
|
+
)
|
|
195
|
+
self.up_blocks.append(nn.ModuleList([resnet, transformer_blocks, upsample]))
|
|
196
|
+
self.final_block = CausalBlock1D(channels[-1], channels[-1]) if self.causal else Block1D(channels[-1], channels[-1])
|
|
197
|
+
self.final_proj = nn.Conv1d(channels[-1], self.out_channels, 1)
|
|
198
|
+
self.initialize_weights()
|
|
199
|
+
self.time_embed_mixer = None
|
|
200
|
+
if self.meanflow:
|
|
201
|
+
self.time_embed_mixer = get_intmeanflow_time_mixer(time_embed_dim)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def dtype(self):
|
|
206
|
+
return self.final_proj.weight.dtype
|
|
207
|
+
|
|
208
|
+
def initialize_weights(self):
|
|
209
|
+
for m in self.modules():
|
|
210
|
+
if isinstance(m, nn.Conv1d):
|
|
211
|
+
nn.init.kaiming_normal_(m.weight, nonlinearity="relu")
|
|
212
|
+
if m.bias is not None:
|
|
213
|
+
nn.init.constant_(m.bias, 0)
|
|
214
|
+
elif isinstance(m, nn.GroupNorm):
|
|
215
|
+
nn.init.constant_(m.weight, 1)
|
|
216
|
+
nn.init.constant_(m.bias, 0)
|
|
217
|
+
elif isinstance(m, nn.Linear):
|
|
218
|
+
nn.init.kaiming_normal_(m.weight, nonlinearity="relu")
|
|
219
|
+
if m.bias is not None:
|
|
220
|
+
nn.init.constant_(m.bias, 0)
|
|
221
|
+
|
|
222
|
+
def forward(self, x, mask, mu, t, spks=None, cond=None, r=None):
|
|
223
|
+
t = self.time_embeddings(t).to(t.dtype)
|
|
224
|
+
t = self.time_mlp(t)
|
|
225
|
+
|
|
226
|
+
if self.meanflow:
|
|
227
|
+
r = self.time_embeddings(r).to(t.dtype)
|
|
228
|
+
r = self.time_mlp(r)
|
|
229
|
+
concat_embed = torch.cat([t, r], dim=1)
|
|
230
|
+
t = self.time_embed_mixer(concat_embed)
|
|
231
|
+
|
|
232
|
+
x = pack([x, mu], "b * t")[0]
|
|
233
|
+
|
|
234
|
+
if spks is not None:
|
|
235
|
+
spks = repeat(spks, "b c -> b c t", t=x.shape[-1])
|
|
236
|
+
x = pack([x, spks], "b * t")[0]
|
|
237
|
+
if cond is not None:
|
|
238
|
+
x = pack([x, cond], "b * t")[0]
|
|
239
|
+
|
|
240
|
+
hiddens = []
|
|
241
|
+
masks = [mask]
|
|
242
|
+
for resnet, transformer_blocks, downsample in self.down_blocks:
|
|
243
|
+
mask_down = masks[-1]
|
|
244
|
+
x = resnet(x, mask_down, t)
|
|
245
|
+
x = rearrange(x, "b c t -> b t c").contiguous()
|
|
246
|
+
attn_mask = add_optional_chunk_mask(x, mask_down.bool(), False, False, 0, self.static_chunk_size, -1)
|
|
247
|
+
attn_mask = mask_to_bias(attn_mask == 1, x.dtype)
|
|
248
|
+
for transformer_block in transformer_blocks:
|
|
249
|
+
x = transformer_block(
|
|
250
|
+
hidden_states=x,
|
|
251
|
+
attention_mask=attn_mask,
|
|
252
|
+
timestep=t,
|
|
253
|
+
)
|
|
254
|
+
x = rearrange(x, "b t c -> b c t").contiguous()
|
|
255
|
+
hiddens.append(x)
|
|
256
|
+
x = downsample(x * mask_down)
|
|
257
|
+
masks.append(mask_down[:, :, ::2])
|
|
258
|
+
masks = masks[:-1]
|
|
259
|
+
mask_mid = masks[-1]
|
|
260
|
+
|
|
261
|
+
for resnet, transformer_blocks in self.mid_blocks:
|
|
262
|
+
x = resnet(x, mask_mid, t)
|
|
263
|
+
x = rearrange(x, "b c t -> b t c").contiguous()
|
|
264
|
+
attn_mask = add_optional_chunk_mask(x, mask_mid.bool(), False, False, 0, self.static_chunk_size, -1)
|
|
265
|
+
attn_mask = mask_to_bias(attn_mask == 1, x.dtype)
|
|
266
|
+
for transformer_block in transformer_blocks:
|
|
267
|
+
x = transformer_block(
|
|
268
|
+
hidden_states=x,
|
|
269
|
+
attention_mask=attn_mask,
|
|
270
|
+
timestep=t,
|
|
271
|
+
)
|
|
272
|
+
x = rearrange(x, "b t c -> b c t").contiguous()
|
|
273
|
+
|
|
274
|
+
for resnet, transformer_blocks, upsample in self.up_blocks:
|
|
275
|
+
mask_up = masks.pop()
|
|
276
|
+
skip = hiddens.pop()
|
|
277
|
+
x = pack([x[:, :, :skip.shape[-1]], skip], "b * t")[0]
|
|
278
|
+
x = resnet(x, mask_up, t)
|
|
279
|
+
x = rearrange(x, "b c t -> b t c").contiguous()
|
|
280
|
+
attn_mask = add_optional_chunk_mask(x, mask_up.bool(), False, False, 0, self.static_chunk_size, -1)
|
|
281
|
+
attn_mask = mask_to_bias(attn_mask == 1, x.dtype)
|
|
282
|
+
for transformer_block in transformer_blocks:
|
|
283
|
+
x = transformer_block(
|
|
284
|
+
hidden_states=x,
|
|
285
|
+
attention_mask=attn_mask,
|
|
286
|
+
timestep=t,
|
|
287
|
+
)
|
|
288
|
+
x = rearrange(x, "b t c -> b c t").contiguous()
|
|
289
|
+
x = upsample(x * mask_up)
|
|
290
|
+
x = self.final_block(x, mask_up)
|
|
291
|
+
output = self.final_proj(x * mask_up)
|
|
292
|
+
return output * mask
|