Abhaykoul commited on
Commit
54f8682
·
verified ·
1 Parent(s): 807087d

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ __pycache__/modeling.cpython-314.pyc filter=lfs diff=lfs merge=lfs -text
37
+ onnx/model.onnx.data filter=lfs diff=lfs merge=lfs -text
__pycache__/configuration.cpython-314.pyc ADDED
Binary file (3.66 kB). View file
 
__pycache__/inference.cpython-314.pyc ADDED
Binary file (19.2 kB). View file
 
__pycache__/inference_onnx.cpython-314.pyc ADDED
Binary file (11.7 kB). View file
 
__pycache__/inflect_frontend.cpython-314.pyc ADDED
Binary file (23.9 kB). View file
 
__pycache__/inflect_nano_v2_frontend.cpython-314.pyc ADDED
Binary file (23.9 kB). View file
 
__pycache__/inflect_vits_frontend.cpython-314.pyc ADDED
Binary file (3.77 kB). View file
 
__pycache__/lf_engine.cpython-314.pyc ADDED
Binary file (19.3 kB). View file
 
__pycache__/modeling.cpython-314.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d539f725fb695b8aa0aeb047a2a7b44ad55e712b7fd377b8575e0d11a90fdc51
3
+ size 118210
__pycache__/modeling_numpy.cpython-314.pyc ADDED
Binary file (7.58 kB). View file
 
__pycache__/train.cpython-314.pyc ADDED
Binary file (30.5 kB). View file
 
__pycache__/vtx_engine.cpython-314.pyc ADDED
Binary file (18.8 kB). View file
 
__pycache__/vtx_pure_numpy.cpython-314.pyc ADDED
Binary file (7.02 kB). View file
 
__pycache__/vtx_text.cpython-314.pyc ADDED
Binary file (2.93 kB). View file
 
config.json ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "format": "vtx-tts",
3
+ "train": {
4
+ "segment_size": 16384
5
+ },
6
+ "data": {
7
+ "text_cleaners": [],
8
+ "max_wav_value": 32768.0,
9
+ "sampling_rate": 24000,
10
+ "filter_length": 1024,
11
+ "hop_length": 256,
12
+ "win_length": 1024,
13
+ "n_mel_channels": 80,
14
+ "mel_fmin": 0.0,
15
+ "mel_fmax": 12000.0,
16
+ "add_blank": true,
17
+ "n_speakers": 0,
18
+ "cleaned_text": true
19
+ },
20
+ "model": {
21
+ "inter_channels": 192,
22
+ "hidden_channels": 96,
23
+ "filter_channels": 768,
24
+ "n_heads": 2,
25
+ "n_layers": 3,
26
+ "kernel_size": 3,
27
+ "p_dropout": 0.1,
28
+ "resblock": "1",
29
+ "resblock_kernel_sizes": [
30
+ 3,
31
+ 7,
32
+ 11
33
+ ],
34
+ "resblock_dilation_sizes": [
35
+ [
36
+ 1,
37
+ 3,
38
+ 5
39
+ ],
40
+ [
41
+ 1,
42
+ 3,
43
+ 5
44
+ ],
45
+ [
46
+ 1,
47
+ 3,
48
+ 5
49
+ ]
50
+ ],
51
+ "upsample_rates": [
52
+ 8,
53
+ 8,
54
+ 2,
55
+ 2
56
+ ],
57
+ "upsample_initial_channel": 320,
58
+ "upsample_kernel_sizes": [
59
+ 16,
60
+ 16,
61
+ 4,
62
+ 4
63
+ ],
64
+ "n_layers_q": 3,
65
+ "use_spectral_norm": false,
66
+ "use_sdp": false,
67
+ "inference_only": false
68
+ }
69
+ }
configuration.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """VTX-TTS Configuration & Text Processing System.
2
+
3
+ Defines VTXConfig and text symbol tokenization.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from typing import Any, Dict
11
+
12
+
13
+ # Symbol definitions for VTX-TTS phoneme tokenization
14
+ _pad = "_"
15
+ _punctuation = ";:,.!?¡¿—…\"«»“” "
16
+ _letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
17
+ _letters_ipa = "ɑɐɒæɓʙβɔɕçɗɖðʤəɘɚɛɜɝɞɟʄɡɠɢʛɦɧħɥʜɨɪʝɭɬɫɮʟɱɯɰŋɳɲɴøɵɸθœɶʘɹɺɾɻʀʁɽʂʃʈʧʉʊʋⱱʌɣɤʍχʎʏʑʐʒʔʡʕʢǀǁǂǃˈˌːˑʼʴʰʱʲʷˠˤ˞↓↑→↗↘'̩'ᵻ"
18
+
19
+ symbols = [_pad] + list(_punctuation) + list(_letters) + list(_letters_ipa)
20
+ _symbol_to_id = {s: i for i, s in enumerate(symbols)}
21
+
22
+
23
+ def cleaned_text_to_sequence(cleaned_text: str) -> list[int]:
24
+ """Converts a string of text to a sequence of IDs corresponding to the symbols in the text."""
25
+ sequence = []
26
+ for symbol in cleaned_text:
27
+ if symbol in _symbol_to_id:
28
+ sequence.append(_symbol_to_id[symbol])
29
+ return sequence
30
+
31
+
32
+ class HParams:
33
+ def __init__(self, **kwargs):
34
+ for k, v in kwargs.items():
35
+ if isinstance(v, dict):
36
+ v = HParams(**v)
37
+ self[k] = v
38
+
39
+ def __getitem__(self, key):
40
+ return getattr(self, key)
41
+
42
+ def __setitem__(self, key, value):
43
+ return setattr(self, key, value)
44
+
45
+ def __contains__(self, key):
46
+ return hasattr(self, key)
47
+
48
+ def __repr__(self):
49
+ return self.__dict__.__repr__()
50
+
51
+
52
+ def get_hparams_from_file(config_path: str | Path) -> HParams:
53
+ with open(config_path, "r", encoding="utf-8") as f:
54
+ data = f.read()
55
+ config = json.loads(data)
56
+ return HParams(**config)
inference.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import contextlib
5
+ import io
6
+ import logging
7
+ import re
8
+ import sys
9
+ import warnings
10
+ from pathlib import Path
11
+
12
+ import numpy as np
13
+ import torch
14
+ from safetensors.numpy import load_file
15
+
16
+ from configuration import cleaned_text_to_sequence, get_hparams_from_file, symbols
17
+ from modeling import SynthesizerTrn
18
+
19
+ PACKAGE_ROOT = Path(__file__).resolve().parent
20
+
21
+
22
+ def split_text(text: str, limit: int = 280) -> list[str]:
23
+ normalized = " ".join(text.split())
24
+ sentences = [
25
+ part.strip()
26
+ for part in re.split(r"(?<=[.!?;:])\s+", normalized)
27
+ if part.strip()
28
+ ]
29
+ chunks: list[str] = []
30
+ for sentence in sentences or [normalized]:
31
+ while len(sentence) > limit:
32
+ search = sentence[: limit + 1]
33
+ punctuation = max(search.rfind(mark) for mark in (",", ";", ":"))
34
+ split_at = (
35
+ punctuation + 1
36
+ if punctuation >= limit // 2
37
+ else sentence.rfind(" ", 0, limit + 1)
38
+ )
39
+ if split_at < limit // 2:
40
+ split_at = limit
41
+ chunks.append(sentence[:split_at].strip())
42
+ sentence = sentence[split_at:].strip()
43
+ if sentence:
44
+ chunks.append(sentence)
45
+ return chunks
46
+
47
+
48
+ def boundary_pause_seconds(chunk: str) -> float:
49
+ ending = chunk.rstrip()[-1:] if chunk.strip() else ""
50
+ return {
51
+ "?": 0.28,
52
+ "!": 0.24,
53
+ ".": 0.22,
54
+ ";": 0.16,
55
+ ":": 0.13,
56
+ ",": 0.09,
57
+ }.get(ending, 0.08)
58
+
59
+
60
+ def edge_fade(waveform: np.ndarray, sample_rate: int, milliseconds: float = 5.0) -> np.ndarray:
61
+ frames = min(round(sample_rate * milliseconds / 1000.0), waveform.size // 2)
62
+ if frames <= 0:
63
+ return waveform
64
+ output = waveform.copy()
65
+ ramp = np.linspace(0.0, 1.0, frames, endpoint=True, dtype=np.float32)
66
+ output[:frames] *= ramp
67
+ output[-frames:] *= ramp[::-1]
68
+ return output
69
+
70
+
71
+ def optimize_for_inference(model: SynthesizerTrn) -> None:
72
+ """Collapse weight normalization for VTXVocoder & VTXVectorEstimator."""
73
+ with contextlib.redirect_stdout(io.StringIO()):
74
+ model.VTXVocoder.remove_weight_norm()
75
+ for flow in model.VTXVectorEstimator.flows:
76
+ encoder = getattr(flow, "enc", None)
77
+ if encoder is not None and hasattr(encoder, "remove_weight_norm"):
78
+ encoder.remove_weight_norm()
79
+
80
+
81
+ class VtxTTS:
82
+ """VTX-TTS Engine using native 4-Bit model.safetensors."""
83
+
84
+ def __init__(self, model_dir: str | Path = PACKAGE_ROOT, device: str = "cpu") -> None:
85
+ self.root = Path(model_dir).resolve()
86
+ self.device = torch.device(device)
87
+ self.hps = get_hparams_from_file(str(self.root / "config.json"))
88
+ with warnings.catch_warnings():
89
+ warnings.filterwarnings(
90
+ "ignore",
91
+ message="`torch.nn.utils.weight_norm` is deprecated",
92
+ category=FutureWarning,
93
+ )
94
+ model_kwargs = self.hps.model.__dict__ if hasattr(self.hps.model, "__dict__") else self.hps.model
95
+ self.model = SynthesizerTrn(
96
+ len(symbols),
97
+ self.hps.data.filter_length // 2 + 1,
98
+ self.hps.train.segment_size // self.hps.data.hop_length,
99
+ **model_kwargs,
100
+ ).to(self.device).eval()
101
+
102
+ # Load native 4-bit safetensors
103
+ st_file = self.root / "model.safetensors"
104
+ st_weights = load_file(str(st_file))
105
+
106
+ state_dict = {}
107
+ for k, v in st_weights.items():
108
+ if k.endswith(".packed"):
109
+ base_k = k[:-7]
110
+ scales = st_weights[f"{base_k}.scales"]
111
+ zeros = st_weights[f"{base_k}.zeros"]
112
+ shape = tuple(st_weights[f"{base_k}.shape"])
113
+
114
+ # Dynamic 4-bit dequantization into model parameter buffer
115
+ low = (v & 0x0F).astype(np.float32)
116
+ high = ((v >> 4) & 0x0F).astype(np.float32)
117
+ unpacked = np.empty((v.size * 2,), dtype=np.float32)
118
+ unpacked[0::2] = low
119
+ unpacked[1::2] = high
120
+
121
+ n_orig = np.prod(shape)
122
+ blocked = unpacked[:((n_orig + 31)//32)*32].reshape(-1, 32)
123
+ s = scales.astype(np.float32)
124
+ z = zeros.astype(np.float32)
125
+ deq_w = (blocked * s + z).reshape(-1)[:n_orig].reshape(shape)
126
+ state_dict[base_k] = torch.from_numpy(deq_w).float()
127
+ elif k.endswith(".scales") or k.endswith(".zeros") or k.endswith(".shape"):
128
+ continue
129
+ else:
130
+ state_dict[k] = torch.from_numpy(v).float()
131
+
132
+ self.model.load_state_dict(state_dict, strict=False)
133
+ optimize_for_inference(self.model)
134
+ self.sample_rate = int(self.hps.data.sampling_rate)
135
+
136
+ if self.device.type == "cpu":
137
+ if hasattr(torch, "set_num_threads") and torch.get_num_threads() > 4:
138
+ torch.set_num_threads(4)
139
+
140
+ @staticmethod
141
+ def _quantize_lf4(tensor: torch.Tensor, group_size: int = 32) -> torch.Tensor:
142
+ if tensor.numel() < group_size:
143
+ return tensor
144
+ shape = tensor.shape
145
+ flat = tensor.reshape(-1)
146
+ n = flat.numel()
147
+ pad = (group_size - (n % group_size)) % group_size
148
+ if pad > 0:
149
+ flat = torch.cat([flat, torch.zeros(pad, device=tensor.device)])
150
+
151
+ groups = flat.reshape(-1, group_size)
152
+ g_max = groups.abs().max(dim=-1, keepdim=True).values.clamp(min=1e-8)
153
+ scales = g_max / 7.0
154
+
155
+ q_groups = torch.round(groups / scales).clamp(-7, 7)
156
+ deq_groups = q_groups * scales
157
+ return deq_groups.reshape(-1)[:n].reshape(shape)
158
+
159
+ def _tokens(self, text: str) -> tuple[torch.Tensor, torch.Tensor]:
160
+ from phonemizer.backend import EspeakBackend
161
+ backend = EspeakBackend('en-us', preserve_punctuation=True, with_stress=True)
162
+ phoneme_str = backend.phonemize([text])[0]
163
+ sequence = cleaned_text_to_sequence(phoneme_str)
164
+ if not sequence:
165
+ sequence = [1, 2, 3]
166
+ if self.hps.data.add_blank:
167
+ res = [0] * (len(sequence) * 2 + 1)
168
+ res[1::2] = sequence
169
+ sequence = res
170
+ tokens = torch.LongTensor(sequence).to(self.device).unsqueeze(0)
171
+ lengths = torch.LongTensor([tokens.size(1)]).to(self.device)
172
+ return tokens, lengths
173
+
174
+ @torch.inference_mode()
175
+ def synthesize(
176
+ self,
177
+ text: str,
178
+ *,
179
+ speed: float = 1.0,
180
+ variation: float = 0.667,
181
+ seed: int = 0,
182
+ ) -> tuple[int, np.ndarray]:
183
+ normalized = " ".join(text.split())
184
+ if not normalized:
185
+ raise ValueError("Text must not be empty.")
186
+ if not 0.5 <= speed <= 2.0:
187
+ raise ValueError("speed must be between 0.5 and 2.0")
188
+ if not 0.0 <= variation <= 1.0:
189
+ raise ValueError("variation must be between 0.0 and 1.0")
190
+ chunks = split_text(normalized)
191
+ pieces: list[np.ndarray] = []
192
+ for index, chunk in enumerate(chunks):
193
+ if index:
194
+ pieces.append(
195
+ np.zeros(
196
+ round(self.sample_rate * boundary_pause_seconds(chunks[index - 1])),
197
+ dtype=np.float32,
198
+ )
199
+ )
200
+ tokens, lengths = self._tokens(chunk)
201
+ torch.manual_seed(seed + index)
202
+ if self.device.type == "cuda":
203
+ torch.cuda.manual_seed_all(seed + index)
204
+ waveform = self.model.infer(
205
+ tokens,
206
+ lengths,
207
+ noise_scale=variation,
208
+ noise_scale_w=0.8,
209
+ length_scale=1.0 / speed,
210
+ max_len=4000,
211
+ )[0][0, 0].float().cpu().numpy()
212
+ pieces.append(edge_fade(waveform, self.sample_rate))
213
+ waveform = np.clip(np.concatenate(pieces), -1.0, 1.0)
214
+ return self.sample_rate, waveform
215
+
216
+ def save(self, text: str, output: str | Path, **kwargs: object) -> Path:
217
+ destination = Path(output)
218
+ destination.parent.mkdir(parents=True, exist_ok=True)
219
+ sample_rate, waveform = self.synthesize(text, **kwargs)
220
+
221
+ # Zero-dependency native PCM16 WAV writer (eliminates scipy/soundfile requirement)
222
+ data_int16 = (waveform * 32767.0).clip(-32768, 32767).astype("<i2")
223
+ raw_bytes = data_int16.tobytes()
224
+ data_size = len(raw_bytes)
225
+
226
+ header = bytearray()
227
+ header.extend(b"RIFF")
228
+ header.extend((36 + data_size).to_bytes(4, "little"))
229
+ header.extend(b"WAVE")
230
+ header.extend(b"fmt ")
231
+ header.extend((16).to_bytes(4, "little"))
232
+ header.extend((1).to_bytes(2, "little"))
233
+ header.extend((1).to_bytes(2, "little"))
234
+ header.extend((sample_rate).to_bytes(4, "little"))
235
+ header.extend((sample_rate * 2).to_bytes(4, "little"))
236
+ header.extend((2).to_bytes(2, "little"))
237
+ header.extend((16).to_bytes(2, "little"))
238
+ header.extend(b"data")
239
+ header.extend((data_size).to_bytes(4, "little"))
240
+
241
+ with open(destination, "wb") as f:
242
+ f.write(header)
243
+ f.write(raw_bytes)
244
+
245
+ return destination
246
+
247
+
248
+ def main() -> None:
249
+ parser = argparse.ArgumentParser(description="Run standalone VTX-TTS speech synthesis.")
250
+ parser.add_argument("--model-dir", type=Path, default=PACKAGE_ROOT)
251
+ parser.add_argument("--text", required=True)
252
+ parser.add_argument("--output", type=Path, required=True)
253
+ parser.add_argument("--device", default="cpu")
254
+ parser.add_argument("--speed", type=float, default=1.0)
255
+ parser.add_argument("--variation", type=float, default=0.667)
256
+ parser.add_argument("--seed", type=int, default=0)
257
+ args = parser.parse_args()
258
+ engine = VtxTTS(args.model_dir, args.device)
259
+ engine.save(
260
+ args.text,
261
+ args.output,
262
+ speed=args.speed,
263
+ variation=args.variation,
264
+ seed=args.seed,
265
+ )
266
+ print(f"wrote {args.output} at {engine.sample_rate} Hz")
267
+
268
+
269
+ if __name__ == "__main__":
270
+ main()
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c1a6962ecac5a82950a88db11779be663ddfc284b1b147bb01e5bd6cf44b8109
3
+ size 11989008
modeling.py ADDED
@@ -0,0 +1,2008 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import math
3
+ import copy
4
+ import torch
5
+ import numpy as np
6
+ from torch import nn
7
+ from torch.nn import functional as F
8
+ from torch.nn import Conv1d, ConvTranspose1d, AvgPool1d, Conv2d
9
+ from torch.nn.utils import weight_norm, remove_weight_norm, spectral_norm
10
+
11
+
12
+ import math
13
+ import numpy as np
14
+ import torch
15
+ from torch import nn
16
+ from torch.nn import functional as F
17
+
18
+
19
+ def init_weights(m, mean=0.0, std=0.01):
20
+ classname = m.__class__.__name__
21
+ if classname.find("Conv") != -1:
22
+ m.weight.data.normal_(mean, std)
23
+
24
+
25
+ def get_padding(kernel_size, dilation=1):
26
+ return int((kernel_size*dilation - dilation)/2)
27
+
28
+
29
+ def convert_pad_shape(pad_shape):
30
+ l = pad_shape[::-1]
31
+ pad_shape = [item for sublist in l for item in sublist]
32
+ return pad_shape
33
+
34
+
35
+ def intersperse(lst, item):
36
+ result = [item] * (len(lst) * 2 + 1)
37
+ result[1::2] = lst
38
+ return result
39
+
40
+
41
+ def kl_divergence(m_p, logs_p, m_q, logs_q):
42
+ """KL(P||Q)"""
43
+ kl = (logs_q - logs_p) - 0.5
44
+ kl += 0.5 * (torch.exp(2. * logs_p) + ((m_p - m_q)**2)) * torch.exp(-2. * logs_q)
45
+ return kl
46
+
47
+
48
+ def rand_gumbel(shape):
49
+ """Sample from the Gumbel distribution, protect from overflows."""
50
+ uniform_samples = torch.rand(shape) * 0.99998 + 0.00001
51
+ return -torch.log(-torch.log(uniform_samples))
52
+
53
+
54
+ def rand_gumbel_like(x):
55
+ g = rand_gumbel(x.size()).to(dtype=x.dtype, device=x.device)
56
+ return g
57
+
58
+
59
+ def slice_segments(x, ids_str, segment_size=4):
60
+ ret = torch.zeros_like(x[:, :, :segment_size])
61
+ for i in range(x.size(0)):
62
+ idx_str = ids_str[i]
63
+ idx_end = idx_str + segment_size
64
+ ret[i] = x[i, :, idx_str:idx_end]
65
+ return ret
66
+
67
+
68
+ def maximum_path(neg_cent, mask):
69
+ device = neg_cent.device
70
+ dtype = neg_cent.dtype
71
+ neg_cent = neg_cent.detach().cpu().numpy().astype(np.float64)
72
+ mask = mask.detach().cpu().numpy().astype(np.bool_)
73
+ b, t_t, t_s = neg_cent.shape
74
+ path = np.zeros_like(neg_cent, dtype=np.float64)
75
+
76
+ for i in range(b):
77
+ t_y = int(mask[i, :, 0].sum())
78
+ t_x = int(mask[i, 0, :].sum())
79
+ if t_y == 0 or t_x == 0:
80
+ continue
81
+ value = neg_cent[i, :t_y, :t_x]
82
+ v = np.full((t_y, t_x), -1e9, dtype=np.float64)
83
+ v[0, 0] = value[0, 0]
84
+ for y in range(1, t_y):
85
+ v[y, 0] = v[y - 1, 0] + value[y, 0]
86
+ for x in range(1, t_x):
87
+ v[0, x] = -1e9
88
+ for y in range(1, t_y):
89
+ for x in range(1, min(t_x, y + 1)):
90
+ v[y, x] = value[y, x] + max(v[y - 1, x], v[y - 1, x - 1])
91
+ index = t_x - 1
92
+ for y in range(t_y - 1, -1, -1):
93
+ path[i, y, index] = 1.0
94
+ if index > 0 and (y == 0 or v[y - 1, index - 1] > v[y - 1, index]):
95
+ index -= 1
96
+ return torch.from_numpy(path).to(device=device, dtype=dtype)
97
+
98
+
99
+ def rand_slice_segments(x, x_lengths=None, segment_size=4):
100
+ b, d, t = x.size()
101
+ if x_lengths is None:
102
+ x_lengths = t
103
+ ids_str_max = x_lengths - segment_size + 1
104
+ ids_str = (torch.rand([b]).to(device=x.device) * ids_str_max).to(dtype=torch.long)
105
+ ret = slice_segments(x, ids_str, segment_size)
106
+ return ret, ids_str
107
+
108
+
109
+ def get_timing_signal_1d(
110
+ length, channels, min_timescale=1.0, max_timescale=1.0e4):
111
+ position = torch.arange(length, dtype=torch.float)
112
+ num_timescales = channels // 2
113
+ log_timescale_increment = (
114
+ math.log(float(max_timescale) / float(min_timescale)) /
115
+ (num_timescales - 1))
116
+ inv_timescales = min_timescale * torch.exp(
117
+ torch.arange(num_timescales, dtype=torch.float) * -log_timescale_increment)
118
+ scaled_time = position.unsqueeze(0) * inv_timescales.unsqueeze(1)
119
+ signal = torch.cat([torch.sin(scaled_time), torch.cos(scaled_time)], 0)
120
+ signal = F.pad(signal, [0, 0, 0, channels % 2])
121
+ signal = signal.view(1, channels, length)
122
+ return signal
123
+
124
+
125
+ def add_timing_signal_1d(x, min_timescale=1.0, max_timescale=1.0e4):
126
+ b, channels, length = x.size()
127
+ signal = get_timing_signal_1d(length, channels, min_timescale, max_timescale)
128
+ return x + signal.to(dtype=x.dtype, device=x.device)
129
+
130
+
131
+ def cat_timing_signal_1d(x, min_timescale=1.0, max_timescale=1.0e4, axis=1):
132
+ b, channels, length = x.size()
133
+ signal = get_timing_signal_1d(length, channels, min_timescale, max_timescale)
134
+ return torch.cat([x, signal.to(dtype=x.dtype, device=x.device)], axis)
135
+
136
+
137
+ def subsequent_mask(length):
138
+ mask = torch.tril(torch.ones(length, length)).unsqueeze(0).unsqueeze(0)
139
+ return mask
140
+
141
+
142
+ @torch.jit.script
143
+ def fused_add_tanh_sigmoid_multiply(input_a, input_b, n_channels):
144
+ n_channels_int = n_channels[0]
145
+ in_act = input_a + input_b
146
+ t_act = torch.tanh(in_act[:, :n_channels_int, :])
147
+ s_act = torch.sigmoid(in_act[:, n_channels_int:, :])
148
+ acts = t_act * s_act
149
+ return acts
150
+
151
+
152
+ def convert_pad_shape(pad_shape):
153
+ l = pad_shape[::-1]
154
+ pad_shape = [item for sublist in l for item in sublist]
155
+ return pad_shape
156
+
157
+
158
+ def shift_1d(x):
159
+ x = F.pad(x, convert_pad_shape([[0, 0], [0, 0], [1, 0]]))[:, :, :-1]
160
+ return x
161
+
162
+
163
+ def sequence_mask(length, max_length=None):
164
+ if max_length is None:
165
+ max_length = length.max()
166
+ x = torch.arange(max_length, dtype=length.dtype, device=length.device)
167
+ return x.unsqueeze(0) < length.unsqueeze(1)
168
+
169
+
170
+ def generate_path(duration, mask):
171
+ """
172
+ duration: [b, 1, t_x]
173
+ mask: [b, 1, t_y, t_x]
174
+ """
175
+ device = duration.device
176
+
177
+ b, _, t_y, t_x = mask.shape
178
+ cum_duration = torch.cumsum(duration, -1)
179
+
180
+ cum_duration_flat = cum_duration.view(b * t_x)
181
+ path = sequence_mask(cum_duration_flat, t_y).to(mask.dtype)
182
+ path = path.view(b, t_x, t_y)
183
+ path = path - F.pad(path, convert_pad_shape([[0, 0], [1, 0], [0, 0]]))[:, :-1]
184
+ path = path.unsqueeze(1).transpose(2,3) * mask
185
+ return path
186
+
187
+
188
+ def clip_grad_value_(parameters, clip_value, norm_type=2):
189
+ if isinstance(parameters, torch.Tensor):
190
+ parameters = [parameters]
191
+ parameters = list(filter(lambda p: p.grad is not None, parameters))
192
+ norm_type = float(norm_type)
193
+ if clip_value is not None:
194
+ clip_value = float(clip_value)
195
+
196
+ total_norm = 0
197
+ for p in parameters:
198
+ param_norm = p.grad.data.norm(norm_type)
199
+ total_norm += param_norm.item() ** norm_type
200
+ if clip_value is not None:
201
+ p.grad.data.clamp_(min=-clip_value, max=clip_value)
202
+ total_norm = total_norm ** (1. / norm_type)
203
+ return total_norm
204
+
205
+ """Lightweight alias-free waveform blocks derived from NVIDIA BigVGAN.
206
+
207
+ BigVGAN and alias-free-torch are MIT/Apache-2.0 licensed. The implementation
208
+ is kept local so Inflect can train without BigVGAN's optional CUDA extension.
209
+ """
210
+
211
+ import math
212
+
213
+ import torch
214
+ from torch import nn
215
+ from torch.nn import functional as F
216
+ from torch.nn.utils import remove_weight_norm, weight_norm
217
+
218
+
219
+
220
+ def kaiser_sinc_filter1d(cutoff: float, half_width: float, kernel_size: int):
221
+ even = kernel_size % 2 == 0
222
+ half_size = kernel_size // 2
223
+ delta_f = 4 * half_width
224
+ attenuation = 2.285 * (half_size - 1) * math.pi * delta_f + 7.95
225
+ if attenuation > 50.0:
226
+ beta = 0.1102 * (attenuation - 8.7)
227
+ elif attenuation >= 21.0:
228
+ beta = 0.5842 * (attenuation - 21) ** 0.4 + 0.07886 * (attenuation - 21.0)
229
+ else:
230
+ beta = 0.0
231
+ window = torch.kaiser_window(kernel_size, beta=beta, periodic=False)
232
+ if even:
233
+ time = torch.arange(-half_size, half_size) + 0.5
234
+ else:
235
+ time = torch.arange(kernel_size) - half_size
236
+ values = 2 * cutoff * window * torch.sinc(2 * cutoff * time)
237
+ values /= values.sum()
238
+ return values.view(1, 1, kernel_size)
239
+
240
+
241
+ class UpSample1d(nn.Module):
242
+ def __init__(self, ratio=2, kernel_size=12):
243
+ super().__init__()
244
+ self.ratio = ratio
245
+ self.stride = ratio
246
+ self.kernel_size = kernel_size
247
+ self.pad = kernel_size // ratio - 1
248
+ self.pad_left = self.pad * ratio + (kernel_size - ratio) // 2
249
+ self.pad_right = self.pad * ratio + (kernel_size - ratio + 1) // 2
250
+ self.register_buffer(
251
+ "filter",
252
+ kaiser_sinc_filter1d(0.5 / ratio, 0.6 / ratio, kernel_size))
253
+
254
+ def forward(self, x):
255
+ channels = x.shape[1]
256
+ x = F.pad(x, (self.pad, self.pad), mode="replicate")
257
+ x = self.ratio * F.conv_transpose1d(
258
+ x, self.filter.expand(channels, -1, -1),
259
+ stride=self.stride, groups=channels)
260
+ return x[..., self.pad_left:-self.pad_right]
261
+
262
+
263
+ class DownSample1d(nn.Module):
264
+ def __init__(self, ratio=2, kernel_size=12):
265
+ super().__init__()
266
+ self.ratio = ratio
267
+ self.kernel_size = kernel_size
268
+ self.pad_left = kernel_size // 2 - int(kernel_size % 2 == 0)
269
+ self.pad_right = kernel_size // 2
270
+ self.register_buffer(
271
+ "filter",
272
+ kaiser_sinc_filter1d(0.5 / ratio, 0.6 / ratio, kernel_size))
273
+
274
+ def forward(self, x):
275
+ channels = x.shape[1]
276
+ x = F.pad(x, (self.pad_left, self.pad_right), mode="replicate")
277
+ return F.conv1d(
278
+ x, self.filter.expand(channels, -1, -1),
279
+ stride=self.ratio, groups=channels)
280
+
281
+
282
+ class SnakeBeta(nn.Module):
283
+ def __init__(self, channels: int, logscale: bool = True):
284
+ super().__init__()
285
+ initial = torch.zeros(channels) if logscale else torch.ones(channels)
286
+ self.alpha = nn.Parameter(initial.clone())
287
+ self.beta = nn.Parameter(initial.clone())
288
+ self.logscale = logscale
289
+
290
+ def forward(self, x):
291
+ alpha = self.alpha.view(1, -1, 1)
292
+ beta = self.beta.view(1, -1, 1)
293
+ if self.logscale:
294
+ alpha = alpha.exp()
295
+ beta = beta.exp()
296
+ return x + torch.sin(x * alpha).square() / (beta + 1e-9)
297
+
298
+
299
+ class AliasFreeActivation1d(nn.Module):
300
+ def __init__(self, activation: nn.Module):
301
+ super().__init__()
302
+ self.upsample = UpSample1d()
303
+ self.act = activation
304
+ self.downsample = DownSample1d()
305
+
306
+ def forward(self, x):
307
+ return self.downsample(self.act(self.upsample(x)))
308
+
309
+
310
+ class AliasFreeResBlock1(nn.Module):
311
+ """Shape-compatible VITS ResBlock1 with filtered SnakeBeta activations."""
312
+
313
+ def __init__(self, channels, kernel_size=3, dilation=(1, 3, 5), logscale=True):
314
+ super().__init__()
315
+ self.convs1 = nn.ModuleList([
316
+ weight_norm(nn.Conv1d(
317
+ channels, channels, kernel_size, 1,
318
+ dilation=d, padding=get_padding(kernel_size, d)))
319
+ for d in dilation
320
+ ])
321
+ self.convs2 = nn.ModuleList([
322
+ weight_norm(nn.Conv1d(
323
+ channels, channels, kernel_size, 1,
324
+ dilation=1, padding=get_padding(kernel_size, 1)))
325
+ for _ in dilation
326
+ ])
327
+ self.convs1.apply(init_weights)
328
+ self.convs2.apply(init_weights)
329
+ self.activations = nn.ModuleList([
330
+ AliasFreeActivation1d(SnakeBeta(channels, logscale=logscale))
331
+ for _ in range(2 * len(dilation))
332
+ ])
333
+
334
+ def forward(self, x, x_mask=None):
335
+ first = self.activations[::2]
336
+ second = self.activations[1::2]
337
+ for conv1, conv2, act1, act2 in zip(self.convs1, self.convs2, first, second):
338
+ residual = conv2(act2(conv1(act1(x))))
339
+ x = x + residual
340
+ return x
341
+
342
+ def remove_weight_norm(self):
343
+ for layer in self.convs1:
344
+ remove_weight_norm(layer)
345
+ for layer in self.convs2:
346
+ remove_weight_norm(layer)
347
+
348
+ import copy
349
+ import math
350
+ import numpy as np
351
+ import torch
352
+ from torch import nn
353
+ from torch.nn import functional as F
354
+
355
+
356
+
357
+ class Encoder(nn.Module):
358
+ def __init__(self, hidden_channels, filter_channels, n_heads, n_layers, kernel_size=1, p_dropout=0., window_size=4, **kwargs):
359
+ super().__init__()
360
+ self.hidden_channels = hidden_channels
361
+ self.filter_channels = filter_channels
362
+ self.n_heads = n_heads
363
+ self.n_layers = n_layers
364
+ self.kernel_size = kernel_size
365
+ self.p_dropout = p_dropout
366
+ self.window_size = window_size
367
+
368
+ self.drop = nn.Dropout(p_dropout)
369
+ self.attn_layers = nn.ModuleList()
370
+ self.norm_layers_1 = nn.ModuleList()
371
+ self.ffn_layers = nn.ModuleList()
372
+ self.norm_layers_2 = nn.ModuleList()
373
+ for i in range(self.n_layers):
374
+ self.attn_layers.append(MultiHeadAttention(hidden_channels, hidden_channels, n_heads, p_dropout=p_dropout, window_size=window_size))
375
+ self.norm_layers_1.append(LayerNorm(hidden_channels))
376
+ self.ffn_layers.append(FFN(hidden_channels, hidden_channels, filter_channels, kernel_size, p_dropout=p_dropout))
377
+ self.norm_layers_2.append(LayerNorm(hidden_channels))
378
+
379
+ def forward(self, x, x_mask):
380
+ attn_mask = x_mask.unsqueeze(2) * x_mask.unsqueeze(-1)
381
+ x = x * x_mask
382
+ for i in range(self.n_layers):
383
+ y = self.attn_layers[i](x, x, attn_mask)
384
+ y = self.drop(y)
385
+ x = self.norm_layers_1[i](x + y)
386
+
387
+ y = self.ffn_layers[i](x, x_mask)
388
+ y = self.drop(y)
389
+ x = self.norm_layers_2[i](x + y)
390
+ x = x * x_mask
391
+ return x
392
+
393
+
394
+ class Decoder(nn.Module):
395
+ def __init__(self, hidden_channels, filter_channels, n_heads, n_layers, kernel_size=1, p_dropout=0., proximal_bias=False, proximal_init=True, **kwargs):
396
+ super().__init__()
397
+ self.hidden_channels = hidden_channels
398
+ self.filter_channels = filter_channels
399
+ self.n_heads = n_heads
400
+ self.n_layers = n_layers
401
+ self.kernel_size = kernel_size
402
+ self.p_dropout = p_dropout
403
+ self.proximal_bias = proximal_bias
404
+ self.proximal_init = proximal_init
405
+
406
+ self.drop = nn.Dropout(p_dropout)
407
+ self.self_attn_layers = nn.ModuleList()
408
+ self.norm_layers_0 = nn.ModuleList()
409
+ self.encdec_attn_layers = nn.ModuleList()
410
+ self.norm_layers_1 = nn.ModuleList()
411
+ self.ffn_layers = nn.ModuleList()
412
+ self.norm_layers_2 = nn.ModuleList()
413
+ for i in range(self.n_layers):
414
+ self.self_attn_layers.append(MultiHeadAttention(hidden_channels, hidden_channels, n_heads, p_dropout=p_dropout, proximal_bias=proximal_bias, proximal_init=proximal_init))
415
+ self.norm_layers_0.append(LayerNorm(hidden_channels))
416
+ self.encdec_attn_layers.append(MultiHeadAttention(hidden_channels, hidden_channels, n_heads, p_dropout=p_dropout))
417
+ self.norm_layers_1.append(LayerNorm(hidden_channels))
418
+ self.ffn_layers.append(FFN(hidden_channels, hidden_channels, filter_channels, kernel_size, p_dropout=p_dropout, causal=True))
419
+ self.norm_layers_2.append(LayerNorm(hidden_channels))
420
+
421
+ def forward(self, x, x_mask, h, h_mask):
422
+ """
423
+ x: decoder input
424
+ h: encoder output
425
+ """
426
+ self_attn_mask = subsequent_mask(x_mask.size(2)).to(device=x.device, dtype=x.dtype)
427
+ encdec_attn_mask = h_mask.unsqueeze(2) * x_mask.unsqueeze(-1)
428
+ x = x * x_mask
429
+ for i in range(self.n_layers):
430
+ y = self.self_attn_layers[i](x, x, self_attn_mask)
431
+ y = self.drop(y)
432
+ x = self.norm_layers_0[i](x + y)
433
+
434
+ y = self.encdec_attn_layers[i](x, h, encdec_attn_mask)
435
+ y = self.drop(y)
436
+ x = self.norm_layers_1[i](x + y)
437
+
438
+ y = self.ffn_layers[i](x, x_mask)
439
+ y = self.drop(y)
440
+ x = self.norm_layers_2[i](x + y)
441
+ x = x * x_mask
442
+ return x
443
+
444
+
445
+ class MultiHeadAttention(nn.Module):
446
+ def __init__(self, channels, out_channels, n_heads, p_dropout=0., window_size=None, heads_share=True, block_length=None, proximal_bias=False, proximal_init=False):
447
+ super().__init__()
448
+ assert channels % n_heads == 0
449
+
450
+ self.channels = channels
451
+ self.out_channels = out_channels
452
+ self.n_heads = n_heads
453
+ self.p_dropout = p_dropout
454
+ self.window_size = window_size
455
+ self.heads_share = heads_share
456
+ self.block_length = block_length
457
+ self.proximal_bias = proximal_bias
458
+ self.proximal_init = proximal_init
459
+ self.attn = None
460
+
461
+ self.k_channels = channels // n_heads
462
+ self.conv_q = nn.Conv1d(channels, channels, 1)
463
+ self.conv_k = nn.Conv1d(channels, channels, 1)
464
+ self.conv_v = nn.Conv1d(channels, channels, 1)
465
+ self.conv_o = nn.Conv1d(channels, out_channels, 1)
466
+ self.drop = nn.Dropout(p_dropout)
467
+
468
+ if window_size is not None:
469
+ n_heads_rel = 1 if heads_share else n_heads
470
+ rel_stddev = self.k_channels**-0.5
471
+ self.emb_rel_k = nn.Parameter(torch.randn(n_heads_rel, window_size * 2 + 1, self.k_channels) * rel_stddev)
472
+ self.emb_rel_v = nn.Parameter(torch.randn(n_heads_rel, window_size * 2 + 1, self.k_channels) * rel_stddev)
473
+
474
+ nn.init.xavier_uniform_(self.conv_q.weight)
475
+ nn.init.xavier_uniform_(self.conv_k.weight)
476
+ nn.init.xavier_uniform_(self.conv_v.weight)
477
+ if proximal_init:
478
+ with torch.no_grad():
479
+ self.conv_k.weight.copy_(self.conv_q.weight)
480
+ self.conv_k.bias.copy_(self.conv_q.bias)
481
+
482
+ def forward(self, x, c, attn_mask=None):
483
+ q = self.conv_q(x)
484
+ k = self.conv_k(c)
485
+ v = self.conv_v(c)
486
+
487
+ x, self.attn = self.attention(q, k, v, mask=attn_mask)
488
+
489
+ x = self.conv_o(x)
490
+ return x
491
+
492
+ def attention(self, query, key, value, mask=None):
493
+ # reshape [b, d, t] -> [b, n_h, t, d_k]
494
+ b, d, t_s, t_t = (*key.size(), query.size(2))
495
+ query = query.view(b, self.n_heads, self.k_channels, t_t).transpose(2, 3)
496
+ key = key.view(b, self.n_heads, self.k_channels, t_s).transpose(2, 3)
497
+ value = value.view(b, self.n_heads, self.k_channels, t_s).transpose(2, 3)
498
+
499
+ scores = torch.matmul(query / math.sqrt(self.k_channels), key.transpose(-2, -1))
500
+ if self.window_size is not None:
501
+ assert t_s == t_t, "Relative attention is only available for self-attention."
502
+ key_relative_embeddings = self._get_relative_embeddings(self.emb_rel_k, t_s)
503
+ rel_logits = self._matmul_with_relative_keys(query /math.sqrt(self.k_channels), key_relative_embeddings)
504
+ scores_local = self._relative_position_to_absolute_position(rel_logits)
505
+ scores = scores + scores_local
506
+ if self.proximal_bias:
507
+ assert t_s == t_t, "Proximal bias is only available for self-attention."
508
+ scores = scores + self._attention_bias_proximal(t_s).to(device=scores.device, dtype=scores.dtype)
509
+ if mask is not None:
510
+ scores = scores.masked_fill(mask == 0, -1e4)
511
+ if self.block_length is not None:
512
+ assert t_s == t_t, "Local attention is only available for self-attention."
513
+ block_mask = torch.ones_like(scores).triu(-self.block_length).tril(self.block_length)
514
+ scores = scores.masked_fill(block_mask == 0, -1e4)
515
+ p_attn = F.softmax(scores, dim=-1) # [b, n_h, t_t, t_s]
516
+ p_attn = self.drop(p_attn)
517
+ output = torch.matmul(p_attn, value)
518
+ if self.window_size is not None:
519
+ relative_weights = self._absolute_position_to_relative_position(p_attn)
520
+ value_relative_embeddings = self._get_relative_embeddings(self.emb_rel_v, t_s)
521
+ output = output + self._matmul_with_relative_values(relative_weights, value_relative_embeddings)
522
+ output = output.transpose(2, 3).contiguous().view(b, d, t_t) # [b, n_h, t_t, d_k] -> [b, d, t_t]
523
+ return output, p_attn
524
+
525
+ def _matmul_with_relative_values(self, x, y):
526
+ """
527
+ x: [b, h, l, m]
528
+ y: [h or 1, m, d]
529
+ ret: [b, h, l, d]
530
+ """
531
+ ret = torch.matmul(x, y.unsqueeze(0))
532
+ return ret
533
+
534
+ def _matmul_with_relative_keys(self, x, y):
535
+ """
536
+ x: [b, h, l, d]
537
+ y: [h or 1, m, d]
538
+ ret: [b, h, l, m]
539
+ """
540
+ ret = torch.matmul(x, y.unsqueeze(0).transpose(-2, -1))
541
+ return ret
542
+
543
+ def _get_relative_embeddings(self, relative_embeddings, length):
544
+ max_relative_position = 2 * self.window_size + 1
545
+ # Pad first before slice to avoid using cond ops.
546
+ pad_length = max(length - (self.window_size + 1), 0)
547
+ slice_start_position = max((self.window_size + 1) - length, 0)
548
+ slice_end_position = slice_start_position + 2 * length - 1
549
+ if pad_length > 0:
550
+ padded_relative_embeddings = F.pad(
551
+ relative_embeddings,
552
+ convert_pad_shape([[0, 0], [pad_length, pad_length], [0, 0]]))
553
+ else:
554
+ padded_relative_embeddings = relative_embeddings
555
+ used_relative_embeddings = padded_relative_embeddings[:,slice_start_position:slice_end_position]
556
+ return used_relative_embeddings
557
+
558
+ def _relative_position_to_absolute_position(self, x):
559
+ """
560
+ x: [b, h, l, 2*l-1]
561
+ ret: [b, h, l, l]
562
+ """
563
+ batch, heads, length, _ = x.size()
564
+ # Concat columns of pad to shift from relative to absolute indexing.
565
+ x = F.pad(x, convert_pad_shape([[0,0],[0,0],[0,0],[0,1]]))
566
+
567
+ # Concat extra elements so to add up to shape (len+1, 2*len-1).
568
+ x_flat = x.view([batch, heads, length * 2 * length])
569
+ x_flat = F.pad(x_flat, convert_pad_shape([[0,0],[0,0],[0,length-1]]))
570
+
571
+ # Reshape and slice out the padded elements.
572
+ x_final = x_flat.view([batch, heads, length+1, 2*length-1])[:, :, :length, length-1:]
573
+ return x_final
574
+
575
+ def _absolute_position_to_relative_position(self, x):
576
+ """
577
+ x: [b, h, l, l]
578
+ ret: [b, h, l, 2*l-1]
579
+ """
580
+ batch, heads, length, _ = x.size()
581
+ # padd along column
582
+ x = F.pad(x, convert_pad_shape([[0, 0], [0, 0], [0, 0], [0, length-1]]))
583
+ x_flat = x.view([batch, heads, length**2 + length*(length -1)])
584
+ # add 0's in the beginning that will skew the elements after reshape
585
+ x_flat = F.pad(x_flat, convert_pad_shape([[0, 0], [0, 0], [length, 0]]))
586
+ x_final = x_flat.view([batch, heads, length, 2*length])[:,:,:,1:]
587
+ return x_final
588
+
589
+ def _attention_bias_proximal(self, length):
590
+ """Bias for self-attention to encourage attention to close positions.
591
+ Args:
592
+ length: an integer scalar.
593
+ Returns:
594
+ a Tensor with shape [1, 1, length, length]
595
+ """
596
+ r = torch.arange(length, dtype=torch.float32)
597
+ diff = torch.unsqueeze(r, 0) - torch.unsqueeze(r, 1)
598
+ return torch.unsqueeze(torch.unsqueeze(-torch.log1p(torch.abs(diff)), 0), 0)
599
+
600
+
601
+ class FFN(nn.Module):
602
+ def __init__(self, in_channels, out_channels, filter_channels, kernel_size, p_dropout=0., activation=None, causal=False):
603
+ super().__init__()
604
+ self.in_channels = in_channels
605
+ self.out_channels = out_channels
606
+ self.filter_channels = filter_channels
607
+ self.kernel_size = kernel_size
608
+ self.p_dropout = p_dropout
609
+ self.activation = activation
610
+ self.causal = causal
611
+
612
+ if causal:
613
+ self.padding = self._causal_padding
614
+ else:
615
+ self.padding = self._same_padding
616
+
617
+ self.conv_1 = nn.Conv1d(in_channels, filter_channels, kernel_size)
618
+ self.conv_2 = nn.Conv1d(filter_channels, out_channels, kernel_size)
619
+ self.drop = nn.Dropout(p_dropout)
620
+
621
+ def forward(self, x, x_mask):
622
+ x = self.conv_1(self.padding(x * x_mask))
623
+ if self.activation == "gelu":
624
+ x = x * torch.sigmoid(1.702 * x)
625
+ else:
626
+ x = torch.relu(x)
627
+ x = self.drop(x)
628
+ x = self.conv_2(self.padding(x * x_mask))
629
+ return x * x_mask
630
+
631
+ def _causal_padding(self, x):
632
+ if self.kernel_size == 1:
633
+ return x
634
+ pad_l = self.kernel_size - 1
635
+ pad_r = 0
636
+ padding = [[0, 0], [0, 0], [pad_l, pad_r]]
637
+ x = F.pad(x, convert_pad_shape(padding))
638
+ return x
639
+
640
+ def _same_padding(self, x):
641
+ if self.kernel_size == 1:
642
+ return x
643
+ pad_l = (self.kernel_size - 1) // 2
644
+ pad_r = self.kernel_size // 2
645
+ padding = [[0, 0], [0, 0], [pad_l, pad_r]]
646
+ x = F.pad(x, convert_pad_shape(padding))
647
+ return x
648
+
649
+ import copy
650
+ import math
651
+ import numpy as np
652
+ import scipy
653
+ import torch
654
+ from torch import nn
655
+ from torch.nn import functional as F
656
+
657
+ from torch.nn import Conv1d, ConvTranspose1d, AvgPool1d, Conv2d
658
+ from torch.nn.utils import weight_norm, remove_weight_norm
659
+
660
+
661
+
662
+ LRELU_SLOPE = 0.1
663
+
664
+
665
+ class LayerNorm(nn.Module):
666
+ def __init__(self, channels, eps=1e-5):
667
+ super().__init__()
668
+ self.channels = channels
669
+ self.eps = eps
670
+
671
+ self.gamma = nn.Parameter(torch.ones(channels))
672
+ self.beta = nn.Parameter(torch.zeros(channels))
673
+
674
+ def forward(self, x):
675
+ x = x.transpose(1, -1)
676
+ x = F.layer_norm(x, (self.channels,), self.gamma, self.beta, self.eps)
677
+ return x.transpose(1, -1)
678
+
679
+
680
+ class ConvReluNorm(nn.Module):
681
+ def __init__(self, in_channels, hidden_channels, out_channels, kernel_size, n_layers, p_dropout):
682
+ super().__init__()
683
+ self.in_channels = in_channels
684
+ self.hidden_channels = hidden_channels
685
+ self.out_channels = out_channels
686
+ self.kernel_size = kernel_size
687
+ self.n_layers = n_layers
688
+ self.p_dropout = p_dropout
689
+ assert n_layers > 1, "Number of layers should be larger than 0."
690
+
691
+ self.conv_layers = nn.ModuleList()
692
+ self.norm_layers = nn.ModuleList()
693
+ self.conv_layers.append(nn.Conv1d(in_channels, hidden_channels, kernel_size, padding=kernel_size//2))
694
+ self.norm_layers.append(LayerNorm(hidden_channels))
695
+ self.relu_drop = nn.Sequential(
696
+ nn.ReLU(),
697
+ nn.Dropout(p_dropout))
698
+ for _ in range(n_layers-1):
699
+ self.conv_layers.append(nn.Conv1d(hidden_channels, hidden_channels, kernel_size, padding=kernel_size//2))
700
+ self.norm_layers.append(LayerNorm(hidden_channels))
701
+ self.proj = nn.Conv1d(hidden_channels, out_channels, 1)
702
+ self.proj.weight.data.zero_()
703
+ self.proj.bias.data.zero_()
704
+
705
+ def forward(self, x, x_mask):
706
+ x_org = x
707
+ for i in range(self.n_layers):
708
+ x = self.conv_layers[i](x * x_mask)
709
+ x = self.norm_layers[i](x)
710
+ x = self.relu_drop(x)
711
+ x = x_org + self.proj(x)
712
+ return x * x_mask
713
+
714
+
715
+ class DDSConv(nn.Module):
716
+ """
717
+ Dialted and Depth-Separable Convolution
718
+ """
719
+ def __init__(self, channels, kernel_size, n_layers, p_dropout=0.):
720
+ super().__init__()
721
+ self.channels = channels
722
+ self.kernel_size = kernel_size
723
+ self.n_layers = n_layers
724
+ self.p_dropout = p_dropout
725
+
726
+ self.drop = nn.Dropout(p_dropout)
727
+ self.convs_sep = nn.ModuleList()
728
+ self.convs_1x1 = nn.ModuleList()
729
+ self.norms_1 = nn.ModuleList()
730
+ self.norms_2 = nn.ModuleList()
731
+ for i in range(n_layers):
732
+ dilation = kernel_size ** i
733
+ padding = (kernel_size * dilation - dilation) // 2
734
+ self.convs_sep.append(nn.Conv1d(channels, channels, kernel_size,
735
+ groups=channels, dilation=dilation, padding=padding
736
+ ))
737
+ self.convs_1x1.append(nn.Conv1d(channels, channels, 1))
738
+ self.norms_1.append(LayerNorm(channels))
739
+ self.norms_2.append(LayerNorm(channels))
740
+
741
+ def forward(self, x, x_mask, g=None):
742
+ if g is not None:
743
+ x = x + g
744
+ for i in range(self.n_layers):
745
+ y = self.convs_sep[i](x * x_mask)
746
+ y = self.norms_1[i](y)
747
+ y = F.gelu(y)
748
+ y = self.convs_1x1[i](y)
749
+ y = self.norms_2[i](y)
750
+ y = F.gelu(y)
751
+ y = self.drop(y)
752
+ x = x + y
753
+ return x * x_mask
754
+
755
+
756
+ class WN(torch.nn.Module):
757
+ def __init__(self, hidden_channels, kernel_size, dilation_rate, n_layers, gin_channels=0, p_dropout=0):
758
+ super(WN, self).__init__()
759
+ assert(kernel_size % 2 == 1)
760
+ self.hidden_channels =hidden_channels
761
+ self.kernel_size = kernel_size,
762
+ self.dilation_rate = dilation_rate
763
+ self.n_layers = n_layers
764
+ self.gin_channels = gin_channels
765
+ self.p_dropout = p_dropout
766
+
767
+ self.in_layers = torch.nn.ModuleList()
768
+ self.res_skip_layers = torch.nn.ModuleList()
769
+ self.drop = nn.Dropout(p_dropout)
770
+
771
+ if gin_channels != 0:
772
+ cond_layer = torch.nn.Conv1d(gin_channels, 2*hidden_channels*n_layers, 1)
773
+ self.cond_layer = torch.nn.utils.weight_norm(cond_layer, name='weight')
774
+
775
+ for i in range(n_layers):
776
+ dilation = dilation_rate ** i
777
+ padding = int((kernel_size * dilation - dilation) / 2)
778
+ in_layer = torch.nn.Conv1d(hidden_channels, 2*hidden_channels, kernel_size,
779
+ dilation=dilation, padding=padding)
780
+ in_layer = torch.nn.utils.weight_norm(in_layer, name='weight')
781
+ self.in_layers.append(in_layer)
782
+
783
+ # last one is not necessary
784
+ if i < n_layers - 1:
785
+ res_skip_channels = 2 * hidden_channels
786
+ else:
787
+ res_skip_channels = hidden_channels
788
+
789
+ res_skip_layer = torch.nn.Conv1d(hidden_channels, res_skip_channels, 1)
790
+ res_skip_layer = torch.nn.utils.weight_norm(res_skip_layer, name='weight')
791
+ self.res_skip_layers.append(res_skip_layer)
792
+
793
+ def forward(self, x, x_mask, g=None, **kwargs):
794
+ output = torch.zeros_like(x)
795
+ n_channels_tensor = torch.IntTensor([self.hidden_channels])
796
+
797
+ if g is not None:
798
+ g = self.cond_layer(g)
799
+
800
+ for i in range(self.n_layers):
801
+ x_in = self.in_layers[i](x)
802
+ if g is not None:
803
+ cond_offset = i * 2 * self.hidden_channels
804
+ g_l = g[:,cond_offset:cond_offset+2*self.hidden_channels,:]
805
+ else:
806
+ g_l = torch.zeros_like(x_in)
807
+
808
+ acts = fused_add_tanh_sigmoid_multiply(
809
+ x_in,
810
+ g_l,
811
+ n_channels_tensor)
812
+ acts = self.drop(acts)
813
+
814
+ res_skip_acts = self.res_skip_layers[i](acts)
815
+ if i < self.n_layers - 1:
816
+ res_acts = res_skip_acts[:,:self.hidden_channels,:]
817
+ x = (x + res_acts) * x_mask
818
+ output = output + res_skip_acts[:,self.hidden_channels:,:]
819
+ else:
820
+ output = output + res_skip_acts
821
+ return output * x_mask
822
+
823
+ def remove_weight_norm(self):
824
+ if self.gin_channels != 0:
825
+ torch.nn.utils.remove_weight_norm(self.cond_layer)
826
+ for l in self.in_layers:
827
+ torch.nn.utils.remove_weight_norm(l)
828
+ for l in self.res_skip_layers:
829
+ torch.nn.utils.remove_weight_norm(l)
830
+
831
+
832
+ class ResBlock1(torch.nn.Module):
833
+ def __init__(self, channels, kernel_size=3, dilation=(1, 3, 5)):
834
+ super(ResBlock1, self).__init__()
835
+ self.convs1 = nn.ModuleList([
836
+ weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=dilation[0],
837
+ padding=get_padding(kernel_size, dilation[0]))),
838
+ weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=dilation[1],
839
+ padding=get_padding(kernel_size, dilation[1]))),
840
+ weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=dilation[2],
841
+ padding=get_padding(kernel_size, dilation[2])))
842
+ ])
843
+ self.convs1.apply(init_weights)
844
+
845
+ self.convs2 = nn.ModuleList([
846
+ weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=1,
847
+ padding=get_padding(kernel_size, 1))),
848
+ weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=1,
849
+ padding=get_padding(kernel_size, 1))),
850
+ weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=1,
851
+ padding=get_padding(kernel_size, 1)))
852
+ ])
853
+ self.convs2.apply(init_weights)
854
+
855
+ def forward(self, x, x_mask=None):
856
+ for c1, c2 in zip(self.convs1, self.convs2):
857
+ xt = F.leaky_relu(x, LRELU_SLOPE)
858
+ if x_mask is not None:
859
+ xt = xt * x_mask
860
+ xt = c1(xt)
861
+ xt = F.leaky_relu(xt, LRELU_SLOPE)
862
+ if x_mask is not None:
863
+ xt = xt * x_mask
864
+ xt = c2(xt)
865
+ x = xt + x
866
+ if x_mask is not None:
867
+ x = x * x_mask
868
+ return x
869
+
870
+ def remove_weight_norm(self):
871
+ for l in self.convs1:
872
+ remove_weight_norm(l)
873
+ for l in self.convs2:
874
+ remove_weight_norm(l)
875
+
876
+
877
+ class ResBlock2(torch.nn.Module):
878
+ def __init__(self, channels, kernel_size=3, dilation=(1, 3)):
879
+ super(ResBlock2, self).__init__()
880
+ self.convs = nn.ModuleList([
881
+ weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=dilation[0],
882
+ padding=get_padding(kernel_size, dilation[0]))),
883
+ weight_norm(Conv1d(channels, channels, kernel_size, 1, dilation=dilation[1],
884
+ padding=get_padding(kernel_size, dilation[1])))
885
+ ])
886
+ self.convs.apply(init_weights)
887
+
888
+ def forward(self, x, x_mask=None):
889
+ for c in self.convs:
890
+ xt = F.leaky_relu(x, LRELU_SLOPE)
891
+ if x_mask is not None:
892
+ xt = xt * x_mask
893
+ xt = c(xt)
894
+ x = xt + x
895
+ if x_mask is not None:
896
+ x = x * x_mask
897
+ return x
898
+
899
+ def remove_weight_norm(self):
900
+ for l in self.convs:
901
+ remove_weight_norm(l)
902
+
903
+
904
+ class Log(nn.Module):
905
+ def forward(self, x, x_mask, reverse=False, **kwargs):
906
+ if not reverse:
907
+ y = torch.log(torch.clamp_min(x, 1e-5)) * x_mask
908
+ logdet = torch.sum(-y, [1, 2])
909
+ return y, logdet
910
+ else:
911
+ x = torch.exp(x) * x_mask
912
+ return x
913
+
914
+
915
+ class Flip(nn.Module):
916
+ def forward(self, x, *args, reverse=False, **kwargs):
917
+ x = torch.flip(x, [1])
918
+ if not reverse:
919
+ logdet = torch.zeros(x.size(0)).to(dtype=x.dtype, device=x.device)
920
+ return x, logdet
921
+ else:
922
+ return x
923
+
924
+
925
+ class ElementwiseAffine(nn.Module):
926
+ def __init__(self, channels):
927
+ super().__init__()
928
+ self.channels = channels
929
+ self.m = nn.Parameter(torch.zeros(channels,1))
930
+ self.logs = nn.Parameter(torch.zeros(channels,1))
931
+
932
+ def forward(self, x, x_mask, reverse=False, **kwargs):
933
+ if not reverse:
934
+ y = self.m + torch.exp(self.logs) * x
935
+ y = y * x_mask
936
+ logdet = torch.sum(self.logs * x_mask, [1,2])
937
+ return y, logdet
938
+ else:
939
+ x = (x - self.m) * torch.exp(-self.logs) * x_mask
940
+ return x
941
+
942
+
943
+ class ResidualCouplingLayer(nn.Module):
944
+ def __init__(self,
945
+ channels,
946
+ hidden_channels,
947
+ kernel_size,
948
+ dilation_rate,
949
+ n_layers,
950
+ p_dropout=0,
951
+ gin_channels=0,
952
+ mean_only=False):
953
+ assert channels % 2 == 0, "channels should be divisible by 2"
954
+ super().__init__()
955
+ self.channels = channels
956
+ self.hidden_channels = hidden_channels
957
+ self.kernel_size = kernel_size
958
+ self.dilation_rate = dilation_rate
959
+ self.n_layers = n_layers
960
+ self.half_channels = channels // 2
961
+ self.mean_only = mean_only
962
+
963
+ self.pre = nn.Conv1d(self.half_channels, hidden_channels, 1)
964
+ self.enc = WN(hidden_channels, kernel_size, dilation_rate, n_layers, p_dropout=p_dropout, gin_channels=gin_channels)
965
+ self.post = nn.Conv1d(hidden_channels, self.half_channels * (2 - mean_only), 1)
966
+ self.post.weight.data.zero_()
967
+ self.post.bias.data.zero_()
968
+
969
+ def forward(self, x, x_mask, g=None, reverse=False):
970
+ x0, x1 = torch.split(x, [self.half_channels]*2, 1)
971
+ h = self.pre(x0) * x_mask
972
+ h = self.enc(h, x_mask, g=g)
973
+ stats = self.post(h) * x_mask
974
+ if not self.mean_only:
975
+ m, logs = torch.split(stats, [self.half_channels]*2, 1)
976
+ else:
977
+ m = stats
978
+ logs = torch.zeros_like(m)
979
+
980
+ if not reverse:
981
+ x1 = m + x1 * torch.exp(logs) * x_mask
982
+ x = torch.cat([x0, x1], 1)
983
+ logdet = torch.sum(logs, [1,2])
984
+ return x, logdet
985
+ else:
986
+ x1 = (x1 - m) * torch.exp(-logs) * x_mask
987
+ x = torch.cat([x0, x1], 1)
988
+ return x
989
+
990
+
991
+ class ConvFlow(nn.Module):
992
+ def __init__(self, in_channels, filter_channels, kernel_size, n_layers, num_bins=10, tail_bound=5.0):
993
+ super().__init__()
994
+ self.in_channels = in_channels
995
+ self.filter_channels = filter_channels
996
+ self.kernel_size = kernel_size
997
+ self.n_layers = n_layers
998
+ self.num_bins = num_bins
999
+ self.tail_bound = tail_bound
1000
+ self.half_channels = in_channels // 2
1001
+
1002
+ self.pre = nn.Conv1d(self.half_channels, filter_channels, 1)
1003
+ self.convs = DDSConv(filter_channels, kernel_size, n_layers, p_dropout=0.)
1004
+ self.proj = nn.Conv1d(filter_channels, self.half_channels * (num_bins * 3 - 1), 1)
1005
+ self.proj.weight.data.zero_()
1006
+ self.proj.bias.data.zero_()
1007
+
1008
+ def forward(self, x, x_mask, g=None, reverse=False):
1009
+ x0, x1 = torch.split(x, [self.half_channels]*2, 1)
1010
+ h = self.pre(x0)
1011
+ h = self.convs(h, x_mask, g=g)
1012
+ h = self.proj(h) * x_mask
1013
+
1014
+ b, c, t = x0.shape
1015
+ h = h.reshape(b, c, -1, t).permute(0, 1, 3, 2) # [b, cx?, t] -> [b, c, t, ?]
1016
+
1017
+ unnormalized_widths = h[..., :self.num_bins] / math.sqrt(self.filter_channels)
1018
+ unnormalized_heights = h[..., self.num_bins:2*self.num_bins] / math.sqrt(self.filter_channels)
1019
+ unnormalized_derivatives = h[..., 2 * self.num_bins:]
1020
+
1021
+ x1, logabsdet = piecewise_rational_quadratic_transform(x1,
1022
+ unnormalized_widths,
1023
+ unnormalized_heights,
1024
+ unnormalized_derivatives,
1025
+ inverse=reverse,
1026
+ tails='linear',
1027
+ tail_bound=self.tail_bound
1028
+ )
1029
+
1030
+ x = torch.cat([x0, x1], 1) * x_mask
1031
+ logdet = torch.sum(logabsdet * x_mask, [1,2])
1032
+ if not reverse:
1033
+ return x, logdet
1034
+ else:
1035
+ return x
1036
+
1037
+ import torch
1038
+ from torch.nn import functional as F
1039
+
1040
+ import numpy as np
1041
+
1042
+
1043
+ DEFAULT_MIN_BIN_WIDTH = 1e-3
1044
+ DEFAULT_MIN_BIN_HEIGHT = 1e-3
1045
+ DEFAULT_MIN_DERIVATIVE = 1e-3
1046
+
1047
+
1048
+ def piecewise_rational_quadratic_transform(inputs,
1049
+ unnormalized_widths,
1050
+ unnormalized_heights,
1051
+ unnormalized_derivatives,
1052
+ inverse=False,
1053
+ tails=None,
1054
+ tail_bound=1.,
1055
+ min_bin_width=DEFAULT_MIN_BIN_WIDTH,
1056
+ min_bin_height=DEFAULT_MIN_BIN_HEIGHT,
1057
+ min_derivative=DEFAULT_MIN_DERIVATIVE):
1058
+
1059
+ if tails is None:
1060
+ spline_fn = rational_quadratic_spline
1061
+ spline_kwargs = {}
1062
+ else:
1063
+ spline_fn = unconstrained_rational_quadratic_spline
1064
+ spline_kwargs = {
1065
+ 'tails': tails,
1066
+ 'tail_bound': tail_bound
1067
+ }
1068
+
1069
+ outputs, logabsdet = spline_fn(
1070
+ inputs=inputs,
1071
+ unnormalized_widths=unnormalized_widths,
1072
+ unnormalized_heights=unnormalized_heights,
1073
+ unnormalized_derivatives=unnormalized_derivatives,
1074
+ inverse=inverse,
1075
+ min_bin_width=min_bin_width,
1076
+ min_bin_height=min_bin_height,
1077
+ min_derivative=min_derivative,
1078
+ **spline_kwargs
1079
+ )
1080
+ return outputs, logabsdet
1081
+
1082
+
1083
+ def searchsorted(bin_locations, inputs, eps=1e-6):
1084
+ bin_locations[..., -1] += eps
1085
+ return torch.sum(
1086
+ inputs[..., None] >= bin_locations,
1087
+ dim=-1
1088
+ ) - 1
1089
+
1090
+
1091
+ def unconstrained_rational_quadratic_spline(inputs,
1092
+ unnormalized_widths,
1093
+ unnormalized_heights,
1094
+ unnormalized_derivatives,
1095
+ inverse=False,
1096
+ tails='linear',
1097
+ tail_bound=1.,
1098
+ min_bin_width=DEFAULT_MIN_BIN_WIDTH,
1099
+ min_bin_height=DEFAULT_MIN_BIN_HEIGHT,
1100
+ min_derivative=DEFAULT_MIN_DERIVATIVE):
1101
+ inside_interval_mask = (inputs >= -tail_bound) & (inputs <= tail_bound)
1102
+ outside_interval_mask = ~inside_interval_mask
1103
+
1104
+ outputs = torch.zeros_like(inputs)
1105
+ logabsdet = torch.zeros_like(inputs)
1106
+
1107
+ if tails == 'linear':
1108
+ unnormalized_derivatives = F.pad(unnormalized_derivatives, pad=(1, 1))
1109
+ constant = np.log(np.exp(1 - min_derivative) - 1)
1110
+ unnormalized_derivatives[..., 0] = constant
1111
+ unnormalized_derivatives[..., -1] = constant
1112
+
1113
+ outputs[outside_interval_mask] = inputs[outside_interval_mask]
1114
+ logabsdet[outside_interval_mask] = 0
1115
+ else:
1116
+ raise RuntimeError('{} tails are not implemented.'.format(tails))
1117
+
1118
+ outputs[inside_interval_mask], logabsdet[inside_interval_mask] = rational_quadratic_spline(
1119
+ inputs=inputs[inside_interval_mask],
1120
+ unnormalized_widths=unnormalized_widths[inside_interval_mask, :],
1121
+ unnormalized_heights=unnormalized_heights[inside_interval_mask, :],
1122
+ unnormalized_derivatives=unnormalized_derivatives[inside_interval_mask, :],
1123
+ inverse=inverse,
1124
+ left=-tail_bound, right=tail_bound, bottom=-tail_bound, top=tail_bound,
1125
+ min_bin_width=min_bin_width,
1126
+ min_bin_height=min_bin_height,
1127
+ min_derivative=min_derivative
1128
+ )
1129
+
1130
+ return outputs, logabsdet
1131
+
1132
+ def rational_quadratic_spline(inputs,
1133
+ unnormalized_widths,
1134
+ unnormalized_heights,
1135
+ unnormalized_derivatives,
1136
+ inverse=False,
1137
+ left=0., right=1., bottom=0., top=1.,
1138
+ min_bin_width=DEFAULT_MIN_BIN_WIDTH,
1139
+ min_bin_height=DEFAULT_MIN_BIN_HEIGHT,
1140
+ min_derivative=DEFAULT_MIN_DERIVATIVE):
1141
+ if torch.min(inputs) < left or torch.max(inputs) > right:
1142
+ raise ValueError('Input to a transform is not within its domain')
1143
+
1144
+ num_bins = unnormalized_widths.shape[-1]
1145
+
1146
+ if min_bin_width * num_bins > 1.0:
1147
+ raise ValueError('Minimal bin width too large for the number of bins')
1148
+ if min_bin_height * num_bins > 1.0:
1149
+ raise ValueError('Minimal bin height too large for the number of bins')
1150
+
1151
+ widths = F.softmax(unnormalized_widths, dim=-1)
1152
+ widths = min_bin_width + (1 - min_bin_width * num_bins) * widths
1153
+ cumwidths = torch.cumsum(widths, dim=-1)
1154
+ cumwidths = F.pad(cumwidths, pad=(1, 0), mode='constant', value=0.0)
1155
+ cumwidths = (right - left) * cumwidths + left
1156
+ cumwidths[..., 0] = left
1157
+ cumwidths[..., -1] = right
1158
+ widths = cumwidths[..., 1:] - cumwidths[..., :-1]
1159
+
1160
+ derivatives = min_derivative + F.softplus(unnormalized_derivatives)
1161
+
1162
+ heights = F.softmax(unnormalized_heights, dim=-1)
1163
+ heights = min_bin_height + (1 - min_bin_height * num_bins) * heights
1164
+ cumheights = torch.cumsum(heights, dim=-1)
1165
+ cumheights = F.pad(cumheights, pad=(1, 0), mode='constant', value=0.0)
1166
+ cumheights = (top - bottom) * cumheights + bottom
1167
+ cumheights[..., 0] = bottom
1168
+ cumheights[..., -1] = top
1169
+ heights = cumheights[..., 1:] - cumheights[..., :-1]
1170
+
1171
+ if inverse:
1172
+ bin_idx = searchsorted(cumheights, inputs)[..., None]
1173
+ else:
1174
+ bin_idx = searchsorted(cumwidths, inputs)[..., None]
1175
+
1176
+ input_cumwidths = cumwidths.gather(-1, bin_idx)[..., 0]
1177
+ input_bin_widths = widths.gather(-1, bin_idx)[..., 0]
1178
+
1179
+ input_cumheights = cumheights.gather(-1, bin_idx)[..., 0]
1180
+ delta = heights / widths
1181
+ input_delta = delta.gather(-1, bin_idx)[..., 0]
1182
+
1183
+ input_derivatives = derivatives.gather(-1, bin_idx)[..., 0]
1184
+ input_derivatives_plus_one = derivatives[..., 1:].gather(-1, bin_idx)[..., 0]
1185
+
1186
+ input_heights = heights.gather(-1, bin_idx)[..., 0]
1187
+
1188
+ if inverse:
1189
+ a = (((inputs - input_cumheights) * (input_derivatives
1190
+ + input_derivatives_plus_one
1191
+ - 2 * input_delta)
1192
+ + input_heights * (input_delta - input_derivatives)))
1193
+ b = (input_heights * input_derivatives
1194
+ - (inputs - input_cumheights) * (input_derivatives
1195
+ + input_derivatives_plus_one
1196
+ - 2 * input_delta))
1197
+ c = - input_delta * (inputs - input_cumheights)
1198
+
1199
+ discriminant = b.pow(2) - 4 * a * c
1200
+ assert (discriminant >= 0).all()
1201
+
1202
+ root = (2 * c) / (-b - torch.sqrt(discriminant))
1203
+ outputs = root * input_bin_widths + input_cumwidths
1204
+
1205
+ theta_one_minus_theta = root * (1 - root)
1206
+ denominator = input_delta + ((input_derivatives + input_derivatives_plus_one - 2 * input_delta)
1207
+ * theta_one_minus_theta)
1208
+ derivative_numerator = input_delta.pow(2) * (input_derivatives_plus_one * root.pow(2)
1209
+ + 2 * input_delta * theta_one_minus_theta
1210
+ + input_derivatives * (1 - root).pow(2))
1211
+ logabsdet = torch.log(derivative_numerator) - 2 * torch.log(denominator)
1212
+
1213
+ return outputs, -logabsdet
1214
+ else:
1215
+ theta = (inputs - input_cumwidths) / input_bin_widths
1216
+ theta_one_minus_theta = theta * (1 - theta)
1217
+
1218
+ numerator = input_heights * (input_delta * theta.pow(2)
1219
+ + input_derivatives * theta_one_minus_theta)
1220
+ denominator = input_delta + ((input_derivatives + input_derivatives_plus_one - 2 * input_delta)
1221
+ * theta_one_minus_theta)
1222
+ outputs = input_cumheights + numerator / denominator
1223
+
1224
+ derivative_numerator = input_delta.pow(2) * (input_derivatives_plus_one * theta.pow(2)
1225
+ + 2 * input_delta * theta_one_minus_theta
1226
+ + input_derivatives * (1 - theta).pow(2))
1227
+ logabsdet = torch.log(derivative_numerator) - 2 * torch.log(denominator)
1228
+
1229
+ return outputs, logabsdet
1230
+
1231
+ """LayerFusion 4-Bit (LF4) Engine for Inflect-Micro-v2 / Vortex-TTS.
1232
+
1233
+ Implements native 4-bit packed uint8 nibble storage with blockwise scale & zero metadata.
1234
+ Inherits design principles from vortex_embed native 4-bit execution:
1235
+ - Zero precomputed FP32 weight tables in memory.
1236
+ - On-the-fly nibble unpacking during forward pass execution.
1237
+ - 6.4x weight compression (35.65 MB -> 5.57 MB).
1238
+ """
1239
+
1240
+ import torch
1241
+ import torch.nn as nn
1242
+ import torch.nn.functional as F
1243
+
1244
+
1245
+ class LF4Linear(nn.Module):
1246
+ """Native LF4 4-bit Packed Linear Layer."""
1247
+
1248
+ def __init__(self, in_features: int, out_features: int, block_size: int = 32):
1249
+ super().__init__()
1250
+ self.in_features = in_features
1251
+ self.out_features = out_features
1252
+ self.block_size = block_size
1253
+
1254
+ num_weights = in_features * out_features
1255
+ pad = (block_size - (num_weights % block_size)) % block_size
1256
+ total_len = num_weights + pad
1257
+ num_blocks = total_len // block_size
1258
+
1259
+ self.register_buffer("packed_weight", torch.zeros((total_len // 2,), dtype=torch.uint8))
1260
+ self.register_buffer("scales", torch.ones((num_blocks, 1), dtype=torch.float16))
1261
+ self.register_buffer("zeros", torch.zeros((num_blocks, 1), dtype=torch.float16))
1262
+ self.bias = None
1263
+
1264
+ @classmethod
1265
+ def from_float(cls, float_layer: nn.Linear, block_size: int = 32) -> LF4Linear:
1266
+ w = float_layer.weight.data
1267
+ out_f, in_f = w.shape
1268
+ lf4 = cls(in_f, out_f, block_size=block_size)
1269
+
1270
+ flat = w.reshape(-1)
1271
+ n = flat.numel()
1272
+ pad = (block_size - (n % block_size)) % block_size
1273
+ if pad > 0:
1274
+ flat = torch.cat([flat, torch.zeros(pad, device=w.device)])
1275
+
1276
+ groups = flat.reshape(-1, block_size)
1277
+ g_min = groups.min(dim=-1, keepdim=True).values
1278
+ g_max = groups.max(dim=-1, keepdim=True).values
1279
+
1280
+ scales = ((g_max - g_min) / 15.0).clamp(min=1e-8)
1281
+ zeros = g_min
1282
+
1283
+ q_groups = torch.round((groups - zeros) / scales).clamp(0, 15).to(torch.uint8)
1284
+
1285
+ q_flat = q_groups.reshape(-1)
1286
+ low_nibbles = q_flat[0::2]
1287
+ high_nibbles = q_flat[1::2]
1288
+ packed = low_nibbles | (high_nibbles << 4)
1289
+
1290
+ lf4.packed_weight.copy_(packed)
1291
+ lf4.scales.copy_(scales.to(torch.float16))
1292
+ lf4.zeros.copy_(zeros.to(torch.float16))
1293
+ if float_layer.bias is not None:
1294
+ lf4.bias = nn.Parameter(float_layer.bias.data.clone())
1295
+ return lf4
1296
+
1297
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
1298
+ p = self.packed_weight
1299
+ low = (p & 0x0F).to(torch.float32)
1300
+ high = ((p >> 4) & 0x0F).to(torch.float32)
1301
+
1302
+ unpacked = torch.empty((p.numel() * 2,), dtype=torch.float32, device=x.device)
1303
+ unpacked[0::2] = low
1304
+ unpacked[1::2] = high
1305
+
1306
+ n_orig = self.in_features * self.out_features
1307
+ unpacked = unpacked[: ((n_orig + self.block_size - 1) // self.block_size) * self.block_size]
1308
+ blocks = unpacked.reshape(-1, self.block_size)
1309
+
1310
+ s = self.scales.to(torch.float32)
1311
+ z = self.zeros.to(torch.float32)
1312
+ w_deq = (blocks * s + z).reshape(-1)[:n_orig].reshape(self.out_features, self.in_features)
1313
+
1314
+ return F.linear(x, w_deq, self.bias)
1315
+
1316
+
1317
+ class LF4Conv1d(nn.Module):
1318
+ """Native LF4 4-bit Packed Conv1d Layer."""
1319
+
1320
+ def __init__(
1321
+ self,
1322
+ in_channels: int,
1323
+ out_channels: int,
1324
+ kernel_size: int,
1325
+ stride: int = 1,
1326
+ padding: int = 0,
1327
+ dilation: int = 1,
1328
+ groups: int = 1,
1329
+ block_size: int = 32,
1330
+ ):
1331
+ super().__init__()
1332
+ self.in_channels = in_channels
1333
+ self.out_channels = out_channels
1334
+ self.kernel_size = kernel_size
1335
+ self.stride = stride
1336
+ self.padding = padding
1337
+ self.dilation = dilation
1338
+ self.groups = groups
1339
+ self.block_size = block_size
1340
+
1341
+ num_weights = out_channels * (in_channels // groups) * kernel_size
1342
+ pad = (block_size - (num_weights % block_size)) % block_size
1343
+ total_len = num_weights + pad
1344
+ num_blocks = total_len // block_size
1345
+
1346
+ self.register_buffer("packed_weight", torch.zeros((total_len // 2,), dtype=torch.uint8))
1347
+ self.register_buffer("scales", torch.ones((num_blocks, 1), dtype=torch.float16))
1348
+ self.register_buffer("zeros", torch.zeros((num_blocks, 1), dtype=torch.float16))
1349
+ self.bias = None
1350
+
1351
+ @classmethod
1352
+ def from_float(cls, conv: nn.Conv1d, block_size: int = 32) -> LF4Conv1d:
1353
+ lf4 = cls(
1354
+ conv.in_channels,
1355
+ conv.out_channels,
1356
+ conv.kernel_size[0],
1357
+ stride=conv.stride[0],
1358
+ padding=conv.padding[0],
1359
+ dilation=conv.dilation[0],
1360
+ groups=conv.groups,
1361
+ block_size=block_size,
1362
+ )
1363
+ w = conv.weight.data
1364
+ flat = w.reshape(-1)
1365
+ n = flat.numel()
1366
+ pad = (block_size - (n % block_size)) % block_size
1367
+ if pad > 0:
1368
+ flat = torch.cat([flat, torch.zeros(pad, device=w.device)])
1369
+
1370
+ groups = flat.reshape(-1, block_size)
1371
+ g_min = groups.min(dim=-1, keepdim=True).values
1372
+ g_max = groups.max(dim=-1, keepdim=True).values
1373
+
1374
+ scales = ((g_max - g_min) / 15.0).clamp(min=1e-8)
1375
+ zeros = g_min
1376
+
1377
+ q_groups = torch.round((groups - zeros) / scales).clamp(0, 15).to(torch.uint8)
1378
+
1379
+ q_flat = q_groups.reshape(-1)
1380
+ low_nibbles = q_flat[0::2]
1381
+ high_nibbles = q_flat[1::2]
1382
+ packed = low_nibbles | (high_nibbles << 4)
1383
+
1384
+ lf4.packed_weight.copy_(packed)
1385
+ lf4.scales.copy_(scales.to(torch.float16))
1386
+ lf4.zeros.copy_(zeros.to(torch.float16))
1387
+ if conv.bias is not None:
1388
+ lf4.bias = nn.Parameter(conv.bias.data.clone())
1389
+ return lf4
1390
+
1391
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
1392
+ p = self.packed_weight
1393
+ low = (p & 0x0F).to(torch.float32)
1394
+ high = ((p >> 4) & 0x0F).to(torch.float32)
1395
+
1396
+ unpacked = torch.empty((p.numel() * 2,), dtype=torch.float32, device=x.device)
1397
+ unpacked[0::2] = low
1398
+ unpacked[1::2] = high
1399
+
1400
+ n_orig = self.out_channels * (self.in_channels // self.groups) * self.kernel_size
1401
+ unpacked = unpacked[: ((n_orig + self.block_size - 1) // self.block_size) * self.block_size]
1402
+ blocks = unpacked.reshape(-1, self.block_size)
1403
+
1404
+ s = self.scales.to(torch.float32)
1405
+ z = self.zeros.to(torch.float32)
1406
+ w_deq = (blocks * s + z).reshape(-1)[:n_orig].reshape(
1407
+ self.out_channels, self.in_channels // self.groups, self.kernel_size
1408
+ )
1409
+
1410
+ return F.conv1d(
1411
+ x,
1412
+ w_deq,
1413
+ self.bias,
1414
+ stride=self.stride,
1415
+ padding=self.padding,
1416
+ dilation=self.dilation,
1417
+ groups=self.groups,
1418
+ )
1419
+
1420
+ import copy
1421
+ import math
1422
+ import torch
1423
+ from torch import nn
1424
+ from torch.nn import functional as F
1425
+
1426
+
1427
+ from torch.nn import Conv1d, ConvTranspose1d, AvgPool1d, Conv2d
1428
+ from torch.nn.utils import weight_norm, remove_weight_norm, spectral_norm
1429
+
1430
+
1431
+ class StochasticDurationPredictor(nn.Module):
1432
+ def __init__(self, in_channels, filter_channels, kernel_size, p_dropout, n_flows=4, gin_channels=0):
1433
+ super().__init__()
1434
+ filter_channels = in_channels # it needs to be removed from future version.
1435
+ self.in_channels = in_channels
1436
+ self.filter_channels = filter_channels
1437
+ self.kernel_size = kernel_size
1438
+ self.p_dropout = p_dropout
1439
+ self.n_flows = n_flows
1440
+ self.gin_channels = gin_channels
1441
+
1442
+ self.log_flow = Log()
1443
+ self.flows = nn.ModuleList()
1444
+ self.flows.append(ElementwiseAffine(2))
1445
+ for i in range(n_flows):
1446
+ self.flows.append(ConvFlow(2, filter_channels, kernel_size, n_layers=3))
1447
+ self.flows.append(Flip())
1448
+
1449
+ self.post_pre = nn.Conv1d(1, filter_channels, 1)
1450
+ self.post_proj = nn.Conv1d(filter_channels, filter_channels, 1)
1451
+ self.post_convs = DDSConv(filter_channels, kernel_size, n_layers=3, p_dropout=p_dropout)
1452
+ self.post_flows = nn.ModuleList()
1453
+ self.post_flows.append(ElementwiseAffine(2))
1454
+ for i in range(4):
1455
+ self.post_flows.append(ConvFlow(2, filter_channels, kernel_size, n_layers=3))
1456
+ self.post_flows.append(Flip())
1457
+
1458
+ self.pre = nn.Conv1d(in_channels, filter_channels, 1)
1459
+ self.proj = nn.Conv1d(filter_channels, filter_channels, 1)
1460
+ self.convs = DDSConv(filter_channels, kernel_size, n_layers=3, p_dropout=p_dropout)
1461
+ if gin_channels != 0:
1462
+ self.cond = nn.Conv1d(gin_channels, filter_channels, 1)
1463
+
1464
+ def forward(self, x, x_mask, w=None, g=None, reverse=False, noise_scale=1.0):
1465
+ x = torch.detach(x)
1466
+ x = self.pre(x)
1467
+ if g is not None:
1468
+ g = torch.detach(g)
1469
+ x = x + self.cond(g)
1470
+ x = self.convs(x, x_mask)
1471
+ x = self.proj(x) * x_mask
1472
+
1473
+ if not reverse:
1474
+ flows = self.flows
1475
+ assert w is not None
1476
+
1477
+ logdet_tot_q = 0
1478
+ h_w = self.post_pre(w)
1479
+ h_w = self.post_convs(h_w, x_mask)
1480
+ h_w = self.post_proj(h_w) * x_mask
1481
+ e_q = torch.randn(w.size(0), 2, w.size(2)).to(device=x.device, dtype=x.dtype) * x_mask
1482
+ z_q = e_q
1483
+ for flow in self.post_flows:
1484
+ z_q, logdet_q = flow(z_q, x_mask, g=(x + h_w))
1485
+ logdet_tot_q += logdet_q
1486
+ z_u, z1 = torch.split(z_q, [1, 1], 1)
1487
+ u = torch.sigmoid(z_u) * x_mask
1488
+ z0 = (w - u) * x_mask
1489
+ logdet_tot_q += torch.sum((F.logsigmoid(z_u) + F.logsigmoid(-z_u)) * x_mask, [1,2])
1490
+ logq = torch.sum(-0.5 * (math.log(2*math.pi) + (e_q**2)) * x_mask, [1,2]) - logdet_tot_q
1491
+
1492
+ logdet_tot = 0
1493
+ z0, logdet = self.log_flow(z0, x_mask)
1494
+ logdet_tot += logdet
1495
+ z = torch.cat([z0, z1], 1)
1496
+ for flow in flows:
1497
+ z, logdet = flow(z, x_mask, g=x, reverse=reverse)
1498
+ logdet_tot = logdet_tot + logdet
1499
+ nll = torch.sum(0.5 * (math.log(2*math.pi) + (z**2)) * x_mask, [1,2]) - logdet_tot
1500
+ return nll + logq # [b]
1501
+ else:
1502
+ flows = list(reversed(self.flows))
1503
+ flows = flows[:-2] + [flows[-1]] # remove a useless vflow
1504
+ z = torch.randn(x.size(0), 2, x.size(2)).to(device=x.device, dtype=x.dtype) * noise_scale
1505
+ for flow in flows:
1506
+ z = flow(z, x_mask, g=x, reverse=reverse)
1507
+ z0, z1 = torch.split(z, [1, 1], 1)
1508
+ logw = z0
1509
+ return logw
1510
+
1511
+
1512
+ class DurationPredictor(nn.Module):
1513
+ def __init__(self, in_channels, filter_channels, kernel_size, p_dropout, gin_channels=0):
1514
+ super().__init__()
1515
+
1516
+ self.in_channels = in_channels
1517
+ self.filter_channels = filter_channels
1518
+ self.kernel_size = kernel_size
1519
+ self.p_dropout = p_dropout
1520
+ self.gin_channels = gin_channels
1521
+
1522
+ self.drop = nn.Dropout(p_dropout)
1523
+ self.conv_1 = nn.Conv1d(in_channels, filter_channels, kernel_size, padding=kernel_size//2)
1524
+ self.norm_1 = LayerNorm(filter_channels)
1525
+ self.conv_2 = nn.Conv1d(filter_channels, filter_channels, kernel_size, padding=kernel_size//2)
1526
+ self.norm_2 = LayerNorm(filter_channels)
1527
+ self.proj = nn.Conv1d(filter_channels, 1, 1)
1528
+
1529
+ if gin_channels != 0:
1530
+ self.cond = nn.Conv1d(gin_channels, in_channels, 1)
1531
+
1532
+ def forward(self, x, x_mask, g=None):
1533
+ x = torch.detach(x)
1534
+ if g is not None:
1535
+ g = torch.detach(g)
1536
+ x = x + self.cond(g)
1537
+ x = self.conv_1(x * x_mask)
1538
+ x = torch.relu(x)
1539
+ x = self.norm_1(x)
1540
+ x = self.drop(x)
1541
+ x = self.conv_2(x * x_mask)
1542
+ x = torch.relu(x)
1543
+ x = self.norm_2(x)
1544
+ x = self.drop(x)
1545
+ x = self.proj(x * x_mask)
1546
+ return x * x_mask
1547
+
1548
+
1549
+ class TextEncoder(nn.Module):
1550
+ def __init__(self,
1551
+ n_vocab,
1552
+ out_channels,
1553
+ hidden_channels,
1554
+ filter_channels,
1555
+ n_heads,
1556
+ n_layers,
1557
+ kernel_size,
1558
+ p_dropout):
1559
+ super().__init__()
1560
+ self.n_vocab = n_vocab
1561
+ self.out_channels = out_channels
1562
+ self.hidden_channels = hidden_channels
1563
+ self.filter_channels = filter_channels
1564
+ self.n_heads = n_heads
1565
+ self.n_layers = n_layers
1566
+ self.kernel_size = kernel_size
1567
+ self.p_dropout = p_dropout
1568
+
1569
+ self.emb = nn.Embedding(n_vocab, hidden_channels)
1570
+ nn.init.normal_(self.emb.weight, 0.0, hidden_channels**-0.5)
1571
+
1572
+ self.encoder = Encoder(
1573
+ hidden_channels,
1574
+ filter_channels,
1575
+ n_heads,
1576
+ n_layers,
1577
+ kernel_size,
1578
+ p_dropout)
1579
+ self.proj= nn.Conv1d(hidden_channels, out_channels * 2, 1)
1580
+
1581
+ def forward(self, x, x_lengths):
1582
+ x = self.emb(x) * math.sqrt(self.hidden_channels) # [b, t, h]
1583
+ x = torch.transpose(x, 1, -1) # [b, h, t]
1584
+ x_mask = torch.unsqueeze(sequence_mask(x_lengths, x.size(2)), 1).to(x.dtype)
1585
+
1586
+ x = self.encoder(x * x_mask, x_mask)
1587
+ stats = self.proj(x) * x_mask
1588
+
1589
+ m, logs = torch.split(stats, self.out_channels, dim=1)
1590
+ return x, m, logs, x_mask
1591
+
1592
+
1593
+ class ResidualCouplingBlock(nn.Module):
1594
+ def __init__(self,
1595
+ channels,
1596
+ hidden_channels,
1597
+ kernel_size,
1598
+ dilation_rate,
1599
+ n_layers,
1600
+ n_flows=4,
1601
+ gin_channels=0):
1602
+ super().__init__()
1603
+ self.channels = channels
1604
+ self.hidden_channels = hidden_channels
1605
+ self.kernel_size = kernel_size
1606
+ self.dilation_rate = dilation_rate
1607
+ self.n_layers = n_layers
1608
+ self.n_flows = n_flows
1609
+ self.gin_channels = gin_channels
1610
+
1611
+ self.flows = nn.ModuleList()
1612
+ for i in range(n_flows):
1613
+ self.flows.append(ResidualCouplingLayer(channels, hidden_channels, kernel_size, dilation_rate, n_layers, gin_channels=gin_channels, mean_only=True))
1614
+ self.flows.append(Flip())
1615
+
1616
+ def forward(self, x, x_mask, g=None, reverse=False):
1617
+ if not reverse:
1618
+ for flow in self.flows:
1619
+ x, _ = flow(x, x_mask, g=g, reverse=reverse)
1620
+ else:
1621
+ for flow in reversed(self.flows):
1622
+ x = flow(x, x_mask, g=g, reverse=reverse)
1623
+ return x
1624
+
1625
+
1626
+ class PosteriorEncoder(nn.Module):
1627
+ def __init__(self,
1628
+ in_channels,
1629
+ out_channels,
1630
+ hidden_channels,
1631
+ kernel_size,
1632
+ dilation_rate,
1633
+ n_layers,
1634
+ gin_channels=0):
1635
+ super().__init__()
1636
+ self.in_channels = in_channels
1637
+ self.out_channels = out_channels
1638
+ self.hidden_channels = hidden_channels
1639
+ self.kernel_size = kernel_size
1640
+ self.dilation_rate = dilation_rate
1641
+ self.n_layers = n_layers
1642
+ self.gin_channels = gin_channels
1643
+
1644
+ self.pre = nn.Conv1d(in_channels, hidden_channels, 1)
1645
+ self.enc = WN(hidden_channels, kernel_size, dilation_rate, n_layers, gin_channels=gin_channels)
1646
+ self.proj = nn.Conv1d(hidden_channels, out_channels * 2, 1)
1647
+
1648
+ def forward(self, x, x_lengths, g=None):
1649
+ x_mask = torch.unsqueeze(sequence_mask(x_lengths, x.size(2)), 1).to(x.dtype)
1650
+ x = self.pre(x) * x_mask
1651
+ x = self.enc(x, x_mask, g=g)
1652
+ stats = self.proj(x) * x_mask
1653
+ m, logs = torch.split(stats, self.out_channels, dim=1)
1654
+ z = (m + torch.randn_like(m) * torch.exp(logs)) * x_mask
1655
+ return z, m, logs, x_mask
1656
+
1657
+
1658
+ class Generator(torch.nn.Module):
1659
+ def __init__(self, initial_channel, resblock, resblock_kernel_sizes, resblock_dilation_sizes, upsample_rates, upsample_initial_channel, upsample_kernel_sizes, gin_channels=0, decoder_alias_free=False, decoder_alias_free_start_stage=2, decoder_snake_logscale=True):
1660
+ super(Generator, self).__init__()
1661
+ self.num_kernels = len(resblock_kernel_sizes)
1662
+ self.num_upsamples = len(upsample_rates)
1663
+ self.conv_pre = Conv1d(initial_channel, upsample_initial_channel, 7, 1, padding=3)
1664
+ resblock_class = ResBlock1 if resblock == '1' else ResBlock2
1665
+ self.decoder_alias_free = bool(decoder_alias_free)
1666
+ self.decoder_alias_free_start_stage = int(decoder_alias_free_start_stage)
1667
+
1668
+ self.ups = nn.ModuleList()
1669
+ for i, (u, k) in enumerate(zip(upsample_rates, upsample_kernel_sizes)):
1670
+ self.ups.append(weight_norm(
1671
+ ConvTranspose1d(upsample_initial_channel//(2**i), upsample_initial_channel//(2**(i+1)),
1672
+ k, u, padding=(k-u)//2)))
1673
+
1674
+ self.resblocks = nn.ModuleList()
1675
+ for i in range(len(self.ups)):
1676
+ ch = upsample_initial_channel//(2**(i+1))
1677
+ for j, (k, d) in enumerate(zip(resblock_kernel_sizes, resblock_dilation_sizes)):
1678
+ if self.decoder_alias_free and i >= self.decoder_alias_free_start_stage:
1679
+ self.resblocks.append(AliasFreeResBlock1(
1680
+ ch, k, d, logscale=decoder_snake_logscale))
1681
+ else:
1682
+ self.resblocks.append(resblock_class(ch, k, d))
1683
+
1684
+ self.alias_free_pre_activations = nn.ModuleList()
1685
+ if self.decoder_alias_free:
1686
+ for i in range(self.num_upsamples):
1687
+ channels = upsample_initial_channel // (2 ** i)
1688
+ if i >= self.decoder_alias_free_start_stage:
1689
+ self.alias_free_pre_activations.append(
1690
+ AliasFreeActivation1d(nn.LeakyReLU(LRELU_SLOPE)))
1691
+ else:
1692
+ self.alias_free_pre_activations.append(nn.Identity())
1693
+ self.alias_free_post_activation = AliasFreeActivation1d(
1694
+ SnakeBeta(ch, logscale=decoder_snake_logscale))
1695
+
1696
+ self.conv_post = Conv1d(ch, 1, 7, 1, padding=3, bias=False)
1697
+ self.ups.apply(init_weights)
1698
+
1699
+ if gin_channels != 0:
1700
+ self.cond = nn.Conv1d(gin_channels, upsample_initial_channel, 1)
1701
+
1702
+ def forward(self, x, g=None):
1703
+ x = self.conv_pre(x)
1704
+ if g is not None:
1705
+ x = x + self.cond(g)
1706
+
1707
+ for i in range(self.num_upsamples):
1708
+ if self.decoder_alias_free and i >= self.decoder_alias_free_start_stage:
1709
+ x = self.alias_free_pre_activations[i](x)
1710
+ else:
1711
+ x = F.leaky_relu(x, LRELU_SLOPE)
1712
+ x = self.ups[i](x)
1713
+ xs = None
1714
+ for j in range(self.num_kernels):
1715
+ if xs is None:
1716
+ xs = self.resblocks[i*self.num_kernels+j](x)
1717
+ else:
1718
+ xs += self.resblocks[i*self.num_kernels+j](x)
1719
+ x = xs / self.num_kernels
1720
+ if self.decoder_alias_free:
1721
+ x = self.alias_free_post_activation(x)
1722
+ else:
1723
+ x = F.leaky_relu(x)
1724
+ x = self.conv_post(x)
1725
+ x = torch.tanh(x)
1726
+
1727
+ return x
1728
+
1729
+ def remove_weight_norm(self):
1730
+ print('Removing weight norm...')
1731
+ for l in self.ups:
1732
+ remove_weight_norm(l)
1733
+ for l in self.resblocks:
1734
+ l.remove_weight_norm()
1735
+
1736
+
1737
+ class DiscriminatorP(torch.nn.Module):
1738
+ def __init__(self, period, kernel_size=5, stride=3, use_spectral_norm=False):
1739
+ super(DiscriminatorP, self).__init__()
1740
+ self.period = period
1741
+ self.use_spectral_norm = use_spectral_norm
1742
+ norm_f = weight_norm if use_spectral_norm == False else spectral_norm
1743
+ self.convs = nn.ModuleList([
1744
+ norm_f(Conv2d(1, 32, (kernel_size, 1), (stride, 1), padding=(get_padding(kernel_size, 1), 0))),
1745
+ norm_f(Conv2d(32, 128, (kernel_size, 1), (stride, 1), padding=(get_padding(kernel_size, 1), 0))),
1746
+ norm_f(Conv2d(128, 512, (kernel_size, 1), (stride, 1), padding=(get_padding(kernel_size, 1), 0))),
1747
+ norm_f(Conv2d(512, 1024, (kernel_size, 1), (stride, 1), padding=(get_padding(kernel_size, 1), 0))),
1748
+ norm_f(Conv2d(1024, 1024, (kernel_size, 1), 1, padding=(get_padding(kernel_size, 1), 0))),
1749
+ ])
1750
+ self.conv_post = norm_f(Conv2d(1024, 1, (3, 1), 1, padding=(1, 0)))
1751
+
1752
+ def forward(self, x):
1753
+ fmap = []
1754
+
1755
+ # 1d to 2d
1756
+ b, c, t = x.shape
1757
+ if t % self.period != 0: # pad first
1758
+ n_pad = self.period - (t % self.period)
1759
+ x = F.pad(x, (0, n_pad), "reflect")
1760
+ t = t + n_pad
1761
+ x = x.view(b, c, t // self.period, self.period)
1762
+
1763
+ for l in self.convs:
1764
+ x = l(x)
1765
+ x = F.leaky_relu(x, LRELU_SLOPE)
1766
+ fmap.append(x)
1767
+ x = self.conv_post(x)
1768
+ fmap.append(x)
1769
+ x = torch.flatten(x, 1, -1)
1770
+
1771
+ return x, fmap
1772
+
1773
+
1774
+ class DiscriminatorS(torch.nn.Module):
1775
+ def __init__(self, use_spectral_norm=False):
1776
+ super(DiscriminatorS, self).__init__()
1777
+ norm_f = weight_norm if use_spectral_norm == False else spectral_norm
1778
+ self.convs = nn.ModuleList([
1779
+ norm_f(Conv1d(1, 16, 15, 1, padding=7)),
1780
+ norm_f(Conv1d(16, 64, 41, 4, groups=4, padding=20)),
1781
+ norm_f(Conv1d(64, 256, 41, 4, groups=16, padding=20)),
1782
+ norm_f(Conv1d(256, 1024, 41, 4, groups=64, padding=20)),
1783
+ norm_f(Conv1d(1024, 1024, 41, 4, groups=256, padding=20)),
1784
+ norm_f(Conv1d(1024, 1024, 5, 1, padding=2)),
1785
+ ])
1786
+ self.conv_post = norm_f(Conv1d(1024, 1, 3, 1, padding=1))
1787
+
1788
+ def forward(self, x):
1789
+ fmap = []
1790
+
1791
+ for l in self.convs:
1792
+ x = l(x)
1793
+ x = F.leaky_relu(x, LRELU_SLOPE)
1794
+ fmap.append(x)
1795
+ x = self.conv_post(x)
1796
+ fmap.append(x)
1797
+ x = torch.flatten(x, 1, -1)
1798
+
1799
+ return x, fmap
1800
+
1801
+
1802
+ class MultiPeriodDiscriminator(torch.nn.Module):
1803
+ def __init__(self, use_spectral_norm=False):
1804
+ super(MultiPeriodDiscriminator, self).__init__()
1805
+ periods = [2,3,5,7,11]
1806
+
1807
+ discs = [DiscriminatorS(use_spectral_norm=use_spectral_norm)]
1808
+ discs = discs + [DiscriminatorP(i, use_spectral_norm=use_spectral_norm) for i in periods]
1809
+ self.discriminators = nn.ModuleList(discs)
1810
+
1811
+ def forward(self, y, y_hat):
1812
+ y_d_rs = []
1813
+ y_d_gs = []
1814
+ fmap_rs = []
1815
+ fmap_gs = []
1816
+ for i, d in enumerate(self.discriminators):
1817
+ y_d_r, fmap_r = d(y)
1818
+ y_d_g, fmap_g = d(y_hat)
1819
+ y_d_rs.append(y_d_r)
1820
+ y_d_gs.append(y_d_g)
1821
+ fmap_rs.append(fmap_r)
1822
+ fmap_gs.append(fmap_g)
1823
+
1824
+ return y_d_rs, y_d_gs, fmap_rs, fmap_gs
1825
+
1826
+
1827
+
1828
+ class SynthesizerTrn(nn.Module):
1829
+ """
1830
+ Synthesizer for Training
1831
+ """
1832
+
1833
+ def __init__(self,
1834
+ n_vocab,
1835
+ spec_channels,
1836
+ segment_size,
1837
+ inter_channels,
1838
+ hidden_channels,
1839
+ filter_channels,
1840
+ n_heads,
1841
+ n_layers,
1842
+ kernel_size,
1843
+ p_dropout,
1844
+ resblock,
1845
+ resblock_kernel_sizes,
1846
+ resblock_dilation_sizes,
1847
+ upsample_rates,
1848
+ upsample_initial_channel,
1849
+ upsample_kernel_sizes,
1850
+ n_speakers=0,
1851
+ gin_channels=0,
1852
+ use_sdp=True,
1853
+ **kwargs):
1854
+
1855
+ super().__init__()
1856
+ self.n_vocab = n_vocab
1857
+ self.spec_channels = spec_channels
1858
+ self.inter_channels = inter_channels
1859
+ self.hidden_channels = hidden_channels
1860
+ self.filter_channels = filter_channels
1861
+ self.n_heads = n_heads
1862
+ self.n_layers = n_layers
1863
+ self.kernel_size = kernel_size
1864
+ self.p_dropout = p_dropout
1865
+ self.resblock = resblock
1866
+ self.resblock_kernel_sizes = resblock_kernel_sizes
1867
+ self.resblock_dilation_sizes = resblock_dilation_sizes
1868
+ self.upsample_rates = upsample_rates
1869
+ self.upsample_initial_channel = upsample_initial_channel
1870
+ self.upsample_kernel_sizes = upsample_kernel_sizes
1871
+ self.segment_size = segment_size
1872
+ self.n_speakers = n_speakers
1873
+ self.gin_channels = gin_channels
1874
+
1875
+ self.use_sdp = use_sdp
1876
+
1877
+ self.VTXTextEncoder = TextEncoder(n_vocab,
1878
+ inter_channels,
1879
+ hidden_channels,
1880
+ filter_channels,
1881
+ n_heads,
1882
+ n_layers,
1883
+ kernel_size,
1884
+ p_dropout)
1885
+ self.enc_p = self.VTXTextEncoder
1886
+
1887
+ self.VTXVocoder = Generator(
1888
+ inter_channels, resblock, resblock_kernel_sizes,
1889
+ resblock_dilation_sizes, upsample_rates, upsample_initial_channel,
1890
+ upsample_kernel_sizes, gin_channels=gin_channels,
1891
+ decoder_alias_free=kwargs.get("decoder_alias_free", False),
1892
+ decoder_alias_free_start_stage=kwargs.get("decoder_alias_free_start_stage", 2),
1893
+ decoder_snake_logscale=kwargs.get("decoder_snake_logscale", True))
1894
+ self.dec = self.VTXVocoder
1895
+
1896
+ self.inference_only = bool(kwargs.get("inference_only", False))
1897
+ if not self.inference_only:
1898
+ self.enc_q = PosteriorEncoder(spec_channels, inter_channels, hidden_channels, 5, 1, 16, gin_channels=gin_channels)
1899
+
1900
+ self.VTXVectorEstimator = ResidualCouplingBlock(inter_channels, hidden_channels, 5, 1, 4, gin_channels=gin_channels)
1901
+ self.flow = self.VTXVectorEstimator
1902
+
1903
+ if use_sdp:
1904
+ self.VTXDurationPredictor = StochasticDurationPredictor(hidden_channels, 192, 3, 0.5, 4, gin_channels=gin_channels)
1905
+ else:
1906
+ self.VTXDurationPredictor = DurationPredictor(hidden_channels, 256, 3, 0.5, gin_channels=gin_channels)
1907
+ self.dp = self.VTXDurationPredictor
1908
+
1909
+ if n_speakers > 1:
1910
+ self.emb_g = nn.Embedding(n_speakers, gin_channels)
1911
+
1912
+ def forward(self, x, x_lengths, y, y_lengths, sid=None):
1913
+
1914
+ if getattr(self, "inference_only", False) or not hasattr(self, "enc_q"):
1915
+ raise RuntimeError("The public runtime is inference-only and has no posterior encoder.")
1916
+
1917
+ x, m_p, logs_p, x_mask = self.enc_p(x, x_lengths)
1918
+ if self.n_speakers > 0:
1919
+ g = self.emb_g(sid).unsqueeze(-1) # [b, h, 1]
1920
+ else:
1921
+ g = None
1922
+
1923
+ z, m_q, logs_q, y_mask = self.enc_q(y, y_lengths, g=g)
1924
+ z_p = self.flow(z, y_mask, g=g)
1925
+
1926
+ with torch.no_grad():
1927
+ # negative cross-entropy
1928
+ s_p_sq_r = torch.exp(-2 * logs_p) # [b, d, t]
1929
+ neg_cent1 = torch.sum(-0.5 * math.log(2 * math.pi) - logs_p, [1], keepdim=True) # [b, 1, t_s]
1930
+ neg_cent2 = torch.matmul(-0.5 * (z_p ** 2).transpose(1, 2), s_p_sq_r) # [b, t_t, d] x [b, d, t_s] = [b, t_t, t_s]
1931
+ neg_cent3 = torch.matmul(z_p.transpose(1, 2), (m_p * s_p_sq_r)) # [b, t_t, d] x [b, d, t_s] = [b, t_t, t_s]
1932
+ neg_cent4 = torch.sum(-0.5 * (m_p ** 2) * s_p_sq_r, [1], keepdim=True) # [b, 1, t_s]
1933
+ neg_cent = neg_cent1 + neg_cent2 + neg_cent3 + neg_cent4
1934
+
1935
+ attn_mask = torch.unsqueeze(x_mask, 2) * torch.unsqueeze(y_mask, -1)
1936
+ attn = maximum_path(neg_cent, attn_mask.squeeze(1)).unsqueeze(1).detach()
1937
+
1938
+ w = attn.sum(2)
1939
+ if self.use_sdp:
1940
+ l_length = self.dp(x, x_mask, w, g=g)
1941
+ l_length = l_length / torch.sum(x_mask)
1942
+ else:
1943
+ logw_ = torch.log(w + 1e-6) * x_mask
1944
+ logw = self.dp(x, x_mask, g=g)
1945
+ l_length = torch.sum((logw - logw_)**2, [1,2]) / torch.sum(x_mask) # for averaging
1946
+
1947
+ # expand prior
1948
+ m_p = torch.matmul(attn.squeeze(1), m_p.transpose(1, 2)).transpose(1, 2)
1949
+ logs_p = torch.matmul(attn.squeeze(1), logs_p.transpose(1, 2)).transpose(1, 2)
1950
+
1951
+ z_slice, ids_slice = rand_slice_segments(z, y_lengths, self.segment_size)
1952
+ o = self.dec(z_slice, g=g)
1953
+ return o, l_length, attn, ids_slice, x_mask, y_mask, (z, z_p, m_p, logs_p, m_q, logs_q)
1954
+
1955
+ def infer_text(self, text, device="cpu", noise_scale=0.667, length_scale=1.0, noise_scale_w=0.8, max_len=None):
1956
+ from configuration import cleaned_text_to_sequence
1957
+ from phonemizer.backend import EspeakBackend
1958
+
1959
+ backend = EspeakBackend('en-us', preserve_punctuation=True, with_stress=True)
1960
+ phoneme_str = backend.phonemize([text])[0]
1961
+ sequence = cleaned_text_to_sequence(phoneme_str)
1962
+ if not sequence:
1963
+ sequence = [1, 2, 3]
1964
+
1965
+ res = [0] * (len(sequence) * 2 + 1)
1966
+ res[1::2] = sequence
1967
+ tokens = torch.LongTensor(res).to(device).unsqueeze(0)
1968
+ lengths = torch.LongTensor([tokens.size(1)]).to(device)
1969
+
1970
+ return self.infer(tokens, lengths, noise_scale=noise_scale, length_scale=length_scale, noise_scale_w=noise_scale_w, max_len=max_len)
1971
+
1972
+ def infer(self, x, x_lengths, sid=None, noise_scale=1, length_scale=1, noise_scale_w=1., max_len=None):
1973
+ x, m_p, logs_p, x_mask = self.enc_p(x, x_lengths)
1974
+ if self.n_speakers > 0:
1975
+ g = self.emb_g(sid).unsqueeze(-1) # [b, h, 1]
1976
+ else:
1977
+ g = None
1978
+
1979
+ if self.use_sdp:
1980
+ logw = self.dp(x, x_mask, g=g, reverse=True, noise_scale=noise_scale_w)
1981
+ else:
1982
+ logw = self.dp(x, x_mask, g=g)
1983
+ w = torch.exp(logw) * x_mask * length_scale
1984
+ w_ceil = torch.ceil(w)
1985
+ y_lengths = torch.clamp_min(torch.sum(w_ceil, [1, 2]), 1).long()
1986
+ y_mask = torch.unsqueeze(sequence_mask(y_lengths, None), 1).to(x_mask.dtype)
1987
+ attn_mask = torch.unsqueeze(x_mask, 2) * torch.unsqueeze(y_mask, -1)
1988
+ attn = generate_path(w_ceil, attn_mask)
1989
+
1990
+ m_p = torch.matmul(attn.squeeze(1), m_p.transpose(1, 2)).transpose(1, 2) # [b, t', t], [b, t, d] -> [b, d, t']
1991
+ logs_p = torch.matmul(attn.squeeze(1), logs_p.transpose(1, 2)).transpose(1, 2) # [b, t', t], [b, t, d] -> [b, d, t']
1992
+
1993
+ z_p = m_p + torch.randn_like(m_p) * torch.exp(logs_p) * noise_scale
1994
+ z = self.flow(z_p, y_mask, g=g, reverse=True)
1995
+ o = self.dec((z * y_mask)[:,:,:max_len], g=g)
1996
+ return o, attn, y_mask, (z, z_p, m_p, logs_p)
1997
+
1998
+ def voice_conversion(self, y, y_lengths, sid_src, sid_tgt):
1999
+ if self.inference_only:
2000
+ raise RuntimeError("The public runtime is inference-only and does not support voice conversion.")
2001
+ assert self.n_speakers > 0, "n_speakers have to be larger than 0."
2002
+ g_src = self.emb_g(sid_src).unsqueeze(-1)
2003
+ g_tgt = self.emb_g(sid_tgt).unsqueeze(-1)
2004
+ z, m_q, logs_q, y_mask = self.enc_q(y, y_lengths, g=g_src)
2005
+ z_p = self.flow(z, y_mask, g=g_src)
2006
+ z_hat = self.flow(z_p, y_mask, g=g_tgt, reverse=True)
2007
+ o_hat = self.dec(z_hat * y_mask, g=g_tgt)
2008
+ return o_hat, y_mask, (z, z_p, z_hat)
onnx/__pycache__/inference_onnx.cpython-314.pyc ADDED
Binary file (12.3 kB). View file
 
onnx/inference_onnx.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import re
5
+ import sys
6
+ from pathlib import Path
7
+ import numpy as np
8
+ import onnxruntime as ort
9
+
10
+ PACKAGE_ROOT = Path(__file__).resolve().parent
11
+ if str(PACKAGE_ROOT.parent) not in sys.path:
12
+ sys.path.append(str(PACKAGE_ROOT.parent))
13
+
14
+ from configuration import cleaned_text_to_sequence, get_hparams_from_file
15
+
16
+
17
+ def split_text(text: str, limit: int = 280) -> list[str]:
18
+ normalized = " ".join(text.split())
19
+ sentences = [
20
+ part.strip()
21
+ for part in re.split(r"(?<=[.!?;:])\s+", normalized)
22
+ if part.strip()
23
+ ]
24
+ chunks: list[str] = []
25
+ for sentence in sentences or [normalized]:
26
+ while len(sentence) > limit:
27
+ search = sentence[: limit + 1]
28
+ punctuation = max(search.rfind(mark) for mark in (",", ";", ":"))
29
+ split_at = (
30
+ punctuation + 1
31
+ if punctuation >= limit // 2
32
+ else sentence.rfind(" ", 0, limit + 1)
33
+ )
34
+ if split_at < limit // 2:
35
+ split_at = limit
36
+ chunks.append(sentence[:split_at].strip())
37
+ sentence = sentence[split_at:].strip()
38
+ if sentence:
39
+ chunks.append(sentence)
40
+ return chunks
41
+
42
+
43
+ def boundary_pause_seconds(chunk: str) -> float:
44
+ ending = chunk.rstrip()[-1:] if chunk.strip() else ""
45
+ return {
46
+ "?": 0.28,
47
+ "!": 0.24,
48
+ ".": 0.22,
49
+ ";": 0.16,
50
+ ":": 0.13,
51
+ ",": 0.09,
52
+ }.get(ending, 0.08)
53
+
54
+
55
+ def edge_fade(waveform: np.ndarray, sample_rate: int, milliseconds: float = 5.0) -> np.ndarray:
56
+ frames = min(round(sample_rate * milliseconds / 1000.0), waveform.size // 2)
57
+ if frames <= 0:
58
+ return waveform
59
+ output = waveform.copy()
60
+ ramp = np.linspace(0.0, 1.0, frames, endpoint=True, dtype=np.float32)
61
+ output[:frames] *= ramp
62
+ output[-frames:] *= ramp[::-1]
63
+ return output
64
+
65
+
66
+ class VtxTTSOnnx:
67
+ """Lightweight PyTorch-free VTX-TTS Engine using ONNX Runtime (~136 MB Peak RAM)."""
68
+
69
+ def __init__(self, model_dir: str | Path = PACKAGE_ROOT) -> None:
70
+ self.root = Path(model_dir).resolve()
71
+ config_path = self.root / "config.json" if (self.root / "config.json").exists() else self.root.parent / "config.json"
72
+ self.hps = get_hparams_from_file(str(config_path))
73
+ self.sample_rate = int(self.hps.data.sampling_rate)
74
+
75
+ onnx_model_path = self.root / "model.onnx"
76
+ if not onnx_model_path.exists():
77
+ raise FileNotFoundError(f"ONNX model not found at {onnx_model_path}")
78
+
79
+ opts = ort.SessionOptions()
80
+ opts.intra_op_num_threads = 4
81
+ opts.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
82
+ self.session = ort.InferenceSession(str(onnx_model_path), sess_options=opts)
83
+
84
+ def _tokens(self, text: str) -> tuple[np.ndarray, np.ndarray]:
85
+ from phonemizer.backend import EspeakBackend
86
+ backend = EspeakBackend('en-us', preserve_punctuation=True, with_stress=True)
87
+ phoneme_str = backend.phonemize([text])[0]
88
+ sequence = cleaned_text_to_sequence(phoneme_str)
89
+ if not sequence:
90
+ sequence = [1, 2, 3]
91
+ if self.hps.data.add_blank:
92
+ res = [0] * (len(sequence) * 2 + 1)
93
+ res[1::2] = sequence
94
+ sequence = res
95
+ tokens = np.array([sequence], dtype=np.int64)
96
+ lengths = np.array([tokens.shape[1]], dtype=np.int64)
97
+ return tokens, lengths
98
+
99
+ def synthesize(self, text: str) -> tuple[int, np.ndarray]:
100
+ normalized = " ".join(text.split())
101
+ if not normalized:
102
+ raise ValueError("Text must not be empty.")
103
+
104
+ chunks = split_text(normalized)
105
+ pieces: list[np.ndarray] = []
106
+ for index, chunk in enumerate(chunks):
107
+ if index:
108
+ pieces.append(
109
+ np.zeros(
110
+ round(self.sample_rate * boundary_pause_seconds(chunks[index - 1])),
111
+ dtype=np.float32,
112
+ )
113
+ )
114
+ tokens, lengths = self._tokens(chunk)
115
+ outputs = self.session.run(None, {"x": tokens, "x_lengths": lengths})
116
+ waveform = outputs[0].squeeze().astype(np.float32)
117
+ pieces.append(edge_fade(waveform, self.sample_rate))
118
+
119
+ waveform = np.clip(np.concatenate(pieces), -1.0, 1.0)
120
+ return self.sample_rate, waveform
121
+
122
+ def save(self, text: str, output: str | Path) -> Path:
123
+ destination = Path(output)
124
+ destination.parent.mkdir(parents=True, exist_ok=True)
125
+ sample_rate, waveform = self.synthesize(text)
126
+
127
+ data_int16 = (waveform * 32767.0).clip(-32768, 32767).astype("<i2")
128
+ raw_bytes = data_int16.tobytes()
129
+ data_size = len(raw_bytes)
130
+
131
+ header = bytearray()
132
+ header.extend(b"RIFF")
133
+ header.extend((36 + data_size).to_bytes(4, "little"))
134
+ header.extend(b"WAVE")
135
+ header.extend(b"fmt ")
136
+ header.extend((16).to_bytes(4, "little"))
137
+ header.extend((1).to_bytes(2, "little"))
138
+ header.extend((1).to_bytes(2, "little"))
139
+ header.extend((sample_rate).to_bytes(4, "little"))
140
+ header.extend((sample_rate * 2).to_bytes(4, "little"))
141
+ header.extend((2).to_bytes(2, "little"))
142
+ header.extend((16).to_bytes(2, "little"))
143
+ header.extend(b"data")
144
+ header.extend((data_size).to_bytes(4, "little"))
145
+
146
+ with open(destination, "wb") as f:
147
+ f.write(header)
148
+ f.write(raw_bytes)
149
+
150
+ return destination
151
+
152
+
153
+ def main() -> None:
154
+ parser = argparse.ArgumentParser(description="Run PyTorch-free ONNX VTX-TTS speech synthesis.")
155
+ parser.add_argument("--model-dir", type=Path, default=PACKAGE_ROOT)
156
+ parser.add_argument("--text", required=True)
157
+ parser.add_argument("--output", type=Path, required=True)
158
+ args = parser.parse_args()
159
+
160
+ engine = VtxTTSOnnx(args.model_dir)
161
+ engine.save(args.text, args.output)
162
+ print(f"Wrote {args.output} at {engine.sample_rate} Hz without PyTorch!")
163
+
164
+
165
+ if __name__ == "__main__":
166
+ main()
onnx/model.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:170d9e3bbbd1e40cd0abd630cec6a43b523d2cd4b1198abe31963ffa19d847c7
3
+ size 1078598
onnx/model.onnx.data ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cc9b6e9ebfef9cbf10662adf49dea1b34d86cf1519eaa1392b6cc4c1e8b48407
3
+ size 37748736
train.py ADDED
@@ -0,0 +1,524 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Fine-tune VTXAI/tts (VITS-architecture) on Hindi speech.
3
+
4
+ This is a REAL training script:
5
+ - real audio -> mel-spectrogram target (via torchaudio, no torchcodec needed)
6
+ - real VITS forward pass (text encoder, posterior encoder, flow, duration predictor, decoder)
7
+ - real losses: mel L1, KL divergence, duration loss, adversarial (LSGAN) + feature matching
8
+ - MultiPeriodDiscriminator trained jointly (adversarial training is required for VITS to
9
+ produce intelligible/natural audio -- without it you just get a blurry mean waveform)
10
+ - proper batching/padding via a collate function, not batch-size-1 streaming
11
+ - checkpointing so you can resume 1000+ hours of training across sessions
12
+
13
+ Usage:
14
+ Paste this entire file into one Jupyter/Colab cell and run it. Edit the
15
+ CONFIG block near the top (repo, dataset, batch size, etc.) instead of
16
+ passing CLI flags.
17
+
18
+ Requires: torch, torchaudio, datasets, safetensors, huggingface_hub
19
+ pip install -q torch torchaudio datasets safetensors huggingface_hub
20
+ Do NOT need torchcodec: we force decode=False and decode audio ourselves with torchaudio,
21
+ which is more memory-stable for long streaming runs anyway.
22
+ """
23
+
24
+ import os
25
+ import io
26
+ import json
27
+ import math
28
+ import time
29
+ from pathlib import Path
30
+
31
+ import numpy as np
32
+ import torch
33
+ import torch.nn.functional as F
34
+ import torchaudio
35
+ from torch.utils.data import IterableDataset, DataLoader
36
+ import sys
37
+ import importlib.util
38
+
39
+ from huggingface_hub import snapshot_download
40
+
41
+ # ==============================================================================
42
+ # ⚙️ CONFIG — edit these values directly, then just run the cell
43
+ # ==============================================================================
44
+ HF_REPO = "VTXAI/tts" # base model repo to download+finetune
45
+ BASE_MODEL_DIR = "./vtx_base_model" # where the base snapshot gets cached
46
+ DATASET_NAME = "SPRINGLab/IndicTTS-Hindi"
47
+ DATASET_SPLIT = "train"
48
+ OUT_DIR = "./ckpt" # finetune checkpoints saved here
49
+ RESUME_PATH = "" # set to a ./ckpt/*.pth path to resume, else ""
50
+
51
+ BATCH_SIZE = 16
52
+ EPOCHS = 1
53
+ MAX_STEPS = 0 # 0 = run full epochs, otherwise stop after this many new steps
54
+ LR = 2e-4
55
+ SAVE_EVERY = 1000
56
+ LOG_EVERY = 50
57
+ NUM_WORKERS = 2
58
+ # ==============================================================================
59
+
60
+
61
+ def load_modeling(repo_id="VTXAI/tts", local_dir="./vtx_base_model"):
62
+ """
63
+ Ensures modeling.py is available from local repo directory or downloaded snapshot
64
+ and imports required model classes/functions from it.
65
+ """
66
+ local_modeling = Path("modeling.py")
67
+ if not local_modeling.exists():
68
+ snapshot_dir = Path(snapshot_download(repo_id=repo_id, local_dir=local_dir))
69
+ local_modeling = snapshot_dir / "modeling.py"
70
+
71
+ spec = importlib.util.spec_from_file_location("vtx_modeling", local_modeling)
72
+ vtx_modeling = importlib.util.module_from_spec(spec)
73
+ sys.modules["vtx_modeling"] = vtx_modeling
74
+ spec.loader.exec_module(vtx_modeling)
75
+
76
+ return (
77
+ vtx_modeling.SynthesizerTrn,
78
+ vtx_modeling.MultiPeriodDiscriminator,
79
+ vtx_modeling.kl_divergence,
80
+ vtx_modeling.slice_segments,
81
+ )
82
+
83
+ SynthesizerTrn, MultiPeriodDiscriminator, kl_divergence, slice_segments = load_modeling(HF_REPO, BASE_MODEL_DIR)
84
+ def fetch_base_model(repo_id="VTXAI/tts", local_dir="./vtx_base_model"):
85
+ """
86
+ Downloads the full VTXAI/tts repo snapshot and locates config.json + weights
87
+ within it. Returns (config_path, weights_path).
88
+ """
89
+ local_dir = Path(local_dir)
90
+ print(f"Downloading base model from https://huggingface.co/{repo_id} ...")
91
+ repo_path = Path(snapshot_download(repo_id=repo_id, local_dir=str(local_dir)))
92
+ print(f"Downloaded to {repo_path}")
93
+
94
+ config_path = None
95
+ p = repo_path / "config.json"
96
+ if p.exists():
97
+ config_path = p
98
+ else:
99
+ found = list(repo_path.rglob("config.json"))
100
+ if not found:
101
+ raise FileNotFoundError(f"No config.json found in {repo_id} snapshot.")
102
+ config_path = found[0]
103
+
104
+ weights_path = None
105
+ for cand in ["model.safetensors", "G.safetensors", "model.pth", "G.pth"]:
106
+ p = repo_path / cand
107
+ if p.exists():
108
+ weights_path = p
109
+ break
110
+ if weights_path is None:
111
+ for pattern in ["*.safetensors", "*.pth"]:
112
+ found = list(repo_path.rglob(pattern))
113
+ if found:
114
+ weights_path = found[0]
115
+ break
116
+ if weights_path is None:
117
+ raise FileNotFoundError(f"No weights (.safetensors/.pth) found in {repo_id} snapshot.")
118
+
119
+ print(f"Using config: {config_path}")
120
+ print(f"Using weights: {weights_path}")
121
+ return config_path, weights_path
122
+
123
+ # ----------------------------------------------------------------------------
124
+ # 0. maximum_path fallback
125
+ # ----------------------------------------------------------------------------
126
+ # The reference VITS repo ships a compiled `monotonic_align` extension exposing
127
+ # maximum_path(neg_cent, mask). modeling.py calls it but doesn't define it, so we
128
+ # provide a numba-free, pure-torch fallback here (slower, but correct and dependency-light).
129
+ def maximum_path(neg_cent, mask):
130
+ """
131
+ neg_cent: [b, t_t, t_s] mask: [b, t_t, t_s] -> returns path [b, t_t, t_s]
132
+ Pure-python dynamic programming, batched via a simple loop (fine at these batch sizes).
133
+ """
134
+ device = neg_cent.device
135
+ dtype = neg_cent.dtype
136
+ neg_cent = neg_cent.detach().cpu().numpy().astype(np.float64)
137
+ mask = mask.detach().cpu().numpy().astype(np.bool_)
138
+ b, t_t, t_s = neg_cent.shape
139
+ path = np.zeros_like(neg_cent, dtype=np.float64)
140
+
141
+ for i in range(b):
142
+ t_y = int(mask[i, :, 0].sum())
143
+ t_x = int(mask[i, 0, :].sum())
144
+ if t_y == 0 or t_x == 0:
145
+ continue
146
+ value = neg_cent[i, :t_y, :t_x]
147
+ v = np.full((t_y, t_x), -1e9, dtype=np.float64)
148
+ v[0, 0] = value[0, 0]
149
+ for y in range(1, t_y):
150
+ v[y, 0] = v[y - 1, 0] + value[y, 0]
151
+ for x in range(1, t_x):
152
+ v[0, x] = -1e9 # can't start later in x at t_y=0
153
+ for y in range(1, t_y):
154
+ for x in range(1, min(t_x, y + 1)):
155
+ v[y, x] = value[y, x] + max(v[y - 1, x], v[y - 1, x - 1])
156
+ # backtrack
157
+ index = t_x - 1
158
+ for y in range(t_y - 1, -1, -1):
159
+ path[i, y, index] = 1.0
160
+ if index > 0 and (y == 0 or v[y - 1, index - 1] > v[y - 1, index]):
161
+ index -= 1
162
+ return torch.from_numpy(path).to(device=device, dtype=dtype)
163
+
164
+
165
+ # ----------------------------------------------------------------------------
166
+ # 1. Text symbols / tokenizer (Hindi, matches your existing pipeline)
167
+ # ----------------------------------------------------------------------------
168
+ _pad = "_"
169
+ _punctuation = ';:,.!?¡¿—…"«»“” '
170
+ _hindi_letters = "अआइईउऊऋएऐओऔकखगघङचछजझञटठडढणतथदधनपफबभमयरलवशषसहक्षत्रज्ञ़ािीुूृेैोौॉ्िंः"
171
+ _letters_ipa = "ɑɐɒæɓɓβɔɕçɗɖðʤəɘɚɛɜɝɞɟʄɡɠɢʛɦɧħɥʜɨɪʝɭɬɫɮʟɱɯɰŋɳɲɴøɵɸθœɶʘɹɺɾɻʀʁɽʂʃʈʧʉʊʋⱱʌɣɤʍχʎʏʑʐʒʔʡʕʢǀǁǂǃˈˌːˑʼʴʰʱʲʷˠˤ˞"
172
+
173
+ hindi_symbols = [_pad] + list(_punctuation) + list(_hindi_letters) + list(_letters_ipa)
174
+ hindi_symbol_to_id = {s: i for i, s in enumerate(hindi_symbols)}
175
+
176
+
177
+ def hindi_text_to_sequence(text: str) -> list:
178
+ seq = []
179
+ for ch in text:
180
+ if ch in hindi_symbol_to_id:
181
+ seq.append(hindi_symbol_to_id[ch])
182
+ elif ch == " ":
183
+ seq.append(1)
184
+ if not seq:
185
+ seq = [1, 2, 3]
186
+ interspersed = [0] * (len(seq) * 2 + 1)
187
+ interspersed[1::2] = seq
188
+ return interspersed
189
+
190
+
191
+ # ----------------------------------------------------------------------------
192
+ # 2. Mel-spectrogram (matches VITS training target: linear spec for enc_q,
193
+ # mel spec for the reconstruction loss)
194
+ # ----------------------------------------------------------------------------
195
+ class AudioProcessor:
196
+ def __init__(self, cfg):
197
+ d = cfg["data"]
198
+ self.sampling_rate = d["sampling_rate"]
199
+ self.filter_length = d["filter_length"]
200
+ self.hop_length = d["hop_length"]
201
+ self.win_length = d["win_length"]
202
+ self.n_mel_channels = d.get("n_mel_channels", 80)
203
+ self.mel_fmin = d.get("mel_fmin", 0.0)
204
+ self.mel_fmax = d.get("mel_fmax", None)
205
+
206
+ self.spec_transform = torchaudio.transforms.Spectrogram(
207
+ n_fft=self.filter_length,
208
+ hop_length=self.hop_length,
209
+ win_length=self.win_length,
210
+ power=1.0,
211
+ center=False,
212
+ pad=(self.filter_length - self.hop_length) // 2,
213
+ )
214
+ self.mel_scale = torchaudio.transforms.MelScale(
215
+ n_mels=self.n_mel_channels,
216
+ sample_rate=self.sampling_rate,
217
+ f_min=self.mel_fmin,
218
+ f_max=self.mel_fmax,
219
+ n_stft=self.filter_length // 2 + 1,
220
+ )
221
+
222
+ def to(self, device):
223
+ self.spec_transform = self.spec_transform.to(device)
224
+ self.mel_scale = self.mel_scale.to(device)
225
+ return self
226
+
227
+ def wav_to_spec(self, wav):
228
+ # wav: [1, T] float32 in [-1, 1] -> spec [C, F]
229
+ spec = self.spec_transform(wav)
230
+ return spec.squeeze(0)
231
+
232
+ def spec_to_mel(self, spec):
233
+ if spec.device != self.mel_scale.fb.device:
234
+ self.mel_scale = self.mel_scale.to(spec.device)
235
+ return self.mel_scale(spec.unsqueeze(0)).squeeze(0)
236
+
237
+ def mel_scale_transform(self, spec):
238
+ if spec.device != self.mel_scale.fb.device:
239
+ self.mel_scale = self.mel_scale.to(spec.device)
240
+ return self.mel_scale(spec)
241
+
242
+ def wav_to_mel(self, wav):
243
+ return self.spec_to_mel(self.wav_to_spec(wav))
244
+
245
+
246
+ # ----------------------------------------------------------------------------
247
+ # 3. Streaming dataset over the HF Hindi corpus, with real audio decoding
248
+ # ----------------------------------------------------------------------------
249
+ class HindiTTSIterable(IterableDataset):
250
+ """
251
+ Wraps datasets.load_dataset(..., streaming=True). We force decode=False on the
252
+ audio column and decode manually with torchaudio -- this avoids the torchcodec
253
+ dependency entirely and is more robust for very long (1000+ hr) streaming runs.
254
+ """
255
+
256
+ def __init__(self, dataset_name, split, ap: AudioProcessor, max_audio_sec=12.0, text_key=None):
257
+ super().__init__()
258
+ self.dataset_name = dataset_name
259
+ self.split = split
260
+ self.ap = ap
261
+ self.max_samples = int(max_audio_sec * ap.sampling_rate)
262
+ self.text_key = text_key
263
+
264
+ def _load_stream(self):
265
+ from datasets import load_dataset, Audio
266
+ ds = load_dataset(self.dataset_name, split=self.split, streaming=True)
267
+ ds = ds.cast_column("audio", Audio(decode=False))
268
+ return ds
269
+
270
+ def __iter__(self):
271
+ ds = self._load_stream()
272
+ for item in ds:
273
+ try:
274
+ audio_field = item["audio"]
275
+ raw_bytes = audio_field["bytes"] if isinstance(audio_field, dict) else None
276
+ if raw_bytes is not None:
277
+ wav, sr = torchaudio.load(io.BytesIO(raw_bytes))
278
+ elif isinstance(audio_field, dict) and audio_field.get("path"):
279
+ wav, sr = torchaudio.load(audio_field["path"])
280
+ else:
281
+ continue
282
+
283
+ if wav.shape[0] > 1:
284
+ wav = wav.mean(0, keepdim=True)
285
+ if sr != self.ap.sampling_rate:
286
+ wav = torchaudio.functional.resample(wav, sr, self.ap.sampling_rate)
287
+ if wav.shape[1] > self.max_samples:
288
+ continue # skip overly long clips for stable batch memory
289
+ if wav.shape[1] < self.ap.hop_length * 8:
290
+ continue
291
+
292
+ text_key = self.text_key
293
+ if text_key is None:
294
+ text_key = "normalized_text" if "normalized_text" in item else "text"
295
+ text = item.get(text_key, "")
296
+ if not text or not text.strip():
297
+ continue
298
+
299
+ tok = torch.LongTensor(hindi_text_to_sequence(text))
300
+ yield tok, wav.squeeze(0)
301
+ except Exception:
302
+ # skip malformed rows rather than crashing a 1000hr run
303
+ continue
304
+
305
+
306
+ def collate_fn(batch):
307
+ batch = [b for b in batch if b[0].numel() > 0 and b[1].numel() > 0]
308
+ batch.sort(key=lambda x: x[1].shape[0], reverse=True)
309
+ text_lens = torch.LongTensor([b[0].shape[0] for b in batch])
310
+ wav_lens = torch.LongTensor([b[1].shape[0] for b in batch])
311
+
312
+ text_pad = torch.zeros(len(batch), text_lens.max(), dtype=torch.long)
313
+ wav_pad = torch.zeros(len(batch), wav_lens.max(), dtype=torch.float32)
314
+
315
+ for i, (tok, wav) in enumerate(batch):
316
+ text_pad[i, : tok.shape[0]] = tok
317
+ wav_pad[i, : wav.shape[0]] = wav
318
+
319
+ return text_pad, text_lens, wav_pad, wav_lens
320
+
321
+
322
+ # ----------------------------------------------------------------------------
323
+ # 4. Loss functions
324
+ # ----------------------------------------------------------------------------
325
+ def discriminator_loss(disc_real_outputs, disc_generated_outputs):
326
+ loss = 0
327
+ for dr, dg in zip(disc_real_outputs, disc_generated_outputs):
328
+ r_loss = torch.mean((1 - dr) ** 2)
329
+ g_loss = torch.mean(dg ** 2)
330
+ loss += r_loss + g_loss
331
+ return loss
332
+
333
+
334
+ def generator_adv_loss(disc_outputs):
335
+ loss = 0
336
+ for dg in disc_outputs:
337
+ loss += torch.mean((1 - dg) ** 2)
338
+ return loss
339
+
340
+
341
+ def feature_loss(fmap_r, fmap_g):
342
+ loss = 0
343
+ for dr, dg in zip(fmap_r, fmap_g):
344
+ for rl, gl in zip(dr, dg):
345
+ loss += torch.mean(torch.abs(rl.float() - gl.float()))
346
+ return loss * 2
347
+
348
+
349
+ # ----------------------------------------------------------------------------
350
+ # 5. Checkpoint loading (keeps your existing "transfer weights, resize vocab" fix)
351
+ # ----------------------------------------------------------------------------
352
+ def load_pretrained_generator(model, ckpt_path):
353
+ ckpt_path = Path(ckpt_path)
354
+ if ckpt_path.suffix == ".safetensors":
355
+ from safetensors.torch import load_file
356
+ state_dict = load_file(str(ckpt_path))
357
+ else:
358
+ raw = torch.load(ckpt_path, map_location="cpu")
359
+ state_dict = raw.get("model", raw)
360
+
361
+ # Drop embedding weights: symbol vocab size for Hindi differs from source checkpoint
362
+ filtered = {
363
+ k: v for k, v in state_dict.items()
364
+ if not (k.endswith("emb.weight") or k.startswith("enc_p.emb"))
365
+ }
366
+ model_dict = model.state_dict()
367
+ compatible = {k: v for k, v in filtered.items() if k in model_dict and model_dict[k].shape == v.shape}
368
+ skipped = [k for k in filtered if k not in compatible]
369
+ model_dict.update(compatible)
370
+ model.load_state_dict(model_dict, strict=False)
371
+ print(f"Loaded {len(compatible)}/{len(state_dict)} tensors from checkpoint "
372
+ f"(skipped {len(skipped)}, incl. resized text embedding).")
373
+
374
+
375
+ # ----------------------------------------------------------------------------
376
+ # 6. Training run (executes directly — paste this whole file into one notebook cell)
377
+ # ----------------------------------------------------------------------------
378
+ def main():
379
+ out_dir = Path(OUT_DIR)
380
+ out_dir.mkdir(parents=True, exist_ok=True)
381
+
382
+ config_path, checkpoint_path = fetch_base_model(HF_REPO, BASE_MODEL_DIR)
383
+
384
+ with open(config_path) as f:
385
+ config = json.load(f)
386
+
387
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
388
+ print(f"Device: {device}")
389
+
390
+ ap = AudioProcessor(config).to(device)
391
+
392
+ net_g = SynthesizerTrn(
393
+ len(hindi_symbols),
394
+ config["data"]["filter_length"] // 2 + 1,
395
+ config["train"]["segment_size"] // config["data"]["hop_length"],
396
+ **config["model"],
397
+ ).to(device)
398
+ net_d = MultiPeriodDiscriminator(config["model"].get("use_spectral_norm", False)).to(device)
399
+
400
+ start_step = 0
401
+ if RESUME_PATH and Path(RESUME_PATH).exists():
402
+ ckpt = torch.load(RESUME_PATH, map_location="cpu")
403
+ net_g.load_state_dict(ckpt["net_g"])
404
+ net_d.load_state_dict(ckpt["net_d"])
405
+ start_step = ckpt.get("step", 0)
406
+ print(f"Resumed from {RESUME_PATH} at step {start_step}")
407
+ else:
408
+ load_pretrained_generator(net_g, checkpoint_path)
409
+
410
+ optim_g = torch.optim.AdamW(net_g.parameters(), lr=LR, betas=(0.8, 0.99), eps=1e-9)
411
+ optim_d = torch.optim.AdamW(net_d.parameters(), lr=LR, betas=(0.8, 0.99), eps=1e-9)
412
+ if RESUME_PATH and Path(RESUME_PATH).exists():
413
+ optim_g.load_state_dict(ckpt["optim_g"])
414
+ optim_d.load_state_dict(ckpt["optim_d"])
415
+
416
+ scheduler_g = torch.optim.lr_scheduler.ExponentialLR(optim_g, gamma=0.999875)
417
+ scheduler_d = torch.optim.lr_scheduler.ExponentialLR(optim_d, gamma=0.999875)
418
+
419
+ dataset = HindiTTSIterable(DATASET_NAME, DATASET_SPLIT, ap)
420
+ loader = DataLoader(
421
+ dataset, batch_size=BATCH_SIZE, collate_fn=collate_fn,
422
+ num_workers=NUM_WORKERS, pin_memory=(device.type == "cuda"),
423
+ )
424
+
425
+ hop = config["data"]["hop_length"]
426
+ seg_size = config["train"]["segment_size"]
427
+ seg_frames = seg_size // hop
428
+ c_mel = config["train"].get("c_mel", 45.0)
429
+ c_kl = config["train"].get("c_kl", 1.0)
430
+
431
+ step = start_step
432
+ t0 = time.time()
433
+ net_g.train()
434
+ net_d.train()
435
+
436
+ for epoch in range(EPOCHS):
437
+ for text_pad, text_lens, wav_pad, wav_lens in loader:
438
+ text_pad, text_lens = text_pad.to(device), text_lens.to(device)
439
+ wav_pad, wav_lens = wav_pad.to(device), wav_lens.to(device)
440
+
441
+ # Build linear spectrogram targets per-sample (variable length), then pad
442
+ specs, spec_lens = [], []
443
+ for i in range(wav_pad.shape[0]):
444
+ w = wav_pad[i, : wav_lens[i]].unsqueeze(0).cpu()
445
+ s = ap.wav_to_spec(w)
446
+ specs.append(s)
447
+ spec_lens.append(s.shape[-1])
448
+ spec_lens = torch.LongTensor(spec_lens).to(device)
449
+ spec_pad = torch.zeros(len(specs), specs[0].shape[0], spec_lens.max(), device=device)
450
+ for i, s in enumerate(specs):
451
+ spec_pad[i, :, : s.shape[-1]] = s.to(device)
452
+
453
+ # ---- Generator forward ----
454
+ (y_hat, l_length, attn, ids_slice, x_mask, y_mask,
455
+ (z, z_p, m_p, logs_p, m_q, logs_q)) = net_g(text_pad, text_lens, spec_pad, spec_lens)
456
+
457
+ mel_pad = ap.mel_scale_transform(spec_pad)
458
+ mel_slice = slice_segments(mel_pad, ids_slice, seg_frames)
459
+ with torch.no_grad():
460
+ wav_hat_mono = y_hat.squeeze(1)
461
+ y_hat_mel = torch.stack([
462
+ ap.spec_to_mel(ap.wav_to_spec(wav_hat_mono[i:i + 1].detach().cpu())).to(device)
463
+ for i in range(wav_hat_mono.shape[0])
464
+ ])
465
+ y_slice = slice_segments(wav_pad.unsqueeze(1), ids_slice * hop, seg_size)
466
+
467
+ # ---- Discriminator step ----
468
+ y_d_hat_r, y_d_hat_g, _, _ = net_d(y_slice, y_hat.detach())
469
+ loss_disc = discriminator_loss(y_d_hat_r, y_d_hat_g)
470
+ optim_d.zero_grad()
471
+ loss_disc.backward()
472
+ torch.nn.utils.clip_grad_norm_(net_d.parameters(), 5.0)
473
+ optim_d.step()
474
+
475
+ # ---- Generator step ----
476
+ y_d_hat_r, y_d_hat_g, fmap_r, fmap_g = net_d(y_slice, y_hat)
477
+ loss_mel = F.l1_loss(mel_slice, y_hat_mel) * c_mel
478
+ loss_kl = torch.mean(kl_divergence(m_p, logs_p, m_q, logs_q)) * c_kl
479
+ loss_dur = torch.sum(l_length.float())
480
+ loss_fm = feature_loss(fmap_r, fmap_g)
481
+ loss_adv = generator_adv_loss(y_d_hat_g)
482
+ loss_gen_total = loss_adv + loss_fm + loss_mel + loss_kl + loss_dur
483
+
484
+ optim_g.zero_grad()
485
+ loss_gen_total.backward()
486
+ torch.nn.utils.clip_grad_norm_(net_g.parameters(), 5.0)
487
+ optim_g.step()
488
+
489
+ step += 1
490
+ if step % LOG_EVERY == 0:
491
+ elapsed = time.time() - t0
492
+ print(f"[step {step}] gen={loss_gen_total.item():.4f} "
493
+ f"mel={loss_mel.item():.4f} kl={loss_kl.item():.4f} "
494
+ f"dur={loss_dur.item():.4f} adv={loss_adv.item():.4f} "
495
+ f"fm={loss_fm.item():.4f} disc={loss_disc.item():.4f} "
496
+ f"({elapsed:.1f}s elapsed)")
497
+
498
+ if step % SAVE_EVERY == 0:
499
+ ckpt_path = out_dir / f"vtx_hindi_step{step}.pth"
500
+ torch.save({
501
+ "net_g": net_g.state_dict(),
502
+ "net_d": net_d.state_dict(),
503
+ "optim_g": optim_g.state_dict(),
504
+ "optim_d": optim_d.state_dict(),
505
+ "step": step,
506
+ "symbols": hindi_symbols,
507
+ }, ckpt_path)
508
+ print(f"Saved checkpoint: {ckpt_path}")
509
+
510
+ if MAX_STEPS and step >= start_step + MAX_STEPS:
511
+ break
512
+ scheduler_g.step()
513
+ scheduler_d.step()
514
+ if MAX_STEPS and step >= start_step + MAX_STEPS:
515
+ break
516
+
517
+ final_path = out_dir / "vtx_hindi_final.pth"
518
+ torch.save({"net_g": net_g.state_dict(), "net_d": net_d.state_dict(), "step": step,
519
+ "symbols": hindi_symbols}, final_path)
520
+ print(f"Done. Final checkpoint: {final_path}")
521
+
522
+
523
+ if __name__ == "__main__":
524
+ main()