Vansh Chugh commited on
Commit
7afb6d4
·
1 Parent(s): 1abf6fb

initial deploy

Browse files
.gitignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ .DS_Store
4
+ .venv/
5
+ ckpts/
6
+ Vevo2-repo/
README.md CHANGED
@@ -1,13 +1,19 @@
1
  ---
2
  title: Vevo2
3
- emoji: 💻
4
- colorFrom: yellow
5
- colorTo: yellow
6
  sdk: gradio
7
- sdk_version: 6.20.0
8
- python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
1
  ---
2
  title: Vevo2
3
+ emoji: 🎤
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 5.28.0
8
+ python_version: '3.11'
9
  app_file: app.py
10
  pinned: false
11
+ license: mit
12
  ---
13
 
14
+ # Vevo2
15
+
16
+ A unified, controllable framework for speech and singing voice generation and conversion. Supports zero-shot voice/singing conversion, text-to-singing, singing editing, singing style conversion, and melody control (hum or play a melody to control a generated vocal).
17
+
18
+ Paper: [Vevo2: Bridging Controllable Speech and Singing Voice Generation via Unified Prosody Learning](https://arxiv.org/abs/2508.16332)
19
+ Source: [open-mmlab/Amphion](https://github.com/open-mmlab/Amphion/blob/main/models/svc/vevo2/README.md)
SOURCES.md ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # Sources — Vevo2
2
+
3
+ - Source repo: https://github.com/open-mmlab/Amphion
4
+ - Paper: https://arxiv.org/abs/2508.16332
app.py ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ try:
2
+ import spaces
3
+ except ImportError:
4
+ # keep @spaces.GPU usable as a no-op; ZeroGPU requires this exact name.
5
+ class spaces:
6
+ class GPU:
7
+ def __init__(self, func=None, duration=60):
8
+ self.func = func
9
+
10
+ def __call__(self, *args, **kwargs):
11
+ if self.func is not None:
12
+ return self.func(*args, **kwargs)
13
+ func = args[0]
14
+ return func
15
+
16
+
17
+ import sys
18
+
19
+ sys.stdout.reconfigure(line_buffering=True)
20
+
21
+ import os
22
+ import re
23
+ import tempfile
24
+ import threading
25
+
26
+ import torch
27
+ import gradio as gr
28
+ import whisper
29
+ from huggingface_hub import snapshot_download
30
+ from pyharp import ModelCard, build_endpoint
31
+
32
+ from models.svc.vevo2.vevo2_utils import Vevo2InferencePipeline, save_audio
33
+
34
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
35
+
36
+ CKPT_DIR = "./ckpts/Vevo2"
37
+ # training-only artifacts (optimizer/scheduler/rng state, and the earlier
38
+ # "pretrained" AR checkpoint superseded by "posttrained") aren't needed for
39
+ # inference and would roughly double the download.
40
+ CKPT_IGNORE_PATTERNS = [
41
+ "*/optimizer.pt",
42
+ "*/optimizer.bin",
43
+ "*/scheduler.pt",
44
+ "*/scheduler.bin",
45
+ "*/rng_state*.pth",
46
+ "*/random_states_*.pkl",
47
+ "*/trainer_state.json",
48
+ "*/training_args.bin",
49
+ "contentstyle_modeling/pretrained/*",
50
+ ]
51
+
52
+ pipeline = None
53
+ download_ready = False
54
+ download_error = None
55
+
56
+
57
+ FMT_CONFIG_PATH = os.path.join(
58
+ CKPT_DIR, "acoustic_modeling/fm_emilia101k_singnet7k_repa/config.json"
59
+ )
60
+
61
+
62
+ def _fix_whisper_stats_path():
63
+ """The checkpoint's config.json hardcodes whisper_stats_path as
64
+ models/svc/vevosing/config/whisper_stats.pt, relative to the full
65
+ Amphion repo — a directory this deployment doesn't copy. The identical
66
+ file ships right next to this config, so point it there instead."""
67
+ correct_path = os.path.join(
68
+ CKPT_DIR, "acoustic_modeling/fm_emilia101k_singnet7k_repa/whisper_stats.pt"
69
+ )
70
+ with open(FMT_CONFIG_PATH) as f:
71
+ text = f.read()
72
+ text = re.sub(
73
+ r'"whisper_stats_path":\s*"[^"]*"',
74
+ f'"whisper_stats_path": "{correct_path}"',
75
+ text,
76
+ )
77
+ with open(FMT_CONFIG_PATH, "w") as f:
78
+ f.write(text)
79
+
80
+
81
+ def load_checkpoint():
82
+ """Download Vevo2's weights and pre-warm the Whisper cache. CPU-only —
83
+ building the actual pipeline happens lazily on first process_fn call
84
+ (see get_pipeline) instead of here, since Vevo2InferencePipeline moves
85
+ every submodule onto `device` inside its own constructor and ZeroGPU
86
+ only intercepts CUDA calls made inside an @spaces.GPU-decorated call,
87
+ not from a background thread."""
88
+ global download_ready, download_error
89
+ try:
90
+ snapshot_download(
91
+ repo_id="RMSnow/Vevo2",
92
+ local_dir=CKPT_DIR,
93
+ ignore_patterns=CKPT_IGNORE_PATTERNS,
94
+ )
95
+ _fix_whisper_stats_path()
96
+ whisper.load_model("medium", device="cpu") # warms ~/.cache/whisper
97
+ print("Checkpoint download complete.")
98
+ except Exception as e:
99
+ download_error = str(e)
100
+ print(f"Download error: {e}")
101
+ finally:
102
+ download_ready = True
103
+
104
+
105
+ threading.Thread(target=load_checkpoint, daemon=True).start()
106
+
107
+
108
+ def get_pipeline():
109
+ """Build the inference pipeline on first use, inside the @spaces.GPU
110
+ call (see load_checkpoint for why this can't happen in the background
111
+ thread)."""
112
+ global pipeline
113
+ if pipeline is None:
114
+ pipeline = Vevo2InferencePipeline(
115
+ prosody_tokenizer_ckpt_path=os.path.join(
116
+ CKPT_DIR, "tokenizer/prosody_fvq512_6.25hz"
117
+ ),
118
+ content_style_tokenizer_ckpt_path=os.path.join(
119
+ CKPT_DIR, "tokenizer/contentstyle_fvq16384_12.5hz"
120
+ ),
121
+ ar_cfg_path=os.path.join(
122
+ CKPT_DIR, "contentstyle_modeling/posttrained/amphion_config.json"
123
+ ),
124
+ ar_ckpt_path=os.path.join(CKPT_DIR, "contentstyle_modeling/posttrained"),
125
+ fmt_cfg_path=os.path.join(
126
+ CKPT_DIR, "acoustic_modeling/fm_emilia101k_singnet7k_repa/config.json"
127
+ ),
128
+ fmt_ckpt_path=os.path.join(
129
+ CKPT_DIR, "acoustic_modeling/fm_emilia101k_singnet7k_repa"
130
+ ),
131
+ vocoder_cfg_path=os.path.join(CKPT_DIR, "vocoder/config.json"),
132
+ vocoder_ckpt_path=os.path.join(CKPT_DIR, "vocoder"),
133
+ device=DEVICE,
134
+ )
135
+ return pipeline
136
+
137
+
138
+ model_card = ModelCard(
139
+ name="Vevo2",
140
+ description=(
141
+ "Zero-shot speech and singing voice generation and conversion: "
142
+ "voice/singing conversion, text-to-singing, singing editing, "
143
+ "singing style conversion, and melody control."
144
+ ),
145
+ author=(
146
+ "Xueyao Zhang, Junan Zhang, Yuancheng Wang, Chaoren Wang, "
147
+ "Yuanzhe Chen, Dongya Jia, Zhuo Chen, Zhizheng Wu"
148
+ ),
149
+ tags=["voice-conversion", "singing-synthesis", "text-to-speech"],
150
+ )
151
+
152
+ TASKS = [
153
+ "Voice/Singing Conversion",
154
+ "Text-to-Speech / Text-to-Singing",
155
+ "Singing Editing",
156
+ "Singing Style Conversion",
157
+ "Melody Control (Humming/Instrument to Singing)",
158
+ ]
159
+
160
+
161
+ @spaces.GPU(duration=120)
162
+ @torch.inference_mode()
163
+ def process_fn(
164
+ task: str,
165
+ input_audio_path: str,
166
+ reference_audio_path: str,
167
+ target_text: str,
168
+ reference_text: str,
169
+ flow_matching_steps: int,
170
+ ) -> str:
171
+ """Runs the Vevo2 task selected in the Task dropdown. Each task calls a
172
+ different combination of Vevo2InferencePipeline.inference_fm /
173
+ inference_ar_and_fm, mirroring the task wrapper functions in the
174
+ original repo's infer_vevo2_fm.py / infer_vevo2_ar.py."""
175
+ if not download_ready:
176
+ raise gr.Error("Model is still downloading, please wait a moment and try again.")
177
+ if download_error is not None:
178
+ raise gr.Error(f"Model failed to download: {download_error}")
179
+
180
+ pipe = get_pipeline()
181
+ target_text = (target_text or "").strip() or None
182
+ reference_text = reference_text or ""
183
+ reference_audio_path = reference_audio_path or None
184
+
185
+ if task == "Voice/Singing Conversion":
186
+ if reference_audio_path is None:
187
+ raise gr.Error(
188
+ "Voice/Singing Conversion needs a reference voice (Reference Voice input)."
189
+ )
190
+ gen_audio = pipe.inference_fm(
191
+ src_wav_path=input_audio_path,
192
+ timbre_ref_wav_path=reference_audio_path,
193
+ use_pitch_shift=True,
194
+ flow_matching_steps=flow_matching_steps,
195
+ )
196
+ elif task == "Text-to-Speech / Text-to-Singing":
197
+ if target_text is None:
198
+ raise gr.Error("Text-to-Speech needs Text / Lyrics.")
199
+ gen_audio = pipe.inference_ar_and_fm(
200
+ target_text=target_text,
201
+ style_ref_wav_path=input_audio_path,
202
+ style_ref_wav_text=reference_text,
203
+ timbre_ref_wav_path=reference_audio_path or input_audio_path,
204
+ use_prosody_code=False,
205
+ flow_matching_steps=flow_matching_steps,
206
+ )
207
+ elif task == "Singing Editing":
208
+ if target_text is None:
209
+ raise gr.Error("Singing Editing needs the edited Text / Lyrics.")
210
+ gen_audio = pipe.inference_ar_and_fm(
211
+ target_text=target_text,
212
+ prosody_wav_path=input_audio_path,
213
+ style_ref_wav_path=input_audio_path,
214
+ style_ref_wav_text=reference_text,
215
+ timbre_ref_wav_path=input_audio_path,
216
+ use_prosody_code=True,
217
+ flow_matching_steps=flow_matching_steps,
218
+ )
219
+ elif task == "Singing Style Conversion":
220
+ if reference_audio_path is None:
221
+ raise gr.Error(
222
+ "Singing Style Conversion needs a style reference (Reference Voice input)."
223
+ )
224
+ gen_audio = pipe.inference_ar_and_fm(
225
+ target_text=target_text,
226
+ prosody_wav_path=input_audio_path,
227
+ style_ref_wav_path=reference_audio_path,
228
+ style_ref_wav_text=reference_text,
229
+ timbre_ref_wav_path=input_audio_path,
230
+ use_prosody_code=True,
231
+ use_pitch_shift=True,
232
+ flow_matching_steps=flow_matching_steps,
233
+ )
234
+ else: # Melody Control
235
+ if target_text is None:
236
+ raise gr.Error("Melody Control needs the Text / Lyrics to sing.")
237
+ if reference_audio_path is None:
238
+ raise gr.Error(
239
+ "Melody Control needs a reference voice (Reference Voice input) — "
240
+ "the melody input alone (e.g. humming) has no usable vocal timbre."
241
+ )
242
+ gen_audio = pipe.inference_ar_and_fm(
243
+ target_text=target_text,
244
+ prosody_wav_path=input_audio_path,
245
+ style_ref_wav_path=reference_audio_path,
246
+ style_ref_wav_text=reference_text,
247
+ timbre_ref_wav_path=reference_audio_path,
248
+ use_prosody_code=True,
249
+ use_pitch_shift=True,
250
+ flow_matching_steps=flow_matching_steps,
251
+ )
252
+
253
+ with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
254
+ output_path = f.name
255
+ save_audio(gen_audio, output_path=output_path)
256
+ return output_path
257
+
258
+
259
+ with gr.Blocks() as demo:
260
+ input_components = [
261
+ gr.Dropdown(
262
+ choices=TASKS,
263
+ value=TASKS[0],
264
+ label="Task",
265
+ info="What to do with the input audio.",
266
+ ),
267
+ gr.Audio(type="filepath", label="Input Audio").harp_required(True).set_info(
268
+ "Content/source for Voice/Singing Conversion and Singing Style Conversion; "
269
+ "style reference for Text-to-Speech; original recording for Singing Editing; "
270
+ "melody reference (e.g. humming or an instrument) for Melody Control."
271
+ ),
272
+ gr.Audio(type="filepath", label="Reference Voice (Timbre)")
273
+ .harp_required(False)
274
+ .set_info(
275
+ "A clip of the target voice. Required for Voice/Singing Conversion, "
276
+ "Singing Style Conversion, and Melody Control; optional for "
277
+ "Text-to-Speech (defaults to Input Audio's voice); ignored for Singing Editing."
278
+ ),
279
+ gr.Textbox(
280
+ label="Text / Lyrics",
281
+ info="Required for Text-to-Speech, Singing Editing, and Melody Control. "
282
+ "Optional for Singing Style Conversion. Ignored for Voice/Singing Conversion.",
283
+ ),
284
+ gr.Textbox(
285
+ label="Input Audio Transcript (optional)",
286
+ info="Transcript of Input Audio, if known — improves quality. "
287
+ "Not used for Voice/Singing Conversion.",
288
+ ),
289
+ gr.Slider(
290
+ minimum=8,
291
+ maximum=64,
292
+ step=1,
293
+ value=32,
294
+ label="Generation Detail (Steps)",
295
+ info="More steps trade generation speed for audio detail (default: 32, per repo).",
296
+ ),
297
+ ]
298
+ output_components = [
299
+ gr.Audio(type="filepath", label="Output Audio").set_info(
300
+ "Generated speech or singing voice."
301
+ ),
302
+ ]
303
+
304
+ build_endpoint(
305
+ model_card=model_card,
306
+ input_components=input_components,
307
+ output_components=output_components,
308
+ process_fn=process_fn,
309
+ )
310
+
311
+ if __name__ == "__main__":
312
+ demo.queue().launch(pwa=True)
evaluation/__init__.py ADDED
File without changes
evaluation/metrics/__init__.py ADDED
File without changes
evaluation/metrics/f0/__init__.py ADDED
File without changes
evaluation/metrics/f0/f0_corr.py ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import torch
7
+ import librosa
8
+ import numpy as np
9
+ import parselmouth
10
+
11
+ from utils.f0 import interpolate
12
+
13
+
14
+ def get_cents(f0_hz):
15
+ """
16
+ F_{cent} = 1200 * log2 (F/440)
17
+
18
+ Reference:
19
+ APSIPA'17, Perceptual Evaluation of Singing Quality
20
+ """
21
+ voiced_f0 = f0_hz[f0_hz != 0]
22
+ return 1200 * np.log2(voiced_f0 / 440)
23
+
24
+
25
+ def get_pitch_sub_median(f0_hz):
26
+ """
27
+ f0_hz: (,T)
28
+ """
29
+ f0_cent = get_cents(f0_hz)
30
+ return f0_cent - np.median(f0_cent)
31
+
32
+
33
+ def get_f0_features_using_parselmouth(audio, cfg, speed=1):
34
+ """Using parselmouth to extract the f0 feature.
35
+ Args:
36
+ audio
37
+ mel_len
38
+ hop_length
39
+ fs
40
+ f0_min
41
+ f0_max
42
+ speed(default=1)
43
+ Returns:
44
+ f0: numpy array of shape (frame_len,)
45
+ pitch_coarse: numpy array of shape (frame_len,)
46
+ """
47
+ hop_size = int(np.round(cfg.hop_size * speed))
48
+
49
+ # Calculate the time step for pitch extraction
50
+ time_step = hop_size / cfg.sample_rate * 1000
51
+
52
+ f0 = (
53
+ parselmouth.Sound(audio, cfg.sample_rate)
54
+ .to_pitch_ac(
55
+ time_step=time_step / 1000,
56
+ voicing_threshold=0.6,
57
+ pitch_floor=cfg.f0_min,
58
+ pitch_ceiling=cfg.f0_max,
59
+ )
60
+ .selected_array["frequency"]
61
+ )
62
+ return f0
63
+
64
+
65
+ class JsonHParams:
66
+ def __init__(self, **kwargs):
67
+ for k, v in kwargs.items():
68
+ if type(v) == dict:
69
+ v = JsonHParams(**v)
70
+ self[k] = v
71
+
72
+ def keys(self):
73
+ return self.__dict__.keys()
74
+
75
+ def items(self):
76
+ return self.__dict__.items()
77
+
78
+ def values(self):
79
+ return self.__dict__.values()
80
+
81
+ def __len__(self):
82
+ return len(self.__dict__)
83
+
84
+ def __getitem__(self, key):
85
+ return getattr(self, key)
86
+
87
+ def __setitem__(self, key, value):
88
+ return setattr(self, key, value)
89
+
90
+ def __contains__(self, key):
91
+ return key in self.__dict__
92
+
93
+ def __repr__(self):
94
+ return self.__dict__.__repr__()
95
+
96
+
97
+ def extract_f0_hz(
98
+ wav_path,
99
+ fs=16000,
100
+ hop_length=256,
101
+ f0_min=50,
102
+ f0_max=1100,
103
+ ):
104
+ cfg = JsonHParams()
105
+ cfg.sample_rate = fs
106
+ cfg.hop_size = hop_length
107
+ cfg.f0_min = f0_min
108
+ cfg.f0_max = f0_max
109
+ cfg.pitch_bin = 256
110
+ cfg.pitch_max = f0_max
111
+ cfg.pitch_min = f0_min
112
+
113
+ # Compute f0
114
+ audio, _ = librosa.load(wav_path, sr=fs)
115
+ f0 = get_f0_features_using_parselmouth(
116
+ audio,
117
+ cfg,
118
+ )
119
+ f0, _ = interpolate(f0)
120
+ return f0
121
+
122
+
123
+ def extract_fpc(
124
+ audio_ref,
125
+ audio_deg,
126
+ fs=16000,
127
+ need_mean=True,
128
+ hop_length=256,
129
+ f0_min=50,
130
+ f0_max=1100,
131
+ method="dtw",
132
+ ):
133
+ """Compute F0 Pearson Distance (FPC) between the predicted and the ground truth audio.
134
+ audio_ref: path to the ground truth audio.
135
+ audio_deg: path to the predicted audio.
136
+ fs: sampling rate.
137
+ hop_length: hop length.
138
+ f0_min: lower limit for f0.
139
+ f0_max: upper limit for f0.
140
+ pitch_bin: number of bins for f0 quantization.
141
+ pitch_max: upper limit for f0 quantization.
142
+ pitch_min: lower limit for f0 quantization.
143
+ need_mean: subtract the mean value from f0 if "True".
144
+ method: "dtw" will use dtw algorithm to align the length of the ground truth and predicted audio.
145
+ "cut" will cut both audios into a same length according to the one with the shorter length.
146
+ """
147
+ # Initialize method
148
+ from torchmetrics import PearsonCorrCoef
149
+
150
+ pearson = PearsonCorrCoef()
151
+
152
+ # Load audio
153
+ if fs != None:
154
+ audio_ref, _ = librosa.load(audio_ref, sr=fs)
155
+ audio_deg, _ = librosa.load(audio_deg, sr=fs)
156
+ else:
157
+ audio_ref, ref_fs = librosa.load(audio_ref)
158
+ audio_deg, deg_fs = librosa.load(audio_deg)
159
+ assert ref_fs == deg_fs
160
+ fs = ref_fs
161
+
162
+ # Initialize config
163
+ cfg = JsonHParams()
164
+ cfg.sample_rate = fs
165
+ cfg.hop_size = hop_length
166
+ cfg.f0_min = f0_min
167
+ cfg.f0_max = f0_max
168
+ cfg.pitch_bin = 256
169
+ cfg.pitch_max = f0_max
170
+ cfg.pitch_min = f0_min
171
+
172
+ # Compute f0
173
+ f0_ref = get_f0_features_using_parselmouth(
174
+ audio_ref,
175
+ cfg,
176
+ )
177
+
178
+ f0_deg = get_f0_features_using_parselmouth(
179
+ audio_deg,
180
+ cfg,
181
+ )
182
+
183
+ # Subtract mean value from f0
184
+ if need_mean:
185
+ f0_ref = torch.from_numpy(f0_ref)
186
+ f0_deg = torch.from_numpy(f0_deg)
187
+
188
+ f0_ref = get_pitch_sub_median(f0_ref).numpy()
189
+ f0_deg = get_pitch_sub_median(f0_deg).numpy()
190
+
191
+ # Avoid silence
192
+ min_length = min(len(f0_ref), len(f0_deg))
193
+ if min_length <= 1:
194
+ return 1
195
+
196
+ # F0 length alignment
197
+ if method == "cut":
198
+ length = min(len(f0_ref), len(f0_deg))
199
+ f0_ref = f0_ref[:length]
200
+ f0_deg = f0_deg[:length]
201
+ elif method == "dtw":
202
+ _, wp = librosa.sequence.dtw(f0_ref, f0_deg, backtrack=True)
203
+ f0_gt_new = []
204
+ f0_pred_new = []
205
+ for i in range(wp.shape[0]):
206
+ gt_index = wp[i][0]
207
+ pred_index = wp[i][1]
208
+ f0_gt_new.append(f0_ref[gt_index])
209
+ f0_pred_new.append(f0_deg[pred_index])
210
+ f0_ref = np.array(f0_gt_new)
211
+ f0_deg = np.array(f0_pred_new)
212
+ assert len(f0_ref) == len(f0_deg)
213
+
214
+ # Convert to tensor
215
+ f0_ref = torch.from_numpy(f0_ref)
216
+ f0_deg = torch.from_numpy(f0_deg)
217
+
218
+ if torch.cuda.is_available():
219
+ device = torch.device("cuda")
220
+ f0_ref = f0_ref.to(device)
221
+ f0_deg = f0_deg.to(device)
222
+ pearson = pearson.to(device)
223
+
224
+ return pearson(f0_ref, f0_deg).detach().cpu().numpy().tolist()
model.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Vevo2",
3
+ "package_dir": ".",
4
+ "entry_point": "models.svc.vevo2.vevo2_utils.Vevo2InferencePipeline",
5
+ "checkpoint": {
6
+ "repo": "RMSnow/Vevo2",
7
+ "download": "snapshot_download at startup, allow_patterns excluding optimizer/scheduler/rng_state training artifacts",
8
+ "size_mb": 4400
9
+ }
10
+ }
models/__init__.py ADDED
File without changes
models/codec/__init__.py ADDED
File without changes
models/codec/amphion_codec/quantize/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from models.codec.amphion_codec.quantize.factorized_vector_quantize import (
7
+ FactorizedVectorQuantize,
8
+ )
9
+ from models.codec.amphion_codec.quantize.vector_quantize import VectorQuantize
10
+ from models.codec.amphion_codec.quantize.lookup_free_quantize import LookupFreeQuantize
11
+ from models.codec.amphion_codec.quantize.residual_vq import ResidualVQ
models/codec/amphion_codec/quantize/factorized_vector_quantize.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import numpy as np
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+ from einops import rearrange
11
+ from torch.nn.utils import weight_norm
12
+
13
+
14
+ def WNConv1d(*args, **kwargs):
15
+ return weight_norm(nn.Conv1d(*args, **kwargs))
16
+
17
+
18
+ def WNConvTranspose1d(*args, **kwargs):
19
+ return weight_norm(nn.ConvTranspose1d(*args, **kwargs))
20
+
21
+
22
+ class FactorizedVectorQuantize(nn.Module):
23
+ def __init__(
24
+ self,
25
+ input_dim,
26
+ codebook_size,
27
+ codebook_dim,
28
+ commitment=0.005,
29
+ codebook_loss_weight=1.0,
30
+ use_l2_normlize=True,
31
+ ):
32
+ super().__init__()
33
+ self.input_dim = input_dim
34
+ self.codebook_size = codebook_size
35
+ self.codebook_dim = codebook_dim
36
+ self.commitment = commitment
37
+ self.codebook_loss_weight = codebook_loss_weight
38
+ self.use_l2_normlize = use_l2_normlize
39
+
40
+ if self.input_dim != self.codebook_dim:
41
+ self.in_project = WNConv1d(self.input_dim, self.codebook_dim, kernel_size=1)
42
+ self.out_project = WNConv1d(
43
+ self.codebook_dim, self.input_dim, kernel_size=1
44
+ )
45
+
46
+ else:
47
+ self.in_project = nn.Identity()
48
+ self.out_project = nn.Identity()
49
+
50
+ self.codebook = nn.Embedding(self.codebook_size, self.codebook_dim)
51
+
52
+ def forward(self, z):
53
+ """
54
+ Parameters
55
+ ----------
56
+ z: torch.Tensor[B x D x T]
57
+
58
+ Returns
59
+ -------
60
+ z_q: torch.Tensor[B x D x T]
61
+ Quantized continuous representation of input
62
+ commit_loss: Tensor[B]
63
+ Commitment loss to train encoder to predict vectors closer to codebook entries
64
+ codebook_loss: Tensor[B]
65
+ Codebook loss to update the codebook
66
+ indices: torch.Tensor[B x T]
67
+ Codebook indices (quantized discrete representation of input)
68
+ z_e: torch.Tensor[B x D x T]
69
+ Projected latents (continuous representation of input before quantization)
70
+ """
71
+
72
+ # Factorized codes project input into low-dimensional space if self.input_dim != self.codebook_dim
73
+ z_e = self.in_project(z)
74
+ z_q, indices = self.decode_latents(z_e)
75
+
76
+ # Compute commitment loss and codebook loss
77
+ if self.training:
78
+ commit_loss = (
79
+ F.mse_loss(z_e, z_q.detach(), reduction="none").mean([1, 2])
80
+ * self.commitment
81
+ )
82
+ codebook_loss = (
83
+ F.mse_loss(z_q, z_e.detach(), reduction="none").mean([1, 2])
84
+ * self.codebook_loss_weight
85
+ )
86
+ else:
87
+ commit_loss = torch.zeros(z.shape[0], device=z.device)
88
+ codebook_loss = torch.zeros(z.shape[0], device=z.device)
89
+
90
+ z_q = z_e + (z_q - z_e).detach()
91
+
92
+ z_q = self.out_project(z_q)
93
+
94
+ return z_q, commit_loss, codebook_loss, indices, z_e
95
+
96
+ def embed_code(self, embed_id):
97
+ return F.embedding(embed_id, self.codebook.weight)
98
+
99
+ def decode_code(self, embed_id):
100
+ return self.embed_code(embed_id).transpose(1, 2)
101
+
102
+ def decode_latents(self, latents):
103
+ encodings = rearrange(latents, "b d t -> (b t) d")
104
+ codebook = self.codebook.weight
105
+
106
+ # L2 normalize encodings and codebook
107
+ if self.use_l2_normlize:
108
+ encodings = F.normalize(encodings)
109
+ codebook = F.normalize(codebook)
110
+
111
+ # Compute euclidean distance between encodings and codebook,
112
+ # if use_l2_normlize is True, the distance is equal to cosine distance
113
+ dist = (
114
+ encodings.pow(2).sum(1, keepdim=True)
115
+ - 2 * encodings @ codebook.t()
116
+ + codebook.pow(2).sum(1, keepdim=True).t()
117
+ )
118
+ indices = rearrange((-dist).max(1)[1], "(b t) -> b t", b=latents.size(0))
119
+ z_q = self.decode_code(indices)
120
+
121
+ return z_q, indices
122
+
123
+ def vq2emb(self, vq, out_proj=True):
124
+ emb = self.decode_code(vq)
125
+ if out_proj:
126
+ emb = self.out_project(emb)
127
+ return emb
128
+
129
+ def latent2dist(self, latents):
130
+ encodings = rearrange(latents, "b d t -> (b t) d")
131
+ codebook = self.codebook.weight
132
+
133
+ # L2 normalize encodings and codebook
134
+ if self.use_l2_normlize:
135
+ encodings = F.normalize(encodings)
136
+ codebook = F.normalize(codebook)
137
+
138
+ # Compute euclidean distance between encodings and codebook,
139
+ # if use_l2_normlize is True, the distance is equal to cosine distance
140
+ dist = (
141
+ encodings.pow(2).sum(1, keepdim=True)
142
+ - 2 * encodings @ codebook.t()
143
+ + codebook.pow(2).sum(1, keepdim=True).t()
144
+ ) # (b*t, k)
145
+
146
+ indices = rearrange((-dist).max(1)[1], "(b t) -> b t", b=latents.size(0))
147
+ dist = rearrange(dist, "(b t) k -> b t k", b=latents.size(0))
148
+ z_q = self.decode_code(indices)
149
+
150
+ return -dist, indices, z_q
models/codec/amphion_codec/quantize/lookup_free_quantize.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import numpy as np
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+ from einops import rearrange
11
+ from torch.nn.utils import weight_norm
12
+
13
+
14
+ def WNConv1d(*args, **kwargs):
15
+ return weight_norm(nn.Conv1d(*args, **kwargs))
16
+
17
+
18
+ def WNConvTranspose1d(*args, **kwargs):
19
+ return weight_norm(nn.ConvTranspose1d(*args, **kwargs))
20
+
21
+
22
+ class LookupFreeQuantize(nn.Module):
23
+ def __init__(
24
+ self,
25
+ input_dim,
26
+ codebook_size,
27
+ codebook_dim,
28
+ ):
29
+ super().__init__()
30
+ self.input_dim = input_dim
31
+ self.codebook_size = codebook_size
32
+ self.codebook_dim = codebook_dim
33
+
34
+ assert 2**codebook_dim == codebook_size
35
+
36
+ if self.input_dim != self.codebook_dim:
37
+ self.in_project = WNConv1d(self.input_dim, self.codebook_dim, kernel_size=1)
38
+ self.out_project = WNConv1d(
39
+ self.codebook_dim, self.input_dim, kernel_size=1
40
+ )
41
+
42
+ else:
43
+ self.in_project = nn.Identity()
44
+ self.out_project = nn.Identity()
45
+
46
+ def forward(self, z):
47
+ z_e = self.in_project(z)
48
+ z_e = F.sigmoid(z_e)
49
+
50
+ z_q = z_e + (torch.round(z_e) - z_e).detach()
51
+
52
+ z_q = self.out_project(z_q)
53
+
54
+ commit_loss = torch.zeros(z.shape[0], device=z.device)
55
+ codebook_loss = torch.zeros(z.shape[0], device=z.device)
56
+
57
+ bits = (
58
+ 2
59
+ ** torch.arange(self.codebook_dim, device=z.device)
60
+ .unsqueeze(0)
61
+ .unsqueeze(-1)
62
+ .long()
63
+ ) # (1, d, 1)
64
+ indices = (torch.round(z_e.clone().detach()).long() * bits).sum(1).long()
65
+
66
+ return z_q, commit_loss, codebook_loss, indices, z_e
67
+
68
+ def vq2emb(self, vq, out_proj=True):
69
+ emb = torch.zeros(
70
+ vq.shape[0], self.codebook_dim, vq.shape[-1], device=vq.device
71
+ ) # (B, d, T)
72
+ for i in range(self.codebook_dim):
73
+ emb[:, i, :] = (vq % 2).float()
74
+ vq = vq // 2
75
+ if out_proj:
76
+ emb = self.out_project(emb)
77
+ return emb
models/codec/amphion_codec/quantize/residual_vq.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from typing import Union
7
+
8
+ import numpy as np
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from einops import rearrange
13
+ from torch.nn.utils import weight_norm
14
+
15
+ from models.codec.amphion_codec.quantize.factorized_vector_quantize import (
16
+ FactorizedVectorQuantize,
17
+ )
18
+ from models.codec.amphion_codec.quantize.vector_quantize import VectorQuantize
19
+ from models.codec.amphion_codec.quantize.lookup_free_quantize import LookupFreeQuantize
20
+
21
+
22
+ class ResidualVQ(nn.Module):
23
+ """
24
+ Introduced in SoundStream: An end2end neural audio codec
25
+ https://arxiv.org/abs/2107.03312
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ input_dim: int = 256,
31
+ num_quantizers: int = 8,
32
+ codebook_size: int = 1024,
33
+ codebook_dim: int = 256,
34
+ quantizer_type: str = "vq", # "vq" or "fvq" or "lfq"
35
+ quantizer_dropout: float = 0.5,
36
+ **kwargs,
37
+ ):
38
+ super().__init__()
39
+
40
+ self.input_dim = input_dim
41
+ self.num_quantizers = num_quantizers
42
+ self.codebook_size = codebook_size
43
+ self.codebook_dim = codebook_dim
44
+ self.quantizer_type = quantizer_type
45
+ self.quantizer_dropout = quantizer_dropout
46
+
47
+ if quantizer_type == "vq":
48
+ VQ = VectorQuantize
49
+ elif quantizer_type == "fvq":
50
+ VQ = FactorizedVectorQuantize
51
+ elif quantizer_type == "lfq":
52
+ VQ = LookupFreeQuantize
53
+ else:
54
+ raise ValueError(f"Unknown quantizer type {quantizer_type}")
55
+
56
+ self.quantizers = nn.ModuleList(
57
+ [
58
+ VQ(
59
+ input_dim=input_dim,
60
+ codebook_size=codebook_size,
61
+ codebook_dim=codebook_dim,
62
+ **kwargs,
63
+ )
64
+ for _ in range(num_quantizers)
65
+ ]
66
+ )
67
+
68
+ def forward(self, z, n_quantizers: int = None):
69
+ """
70
+ Parameters
71
+ ----------
72
+ z : Tensor[B x D x T]
73
+ n_quantizers : int, optional
74
+ No. of quantizers to use
75
+ (n_quantizers < self.n_codebooks ex: for quantizer dropout)
76
+ Note: if `self.quantizer_dropout` is True, this argument is ignored
77
+ when in training mode, and a random number of quantizers is used.
78
+ Returns
79
+ -------
80
+ "quantized_out" : Tensor[B x D x T]
81
+ Quantized continuous representation of input
82
+ "all_indices" : Tensor[N x B x T]
83
+ Codebook indices for each codebook
84
+ (quantized discrete representation of input)
85
+ "all_commit_losses" : Tensor[N]
86
+ "all_codebook_losses" : Tensor[N]
87
+ "all_quantized" : Tensor[N x B x D x T]
88
+ """
89
+
90
+ quantized_out = 0.0
91
+ residual = z
92
+
93
+ all_commit_losses = []
94
+ all_codebook_losses = []
95
+ all_indices = []
96
+ all_quantized = []
97
+
98
+ if n_quantizers is None:
99
+ n_quantizers = self.num_quantizers
100
+
101
+ if self.training:
102
+ n_quantizers = torch.ones((z.shape[0],)) * self.num_quantizers + 1
103
+ dropout = torch.randint(1, self.num_quantizers + 1, (z.shape[0],))
104
+ n_dropout = int(z.shape[0] * self.quantizer_dropout)
105
+ n_quantizers[:n_dropout] = dropout[:n_dropout]
106
+ n_quantizers = n_quantizers.to(z.device)
107
+
108
+ for i, quantizer in enumerate(self.quantizers):
109
+ if self.training is False and i >= n_quantizers:
110
+ break
111
+
112
+ z_q_i, commit_loss_i, codebook_loss_i, indices_i, z_e_i = quantizer(
113
+ residual
114
+ )
115
+
116
+ # Create mask to apply quantizer dropout
117
+ mask = (
118
+ torch.full((z.shape[0],), fill_value=i, device=z.device) < n_quantizers
119
+ )
120
+ quantized_out = quantized_out + z_q_i * mask[:, None, None]
121
+ residual = residual - z_q_i
122
+
123
+ commit_loss_i = (commit_loss_i * mask).mean()
124
+ codebook_loss_i = (codebook_loss_i * mask).mean()
125
+
126
+ all_commit_losses.append(commit_loss_i)
127
+ all_codebook_losses.append(codebook_loss_i)
128
+ all_indices.append(indices_i)
129
+ all_quantized.append(z_q_i)
130
+
131
+ all_commit_losses, all_codebook_losses, all_indices, all_quantized = map(
132
+ torch.stack,
133
+ (all_commit_losses, all_codebook_losses, all_indices, all_quantized),
134
+ )
135
+
136
+ return (
137
+ quantized_out,
138
+ all_indices,
139
+ all_commit_losses,
140
+ all_codebook_losses,
141
+ all_quantized,
142
+ )
143
+
144
+ def vq2emb(self, vq, n_quantizers=None):
145
+ quantized_out = 0.0
146
+ if n_quantizers is None:
147
+ n_quantizers = self.num_quantizers
148
+ for idx, quantizer in enumerate(self.quantizers):
149
+ if idx >= n_quantizers:
150
+ break
151
+ quantized_out += quantizer.vq2emb(vq[idx])
152
+ return quantized_out
153
+
154
+ def latent2dist(self, z, n_quantizers=None):
155
+ quantized_out = 0.0
156
+ residual = z
157
+
158
+ all_dists = []
159
+ all_indices = []
160
+
161
+ if n_quantizers is None:
162
+ n_quantizers = self.num_quantizers
163
+
164
+ for i, quantizer in enumerate(self.quantizers):
165
+ if self.training is False and i >= n_quantizers:
166
+ break
167
+ dist_i, indices_i, z_q_i = quantizer.latent2dist(residual)
168
+ all_dists.append(dist_i)
169
+ all_indices.append(indices_i)
170
+
171
+ quantized_out = quantized_out + z_q_i
172
+ residual = residual - z_q_i
173
+
174
+ all_dists = torch.stack(all_dists)
175
+ all_indices = torch.stack(all_indices)
176
+
177
+ return all_dists, all_indices
models/codec/amphion_codec/quantize/vector_quantize.py ADDED
@@ -0,0 +1,401 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import numpy as np
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+ from einops import rearrange, repeat
11
+ from torch.nn.utils import weight_norm
12
+
13
+
14
+ def WNConv1d(*args, **kwargs):
15
+ return weight_norm(nn.Conv1d(*args, **kwargs))
16
+
17
+
18
+ def WNConvTranspose1d(*args, **kwargs):
19
+ return weight_norm(nn.ConvTranspose1d(*args, **kwargs))
20
+
21
+
22
+ def l2norm(t):
23
+ return F.normalize(t, p=2, dim=-1)
24
+
25
+
26
+ def ema_inplace(moving_avg, new, decay):
27
+ moving_avg.data.mul_(decay).add_(new, alpha=(1 - decay))
28
+
29
+
30
+ def laplace_smoothing(x, n_categories, eps=1e-5):
31
+ return (x + eps) / (x.sum() + n_categories * eps)
32
+
33
+
34
+ def sample_vectors(samples, num):
35
+ num_samples, device = samples.shape[0], samples.device
36
+
37
+ if num_samples >= num:
38
+ indices = torch.randperm(num_samples, device=device)[:num]
39
+ else:
40
+ indices = torch.randint(0, num_samples, (num,), device=device)
41
+
42
+ return samples[indices]
43
+
44
+
45
+ def kmeans(samples, num_clusters, num_iters=10, use_cosine_sim=False):
46
+ dim, dtype, device = samples.shape[-1], samples.dtype, samples.device
47
+
48
+ means = sample_vectors(samples, num_clusters)
49
+
50
+ for _ in range(num_iters):
51
+ if use_cosine_sim:
52
+ dists = samples @ means.t()
53
+ else:
54
+ diffs = rearrange(samples, "n d -> n () d") - rearrange(
55
+ means, "c d -> () c d"
56
+ )
57
+ dists = -(diffs**2).sum(dim=-1)
58
+
59
+ buckets = dists.max(dim=-1).indices
60
+ bins = torch.bincount(buckets, minlength=num_clusters)
61
+ zero_mask = bins == 0
62
+ bins_min_clamped = bins.masked_fill(zero_mask, 1)
63
+
64
+ new_means = buckets.new_zeros(num_clusters, dim, dtype=dtype)
65
+ new_means.scatter_add_(0, repeat(buckets, "n -> n d", d=dim), samples)
66
+ new_means = new_means / bins_min_clamped[..., None]
67
+
68
+ if use_cosine_sim:
69
+ new_means = l2norm(new_means)
70
+
71
+ means = torch.where(zero_mask[..., None], means, new_means)
72
+
73
+ return means, bins
74
+
75
+
76
+ class EuclideanCodebook(nn.Module):
77
+ def __init__(
78
+ self,
79
+ dim,
80
+ codebook_size,
81
+ kmeans_init=False,
82
+ kmeans_iters=10,
83
+ decay=0.8,
84
+ eps=1e-5,
85
+ threshold_ema_dead_code=2,
86
+ weight_init=False,
87
+ ):
88
+ super().__init__()
89
+
90
+ self.decay = decay
91
+ init_fn = torch.randn if not weight_init else torch.zeros
92
+ embed = init_fn(codebook_size, dim)
93
+
94
+ if weight_init:
95
+ nn.init.uniform_(embed, -1 / codebook_size, 1 / codebook_size)
96
+
97
+ self.codebook_size = codebook_size
98
+ self.kmeans_iters = kmeans_iters
99
+ self.eps = eps
100
+ self.threshold_ema_dead_code = threshold_ema_dead_code
101
+
102
+ self.register_buffer(
103
+ "initted", torch.Tensor([not kmeans_init])
104
+ ) # if kmeans_init is True, then initted is False; otherwise, initted is True
105
+ self.register_buffer("cluster_size", torch.zeros(codebook_size))
106
+ self.register_buffer("embed", embed)
107
+ self.register_buffer("embed_avg", embed.clone())
108
+
109
+ def init_embed_(self, data):
110
+ embed, cluster_size = kmeans(data, self.codebook_size, self.kmeans_iters)
111
+ self.embed.data.copy_(embed)
112
+ self.embed_avg.data.copy_(embed)
113
+ self.cluster_size.data.copy_(cluster_size)
114
+ self.initted.data.copy_(torch.Tensor([True]))
115
+
116
+ def replace(self, samples, mask):
117
+ modified_codebook = torch.where(
118
+ mask[..., None], sample_vectors(samples, self.codebook_size), self.embed
119
+ )
120
+ self.embed.data.copy_(modified_codebook)
121
+
122
+ def expire_codes_(self, batch_samples):
123
+ if self.threshold_ema_dead_code == 0:
124
+ return
125
+
126
+ expired_codes = self.cluster_size < self.threshold_ema_dead_code
127
+ if not torch.any(expired_codes):
128
+ return
129
+ batch_samples = rearrange(batch_samples, "... d -> (...) d")
130
+ self.replace(batch_samples, mask=expired_codes)
131
+
132
+ def forward(self, x):
133
+ shape, dtype = x.shape, x.dtype
134
+ flatten = rearrange(x, "... d -> (...) d")
135
+ embed = self.embed.t() # (codebook_size, dim) -> (dim, codebook_size)
136
+
137
+ if not self.initted:
138
+ self.init_embed_(flatten)
139
+
140
+ dist = -(
141
+ flatten.pow(2).sum(1, keepdim=True)
142
+ - 2 * flatten @ embed
143
+ + embed.pow(2).sum(0, keepdim=True)
144
+ )
145
+
146
+ embed_ind = dist.max(dim=-1).indices
147
+ embed_onehot = F.one_hot(embed_ind, self.codebook_size).type(dtype)
148
+ embed_ind = embed_ind.view(*shape[:-1])
149
+ quantize = F.embedding(embed_ind, self.embed)
150
+
151
+ if self.training:
152
+ ema_inplace(self.cluster_size, embed_onehot.sum(0), self.decay)
153
+ embed_sum = (
154
+ flatten.t() @ embed_onehot
155
+ ) # (dim, ...) @ (..., codebook_size) -> (dim, codebook_size)
156
+ ema_inplace(self.embed_avg, embed_sum.t(), self.decay)
157
+ cluster_size = (
158
+ laplace_smoothing(self.cluster_size, self.codebook_size, self.eps)
159
+ * self.cluster_size.sum()
160
+ )
161
+ embed_normalized = self.embed_avg / cluster_size.unsqueeze(1)
162
+ self.embed.data.copy_(embed_normalized)
163
+ self.expire_codes_(x)
164
+
165
+ return quantize, embed_ind
166
+
167
+ def vq2emb(self, vq):
168
+ quantize = F.embedding(vq, self.embed)
169
+ return quantize
170
+
171
+ def latent2dist(self, x):
172
+ shape, dtype = x.shape, x.dtype
173
+ flatten = rearrange(x, "... d -> (...) d")
174
+ embed = self.embed.t() # (codebook_size, dim) -> (dim, codebook_size)
175
+
176
+ if not self.initted:
177
+ self.init_embed_(flatten)
178
+
179
+ dist = -(
180
+ flatten.pow(2).sum(1, keepdim=True)
181
+ - 2 * flatten @ embed
182
+ + embed.pow(2).sum(0, keepdim=True)
183
+ )
184
+
185
+ embed_ind = dist.max(dim=-1).indices
186
+ embed_ind = embed_ind.view(*shape[:-1])
187
+ quantize = F.embedding(embed_ind, self.embed)
188
+
189
+ dist = dist.view(*shape[:-1], -1)
190
+
191
+ return dist, embed_ind, quantize
192
+
193
+
194
+ class SimpleCodebook(nn.Module):
195
+ def __init__(
196
+ self,
197
+ dim,
198
+ codebook_size,
199
+ use_l2_normlize=False,
200
+ ):
201
+ super().__init__()
202
+
203
+ self.dim = dim
204
+ self.codebook_size = codebook_size
205
+ self.use_l2_normlize = use_l2_normlize
206
+
207
+ self.embed = nn.Embedding(self.codebook_size, self.dim)
208
+
209
+ def forward(self, x):
210
+ shape, dtype = x.shape, x.dtype
211
+ flatten = rearrange(x, "... d -> (...) d")
212
+ embed = self.embed.weight.t() # (codebook_size, dim) -> (dim, codebook_size)
213
+
214
+ if self.use_l2_normlize:
215
+ flatten = F.normalize(flatten)
216
+ embed = F.normalize(embed)
217
+
218
+ dist = -(
219
+ flatten.pow(2).sum(1, keepdim=True)
220
+ - 2 * flatten @ embed
221
+ + embed.pow(2).sum(0, keepdim=True)
222
+ )
223
+
224
+ embed_ind = dist.max(dim=-1).indices
225
+ embed_ind = embed_ind.view(*shape[:-1])
226
+ quantize = F.embedding(embed_ind, self.embed)
227
+
228
+ return quantize, embed_ind
229
+
230
+ def vq2emb(self, vq):
231
+ quantize = F.embedding(vq, self.embed.weight)
232
+ return quantize
233
+
234
+ def latent2dist(self, x):
235
+ shape, dtype = x.shape, x.dtype
236
+ flatten = rearrange(x, "... d -> (...) d")
237
+ embed = self.embed.weight.t() # (codebook_size, dim) -> (dim, codebook_size)
238
+
239
+ if self.use_l2_normlize:
240
+ flatten = F.normalize(flatten)
241
+ embed = F.normalize(embed)
242
+
243
+ dist = -(
244
+ flatten.pow(2).sum(1, keepdim=True)
245
+ - 2 * flatten @ embed
246
+ + embed.pow(2).sum(0, keepdim=True)
247
+ )
248
+
249
+ embed_ind = dist.max(dim=-1).indices
250
+ embed_ind = embed_ind.view(*shape[:-1])
251
+ quantize = F.embedding(embed_ind, self.embed)
252
+
253
+ dist = dist.view(*shape[:-1], -1)
254
+
255
+ return dist, embed_ind, quantize
256
+
257
+
258
+ class VectorQuantize(nn.Module):
259
+ """Vector quantization and factorized vecotor quantization implementation
260
+ Args:
261
+ input_dim (int): Dimension of input.
262
+ codebook_size (int): Codebook size.
263
+ codebook_dim (int): Codebook dimension. We suggest use codebook_dim = input_dim
264
+ if use codebook_type == "euclidean", otherwise, if you want to use
265
+ factorized vector quantization, use codebook_dim as small number (e.g. 8 or 32).
266
+ commitment (float): Weight for commitment loss.
267
+ use_l2_normlize (bool): Whether to use l2 normlized codes for factorized vecotor quantization,
268
+ we suggest use it as True if you want to use factorized vector quantization
269
+ kmeans_init (bool): Whether to use kmeans to initialize the codebooks.
270
+ kmeans_iters (int): Number of iterations used for kmeans initialization.
271
+ decay (float): Decay for exponential moving average over the codebooks.
272
+ epsilon (float): Epsilon value for numerical stability.
273
+ threshold_ema_dead_code (int): Threshold for dead code expiration. Replace any codes
274
+ that have an exponential moving average cluster size less than the specified threshold with
275
+ randomly selected vector from the current batch.
276
+ """
277
+
278
+ def __init__(
279
+ self,
280
+ input_dim,
281
+ codebook_size,
282
+ codebook_dim,
283
+ commitment=0.005,
284
+ codebook_loss_weight=1.0,
285
+ use_l2_normlize=False,
286
+ codebook_type="euclidean", # "euclidean" or "simple"
287
+ kmeans_init=False,
288
+ kmeans_iters=10,
289
+ decay=0.8,
290
+ eps=1e-5,
291
+ threshold_ema_dead_code=2,
292
+ weight_init=False,
293
+ ):
294
+ super().__init__()
295
+ self.input_dim = input_dim
296
+ self.codebook_size = codebook_size
297
+ self.codebook_dim = codebook_dim
298
+ self.commitment = commitment
299
+ self.codebook_loss_weight = codebook_loss_weight
300
+ self.use_l2_normlize = use_l2_normlize
301
+ self.codebook_type = codebook_type
302
+ self.kmeans_init = kmeans_init
303
+ self.kmeans_iters = kmeans_iters
304
+ self.decay = decay
305
+ self.eps = eps
306
+ self.threshold_ema_dead_code = threshold_ema_dead_code
307
+ self.weight_init = weight_init
308
+
309
+ if self.input_dim != self.codebook_dim:
310
+ self.in_project = WNConv1d(self.input_dim, self.codebook_dim, kernel_size=1)
311
+ self.out_project = WNConv1d(
312
+ self.codebook_dim, self.input_dim, kernel_size=1
313
+ )
314
+
315
+ else:
316
+ self.in_project = nn.Identity()
317
+ self.out_project = nn.Identity()
318
+
319
+ if self.codebook_type == "euclidean":
320
+ self.codebook = EuclideanCodebook(
321
+ self.codebook_dim,
322
+ codebook_size=self.codebook_size,
323
+ kmeans_init=self.kmeans_init,
324
+ kmeans_iters=self.kmeans_iters,
325
+ decay=self.decay,
326
+ eps=self.eps,
327
+ threshold_ema_dead_code=self.threshold_ema_dead_code,
328
+ weight_init=self.weight_init,
329
+ )
330
+ elif self.codebook_type == "simple":
331
+ self.codebook = SimpleCodebook(
332
+ self.codebook_dim,
333
+ codebook_size=self.codebook_size,
334
+ use_l2_normlize=self.use_l2_normlize,
335
+ )
336
+ else:
337
+ raise NotImplementedError(
338
+ f"codebook_type {self.codebook_type} is not implemented!"
339
+ )
340
+
341
+ def forward(self, z):
342
+ """
343
+ Parameters
344
+ ----------
345
+ z: torch.Tensor[B x D x T]
346
+
347
+ Returns
348
+ -------
349
+ z_q: torch.Tensor[B x D x T]
350
+ Quantized continuous representation of input
351
+ commit_loss: Tensor[B]
352
+ Commitment loss to train encoder to predict vectors closer to codebook entries
353
+ codebook_loss: Tensor[B]
354
+ Codebook loss to update the codebook
355
+ indices: torch.Tensor[B x T]
356
+ Codebook indices (quantized discrete representation of input)
357
+ z_e: torch.Tensor[B x D x T]
358
+ Projected latents (continuous representation of input before quantization)
359
+ """
360
+
361
+ # Factorized codes project input into low-dimensional space if self.input_dim != self.codebook_dim
362
+ z_e = self.in_project(z)
363
+ z_q, indices = self.decode_latents(z_e)
364
+
365
+ # Compute commitment loss and codebook loss
366
+ if self.training:
367
+ commit_loss = (
368
+ F.mse_loss(z_e, z_q.detach(), reduction="none").mean([1, 2])
369
+ * self.commitment
370
+ )
371
+ codebook_loss = (
372
+ F.mse_loss(z_q, z_e.detach(), reduction="none").mean([1, 2])
373
+ * self.codebook_loss_weight
374
+ )
375
+ else:
376
+ commit_loss = torch.zeros(z.shape[0], device=z.device)
377
+ codebook_loss = torch.zeros(z.shape[0], device=z.device)
378
+
379
+ z_q = z_e + (z_q - z_e).detach()
380
+
381
+ z_q = self.out_project(z_q)
382
+
383
+ return z_q, commit_loss, codebook_loss, indices, z_e
384
+
385
+ def decode_latents(self, latents):
386
+ encodings = rearrange(latents, "b d t -> b t d")
387
+ z_q, indices = self.codebook(encodings)
388
+ z_q = z_q.transpose(1, 2)
389
+ return z_q, indices
390
+
391
+ def vq2emb(self, vq, out_proj=True):
392
+ emb = self.codebook.vq2emb(vq)
393
+ emb = emb.transpose(1, 2)
394
+ if out_proj:
395
+ emb = self.out_project(emb)
396
+ return emb
397
+
398
+ def latent2dist(self, latents):
399
+ latents = rearrange(latents, "b d t -> b t d")
400
+ dist, embed_ind, quantize = self.codebook.latent2dist(latents)
401
+ return dist, embed_ind, quantize.transpose(1, 2)
models/codec/amphion_codec/vocos.py ADDED
@@ -0,0 +1,881 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from typing import Optional, Tuple
7
+
8
+ import numpy as np
9
+ import scipy
10
+ import torch
11
+ from torch import nn, view_as_real, view_as_complex
12
+ from torch import nn
13
+ from torch.nn.utils import weight_norm, remove_weight_norm
14
+ from torchaudio.functional.functional import _hz_to_mel, _mel_to_hz
15
+ import librosa
16
+
17
+
18
+ def safe_log(x: torch.Tensor, clip_val: float = 1e-7) -> torch.Tensor:
19
+ """
20
+ Computes the element-wise logarithm of the input tensor with clipping to avoid near-zero values.
21
+
22
+ Args:
23
+ x (Tensor): Input tensor.
24
+ clip_val (float, optional): Minimum value to clip the input tensor. Defaults to 1e-7.
25
+
26
+ Returns:
27
+ Tensor: Element-wise logarithm of the input tensor with clipping applied.
28
+ """
29
+ return torch.log(torch.clip(x, min=clip_val))
30
+
31
+
32
+ def symlog(x: torch.Tensor) -> torch.Tensor:
33
+ return torch.sign(x) * torch.log1p(x.abs())
34
+
35
+
36
+ def symexp(x: torch.Tensor) -> torch.Tensor:
37
+ return torch.sign(x) * (torch.exp(x.abs()) - 1)
38
+
39
+
40
+ class STFT(nn.Module):
41
+ def __init__(
42
+ self,
43
+ n_fft: int,
44
+ hop_length: int,
45
+ win_length: int,
46
+ center=True,
47
+ ):
48
+ super().__init__()
49
+ self.center = center
50
+ self.n_fft = n_fft
51
+ self.hop_length = hop_length
52
+ self.win_length = win_length
53
+ window = torch.hann_window(win_length)
54
+ self.register_buffer("window", window)
55
+
56
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
57
+ # x: (B, T * hop_length)
58
+
59
+ if not self.center:
60
+ pad = self.win_length - self.hop_length
61
+ x = torch.nn.functional.pad(x, (pad // 2, pad // 2), mode="reflect")
62
+
63
+ stft_spec = torch.stft(
64
+ x,
65
+ self.n_fft,
66
+ hop_length=self.hop_length,
67
+ win_length=self.win_length,
68
+ window=self.window,
69
+ center=self.center,
70
+ return_complex=False,
71
+ ) # (B, n_fft // 2 + 1, T, 2)
72
+
73
+ rea = stft_spec[:, :, :, 0] # (B, n_fft // 2 + 1, T, 2)
74
+ imag = stft_spec[:, :, :, 1] # (B, n_fft // 2 + 1, T, 2)
75
+
76
+ log_mag = torch.log(
77
+ torch.abs(torch.sqrt(torch.pow(rea, 2) + torch.pow(imag, 2))) + 1e-5
78
+ ) # (B, n_fft // 2 + 1, T)
79
+ phase = torch.atan2(imag, rea) # (B, n_fft // 2 + 1, T)
80
+
81
+ return log_mag, phase
82
+
83
+
84
+ class ISTFT(nn.Module):
85
+ """
86
+ Custom implementation of ISTFT since torch.istft doesn't allow custom padding (other than `center=True`) with
87
+ windowing. This is because the NOLA (Nonzero Overlap Add) check fails at the edges.
88
+ See issue: https://github.com/pytorch/pytorch/issues/62323
89
+ Specifically, in the context of neural vocoding we are interested in "same" padding analogous to CNNs.
90
+ The NOLA constraint is met as we trim padded samples anyway.
91
+
92
+ Args:
93
+ n_fft (int): Size of Fourier transform.
94
+ hop_length (int): The distance between neighboring sliding window frames.
95
+ win_length (int): The size of window frame and STFT filter.
96
+ padding (str, optional): Type of padding. Options are "center" or "same". Defaults to "same".
97
+ """
98
+
99
+ def __init__(
100
+ self, n_fft: int, hop_length: int, win_length: int, padding: str = "same"
101
+ ):
102
+ super().__init__()
103
+ if padding not in ["center", "same"]:
104
+ raise ValueError("Padding must be 'center' or 'same'.")
105
+ self.padding = padding
106
+ self.n_fft = n_fft
107
+ self.hop_length = hop_length
108
+ self.win_length = win_length
109
+ window = torch.hann_window(win_length)
110
+ self.register_buffer("window", window)
111
+
112
+ def forward(self, spec: torch.Tensor) -> torch.Tensor:
113
+ """
114
+ Compute the Inverse Short Time Fourier Transform (ISTFT) of a complex spectrogram.
115
+
116
+ Args:
117
+ spec (Tensor): Input complex spectrogram of shape (B, N, T), where B is the batch size,
118
+ N is the number of frequency bins, and T is the number of time frames.
119
+
120
+ Returns:
121
+ Tensor: Reconstructed time-domain signal of shape (B, L), where L is the length of the output signal.
122
+ """
123
+ if self.padding == "center":
124
+ # Fallback to pytorch native implementation
125
+ return torch.istft(
126
+ spec,
127
+ self.n_fft,
128
+ self.hop_length,
129
+ self.win_length,
130
+ self.window,
131
+ center=True,
132
+ )
133
+ elif self.padding == "same":
134
+ pad = (self.win_length - self.hop_length) // 2
135
+ else:
136
+ raise ValueError("Padding must be 'center' or 'same'.")
137
+
138
+ assert spec.dim() == 3, "Expected a 3D tensor as input"
139
+ B, N, T = spec.shape
140
+
141
+ # Inverse FFT
142
+ ifft = torch.fft.irfft(spec, self.n_fft, dim=1, norm="backward")
143
+ ifft = ifft * self.window[None, :, None]
144
+
145
+ # Overlap and Add
146
+ output_size = (T - 1) * self.hop_length + self.win_length
147
+ y = torch.nn.functional.fold(
148
+ ifft,
149
+ output_size=(1, output_size),
150
+ kernel_size=(1, self.win_length),
151
+ stride=(1, self.hop_length),
152
+ )[:, 0, 0, pad:-pad]
153
+
154
+ # Window envelope
155
+ window_sq = self.window.square().expand(1, T, -1).transpose(1, 2)
156
+ window_envelope = torch.nn.functional.fold(
157
+ window_sq,
158
+ output_size=(1, output_size),
159
+ kernel_size=(1, self.win_length),
160
+ stride=(1, self.hop_length),
161
+ ).squeeze()[pad:-pad]
162
+
163
+ # Normalize
164
+ assert (window_envelope > 1e-11).all()
165
+ y = y / window_envelope
166
+
167
+ return y
168
+
169
+
170
+ class MDCT(nn.Module):
171
+ """
172
+ Modified Discrete Cosine Transform (MDCT) module.
173
+
174
+ Args:
175
+ frame_len (int): Length of the MDCT frame.
176
+ padding (str, optional): Type of padding. Options are "center" or "same". Defaults to "same".
177
+ """
178
+
179
+ def __init__(self, frame_len: int, padding: str = "same"):
180
+ super().__init__()
181
+ if padding not in ["center", "same"]:
182
+ raise ValueError("Padding must be 'center' or 'same'.")
183
+ self.padding = padding
184
+ self.frame_len = frame_len
185
+ N = frame_len // 2
186
+ n0 = (N + 1) / 2
187
+ window = torch.from_numpy(scipy.signal.cosine(frame_len)).float()
188
+ self.register_buffer("window", window)
189
+
190
+ pre_twiddle = torch.exp(-1j * torch.pi * torch.arange(frame_len) / frame_len)
191
+ post_twiddle = torch.exp(-1j * torch.pi * n0 * (torch.arange(N) + 0.5) / N)
192
+ # view_as_real: NCCL Backend does not support ComplexFloat data type
193
+ # https://github.com/pytorch/pytorch/issues/71613
194
+ self.register_buffer("pre_twiddle", view_as_real(pre_twiddle))
195
+ self.register_buffer("post_twiddle", view_as_real(post_twiddle))
196
+
197
+ def forward(self, audio: torch.Tensor) -> torch.Tensor:
198
+ """
199
+ Apply the Modified Discrete Cosine Transform (MDCT) to the input audio.
200
+
201
+ Args:
202
+ audio (Tensor): Input audio waveform of shape (B, T), where B is the batch size
203
+ and T is the length of the audio.
204
+
205
+ Returns:
206
+ Tensor: MDCT coefficients of shape (B, L, N), where L is the number of output frames
207
+ and N is the number of frequency bins.
208
+ """
209
+ if self.padding == "center":
210
+ audio = torch.nn.functional.pad(
211
+ audio, (self.frame_len // 2, self.frame_len // 2)
212
+ )
213
+ elif self.padding == "same":
214
+ # hop_length is 1/2 frame_len
215
+ audio = torch.nn.functional.pad(
216
+ audio, (self.frame_len // 4, self.frame_len // 4)
217
+ )
218
+ else:
219
+ raise ValueError("Padding must be 'center' or 'same'.")
220
+
221
+ x = audio.unfold(-1, self.frame_len, self.frame_len // 2)
222
+ N = self.frame_len // 2
223
+ x = x * self.window.expand(x.shape)
224
+ X = torch.fft.fft(
225
+ x * view_as_complex(self.pre_twiddle).expand(x.shape), dim=-1
226
+ )[..., :N]
227
+ res = X * view_as_complex(self.post_twiddle).expand(X.shape) * np.sqrt(1 / N)
228
+ return torch.real(res) * np.sqrt(2)
229
+
230
+
231
+ class IMDCT(nn.Module):
232
+ """
233
+ Inverse Modified Discrete Cosine Transform (IMDCT) module.
234
+
235
+ Args:
236
+ frame_len (int): Length of the MDCT frame.
237
+ padding (str, optional): Type of padding. Options are "center" or "same". Defaults to "same".
238
+ """
239
+
240
+ def __init__(self, frame_len: int, padding: str = "same"):
241
+ super().__init__()
242
+ if padding not in ["center", "same"]:
243
+ raise ValueError("Padding must be 'center' or 'same'.")
244
+ self.padding = padding
245
+ self.frame_len = frame_len
246
+ N = frame_len // 2
247
+ n0 = (N + 1) / 2
248
+ window = torch.from_numpy(scipy.signal.cosine(frame_len)).float()
249
+ self.register_buffer("window", window)
250
+
251
+ pre_twiddle = torch.exp(1j * torch.pi * n0 * torch.arange(N * 2) / N)
252
+ post_twiddle = torch.exp(1j * torch.pi * (torch.arange(N * 2) + n0) / (N * 2))
253
+ self.register_buffer("pre_twiddle", view_as_real(pre_twiddle))
254
+ self.register_buffer("post_twiddle", view_as_real(post_twiddle))
255
+
256
+ def forward(self, X: torch.Tensor) -> torch.Tensor:
257
+ """
258
+ Apply the Inverse Modified Discrete Cosine Transform (IMDCT) to the input MDCT coefficients.
259
+
260
+ Args:
261
+ X (Tensor): Input MDCT coefficients of shape (B, L, N), where B is the batch size,
262
+ L is the number of frames, and N is the number of frequency bins.
263
+
264
+ Returns:
265
+ Tensor: Reconstructed audio waveform of shape (B, T), where T is the length of the audio.
266
+ """
267
+ B, L, N = X.shape
268
+ Y = torch.zeros((B, L, N * 2), dtype=X.dtype, device=X.device)
269
+ Y[..., :N] = X
270
+ Y[..., N:] = -1 * torch.conj(torch.flip(X, dims=(-1,)))
271
+ y = torch.fft.ifft(
272
+ Y * view_as_complex(self.pre_twiddle).expand(Y.shape), dim=-1
273
+ )
274
+ y = (
275
+ torch.real(y * view_as_complex(self.post_twiddle).expand(y.shape))
276
+ * np.sqrt(N)
277
+ * np.sqrt(2)
278
+ )
279
+ result = y * self.window.expand(y.shape)
280
+ output_size = (1, (L + 1) * N)
281
+ audio = torch.nn.functional.fold(
282
+ result.transpose(1, 2),
283
+ output_size=output_size,
284
+ kernel_size=(1, self.frame_len),
285
+ stride=(1, self.frame_len // 2),
286
+ )[:, 0, 0, :]
287
+
288
+ if self.padding == "center":
289
+ pad = self.frame_len // 2
290
+ elif self.padding == "same":
291
+ pad = self.frame_len // 4
292
+ else:
293
+ raise ValueError("Padding must be 'center' or 'same'.")
294
+
295
+ audio = audio[:, pad:-pad]
296
+ return audio
297
+
298
+
299
+ class FourierHead(nn.Module):
300
+ """Base class for inverse fourier modules."""
301
+
302
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
303
+ """
304
+ Args:
305
+ x (Tensor): Input tensor of shape (B, L, H), where B is the batch size,
306
+ L is the sequence length, and H denotes the model dimension.
307
+
308
+ Returns:
309
+ Tensor: Reconstructed time-domain audio signal of shape (B, T), where T is the length of the output signal.
310
+ """
311
+ raise NotImplementedError("Subclasses must implement the forward method.")
312
+
313
+
314
+ class ISTFTHead(FourierHead):
315
+ """
316
+ ISTFT Head module for predicting STFT complex coefficients.
317
+
318
+ Args:
319
+ dim (int): Hidden dimension of the model.
320
+ n_fft (int): Size of Fourier transform.
321
+ hop_length (int): The distance between neighboring sliding window frames, which should align with
322
+ the resolution of the input features.
323
+ padding (str, optional): Type of padding. Options are "center" or "same". Defaults to "same".
324
+ """
325
+
326
+ def __init__(self, dim: int, n_fft: int, hop_length: int, padding: str = "same"):
327
+ super().__init__()
328
+ out_dim = n_fft + 2
329
+ self.out = torch.nn.Linear(dim, out_dim)
330
+ self.istft = ISTFT(
331
+ n_fft=n_fft, hop_length=hop_length, win_length=n_fft, padding=padding
332
+ )
333
+
334
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
335
+ """
336
+ Forward pass of the ISTFTHead module.
337
+
338
+ Args:
339
+ x (Tensor): Input tensor of shape (B, L, H), where B is the batch size,
340
+ L is the sequence length, and H denotes the model dimension.
341
+
342
+ Returns:
343
+ Tensor: Reconstructed time-domain audio signal of shape (B, T), where T is the length of the output signal.
344
+ """
345
+ x = self.out(x).transpose(1, 2)
346
+ mag, p = x.chunk(2, dim=1)
347
+ mag = torch.exp(mag)
348
+ mag = torch.clip(
349
+ mag, max=1e2
350
+ ) # safeguard to prevent excessively large magnitudes
351
+ # wrapping happens here. These two lines produce real and imaginary value
352
+ x = torch.cos(p)
353
+ y = torch.sin(p)
354
+ # recalculating phase here does not produce anything new
355
+ # only costs time
356
+ # phase = torch.atan2(y, x)
357
+ # S = mag * torch.exp(phase * 1j)
358
+ # better directly produce the complex value
359
+ S = mag * (x + 1j * y)
360
+ audio = self.istft(S)
361
+ return audio
362
+
363
+
364
+ class IMDCTSymExpHead(FourierHead):
365
+ """
366
+ IMDCT Head module for predicting MDCT coefficients with symmetric exponential function
367
+
368
+ Args:
369
+ dim (int): Hidden dimension of the model.
370
+ mdct_frame_len (int): Length of the MDCT frame.
371
+ padding (str, optional): Type of padding. Options are "center" or "same". Defaults to "same".
372
+ sample_rate (int, optional): The sample rate of the audio. If provided, the last layer will be initialized
373
+ based on perceptual scaling. Defaults to None.
374
+ clip_audio (bool, optional): Whether to clip the audio output within the range of [-1.0, 1.0]. Defaults to False.
375
+ """
376
+
377
+ def __init__(
378
+ self,
379
+ dim: int,
380
+ mdct_frame_len: int,
381
+ padding: str = "same",
382
+ sample_rate: Optional[int] = None,
383
+ clip_audio: bool = False,
384
+ ):
385
+ super().__init__()
386
+ out_dim = mdct_frame_len // 2
387
+ self.out = nn.Linear(dim, out_dim)
388
+ self.imdct = IMDCT(frame_len=mdct_frame_len, padding=padding)
389
+ self.clip_audio = clip_audio
390
+
391
+ if sample_rate is not None:
392
+ # optionally init the last layer following mel-scale
393
+ m_max = _hz_to_mel(sample_rate // 2)
394
+ m_pts = torch.linspace(0, m_max, out_dim)
395
+ f_pts = _mel_to_hz(m_pts)
396
+ scale = 1 - (f_pts / f_pts.max())
397
+
398
+ with torch.no_grad():
399
+ self.out.weight.mul_(scale.view(-1, 1))
400
+
401
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
402
+ """
403
+ Forward pass of the IMDCTSymExpHead module.
404
+
405
+ Args:
406
+ x (Tensor): Input tensor of shape (B, L, H), where B is the batch size,
407
+ L is the sequence length, and H denotes the model dimension.
408
+
409
+ Returns:
410
+ Tensor: Reconstructed time-domain audio signal of shape (B, T), where T is the length of the output signal.
411
+ """
412
+ x = self.out(x)
413
+ x = symexp(x)
414
+ x = torch.clip(
415
+ x, min=-1e2, max=1e2
416
+ ) # safeguard to prevent excessively large magnitudes
417
+ audio = self.imdct(x)
418
+ if self.clip_audio:
419
+ audio = torch.clip(x, min=-1.0, max=1.0)
420
+
421
+ return audio
422
+
423
+
424
+ class IMDCTCosHead(FourierHead):
425
+ """
426
+ IMDCT Head module for predicting MDCT coefficients with parametrizing MDCT = exp(m) · cos(p)
427
+
428
+ Args:
429
+ dim (int): Hidden dimension of the model.
430
+ mdct_frame_len (int): Length of the MDCT frame.
431
+ padding (str, optional): Type of padding. Options are "center" or "same". Defaults to "same".
432
+ clip_audio (bool, optional): Whether to clip the audio output within the range of [-1.0, 1.0]. Defaults to False.
433
+ """
434
+
435
+ def __init__(
436
+ self,
437
+ dim: int,
438
+ mdct_frame_len: int,
439
+ padding: str = "same",
440
+ clip_audio: bool = False,
441
+ ):
442
+ super().__init__()
443
+ self.clip_audio = clip_audio
444
+ self.out = nn.Linear(dim, mdct_frame_len)
445
+ self.imdct = IMDCT(frame_len=mdct_frame_len, padding=padding)
446
+
447
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
448
+ """
449
+ Forward pass of the IMDCTCosHead module.
450
+
451
+ Args:
452
+ x (Tensor): Input tensor of shape (B, L, H), where B is the batch size,
453
+ L is the sequence length, and H denotes the model dimension.
454
+
455
+ Returns:
456
+ Tensor: Reconstructed time-domain audio signal of shape (B, T), where T is the length of the output signal.
457
+ """
458
+ x = self.out(x)
459
+ m, p = x.chunk(2, dim=2)
460
+ m = torch.exp(m).clip(
461
+ max=1e2
462
+ ) # safeguard to prevent excessively large magnitudes
463
+ audio = self.imdct(m * torch.cos(p))
464
+ if self.clip_audio:
465
+ audio = torch.clip(x, min=-1.0, max=1.0)
466
+ return audio
467
+
468
+
469
+ class ConvNeXtBlock(nn.Module):
470
+ """ConvNeXt Block adapted from https://github.com/facebookresearch/ConvNeXt to 1D audio signal.
471
+
472
+ Args:
473
+ dim (int): Number of input channels.
474
+ intermediate_dim (int): Dimensionality of the intermediate layer.
475
+ layer_scale_init_value (float, optional): Initial value for the layer scale. None means no scaling.
476
+ Defaults to None.
477
+ adanorm_num_embeddings (int, optional): Number of embeddings for AdaLayerNorm.
478
+ None means non-conditional LayerNorm. Defaults to None.
479
+ """
480
+
481
+ def __init__(
482
+ self,
483
+ dim: int,
484
+ intermediate_dim: int,
485
+ layer_scale_init_value: float,
486
+ adanorm_num_embeddings: Optional[int] = None,
487
+ ):
488
+ super().__init__()
489
+ self.dwconv = nn.Conv1d(
490
+ dim, dim, kernel_size=7, padding=3, groups=dim
491
+ ) # depthwise conv
492
+ self.adanorm = adanorm_num_embeddings is not None
493
+ if adanorm_num_embeddings:
494
+ self.norm = AdaLayerNorm(adanorm_num_embeddings, dim, eps=1e-6)
495
+ else:
496
+ self.norm = nn.LayerNorm(dim, eps=1e-6)
497
+ self.pwconv1 = nn.Linear(
498
+ dim, intermediate_dim
499
+ ) # pointwise/1x1 convs, implemented with linear layers
500
+ self.act = nn.GELU()
501
+ self.pwconv2 = nn.Linear(intermediate_dim, dim)
502
+ self.gamma = (
503
+ nn.Parameter(layer_scale_init_value * torch.ones(dim), requires_grad=True)
504
+ if layer_scale_init_value > 0
505
+ else None
506
+ )
507
+
508
+ def forward(
509
+ self, x: torch.Tensor, cond_embedding_id: Optional[torch.Tensor] = None
510
+ ) -> torch.Tensor:
511
+ residual = x
512
+ x = self.dwconv(x)
513
+ x = x.transpose(1, 2) # (B, C, T) -> (B, T, C)
514
+ if self.adanorm:
515
+ assert cond_embedding_id is not None
516
+ x = self.norm(x, cond_embedding_id)
517
+ else:
518
+ x = self.norm(x)
519
+ x = self.pwconv1(x)
520
+ x = self.act(x)
521
+ x = self.pwconv2(x)
522
+ if self.gamma is not None:
523
+ x = self.gamma * x
524
+ x = x.transpose(1, 2) # (B, T, C) -> (B, C, T)
525
+
526
+ x = residual + x
527
+ return x
528
+
529
+
530
+ class AdaLayerNorm(nn.Module):
531
+ """
532
+ Adaptive Layer Normalization module with learnable embeddings per `num_embeddings` classes
533
+
534
+ Args:
535
+ num_embeddings (int): Number of embeddings.
536
+ embedding_dim (int): Dimension of the embeddings.
537
+ """
538
+
539
+ def __init__(self, num_embeddings: int, embedding_dim: int, eps: float = 1e-6):
540
+ super().__init__()
541
+ self.eps = eps
542
+ self.dim = embedding_dim
543
+ self.scale = nn.Embedding(
544
+ num_embeddings=num_embeddings, embedding_dim=embedding_dim
545
+ )
546
+ self.shift = nn.Embedding(
547
+ num_embeddings=num_embeddings, embedding_dim=embedding_dim
548
+ )
549
+ torch.nn.init.ones_(self.scale.weight)
550
+ torch.nn.init.zeros_(self.shift.weight)
551
+
552
+ def forward(self, x: torch.Tensor, cond_embedding_id: torch.Tensor) -> torch.Tensor:
553
+ scale = self.scale(cond_embedding_id)
554
+ shift = self.shift(cond_embedding_id)
555
+ x = nn.functional.layer_norm(x, (self.dim,), eps=self.eps)
556
+ x = x * scale + shift
557
+ return x
558
+
559
+
560
+ class ResBlock1(nn.Module):
561
+ """
562
+ ResBlock adapted from HiFi-GAN V1 (https://github.com/jik876/hifi-gan) with dilated 1D convolutions,
563
+ but without upsampling layers.
564
+
565
+ Args:
566
+ dim (int): Number of input channels.
567
+ kernel_size (int, optional): Size of the convolutional kernel. Defaults to 3.
568
+ dilation (tuple[int], optional): Dilation factors for the dilated convolutions.
569
+ Defaults to (1, 3, 5).
570
+ lrelu_slope (float, optional): Negative slope of the LeakyReLU activation function.
571
+ Defaults to 0.1.
572
+ layer_scale_init_value (float, optional): Initial value for the layer scale. None means no scaling.
573
+ Defaults to None.
574
+ """
575
+
576
+ def __init__(
577
+ self,
578
+ dim: int,
579
+ kernel_size: int = 3,
580
+ dilation: Tuple[int, int, int] = (1, 3, 5),
581
+ lrelu_slope: float = 0.1,
582
+ layer_scale_init_value: Optional[float] = None,
583
+ ):
584
+ super().__init__()
585
+ self.lrelu_slope = lrelu_slope
586
+ self.convs1 = nn.ModuleList(
587
+ [
588
+ weight_norm(
589
+ nn.Conv1d(
590
+ dim,
591
+ dim,
592
+ kernel_size,
593
+ 1,
594
+ dilation=dilation[0],
595
+ padding=self.get_padding(kernel_size, dilation[0]),
596
+ )
597
+ ),
598
+ weight_norm(
599
+ nn.Conv1d(
600
+ dim,
601
+ dim,
602
+ kernel_size,
603
+ 1,
604
+ dilation=dilation[1],
605
+ padding=self.get_padding(kernel_size, dilation[1]),
606
+ )
607
+ ),
608
+ weight_norm(
609
+ nn.Conv1d(
610
+ dim,
611
+ dim,
612
+ kernel_size,
613
+ 1,
614
+ dilation=dilation[2],
615
+ padding=self.get_padding(kernel_size, dilation[2]),
616
+ )
617
+ ),
618
+ ]
619
+ )
620
+
621
+ self.convs2 = nn.ModuleList(
622
+ [
623
+ weight_norm(
624
+ nn.Conv1d(
625
+ dim,
626
+ dim,
627
+ kernel_size,
628
+ 1,
629
+ dilation=1,
630
+ padding=self.get_padding(kernel_size, 1),
631
+ )
632
+ ),
633
+ weight_norm(
634
+ nn.Conv1d(
635
+ dim,
636
+ dim,
637
+ kernel_size,
638
+ 1,
639
+ dilation=1,
640
+ padding=self.get_padding(kernel_size, 1),
641
+ )
642
+ ),
643
+ weight_norm(
644
+ nn.Conv1d(
645
+ dim,
646
+ dim,
647
+ kernel_size,
648
+ 1,
649
+ dilation=1,
650
+ padding=self.get_padding(kernel_size, 1),
651
+ )
652
+ ),
653
+ ]
654
+ )
655
+
656
+ self.gamma = nn.ParameterList(
657
+ [
658
+ (
659
+ nn.Parameter(
660
+ layer_scale_init_value * torch.ones(dim, 1), requires_grad=True
661
+ )
662
+ if layer_scale_init_value is not None
663
+ else None
664
+ ),
665
+ (
666
+ nn.Parameter(
667
+ layer_scale_init_value * torch.ones(dim, 1), requires_grad=True
668
+ )
669
+ if layer_scale_init_value is not None
670
+ else None
671
+ ),
672
+ (
673
+ nn.Parameter(
674
+ layer_scale_init_value * torch.ones(dim, 1), requires_grad=True
675
+ )
676
+ if layer_scale_init_value is not None
677
+ else None
678
+ ),
679
+ ]
680
+ )
681
+
682
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
683
+ for c1, c2, gamma in zip(self.convs1, self.convs2, self.gamma):
684
+ xt = torch.nn.functional.leaky_relu(x, negative_slope=self.lrelu_slope)
685
+ xt = c1(xt)
686
+ xt = torch.nn.functional.leaky_relu(xt, negative_slope=self.lrelu_slope)
687
+ xt = c2(xt)
688
+ if gamma is not None:
689
+ xt = gamma * xt
690
+ x = xt + x
691
+ return x
692
+
693
+ def remove_weight_norm(self):
694
+ for l in self.convs1:
695
+ remove_weight_norm(l)
696
+ for l in self.convs2:
697
+ remove_weight_norm(l)
698
+
699
+ @staticmethod
700
+ def get_padding(kernel_size: int, dilation: int = 1) -> int:
701
+ return int((kernel_size * dilation - dilation) / 2)
702
+
703
+
704
+ class Backbone(nn.Module):
705
+ """Base class for the generator's backbone. It preserves the same temporal resolution across all layers."""
706
+
707
+ def forward(self, x: torch.Tensor, **kwargs) -> torch.Tensor:
708
+ """
709
+ Args:
710
+ x (Tensor): Input tensor of shape (B, C, L), where B is the batch size,
711
+ C denotes output features, and L is the sequence length.
712
+
713
+ Returns:
714
+ Tensor: Output of shape (B, L, H), where B is the batch size, L is the sequence length,
715
+ and H denotes the model dimension.
716
+ """
717
+ raise NotImplementedError("Subclasses must implement the forward method.")
718
+
719
+
720
+ class VocosBackbone(Backbone):
721
+ """
722
+ Vocos backbone module built with ConvNeXt blocks. Supports additional conditioning with Adaptive Layer Normalization
723
+
724
+ Args:
725
+ input_channels (int): Number of input features channels.
726
+ dim (int): Hidden dimension of the model.
727
+ intermediate_dim (int): Intermediate dimension used in ConvNeXtBlock.
728
+ num_layers (int): Number of ConvNeXtBlock layers.
729
+ layer_scale_init_value (float, optional): Initial value for layer scaling. Defaults to `1 / num_layers`.
730
+ adanorm_num_embeddings (int, optional): Number of embeddings for AdaLayerNorm.
731
+ None means non-conditional model. Defaults to None.
732
+ """
733
+
734
+ def __init__(
735
+ self,
736
+ input_channels: int,
737
+ dim: int,
738
+ intermediate_dim: int,
739
+ num_layers: int,
740
+ layer_scale_init_value: Optional[float] = None,
741
+ adanorm_num_embeddings: Optional[int] = None,
742
+ ):
743
+ super().__init__()
744
+ self.input_channels = input_channels
745
+ self.embed = nn.Conv1d(input_channels, dim, kernel_size=7, padding=3)
746
+ self.adanorm = adanorm_num_embeddings is not None
747
+ if adanorm_num_embeddings:
748
+ self.norm = AdaLayerNorm(adanorm_num_embeddings, dim, eps=1e-6)
749
+ else:
750
+ self.norm = nn.LayerNorm(dim, eps=1e-6)
751
+ layer_scale_init_value = layer_scale_init_value or 1 / num_layers
752
+ self.convnext = nn.ModuleList(
753
+ [
754
+ ConvNeXtBlock(
755
+ dim=dim,
756
+ intermediate_dim=intermediate_dim,
757
+ layer_scale_init_value=layer_scale_init_value,
758
+ adanorm_num_embeddings=adanorm_num_embeddings,
759
+ )
760
+ for _ in range(num_layers)
761
+ ]
762
+ )
763
+ self.final_layer_norm = nn.LayerNorm(dim, eps=1e-6)
764
+ self.apply(self._init_weights)
765
+
766
+ def _init_weights(self, m):
767
+ if isinstance(m, (nn.Conv1d, nn.Linear)):
768
+ nn.init.trunc_normal_(m.weight, std=0.02)
769
+ nn.init.constant_(m.bias, 0)
770
+
771
+ def forward(self, x: torch.Tensor, **kwargs) -> torch.Tensor:
772
+ bandwidth_id = kwargs.get("bandwidth_id", None)
773
+ x = self.embed(x)
774
+ if self.adanorm:
775
+ assert bandwidth_id is not None
776
+ x = self.norm(x.transpose(1, 2), cond_embedding_id=bandwidth_id)
777
+ else:
778
+ x = self.norm(x.transpose(1, 2))
779
+ x = x.transpose(1, 2)
780
+ for conv_block in self.convnext:
781
+ x = conv_block(x, cond_embedding_id=bandwidth_id)
782
+ x = self.final_layer_norm(x.transpose(1, 2))
783
+ return x
784
+
785
+
786
+ class VocosResNetBackbone(Backbone):
787
+ """
788
+ Vocos backbone module built with ResBlocks.
789
+
790
+ Args:
791
+ input_channels (int): Number of input features channels.
792
+ dim (int): Hidden dimension of the model.
793
+ num_blocks (int): Number of ResBlock1 blocks.
794
+ layer_scale_init_value (float, optional): Initial value for layer scaling. Defaults to None.
795
+ """
796
+
797
+ def __init__(
798
+ self,
799
+ input_channels,
800
+ dim,
801
+ num_blocks,
802
+ layer_scale_init_value=None,
803
+ ):
804
+ super().__init__()
805
+ self.input_channels = input_channels
806
+ self.embed = weight_norm(
807
+ nn.Conv1d(input_channels, dim, kernel_size=3, padding=1)
808
+ )
809
+ layer_scale_init_value = layer_scale_init_value or 1 / num_blocks / 3
810
+ self.resnet = nn.Sequential(
811
+ *[
812
+ ResBlock1(dim=dim, layer_scale_init_value=layer_scale_init_value)
813
+ for _ in range(num_blocks)
814
+ ]
815
+ )
816
+
817
+ def forward(self, x: torch.Tensor, **kwargs) -> torch.Tensor:
818
+ x = self.embed(x)
819
+ x = self.resnet(x)
820
+ x = x.transpose(1, 2)
821
+ return x
822
+
823
+
824
+ class Vocos(nn.Module):
825
+ def __init__(
826
+ self,
827
+ input_channels: int = 256,
828
+ dim: int = 384,
829
+ intermediate_dim: int = 1152,
830
+ num_layers: int = 8,
831
+ n_fft: int = 800,
832
+ hop_size: int = 200,
833
+ padding: str = "same",
834
+ adanorm_num_embeddings=None,
835
+ cfg=None,
836
+ ):
837
+ super().__init__()
838
+
839
+ input_channels = (
840
+ cfg.input_channels
841
+ if cfg is not None and hasattr(cfg, "input_channels")
842
+ else input_channels
843
+ )
844
+ dim = cfg.dim if cfg is not None and hasattr(cfg, "dim") else dim
845
+ intermediate_dim = (
846
+ cfg.intermediate_dim
847
+ if cfg is not None and hasattr(cfg, "intermediate_dim")
848
+ else intermediate_dim
849
+ )
850
+ num_layers = (
851
+ cfg.num_layers
852
+ if cfg is not None and hasattr(cfg, "num_layers")
853
+ else num_layers
854
+ )
855
+ adanorm_num_embeddings = (
856
+ cfg.adanorm_num_embeddings
857
+ if cfg is not None and hasattr(cfg, "adanorm_num_embeddings")
858
+ else adanorm_num_embeddings
859
+ )
860
+ n_fft = cfg.n_fft if cfg is not None and hasattr(cfg, "n_fft") else n_fft
861
+ hop_size = (
862
+ cfg.hop_size if cfg is not None and hasattr(cfg, "hop_size") else hop_size
863
+ )
864
+ padding = (
865
+ cfg.padding if cfg is not None and hasattr(cfg, "padding") else padding
866
+ )
867
+
868
+ self.backbone = VocosBackbone(
869
+ input_channels=input_channels,
870
+ dim=dim,
871
+ intermediate_dim=intermediate_dim,
872
+ num_layers=num_layers,
873
+ adanorm_num_embeddings=adanorm_num_embeddings,
874
+ )
875
+ self.head = ISTFTHead(dim, n_fft, hop_size, padding)
876
+
877
+ def forward(self, x):
878
+ x = self.backbone(x)
879
+ x = self.head(x)
880
+
881
+ return x[:, None, :]
models/codec/coco/rep_coco_model.py ADDED
@@ -0,0 +1,441 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from concurrent.futures import ALL_COMPLETED
7
+ import numpy as np
8
+ import torch
9
+ import torch.nn as nn
10
+
11
+ from torch.nn import functional as F
12
+
13
+ from models.codec.amphion_codec.quantize import ResidualVQ
14
+ from models.codec.amphion_codec.vocos import VocosBackbone
15
+
16
+
17
+ def init_weights(m):
18
+ if isinstance(m, nn.Conv1d):
19
+ nn.init.trunc_normal_(m.weight, std=0.02)
20
+ nn.init.constant_(m.bias, 0)
21
+ if isinstance(m, nn.Linear):
22
+ nn.init.trunc_normal_(m.weight, std=0.02)
23
+ nn.init.constant_(m.bias, 0)
24
+
25
+
26
+ def compute_codebook_perplexity(indices, codebook_size):
27
+ indices = indices.flatten()
28
+ prob = torch.bincount(indices, minlength=codebook_size).float() / indices.size(0)
29
+ perp = torch.exp(-torch.sum(prob * torch.log(prob + 1e-10)))
30
+ return perp
31
+
32
+
33
+ class CocoContentStyle(nn.Module):
34
+ def __init__(
35
+ self,
36
+ codebook_size=8192,
37
+ hidden_size=1024,
38
+ codebook_dim=8,
39
+ num_quantizers=1,
40
+ quantizer_type="fvq",
41
+ use_whisper=True,
42
+ use_chromagram=True,
43
+ construct_only_for_quantizer=False,
44
+ cfg=None,
45
+ ):
46
+ super().__init__()
47
+
48
+ assert cfg is not None
49
+ self.cfg = cfg
50
+
51
+ codebook_size = getattr(cfg, "codebook_size", codebook_size)
52
+ hidden_size = getattr(cfg, "hidden_size", hidden_size)
53
+ codebook_dim = getattr(cfg, "codebook_dim", codebook_dim)
54
+ num_quantizers = getattr(cfg, "num_quantizers", num_quantizers)
55
+ quantizer_type = getattr(cfg, "quantizer_type", quantizer_type)
56
+
57
+ self.codebook_size = codebook_size
58
+ self.codebook_dim = codebook_dim
59
+ self.hidden_size = hidden_size
60
+ self.num_quantizers = num_quantizers
61
+ self.quantizer_type = quantizer_type
62
+
63
+ if use_whisper:
64
+ self.whisper_input_layer = nn.Linear(self.cfg.whisper_dim, hidden_size)
65
+ if use_chromagram:
66
+ self.chromagram_input_layer = nn.Linear(
67
+ self.cfg.chromagram_dim, hidden_size
68
+ )
69
+
70
+ downsample_rate = getattr(cfg, "downsample_rate", 1)
71
+ if downsample_rate > 1:
72
+ self.do_downsample = True
73
+ assert np.log2(downsample_rate).is_integer()
74
+
75
+ down_layers = []
76
+ up_layers = []
77
+ for _ in range(int(np.log2(downsample_rate))):
78
+ down_layers.extend(
79
+ [
80
+ nn.Conv1d(
81
+ hidden_size,
82
+ hidden_size,
83
+ kernel_size=3,
84
+ stride=2,
85
+ padding=1,
86
+ ),
87
+ nn.GELU(),
88
+ ]
89
+ )
90
+ up_layers.extend(
91
+ [
92
+ nn.ConvTranspose1d(
93
+ hidden_size, hidden_size, kernel_size=4, stride=2, padding=1
94
+ ),
95
+ nn.GELU(),
96
+ ]
97
+ )
98
+ self.downsample_layers = nn.Sequential(*down_layers)
99
+ self.upsample_layers = nn.Sequential(*up_layers)
100
+
101
+ else:
102
+ self.do_downsample = False
103
+
104
+ self.encoder = nn.Sequential(
105
+ VocosBackbone(
106
+ input_channels=self.hidden_size,
107
+ dim=self.cfg.encoder.vocos_dim,
108
+ intermediate_dim=self.cfg.encoder.vocos_intermediate_dim,
109
+ num_layers=self.cfg.encoder.vocos_num_layers,
110
+ adanorm_num_embeddings=None,
111
+ ),
112
+ nn.Linear(self.cfg.encoder.vocos_dim, self.hidden_size),
113
+ )
114
+
115
+ self.quantizer = ResidualVQ(
116
+ input_dim=hidden_size,
117
+ num_quantizers=num_quantizers,
118
+ codebook_size=codebook_size,
119
+ codebook_dim=codebook_dim,
120
+ quantizer_type=quantizer_type,
121
+ quantizer_dropout=0.0,
122
+ commitment=0.15,
123
+ codebook_loss_weight=1.0,
124
+ use_l2_normlize=True,
125
+ )
126
+
127
+ if not construct_only_for_quantizer:
128
+ self.decoder = nn.Sequential(
129
+ VocosBackbone(
130
+ input_channels=self.hidden_size,
131
+ dim=self.cfg.decoder.vocos_dim,
132
+ intermediate_dim=self.cfg.decoder.vocos_intermediate_dim,
133
+ num_layers=self.cfg.decoder.vocos_num_layers,
134
+ adanorm_num_embeddings=None,
135
+ ),
136
+ nn.Linear(self.cfg.decoder.vocos_dim, self.hidden_size),
137
+ )
138
+
139
+ if use_whisper:
140
+ self.whisper_output_layer = nn.Linear(
141
+ self.hidden_size, self.cfg.whisper_dim
142
+ )
143
+ if use_chromagram:
144
+ self.chromagram_output_layer = nn.Linear(
145
+ self.hidden_size, self.cfg.chromagram_dim
146
+ )
147
+
148
+ self.reset_parameters()
149
+
150
+ def forward(
151
+ self,
152
+ whisper_feats,
153
+ chromagram_feats,
154
+ return_for_quantizer=False,
155
+ ):
156
+ """
157
+ Args:
158
+ whisper_feats: [B, T, 1024]
159
+ chromagram_feats: [B, T, 24]
160
+ Returns:
161
+ whisper_rec: [B, T, 1024]
162
+ chromagram_rec: [B, T, 24]
163
+ codebook_loss: float
164
+ all_indices: [N, B, T] or [B, T] if num_of_quantizers == 1
165
+ """
166
+ T = whisper_feats.shape[1]
167
+
168
+ # [B, T, D]
169
+ x = self.whisper_input_layer(whisper_feats) + self.chromagram_input_layer(
170
+ chromagram_feats
171
+ )
172
+ # print("Before downsample:", x.shape)
173
+
174
+ # ====== Downsample ======
175
+ if self.do_downsample:
176
+ x = self.downsample_layers(x.transpose(1, 2)).transpose(1, 2)
177
+
178
+ # print("After downsample:", x.shape)
179
+
180
+ # ====== Encoder ======
181
+ x = self.encoder(x.transpose(1, 2)).transpose(1, 2) # [B, T, D] -> [B, D, T]
182
+
183
+ # ====== Quantizer ======
184
+ (
185
+ quantized_out, # [B, D, T]
186
+ all_indices, # [num_of_quantizers, B, T]
187
+ all_commit_losses, # [num_of_quantizers]
188
+ all_codebook_losses, # [num_of_quantizers]
189
+ _,
190
+ ) = self.quantizer(x)
191
+
192
+ if return_for_quantizer:
193
+ if all_indices.shape[0] == 1:
194
+ return all_indices.squeeze(0), quantized_out.transpose(1, 2)
195
+ return all_indices, quantized_out.transpose(1, 2)
196
+
197
+ # ====== Decoder ======
198
+ x_rec = self.decoder(quantized_out) # [B, T, D]
199
+
200
+ # ====== Upsample ======
201
+ if self.do_downsample:
202
+ x_rec = self.upsample_layers(x_rec.transpose(1, 2)).transpose(1, 2)
203
+
204
+ # print("After upsample:", x_rec.shape)
205
+
206
+ # Ensure output dimensions match input
207
+ if x_rec.shape[1] >= T: # Check time dimension
208
+ x_rec = x_rec[:, :T, :]
209
+ else:
210
+ padding_frames = T - x_rec.shape[1]
211
+ last_frame = x_rec[:, -1:, :]
212
+ padding = last_frame.repeat(1, padding_frames, 1)
213
+ x_rec = torch.cat([x_rec, padding], dim=1)
214
+
215
+ # ====== Loss ======
216
+ whisper_rec = self.whisper_output_layer(x_rec) # [B, T, 1024]
217
+ chromagram_rec = self.chromagram_output_layer(x_rec) # [B, T, 24]
218
+
219
+ codebook_loss = (all_codebook_losses + all_commit_losses).mean()
220
+ all_indices = all_indices
221
+
222
+ return whisper_rec, chromagram_rec, codebook_loss, all_indices
223
+
224
+ def quantize(self, whisper_feats, chromagram_feats):
225
+ """
226
+ Args:
227
+ whisper_feats: [B, T, 1024]
228
+ chromagram_feats: [B, T, 24]
229
+ Returns:
230
+ all_indices: [N, B, T], or [B, T] if num_of_quantizers == 1
231
+ quantized_out: [B, D, T]
232
+ """
233
+ all_indices, quantized_out = self.forward(
234
+ whisper_feats,
235
+ chromagram_feats,
236
+ return_for_quantizer=True,
237
+ )
238
+ return all_indices, quantized_out
239
+
240
+ def reset_parameters(self):
241
+ self.apply(init_weights)
242
+
243
+
244
+ class CocoContent(CocoContentStyle):
245
+ def __init__(
246
+ self,
247
+ cfg,
248
+ use_whisper=True,
249
+ use_chromagram=False,
250
+ construct_only_for_quantizer=False,
251
+ ):
252
+ super().__init__(
253
+ cfg=cfg,
254
+ use_whisper=use_whisper,
255
+ use_chromagram=use_chromagram,
256
+ construct_only_for_quantizer=construct_only_for_quantizer,
257
+ )
258
+
259
+ def forward(
260
+ self,
261
+ whisper_feats,
262
+ return_for_quantizer=False,
263
+ ):
264
+ """
265
+ Args:
266
+ whisper_feats: [B, T, 1024]
267
+ Returns:
268
+ whisper_rec: [B, T, 1024]
269
+ codebook_loss: float
270
+ all_indices: [N, B, T]
271
+ """
272
+ T = whisper_feats.shape[1]
273
+
274
+ # [B, T, D]
275
+ x = self.whisper_input_layer(whisper_feats)
276
+
277
+ # ====== Downsample ======
278
+ if self.do_downsample:
279
+ x = self.downsample_layers(x.transpose(1, 2)).transpose(1, 2)
280
+
281
+ # ====== Encoder ======
282
+ x = self.encoder(x.transpose(1, 2)).transpose(1, 2) # [B, T, D] -> [B, D, T]
283
+
284
+ # ====== Quantizer ======
285
+ (
286
+ quantized_out, # [B, D, T]
287
+ all_indices, # [num_of_quantizers, B, T]
288
+ all_commit_losses, # [num_of_quantizers]
289
+ all_codebook_losses, # [num_of_quantizers]
290
+ _,
291
+ ) = self.quantizer(x)
292
+
293
+ if return_for_quantizer:
294
+ if all_indices.shape[0] == 1:
295
+ return all_indices.squeeze(0), quantized_out.transpose(1, 2)
296
+ return all_indices, quantized_out.transpose(1, 2)
297
+
298
+ # ====== Decoder ======
299
+ x_rec = self.decoder(quantized_out) # [B, T, D]
300
+
301
+ # ====== Upsample ======
302
+ if self.do_downsample:
303
+ x_rec = self.upsample_layers(x_rec.transpose(1, 2)).transpose(1, 2)
304
+
305
+ # Ensure output dimensions match input
306
+ if x_rec.shape[1] >= T: # Check time dimension
307
+ x_rec = x_rec[:, :T, :]
308
+ else:
309
+ padding_frames = T - x_rec.shape[1]
310
+ last_frame = x_rec[:, -1:, :]
311
+ padding = last_frame.repeat(1, padding_frames, 1)
312
+ x_rec = torch.cat([x_rec, padding], dim=1)
313
+
314
+ # ====== Loss ======
315
+ whisper_rec = self.whisper_output_layer(x_rec) # [B, T, 1024]
316
+
317
+ codebook_loss = (all_codebook_losses + all_commit_losses).mean()
318
+ all_indices = all_indices
319
+
320
+ return whisper_rec, codebook_loss, all_indices
321
+
322
+ def quantize(self, whisper_feats):
323
+ all_indices, quantized_out = self.forward(
324
+ whisper_feats, return_for_quantizer=True
325
+ )
326
+ return all_indices, quantized_out
327
+
328
+
329
+ class CocoStyle(CocoContentStyle):
330
+ def __init__(
331
+ self,
332
+ cfg,
333
+ use_whisper=False,
334
+ use_chromagram=True,
335
+ construct_only_for_quantizer=False,
336
+ ):
337
+ super().__init__(
338
+ cfg=cfg,
339
+ use_whisper=use_whisper,
340
+ use_chromagram=use_chromagram,
341
+ construct_only_for_quantizer=construct_only_for_quantizer,
342
+ )
343
+
344
+ def forward(
345
+ self,
346
+ chromagram_feats,
347
+ return_for_quantizer=False,
348
+ ):
349
+ """
350
+ Args:
351
+ chromagram_feats: [B, T, 24]
352
+ Returns:
353
+ chromagram_rec: [B, T, 24]
354
+ codebook_loss: float
355
+ all_indices: [N, B, T]
356
+ """
357
+ T = chromagram_feats.shape[1]
358
+
359
+ # [B, T, D]
360
+ x = self.chromagram_input_layer(chromagram_feats)
361
+
362
+ # ====== Downsample ======
363
+ if self.do_downsample:
364
+ x = self.downsample_layers(x.transpose(1, 2)).transpose(1, 2)
365
+
366
+ # ====== Encoder ======
367
+ x = self.encoder(x.transpose(1, 2)).transpose(1, 2) # [B, T, D] -> [B, D, T]
368
+
369
+ # ====== Quantizer ======
370
+ (
371
+ quantized_out, # [B, D, T]
372
+ all_indices, # [num_of_quantizers, B, T]
373
+ all_commit_losses, # [num_of_quantizers]
374
+ all_codebook_losses, # [num_of_quantizers]
375
+ _,
376
+ ) = self.quantizer(x)
377
+
378
+ if return_for_quantizer:
379
+ if all_indices.shape[0] == 1:
380
+ return all_indices.squeeze(0), quantized_out.transpose(1, 2)
381
+ return all_indices, quantized_out.transpose(1, 2)
382
+
383
+ # ====== Decoder ======
384
+ x_rec = self.decoder(quantized_out) # [B, T, D]
385
+
386
+ # ====== Upsample ======
387
+ if self.do_downsample:
388
+ x_rec = self.upsample_layers(x_rec.transpose(1, 2)).transpose(1, 2)
389
+
390
+ # Ensure output dimensions match input
391
+ if x_rec.shape[1] >= T: # Check time dimension
392
+ x_rec = x_rec[:, :T, :]
393
+ else:
394
+ padding_frames = T - x_rec.shape[1]
395
+ last_frame = x_rec[:, -1:, :]
396
+ padding = last_frame.repeat(1, padding_frames, 1)
397
+ x_rec = torch.cat([x_rec, padding], dim=1)
398
+
399
+ # ====== Loss ======
400
+ chromagram_rec = self.chromagram_output_layer(x_rec) # [B, T, 24]
401
+
402
+ codebook_loss = (all_codebook_losses + all_commit_losses).mean()
403
+ all_indices = all_indices
404
+
405
+ return chromagram_rec, codebook_loss, all_indices
406
+
407
+ def quantize(self, chromagram_feats):
408
+ all_indices, quantized_out = self.forward(
409
+ chromagram_feats, return_for_quantizer=True
410
+ )
411
+ return all_indices, quantized_out
412
+
413
+
414
+ # if __name__ == "__main__":
415
+ # from utils.util import JsonHParams
416
+
417
+ # cfg = JsonHParams(
418
+ # **{
419
+ # "whisper_dim": 1024,
420
+ # "chromagram_dim": 24,
421
+ # "global_speaker_encoder": {
422
+ # "input_dim": 128, # Eg: n_mels
423
+ # "hidden_size": 512, # 768 for emilia298k
424
+ # "num_hidden_layers": 4, # 6 for emilia298k
425
+ # "num_attention_heads": 8,
426
+ # },
427
+ # }
428
+ # )
429
+ # model = Coco(cfg=cfg)
430
+
431
+ # x = torch.randn(2, 150, 1024)
432
+ # tone_height = torch.randn(2)
433
+ # mels = torch.randn(2, 150, 128)
434
+ # mel_mask = torch.ones(2, 150)
435
+
436
+ # x_rec, codebook_loss, all_indices, auxillary_pred_outputs = model(
437
+ # x, tone_height, mels, mel_mask
438
+ # )
439
+ # print(x_rec.shape, codebook_loss, all_indices.shape)
440
+ # for k, v in auxillary_pred_outputs.items():
441
+ # print(k, v.shape)
models/codec/melvqgan/melspec.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import torch
7
+ import pyworld as pw
8
+ import numpy as np
9
+ import soundfile as sf
10
+ import os
11
+ from torchaudio.functional import pitch_shift
12
+ import librosa
13
+ from librosa.filters import mel as librosa_mel_fn
14
+ import torch.nn as nn
15
+ import torch.nn.functional as F
16
+ import tqdm
17
+
18
+
19
+ def dynamic_range_compression(x, C=1, clip_val=1e-5):
20
+ return np.log(np.clip(x, a_min=clip_val, a_max=None) * C)
21
+
22
+
23
+ def dynamic_range_decompression(x, C=1):
24
+ return np.exp(x) / C
25
+
26
+
27
+ def dynamic_range_compression_torch(x, C=1, clip_val=1e-5):
28
+ return torch.log(torch.clamp(x, min=clip_val) * C)
29
+
30
+
31
+ def dynamic_range_decompression_torch(x, C=1):
32
+ return torch.exp(x) / C
33
+
34
+
35
+ def spectral_normalize_torch(magnitudes):
36
+ output = dynamic_range_compression_torch(magnitudes)
37
+ return output
38
+
39
+
40
+ def spectral_de_normalize_torch(magnitudes):
41
+ output = dynamic_range_decompression_torch(magnitudes)
42
+ return output
43
+
44
+
45
+ class MelSpectrogram(nn.Module):
46
+ def __init__(
47
+ self,
48
+ n_fft,
49
+ num_mels,
50
+ sampling_rate,
51
+ hop_size,
52
+ win_size,
53
+ fmin,
54
+ fmax,
55
+ center=False,
56
+ ):
57
+ super(MelSpectrogram, self).__init__()
58
+ self.n_fft = n_fft
59
+ self.hop_size = hop_size
60
+ self.win_size = win_size
61
+ self.sampling_rate = sampling_rate
62
+ self.num_mels = num_mels
63
+ self.fmin = fmin
64
+ self.fmax = fmax
65
+ self.center = center
66
+
67
+ mel_basis = {}
68
+ hann_window = {}
69
+
70
+ mel = librosa_mel_fn(
71
+ sr=sampling_rate, n_fft=n_fft, n_mels=num_mels, fmin=fmin, fmax=fmax
72
+ )
73
+ mel_basis = torch.from_numpy(mel).float()
74
+ hann_window = torch.hann_window(win_size)
75
+
76
+ self.register_buffer("mel_basis", mel_basis)
77
+ self.register_buffer("hann_window", hann_window)
78
+
79
+ def forward(self, y):
80
+ y = torch.nn.functional.pad(
81
+ y.unsqueeze(1),
82
+ (
83
+ int((self.n_fft - self.hop_size) / 2),
84
+ int((self.n_fft - self.hop_size) / 2),
85
+ ),
86
+ mode="reflect",
87
+ )
88
+ y = y.squeeze(1)
89
+ spec = torch.stft(
90
+ y,
91
+ self.n_fft,
92
+ hop_length=self.hop_size,
93
+ win_length=self.win_size,
94
+ window=self.hann_window,
95
+ center=self.center,
96
+ pad_mode="reflect",
97
+ normalized=False,
98
+ onesided=True,
99
+ return_complex=True,
100
+ )
101
+ spec = torch.view_as_real(spec)
102
+
103
+ spec = torch.sqrt(spec.pow(2).sum(-1) + (1e-9))
104
+
105
+ spec = torch.matmul(self.mel_basis, spec)
106
+ spec = spectral_normalize_torch(spec)
107
+
108
+ return spec
models/svc/__init__.py ADDED
File without changes
models/svc/flow_matching_transformer/fmt_model.py ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import torch
7
+ import numpy as np
8
+ import torch.nn as nn
9
+ import math
10
+ from einops import rearrange
11
+ from models.vc.flow_matching_transformer.llama_nar import DiffLlama
12
+ import torch.nn.functional as F
13
+
14
+
15
+ class FlowMatchingTransformer(nn.Module):
16
+ def __init__(
17
+ self,
18
+ mel_dim=100,
19
+ hidden_size=1024,
20
+ num_layers=12,
21
+ num_heads=16,
22
+ cfg_scale=0.2,
23
+ cond_codebook_size=1024,
24
+ cond_scale_factor=1,
25
+ sigma=1e-5,
26
+ time_scheduler="linear",
27
+ cfg=None,
28
+ ):
29
+ super().__init__()
30
+ self.cfg = cfg
31
+
32
+ if cfg is not None:
33
+ mel_dim = getattr(cfg, "mel_dim", mel_dim)
34
+ hidden_size = getattr(cfg, "hidden_size", hidden_size)
35
+ num_layers = getattr(cfg, "num_layers", num_layers)
36
+ num_heads = getattr(cfg, "num_heads", num_heads)
37
+ cfg_scale = getattr(cfg, "cfg_scale", cfg_scale)
38
+ cond_codebook_size = getattr(cfg, "cond_codebook_size", cond_codebook_size)
39
+ time_scheduler = getattr(cfg, "time_scheduler", time_scheduler)
40
+ sigma = getattr(cfg, "sigma", sigma)
41
+ cond_scale_factor = getattr(cfg, "cond_scale_factor", cond_scale_factor)
42
+
43
+ self.mel_dim = mel_dim
44
+ self.hidden_size = hidden_size
45
+ self.num_layers = num_layers
46
+ self.num_heads = num_heads
47
+ self.cfg_scale = cfg_scale
48
+ self.cond_codebook_size = cond_codebook_size
49
+ self.time_scheduler = time_scheduler
50
+ self.sigma = sigma
51
+ self.cond_scale_factor = cond_scale_factor
52
+
53
+ self.cond_emb = nn.Embedding(cond_codebook_size, self.hidden_size)
54
+
55
+ if cond_scale_factor != 1:
56
+ self.do_resampling = True
57
+ assert np.log2(cond_scale_factor).is_integer()
58
+
59
+ up_layers = []
60
+ for _ in range(int(np.log2(cond_scale_factor))):
61
+ up_layers.extend(
62
+ [
63
+ nn.ConvTranspose1d(
64
+ hidden_size, hidden_size, kernel_size=4, stride=2, padding=1
65
+ ),
66
+ nn.GELU(),
67
+ ]
68
+ )
69
+ self.resampling_layers = nn.Sequential(*up_layers)
70
+ else:
71
+ self.do_resampling = False
72
+
73
+ ### REPA: Use the Wav2Vec2Bert features to align. ###
74
+ self.use_repa = "repa" in cfg
75
+ self.repa_layer_index = None
76
+ if self.use_repa:
77
+ self.repa_layer_index = cfg.repa.layer_index
78
+
79
+ self.repa_mlp_layer = nn.Sequential(
80
+ nn.Linear(hidden_size, hidden_size * 4),
81
+ nn.SiLU(),
82
+ nn.Linear(hidden_size * 4, cfg.repa.output_dim),
83
+ )
84
+
85
+ ### CTC: Use the ASR loss ###
86
+ self.use_ctc = "ctc" in cfg
87
+ self.ctc_layer_index = None
88
+ if self.use_ctc:
89
+ self.ctc_layer_index = cfg.ctc.layer_index
90
+
91
+ self.ctc_mlp_layer = nn.Sequential(
92
+ nn.Linear(hidden_size, hidden_size * 4),
93
+ nn.SiLU(),
94
+ nn.Linear(hidden_size * 4, cfg.ctc.output_dim),
95
+ )
96
+
97
+ self.reset_parameters()
98
+
99
+ self.diff_estimator = DiffLlama(
100
+ mel_dim=mel_dim,
101
+ hidden_size=hidden_size,
102
+ num_heads=num_heads,
103
+ num_layers=num_layers,
104
+ )
105
+
106
+ self.sigma = sigma
107
+
108
+ @torch.no_grad()
109
+ def forward_diffusion(self, x, t):
110
+ """
111
+ x: (B, T, mel_dim)
112
+ t: (B,)
113
+ """
114
+ new_t = t
115
+ t = t.unsqueeze(-1).unsqueeze(-1)
116
+ z = torch.randn(
117
+ x.shape, dtype=x.dtype, device=x.device, requires_grad=False
118
+ ) # (B, T, mel_dim)
119
+
120
+ cfg_scale = self.cfg_scale
121
+
122
+ # get prompt len
123
+ if torch.rand(1) > cfg_scale:
124
+ prompt_len = torch.randint(
125
+ min(x.shape[1] // 4, 5), int(x.shape[1] * 0.4), (x.shape[0],)
126
+ ).to(
127
+ x.device
128
+ ) # (B,)
129
+ else:
130
+ prompt_len = torch.zeros(x.shape[0]).to(x) # (B,)
131
+
132
+ # get is prompt
133
+ is_prompt = torch.zeros_like(x[:, :, 0]) # (B, T)
134
+ col_indices = (
135
+ torch.arange(is_prompt.shape[1])
136
+ .repeat(is_prompt.shape[0], 1)
137
+ .to(prompt_len)
138
+ ) # (B, T)
139
+ is_prompt[col_indices < prompt_len.unsqueeze(1)] = 1 # (B, T) 1 if prompt
140
+
141
+ mask = torch.ones_like(x[:, :, 0]) # mask if 1, not mask if 0
142
+ mask[is_prompt.bool()] = 0
143
+ mask = mask[:, :, None]
144
+
145
+ # flow matching: xt = (1 - (1 - sigma) * t) * x0 + t * x; where x0 ~ N(0, 1), x is a sample
146
+ # flow gt: x - (1 - sigma) * x0 = x - (1 - sigma) * noise
147
+ xt = ((1 - (1 - self.sigma) * t) * z + t * x) * mask + x * (1 - mask)
148
+
149
+ return xt, z, new_t, prompt_len, mask
150
+
151
+ def loss_t(
152
+ self,
153
+ x,
154
+ x_mask,
155
+ t,
156
+ cond=None,
157
+ ):
158
+ xt, z, new_t, prompt_len, mask = self.forward_diffusion(x, t)
159
+
160
+ noise = z
161
+
162
+ # drop all condition for cfg, so if prompt_len is 0, we also drop cond
163
+ if cond is not None:
164
+ cond = cond * torch.where(
165
+ prompt_len > 0,
166
+ torch.ones_like(prompt_len),
167
+ torch.zeros_like(prompt_len),
168
+ ).to(cond.device).unsqueeze(-1).unsqueeze(-1)
169
+
170
+ dit_output = self.diff_estimator(xt, new_t, cond, x_mask, return_dict=True)
171
+ flow_pred = dit_output["output"] # (B, T, mel_dim)
172
+
173
+ # final mask used for loss calculation
174
+ final_mask = mask * x_mask[..., None] # (B, T, 1)
175
+
176
+ results = {"output": (noise, x, flow_pred, final_mask, prompt_len)}
177
+
178
+ if self.use_repa:
179
+ repa_hidden_states = dit_output["hidden_states"][
180
+ self.repa_layer_index
181
+ ] # (B, T, hidden_size)
182
+
183
+ repa_pred = self.repa_mlp_layer(repa_hidden_states) # (B, T, repa_dim)
184
+ results["repa"] = repa_pred
185
+
186
+ if self.use_ctc:
187
+ ctc_hidden_states = dit_output["hidden_states"][
188
+ self.ctc_layer_index
189
+ ] # (B, T, hidden_size)
190
+ ctc_pred = self.ctc_mlp_layer(ctc_hidden_states) # (B, T, ctc_dim)
191
+ results["ctc"] = ctc_pred
192
+
193
+ return results
194
+
195
+ def compute_loss(self, x, x_mask, cond=None):
196
+ # x0: (B, T, num_quantizer)
197
+ # x_mask: (B, T) mask is 0 for padding
198
+ t = torch.rand(x.shape[0], device=x.device, requires_grad=False)
199
+ t = torch.clamp(t, 1e-5, 1.0)
200
+ # from CosyVoice: considering the generation process at the beginning is harder than follows, we involve a cosine scheduler for the timestep t
201
+ if self.time_scheduler == "cos":
202
+ t = 1 - torch.cos(t * math.pi * 0.5)
203
+ else:
204
+ pass
205
+ return self.loss_t(x, x_mask, t, cond)
206
+
207
+ def reset_parameters(self):
208
+ def _reset_parameters(m):
209
+ if isinstance(m, nn.MultiheadAttention):
210
+ if m._qkv_same_embed_dim:
211
+ nn.init.normal_(m.in_proj_weight, std=0.02)
212
+ else:
213
+ nn.init.normal_(m.q_proj_weight, std=0.02)
214
+ nn.init.normal_(m.k_proj_weight, std=0.02)
215
+ nn.init.normal_(m.v_proj_weight, std=0.02)
216
+
217
+ if m.in_proj_bias is not None:
218
+ nn.init.constant_(m.in_proj_bias, 0.0)
219
+ nn.init.constant_(m.out_proj.bias, 0.0)
220
+ if m.bias_k is not None:
221
+ nn.init.xavier_normal_(m.bias_k)
222
+ if m.bias_v is not None:
223
+ nn.init.xavier_normal_(m.bias_v)
224
+
225
+ elif (
226
+ isinstance(m, nn.Conv1d)
227
+ or isinstance(m, nn.ConvTranspose1d)
228
+ or isinstance(m, nn.Conv2d)
229
+ or isinstance(m, nn.ConvTranspose2d)
230
+ ):
231
+ m.weight.data.normal_(0.0, 0.02)
232
+
233
+ elif isinstance(m, nn.Linear):
234
+ m.weight.data.normal_(mean=0.0, std=0.02)
235
+ if m.bias is not None:
236
+ m.bias.data.zero_()
237
+
238
+ elif isinstance(m, nn.Embedding):
239
+ m.weight.data.normal_(mean=0.0, std=0.02)
240
+ if m.padding_idx is not None:
241
+ m.weight.data[m.padding_idx].zero_()
242
+
243
+ self.apply(_reset_parameters)
244
+
245
+ @torch.no_grad()
246
+ def reverse_diffusion(
247
+ self,
248
+ cond,
249
+ prompt,
250
+ x_mask=None,
251
+ prompt_mask=None,
252
+ text_embedding=None,
253
+ n_timesteps=10,
254
+ cfg=1.0,
255
+ rescale_cfg=0.75,
256
+ ):
257
+ h = 1.0 / n_timesteps
258
+ prompt_len = prompt.shape[1]
259
+ target_len = cond.shape[1] - prompt_len
260
+
261
+ if x_mask == None:
262
+ x_mask = torch.ones(cond.shape[0], target_len).to(cond.device) # (B, T)
263
+ if prompt_mask == None:
264
+ prompt_mask = torch.ones(cond.shape[0], prompt_len).to(
265
+ cond.device
266
+ ) # (B, prompt_len)
267
+ xt_mask = torch.cat([prompt_mask, x_mask], dim=1)
268
+ z = torch.randn(
269
+ (cond.shape[0], target_len, self.mel_dim),
270
+ dtype=cond.dtype,
271
+ device=cond.device,
272
+ requires_grad=False,
273
+ )
274
+ xt = z
275
+
276
+ # t from 0 to 1: x0 = z ~ N(0, 1)
277
+ for i in range(n_timesteps):
278
+ xt_input = torch.cat([prompt, xt], dim=1)
279
+ t = (0 + (i + 0.5) * h) * torch.ones(
280
+ z.shape[0], dtype=z.dtype, device=z.device
281
+ )
282
+ flow_pred = self.diff_estimator(xt_input, t, cond, xt_mask)
283
+ flow_pred = flow_pred[:, prompt_len:, :]
284
+
285
+ # cfg
286
+ if cfg > 0:
287
+ uncond_flow_pred = self.diff_estimator(
288
+ xt, t, torch.zeros_like(cond)[:, : xt.shape[1], :], x_mask
289
+ )
290
+ pos_flow_pred_std = flow_pred.std()
291
+ flow_pred_cfg = flow_pred + cfg * (flow_pred - uncond_flow_pred)
292
+ rescale_flow_pred = (
293
+ flow_pred_cfg * pos_flow_pred_std / flow_pred_cfg.std()
294
+ )
295
+ flow_pred = (
296
+ rescale_cfg * rescale_flow_pred + (1 - rescale_cfg) * flow_pred_cfg
297
+ )
298
+
299
+ dxt = flow_pred * h
300
+ xt = xt + dxt
301
+
302
+ return xt
303
+
304
+ def forward(self, x, x_mask, cond_code):
305
+ """
306
+ Args:
307
+ x: (B, T, mel_dim)
308
+ x_mask: (B, T)
309
+ cond_code: (B, T), Note that cond_code might be not at 50Hz!
310
+ """
311
+ T = x.shape[1]
312
+
313
+ cond = self.cond_emb(cond_code) # (B, T, hidden_size)
314
+ if self.do_resampling:
315
+ # Align to the frame rate of Mels
316
+ cond = self.resampling_layers(cond.transpose(1, 2)).transpose(1, 2)
317
+
318
+ # print("cond_code: {}, after resampling: {}".format(cond_code.shape, cond.shape))
319
+
320
+ if cond.shape[1] >= T: # Check time dimension
321
+ cond = cond[:, :T, :]
322
+ else:
323
+ padding_frames = T - cond.shape[1]
324
+ last_frame = cond[:, -1:, :]
325
+ padding = last_frame.repeat(1, padding_frames, 1)
326
+ cond = torch.cat([cond, padding], dim=1)
327
+
328
+ return self.compute_loss(x, x_mask, cond)
models/svc/vevo2/qwen_utils.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import torch
3
+
4
+
5
+ def format_chat_prompt(messages, add_assistant_token):
6
+ """
7
+ Convert the messages list into the Qwen chat template format.
8
+
9
+ Args:
10
+ messages: A list of messages containing role and content.
11
+ add_assistant_token: Whether to add assistant token at the end.
12
+
13
+ Returns:
14
+ str: The formatted prompt string.
15
+ """
16
+ prompt = ""
17
+ for msg in messages:
18
+ role = msg["role"]
19
+ content = msg["content"]
20
+ # Add start and end tags for all messages except the last assistant message
21
+ if msg != messages[-1] or role != "assistant":
22
+ prompt += f"<|im_start|>{role}\n{content}<|im_end|>\n"
23
+ else:
24
+ # For the last assistant message, only add the start tag and content
25
+ prompt += f"<|im_start|>{role}\n{content}"
26
+
27
+ # If the last message is not from assistant and add_assistant_token is True
28
+ if messages[-1]["role"] != "assistant" and add_assistant_token:
29
+ prompt += f"<|im_start|>assistant\n"
30
+
31
+ return prompt
32
+
33
+
34
+ def gen_chat_prompt(text, add_assistant_token, follow_prosody_instruction):
35
+ """
36
+ Args:
37
+ text (str): The text to be spoken.
38
+ add_assistant_token (bool): Whether to add assistant token at the end. For pre-training, False. For Inference, True.
39
+ follow_prosody_instruction (bool): Whether to follow the prosody instruction. When prosody_ids is not None, True. Otherwise, False.
40
+ """
41
+ if follow_prosody_instruction:
42
+ synthesis_instruction = "User will provide you with a text. Please first generate a good prosodic instruction, then vocalize the text based on it."
43
+ else:
44
+ synthesis_instruction = "User will provide you with a text. Please vocalize it with natural expression."
45
+
46
+ template = [
47
+ {
48
+ "role": "system",
49
+ "content": synthesis_instruction,
50
+ },
51
+ {
52
+ "role": "user",
53
+ "content": text,
54
+ },
55
+ ]
56
+ return format_chat_prompt(template, add_assistant_token)
57
+
58
+
59
+ def gen_chat_response(prosody_ids, content_style_ids, is_full_response=True):
60
+ """
61
+ Args:
62
+ prosody_ids (list): The prosody ids of the text.
63
+ content_style_ids (list): The content style ids of the text.
64
+ """
65
+ if prosody_ids is not None:
66
+ prosody_text = "".join(["<|prosody_{}|>".format(int(i)) for i in prosody_ids])
67
+ prosody_text = "<|prosody_start|>" + prosody_text + "<|prosody_end|>"
68
+ else:
69
+ prosody_text = ""
70
+
71
+ if content_style_ids is not None:
72
+ content_style_text = "".join(
73
+ ["<|content_style_{}|>".format(int(i)) for i in content_style_ids]
74
+ )
75
+ else:
76
+ content_style_text = ""
77
+
78
+ if is_full_response:
79
+ return (
80
+ prosody_text
81
+ + "<|content_style_start|>"
82
+ + content_style_text
83
+ + "<|content_style_end|>"
84
+ + "<|im_end|>"
85
+ )
86
+ else:
87
+ return prosody_text + "<|content_style_start|>" + content_style_text
88
+
89
+
90
+ def extract_content_style_ids(text):
91
+ """
92
+ Extract the content_style IDs from the text
93
+
94
+ Args:
95
+ text (str): A string containing content_style tags
96
+
97
+ Returns:
98
+ torch.Tensor: [T]
99
+ """
100
+ # Use regex to match all <|content_style_数字|> patterns
101
+ pattern = r"<\|content_style_(\d+)\|>"
102
+ # Find all matches and extract the numeric part
103
+ matches = re.findall(pattern, text)
104
+ # Convert string numbers to integers
105
+ return torch.tensor([int(match) for match in matches], dtype=torch.long)
models/svc/vevo2/vevo2_utils.py ADDED
@@ -0,0 +1,961 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import json
3
+ import librosa
4
+ import soundfile as sf
5
+ import torch
6
+ import torchaudio
7
+ import accelerate
8
+ import safetensors
9
+ import numpy as np
10
+ import os
11
+ import yaml
12
+ from IPython.display import display, Audio
13
+
14
+ import torchvision
15
+ import random
16
+ import numpy as np
17
+ import whisper
18
+ from librosa.feature import chroma_stft
19
+ from librosa.effects import pitch_shift
20
+
21
+ from models.codec.coco.rep_coco_model import CocoContentStyle, CocoContent, CocoStyle
22
+ from models.svc.flow_matching_transformer.fmt_model import FlowMatchingTransformer
23
+ from models.codec.melvqgan.melspec import MelSpectrogram
24
+ from models.codec.amphion_codec.vocos import Vocos
25
+
26
+ from transformers import AutoModelForCausalLM, AutoTokenizer
27
+
28
+ from utils.util import load_config
29
+ from models.svc.vevo2.qwen_utils import gen_chat_prompt
30
+ from evaluation.metrics.f0.f0_corr import extract_f0_hz
31
+
32
+ from transformers.utils import is_flash_attn_2_available
33
+
34
+ supported_flash_attn = False
35
+ if not torch.cuda.is_available():
36
+ print("No CUDA available")
37
+ supported_flash_attn = False
38
+
39
+ # To check if flash attention is supported
40
+ if is_flash_attn_2_available():
41
+ supported_flash_attn = True
42
+ print("Flash Attention is supported")
43
+ else:
44
+ print("Flash Attention is not supported")
45
+
46
+
47
+ # Coco Tokenizer
48
+ def build_coco_model(coco_cfg, device, loading_decoder=False):
49
+ coco_model_type = getattr(coco_cfg, "coco_type", "content_style")
50
+ if coco_model_type == "content_style":
51
+ model = CocoContentStyle(
52
+ cfg=coco_cfg, construct_only_for_quantizer=not loading_decoder
53
+ )
54
+ elif coco_model_type == "content":
55
+ model = CocoContent(
56
+ cfg=coco_cfg, construct_only_for_quantizer=not loading_decoder
57
+ )
58
+ elif coco_model_type == "style":
59
+ model = CocoStyle(
60
+ cfg=coco_cfg, construct_only_for_quantizer=not loading_decoder
61
+ )
62
+ else:
63
+ raise ValueError(f"Unknown coco type: {coco_model_type}")
64
+
65
+ model.eval()
66
+ model.to(device)
67
+ return model
68
+
69
+
70
+ # Flow Matching Transformer
71
+ def build_fmt_model(cfg, device):
72
+ model = FlowMatchingTransformer(cfg=cfg.model.flow_matching_transformer)
73
+ model.eval()
74
+ model.to(device)
75
+ return model
76
+
77
+
78
+ # Autoregressive Transformer
79
+ def build_ar_model(ckpt_path, device):
80
+ model_kwargs = {
81
+ "device_map": device,
82
+ "torch_dtype": "auto",
83
+ "trust_remote_code": True,
84
+ }
85
+
86
+ # Only add flash attention parameter if supported
87
+ if supported_flash_attn:
88
+ model_kwargs["attn_implementation"] = "flash_attention_2"
89
+
90
+ # model = AutoModelForCausalLM.from_pretrained(
91
+ # cfg.model.pretrained_model_path, **model_kwargs
92
+ # )
93
+ model = AutoModelForCausalLM.from_pretrained(ckpt_path, **model_kwargs)
94
+
95
+ model.eval()
96
+ return model
97
+
98
+
99
+ # Melspectrogram Extractor
100
+ def build_mel_model(cfg, device):
101
+ mel_model = MelSpectrogram(
102
+ sampling_rate=cfg.preprocess.sample_rate,
103
+ n_fft=cfg.preprocess.n_fft,
104
+ num_mels=cfg.preprocess.num_mels,
105
+ hop_size=cfg.preprocess.hop_size,
106
+ win_size=cfg.preprocess.win_size,
107
+ fmin=cfg.preprocess.fmin,
108
+ fmax=cfg.preprocess.fmax,
109
+ )
110
+ mel_model.eval()
111
+ mel_model.to(device)
112
+ return mel_model
113
+
114
+
115
+ # Vocoder
116
+ def build_vocoder_model(cfg, device):
117
+ vocoder_model = Vocos(cfg=cfg.model.vocos)
118
+ vocoder_model.eval()
119
+ vocoder_model.to(device)
120
+ return vocoder_model
121
+
122
+
123
+ def load_checkpoint(build_model_func, cfg, ckpt_path, device):
124
+ model = build_model_func(cfg, device)
125
+ accelerate.load_checkpoint_and_dispatch(model, ckpt_path)
126
+ return model
127
+
128
+
129
+ def count_parameters(model):
130
+ total_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
131
+ if total_params < 1e6:
132
+ return f"{total_params} params" # Parameters
133
+ elif total_params < 1e9:
134
+ return f"{total_params / 1e6:.2f} M" # Millions
135
+ else:
136
+ return f"{total_params / 1e9:.2f} B" # Billions
137
+
138
+
139
+ def load_wav(wav_path, device, used_duration=None):
140
+ if wav_path is None:
141
+ speech = np.zeros((0)) # [T]
142
+ speech_tensor = torch.zeros(1, 0).to(device) # [1, T]
143
+ speech16k = torch.zeros(1, 0).to(device) # [1, T']
144
+ else:
145
+ speech = librosa.load(wav_path, sr=24000)[0] # [T]
146
+
147
+ if used_duration is not None:
148
+ speech = speech[: int(used_duration * 24000)]
149
+
150
+ speech_tensor = torch.tensor(speech).unsqueeze(0).to(device) # [1, T]
151
+ speech16k = torchaudio.functional.resample(
152
+ speech_tensor, 24000, 16000
153
+ ) # [1, T']
154
+
155
+ return speech, speech_tensor, speech16k
156
+
157
+
158
+ def display_audio_in_notebook(wav, rate=24000):
159
+ display(Audio(wav, rate=rate))
160
+
161
+
162
+ def save_audio(
163
+ waveform, sr=24000, output_path=None, target_sample_rate=None, target_db=-25.0
164
+ ):
165
+ """
166
+ waveform: [1, T]
167
+ """
168
+ if target_sample_rate is not None and sr != target_sample_rate:
169
+ resampler = torchaudio.transforms.Resample(
170
+ orig_freq=sr, new_freq=target_sample_rate
171
+ )
172
+ waveform = resampler(waveform)
173
+ else:
174
+ target_sample_rate = sr
175
+
176
+ rms = torch.sqrt(torch.mean(waveform**2))
177
+ current_db = 20 * torch.log10(rms + 1e-9)
178
+
179
+ gain = target_db - current_db
180
+ normalized_waveform = waveform * (10 ** (gain / 20))
181
+
182
+ # deviation: torchaudio.save() known to cause dep issues; using soundfile instead
183
+ sf.write(
184
+ output_path,
185
+ normalized_waveform.squeeze(0).cpu().numpy(),
186
+ target_sample_rate,
187
+ )
188
+ return output_path
189
+
190
+
191
+ def extract_special_ids(text, prefix="content_style"):
192
+ """
193
+ Extract all audio IDs from a string containing <|content_style_X|> or <|prosody_X|> tags
194
+
195
+ Args:
196
+ text (str): A string containing audio tags
197
+ prefix (str): The prefix of the audio tags
198
+
199
+ Returns:
200
+ list: A list of all audio IDs
201
+ """
202
+ import re
203
+
204
+ if prefix == "content_style":
205
+ # Use regex to match all <|content_style_X|> format tags
206
+ pattern = r"<\|content_style_(\d+)\|>"
207
+ elif prefix == "prosody":
208
+ pattern = r"<\|prosody_(\d+)\|>"
209
+ else:
210
+ raise ValueError(f"Unknown prefix: {prefix}")
211
+
212
+ # Find all matches and extract the numeric part
213
+ audio_ids = re.findall(pattern, text)
214
+
215
+ # Convert string IDs to integers
216
+ audio_ids = [int(id) for id in audio_ids]
217
+
218
+ return audio_ids
219
+
220
+
221
+ class Vevo2InferencePipeline:
222
+ def __init__(
223
+ self,
224
+ prosody_tokenizer_ckpt_path=None,
225
+ content_style_tokenizer_ckpt_path=None,
226
+ ar_cfg_path=None,
227
+ ar_ckpt_path=None,
228
+ fmt_cfg_path=None,
229
+ fmt_ckpt_path=None,
230
+ vocoder_cfg_path=None,
231
+ vocoder_ckpt_path=None,
232
+ device=None,
233
+ use_vllm=False,
234
+ ):
235
+ self.device = device
236
+ self.use_vllm = use_vllm
237
+
238
+ self.prosody_tokenizer_ckpt_path = prosody_tokenizer_ckpt_path
239
+ self.content_style_tokenizer_ckpt_path = content_style_tokenizer_ckpt_path
240
+
241
+ if ar_cfg_path is not None and ar_ckpt_path is not None:
242
+ self.ar_cfg = load_config(ar_cfg_path)
243
+
244
+ assert not use_vllm, "VLLM is not supported yet"
245
+
246
+ if use_vllm:
247
+ pass
248
+ else:
249
+ # self.ar_model = load_checkpoint(
250
+ # build_ar_model, self.ar_cfg, ar_ckpt_path, device
251
+ # )
252
+
253
+ self.ar_model = build_ar_model(ar_ckpt_path, device)
254
+ print(f"#Params of AR model: {count_parameters(self.ar_model)}")
255
+
256
+ # self.ar_tokenizer = AutoTokenizer.from_pretrained(
257
+ # self.ar_cfg.preprocess.tokenizer_path, local_files_only=True
258
+ # )
259
+ self.ar_tokenizer = AutoTokenizer.from_pretrained(
260
+ ar_ckpt_path, local_files_only=True
261
+ )
262
+ else:
263
+ self.ar_cfg = None
264
+ self.ar_model = None
265
+
266
+ if fmt_cfg_path is not None and fmt_ckpt_path is not None:
267
+ self.fmt_cfg = load_config(fmt_cfg_path)
268
+ self.fmt_model = load_checkpoint(
269
+ build_fmt_model, self.fmt_cfg, fmt_ckpt_path, device
270
+ )
271
+ print(f"#Params of Flow Matching model: {count_parameters(self.fmt_model)}")
272
+
273
+ if getattr(
274
+ self.fmt_cfg.model.flow_matching_transformer,
275
+ "use_text_as_condition",
276
+ False,
277
+ ):
278
+ self.fmt_use_text_as_condition = True
279
+ self.fmt_text_tokenizer = AutoTokenizer.from_pretrained(
280
+ self.fmt_cfg.preprocess.tokenizer_path
281
+ )
282
+ else:
283
+ self.fmt_use_text_as_condition = False
284
+
285
+ self.init_coco_tokenizer()
286
+
287
+ if vocoder_cfg_path is not None and vocoder_ckpt_path is not None:
288
+ self.vocoder_cfg = load_config(vocoder_cfg_path)
289
+ self.mel_model = build_mel_model(self.vocoder_cfg, device)
290
+ self.vocoder_model = load_checkpoint(
291
+ build_vocoder_model, self.vocoder_cfg, vocoder_ckpt_path, device
292
+ )
293
+ print(f"#Params of Vocoder model: {count_parameters(self.vocoder_model)}")
294
+
295
+ def init_coco_tokenizer(self):
296
+ ## Whisper ##
297
+ self.whisper_model = whisper.load_model("medium", self.device) # 1024 dim
298
+ self.whisper_model.eval()
299
+
300
+ self.use_normed_whisper = getattr(
301
+ self.fmt_cfg.model.coco, "use_normed_whisper", False
302
+ )
303
+ if self.use_normed_whisper:
304
+ whisper_stats = torch.load(
305
+ self.fmt_cfg.model.coco.whisper_stats_path,
306
+ map_location=self.device,
307
+ )
308
+ self.whisper_mean = whisper_stats["mean"] # (1024,)
309
+ self.whisper_std = whisper_stats["std"] # (1024,)
310
+
311
+ ## Prosody Tokenizer ##
312
+ if self.ar_model is not None:
313
+ self.style_tokenizer = load_checkpoint(
314
+ build_coco_model,
315
+ self.ar_cfg.model.coco_style,
316
+ self.prosody_tokenizer_ckpt_path,
317
+ self.device,
318
+ )
319
+ print(
320
+ f"#Params of CocoStyle model: {count_parameters(self.style_tokenizer)}"
321
+ )
322
+
323
+ ## Content-Style Tokenizer ##
324
+ self.content_style_tokenizer = load_checkpoint(
325
+ build_coco_model,
326
+ self.fmt_cfg.model.coco,
327
+ self.content_style_tokenizer_ckpt_path,
328
+ self.device,
329
+ )
330
+ print(
331
+ f"#Params of CocoContentStyle model: {count_parameters(self.content_style_tokenizer)}"
332
+ )
333
+
334
+ @torch.no_grad()
335
+ def extract_mel_feature(self, speech):
336
+ mel_feature = self.mel_model(speech) # (B, d, T)
337
+ mel_feature = mel_feature.transpose(1, 2)
338
+ mel_feature = (mel_feature - self.vocoder_cfg.preprocess.mel_mean) / math.sqrt(
339
+ self.vocoder_cfg.preprocess.mel_var
340
+ )
341
+ return mel_feature
342
+
343
+ def spec_augment(self, mel, height):
344
+ """
345
+ Args:
346
+ mel: tensor (..., n_mels, frames)
347
+ height: int 68-92 for default 80 mels
348
+ """
349
+ tgt = torchvision.transforms.functional.resize(mel, (height, mel.shape[-1]))
350
+ if height >= mel.shape[-2]:
351
+ return tgt[:, : mel.shape[-2], :]
352
+ else:
353
+ silence = tgt[:, -1:, :].repeat(1, mel.shape[-2] - height, 1)
354
+ silence += torch.randn_like(silence) / 10
355
+ return torch.cat((tgt, silence), 1)
356
+
357
+ @torch.no_grad()
358
+ def extract_whisper_features(self, wavs, frame_lens, spec_perturb=False):
359
+ """
360
+ Args:
361
+ wavs: (B, T) at 16khz. Note that the max duration should be 30s
362
+ frame_lens: (B,)
363
+ Returns:
364
+ features: (B, T, D)
365
+ """
366
+ # wavs: (batch, max_len)
367
+ wavs = whisper.pad_or_trim(wavs)
368
+ # batch_mel: (batch, 80, 3000)
369
+ batch_mel = whisper.log_mel_spectrogram(wavs, device=self.device)
370
+
371
+ if spec_perturb:
372
+ height = random.randint(68, 92)
373
+ batch_mel = self.spec_augment(batch_mel, height)
374
+
375
+ with torch.no_grad():
376
+ # (batch, 1500, 1024)
377
+ features = self.whisper_model.embed_audio(batch_mel)
378
+
379
+ max_len = int(frame_lens.max().item())
380
+ mask = torch.arange(features.size(1), device=features.device).expand(
381
+ len(frame_lens), -1
382
+ ) < frame_lens.unsqueeze(1)
383
+ features = torch.where(mask.unsqueeze(-1), features, torch.zeros_like(features))
384
+
385
+ if features.shape[1] >= max_len:
386
+ features = features[:, :max_len, :]
387
+ else:
388
+ padding_frames = max_len - features.shape[1]
389
+ last_frame = features[:, -1:, :]
390
+ padding = last_frame.repeat(1, padding_frames, 1)
391
+ features = torch.cat([features, padding], dim=1)
392
+
393
+ if self.use_normed_whisper:
394
+ features = (features - self.whisper_mean) / self.whisper_std
395
+
396
+ return features
397
+
398
+ @torch.no_grad()
399
+ def extract_coco_codec(
400
+ self,
401
+ coco_codec_type,
402
+ wav16k,
403
+ wav24k_numpy,
404
+ whisper_spec_perturb=False,
405
+ frame_len_ratio=1.0,
406
+ use_shifted_wav_to_extract_chromagram=False,
407
+ use_shifted_wav_to_extract_whisper=False,
408
+ pitch_shift_steps=0,
409
+ ):
410
+ """
411
+ Args:
412
+ coco_codec_type: "content", "style", or "content_style"
413
+ wav16k: [1, T]
414
+ wav24k_numpy: [T]
415
+ Returns:
416
+ codecs: [1, T]. Note that codecs might be not at 50Hz!
417
+ """
418
+ frame_len = len(wav24k_numpy) // self.fmt_cfg.preprocess.hop_size
419
+
420
+ if use_shifted_wav_to_extract_chromagram:
421
+ chromagram_feats = self.get_chromagram(
422
+ pitch_shift(wav24k_numpy, sr=24000, n_steps=pitch_shift_steps),
423
+ frame_len,
424
+ ) # [T, 24]
425
+ else:
426
+ chromagram_feats = self.get_chromagram(wav24k_numpy, frame_len) # [T, 24]
427
+
428
+ chromagram_feats = (
429
+ torch.tensor(chromagram_feats, dtype=torch.float)
430
+ .unsqueeze(0)
431
+ .to(self.device)
432
+ ) # [1, T, 24]
433
+
434
+ if frame_len_ratio != 1.0:
435
+ raw_len = chromagram_feats.shape[1]
436
+ # Convert [1, T, 24] to [1, 24, T] for interpolation on the last dimension
437
+ chromagram_feats = chromagram_feats.transpose(1, 2)
438
+ chromagram_feats = torch.nn.functional.interpolate(
439
+ chromagram_feats,
440
+ size=int(
441
+ raw_len * frame_len_ratio
442
+ ), # Explicitly specify the target length
443
+ mode="linear",
444
+ align_corners=False,
445
+ ) # [1, 24, T']
446
+ # Convert back to the original shape [1, T', 24]
447
+ chromagram_feats = chromagram_feats.transpose(1, 2)
448
+ print(
449
+ f"Chromagram feats are sampled from {raw_len} to {chromagram_feats.shape[1]}, ratio = {frame_len_ratio}"
450
+ )
451
+
452
+ if use_shifted_wav_to_extract_whisper:
453
+ wav16k = pitch_shift(
454
+ wav16k.cpu().numpy()[0], sr=16000, n_steps=pitch_shift_steps
455
+ ) # [T]
456
+ wav16k = torch.tensor(wav16k).unsqueeze(0).to(self.device) # [1, T]
457
+
458
+ if coco_codec_type in ["content_style", "content"]:
459
+ whisper_feats = self.extract_whisper_features(
460
+ wav16k,
461
+ torch.tensor([frame_len], dtype=torch.long).to(self.device),
462
+ spec_perturb=whisper_spec_perturb,
463
+ ) # [1, T, D]
464
+
465
+ if coco_codec_type == "content_style":
466
+ codecs, _ = self.content_style_tokenizer.quantize(
467
+ whisper_feats.to(torch.float32), chromagram_feats.to(torch.float32)
468
+ )
469
+ elif coco_codec_type == "style":
470
+ codecs, _ = self.style_tokenizer.quantize(
471
+ chromagram_feats.to(torch.float32)
472
+ )
473
+ else:
474
+ raise ValueError(f"Unknown coco type: {coco_codec_type}")
475
+
476
+ return codecs
477
+
478
+ def get_chromagram(self, speech, speech_frames):
479
+ # [24, T] -> [T, 24]
480
+ chromagram = chroma_stft(
481
+ y=speech,
482
+ sr=self.fmt_cfg.preprocess.sample_rate,
483
+ n_fft=self.fmt_cfg.preprocess.n_fft,
484
+ hop_length=self.fmt_cfg.preprocess.hop_size,
485
+ win_length=self.fmt_cfg.preprocess.win_size,
486
+ n_chroma=24,
487
+ ).T
488
+
489
+ if chromagram.shape[0] < speech_frames:
490
+ chromagram = np.pad(
491
+ chromagram, (0, speech_frames - chromagram.shape[0]), mode="edge"
492
+ )
493
+ else:
494
+ chromagram = chromagram[:speech_frames]
495
+
496
+ return chromagram
497
+
498
+ def get_shifted_steps(self, src_wav_path, timbre_ref_wav_path):
499
+ if src_wav_path == timbre_ref_wav_path:
500
+ return 0
501
+
502
+ src_f0 = extract_f0_hz(src_wav_path)
503
+ timbre_ref_f0 = extract_f0_hz(timbre_ref_wav_path)
504
+
505
+ src_f0_median = np.median(src_f0)
506
+ timbre_ref_f0_median = np.median(timbre_ref_f0)
507
+
508
+ src_shifted_steps = 12 * np.log2(timbre_ref_f0_median / src_f0_median)
509
+ src_shifted_steps = round(src_shifted_steps)
510
+
511
+ if src_shifted_steps > 12:
512
+ src_shifted_steps = src_shifted_steps % 12
513
+ elif src_shifted_steps < -12:
514
+ src_shifted_steps = src_shifted_steps % -12
515
+
516
+ return src_shifted_steps
517
+
518
+ @torch.no_grad()
519
+ def inference_fm(
520
+ self,
521
+ src_wav_path,
522
+ timbre_ref_wav_path,
523
+ src_wav_text="",
524
+ timbre_ref_wav_text="",
525
+ whisper_spec_perturb=False,
526
+ use_pitch_shift=False,
527
+ used_duration_of_timbre_ref_wav_path=None,
528
+ flow_matching_steps=32,
529
+ display_audio=False,
530
+ ):
531
+ src_speech, src_speech24k, src_speech16k = load_wav(src_wav_path, self.device)
532
+
533
+ if display_audio:
534
+ print("-" * 20)
535
+ if src_wav_path == timbre_ref_wav_path:
536
+ print("We want to reconstruct this audio:", src_wav_path)
537
+ display_audio_in_notebook(src_wav_path, rate=24000)
538
+ else:
539
+ print("Source audio:")
540
+ display_audio_in_notebook(src_speech, rate=24000)
541
+
542
+ ## Whether to use shifted src to extract prosody and content-style ##
543
+ if use_pitch_shift:
544
+ src_shifted_steps = self.get_shifted_steps(
545
+ src_wav_path, timbre_ref_wav_path
546
+ )
547
+
548
+ if display_audio:
549
+ print("-" * 20)
550
+ print(f"src_shifted_steps: {src_shifted_steps}")
551
+ else:
552
+ src_shifted_steps = 0
553
+
554
+ ## Diffusion ##
555
+ src_codecs = self.extract_coco_codec(
556
+ "content_style",
557
+ src_speech16k,
558
+ src_speech,
559
+ whisper_spec_perturb=whisper_spec_perturb,
560
+ use_shifted_wav_to_extract_chromagram=use_pitch_shift,
561
+ pitch_shift_steps=src_shifted_steps,
562
+ ) # [1, T]
563
+
564
+ predict_mel_feat = self.code2mel(
565
+ src_codecs,
566
+ timbre_ref_wav_path,
567
+ prefix_text=timbre_ref_wav_text + " " + src_wav_text,
568
+ used_duration_of_timbre_ref_wav_path=used_duration_of_timbre_ref_wav_path,
569
+ flow_matching_steps=flow_matching_steps,
570
+ logging=display_audio,
571
+ ) # [1, T, D]
572
+
573
+ ## Vocoder and Display ##
574
+ synthesized_audio = self.mel2audio(
575
+ predict_mel_feat, logging=display_audio
576
+ ) # [1, T]
577
+
578
+ return synthesized_audio
579
+
580
+ def get_llm_prompt_text(self, text, follow_prosody_instruction, logging=False):
581
+ llm_prompt_text = gen_chat_prompt(
582
+ text,
583
+ add_assistant_token=True,
584
+ follow_prosody_instruction=follow_prosody_instruction,
585
+ )
586
+
587
+ # if logging:
588
+ # print("-" * 20)
589
+ # print("LLM Prompt Text:\n{}".format(llm_prompt_text))
590
+
591
+ return llm_prompt_text
592
+
593
+ def get_llm_prompt_prosody(
594
+ self,
595
+ use_prosody_code,
596
+ predict_target_prosody,
597
+ prosody_wav_path=None,
598
+ style_ref_wav_path=None,
599
+ use_pitch_shift=False,
600
+ prosody_wav_pitch_shift_steps=0,
601
+ style_ref_wav_pitch_shift_steps=0,
602
+ target_duration=None,
603
+ logging=False,
604
+ ):
605
+ if not use_prosody_code:
606
+ return ""
607
+
608
+ if not predict_target_prosody:
609
+ # Just use the ground truth prosody #
610
+
611
+ assert prosody_wav_path is not None
612
+ prosody_speech, prosody_speech24k, prosody_speech16k = load_wav(
613
+ prosody_wav_path, self.device
614
+ )
615
+
616
+ if target_duration is not None:
617
+ # Calculate the chromagram frame len ratio
618
+ prosody_wav_duration = prosody_speech.shape[0] / 24000
619
+ prosody_wav_chromagram_frame_len_ratio = (
620
+ target_duration / prosody_wav_duration
621
+ )
622
+ else:
623
+ prosody_wav_chromagram_frame_len_ratio = 1.0
624
+
625
+ prosody_wav_prosody_ids = self.extract_coco_codec(
626
+ "style",
627
+ prosody_speech16k,
628
+ prosody_speech,
629
+ frame_len_ratio=prosody_wav_chromagram_frame_len_ratio,
630
+ use_shifted_wav_to_extract_chromagram=use_pitch_shift,
631
+ pitch_shift_steps=prosody_wav_pitch_shift_steps,
632
+ ) # [1, T]
633
+
634
+ if style_ref_wav_path is not None:
635
+ style_ref_speech, style_ref_speech24k, style_ref_speech16k = load_wav(
636
+ style_ref_wav_path, self.device
637
+ )
638
+ style_ref_wav_prosody_ids = self.extract_coco_codec(
639
+ "style",
640
+ style_ref_speech16k,
641
+ style_ref_speech,
642
+ use_shifted_wav_to_extract_chromagram=use_pitch_shift,
643
+ pitch_shift_steps=style_ref_wav_pitch_shift_steps,
644
+ ) # [1, T]
645
+ else:
646
+ style_ref_wav_prosody_ids = torch.zeros(1, 0).to(self.device)
647
+
648
+ prosody_ids = torch.cat(
649
+ [style_ref_wav_prosody_ids, prosody_wav_prosody_ids], dim=1
650
+ ) # [1, T]
651
+
652
+ prosody_ids = prosody_ids[0].tolist()
653
+ prosody_text = "".join(["<|prosody_{}|>".format(i) for i in prosody_ids])
654
+ prosody_text = "<|prosody_start|>" + prosody_text + "<|prosody_end|>"
655
+
656
+ if logging:
657
+ print("-" * 20)
658
+ print("Prosody (Melody) Audio: ", prosody_wav_path)
659
+ display_audio_in_notebook(prosody_speech, rate=24000)
660
+
661
+ else:
662
+ raise NotImplementedError("Not implemented yet")
663
+
664
+ return prosody_text
665
+
666
+ def get_llm_prompt_contentstyle(
667
+ self,
668
+ style_ref_wav_path=None,
669
+ use_pitch_shift=False,
670
+ pitch_shift_steps=0,
671
+ logging=False,
672
+ ):
673
+ if style_ref_wav_path is None:
674
+ return "<|content_style_start|>"
675
+
676
+ style_ref_speech, style_ref_speech24k, style_ref_speech16k = load_wav(
677
+ style_ref_wav_path, self.device
678
+ )
679
+ if logging:
680
+ print("-" * 20)
681
+ print("Style Reference Audio: ", style_ref_wav_path)
682
+ display_audio_in_notebook(style_ref_speech, rate=24000)
683
+
684
+ prompt_output_ids = self.extract_coco_codec(
685
+ "content_style",
686
+ style_ref_speech16k,
687
+ style_ref_speech,
688
+ use_shifted_wav_to_extract_chromagram=use_pitch_shift,
689
+ pitch_shift_steps=pitch_shift_steps,
690
+ ) # [1, T]
691
+
692
+ prompt_output_ids = prompt_output_ids[0].tolist()
693
+ prompt_output_text = "".join(
694
+ ["<|content_style_{}|>".format(i) for i in prompt_output_ids]
695
+ )
696
+ prompt_output_text = "<|content_style_start|>" + prompt_output_text
697
+ return prompt_output_text
698
+
699
+ def parse_llm_generated_ids(self, generated_ids, llm_input_ids, logging=False):
700
+ """
701
+ Args:
702
+ generated_ids: [1, T]
703
+ llm_input_ids: [1, T]
704
+ Returns:
705
+ coco_codecs: [1, T]
706
+ """
707
+ input_len = llm_input_ids.shape[1]
708
+ generated_ids = generated_ids[:, input_len:]
709
+
710
+ # Eg: <|content_style_start|> <|content_style_1|> <|content_style_2|> <|content_style_end|> <|im_end|>
711
+ generated_text = self.ar_tokenizer.decode(
712
+ generated_ids[0], skip_special_tokens=False
713
+ )
714
+
715
+ content_style_ids = extract_special_ids(generated_text, prefix="content_style")
716
+ content_style_ids = (
717
+ torch.tensor(content_style_ids, dtype=torch.long)
718
+ .to(self.device)
719
+ .unsqueeze(0)
720
+ ) # [1, T]
721
+
722
+ if logging:
723
+ print("-" * 20)
724
+ print("LLM input_ids: ", llm_input_ids.shape)
725
+ print("Generated content-style ids: ", content_style_ids.shape)
726
+
727
+ return content_style_ids
728
+
729
+ @torch.no_grad()
730
+ def code2mel(
731
+ self,
732
+ contentstyle_codecs,
733
+ timbre_ref_wav_path,
734
+ prefix_text="",
735
+ used_duration_of_timbre_ref_wav_path=None,
736
+ flow_matching_steps=32,
737
+ logging=False,
738
+ ):
739
+ timbre_ref_speech, timbre_ref_speech24k, timbre_ref_speech16k = load_wav(
740
+ timbre_ref_wav_path,
741
+ self.device,
742
+ used_duration=used_duration_of_timbre_ref_wav_path,
743
+ )
744
+ if logging:
745
+ print("-" * 20)
746
+ print("Timbre Reference Audio: ", timbre_ref_wav_path)
747
+ display_audio_in_notebook(timbre_ref_speech, rate=24000)
748
+
749
+ timbre_ref_codecs = self.extract_coco_codec(
750
+ "content_style",
751
+ timbre_ref_speech16k,
752
+ timbre_ref_speech,
753
+ ) # [1, T]
754
+
755
+ diffusion_input_codecs = torch.cat(
756
+ [timbre_ref_codecs, contentstyle_codecs], dim=1
757
+ )
758
+
759
+ # Prepare the condition for diffusion
760
+ diffusion_cond = self.fmt_model.cond_emb(diffusion_input_codecs) # [1, T, D]
761
+ if self.fmt_model.do_resampling:
762
+ # Align to the frame rate of Mels
763
+ diffusion_cond = self.fmt_model.resampling_layers(
764
+ diffusion_cond.transpose(1, 2)
765
+ ).transpose(1, 2)
766
+
767
+ timbre_ref_mels = self.extract_mel_feature(timbre_ref_speech24k) # [1, T, D]
768
+
769
+ # Text as condition
770
+ if self.fmt_use_text_as_condition:
771
+ prefix_text_ids = self.fmt_text_tokenizer.encode(
772
+ prefix_text, add_special_tokens=False
773
+ )
774
+ prefix_text_ids = torch.tensor(prefix_text_ids, dtype=torch.long).to(
775
+ self.device
776
+ ) # [T]
777
+ prefix_text_ids = prefix_text_ids.unsqueeze(0) # [1, T]
778
+ prefix_text_embedding = self.fmt_model.text_cond_emb(
779
+ prefix_text_ids
780
+ ) # [1, T, D]
781
+ else:
782
+ prefix_text_embedding = None
783
+
784
+ # [1, T, D]
785
+ predict_mel_feat = self.fmt_model.reverse_diffusion(
786
+ cond=diffusion_cond,
787
+ prompt=timbre_ref_mels,
788
+ text_embedding=prefix_text_embedding,
789
+ n_timesteps=flow_matching_steps,
790
+ )
791
+ return predict_mel_feat
792
+
793
+ @torch.no_grad()
794
+ def mel2audio(self, predict_mel_feat, logging=False):
795
+ # [1, 1, T] -> [1, T]
796
+ synthesized_audio = (
797
+ self.vocoder_model(predict_mel_feat.transpose(1, 2)).detach().cpu()
798
+ )[0]
799
+
800
+ if logging:
801
+ print("-" * 20)
802
+ print("Synthesized Audio:")
803
+ # [T]
804
+ audio = synthesized_audio.numpy()[0]
805
+ display_audio_in_notebook(audio, rate=24000)
806
+
807
+ return synthesized_audio
808
+
809
+ @torch.no_grad()
810
+ def inference_ar_and_fm(
811
+ self,
812
+ target_text=None,
813
+ prosody_wav_path=None,
814
+ style_ref_wav_path=None,
815
+ style_ref_wav_text="",
816
+ timbre_ref_wav_path=None,
817
+ use_prosody_code=True,
818
+ predict_target_prosody=False,
819
+ top_k=25,
820
+ top_p=0.8,
821
+ temperature=1.0,
822
+ use_pitch_shift=False,
823
+ prosody_wav_shifted_steps=0,
824
+ style_ref_wav_shifted_steps=0,
825
+ target_duration=None,
826
+ used_duration_of_timbre_ref_wav_path=None,
827
+ flow_matching_steps=32,
828
+ display_audio=False,
829
+ ):
830
+ """
831
+ Based on the style reference wav to conduct the continuation generation:
832
+ [Style_reference_Text, Target_Text], [Style_reference_Prosody, Target_Prosody], [Style_reference_cscodes, Target_cscodes]
833
+ """
834
+ assert self.ar_model is not None
835
+ assert target_text is not None
836
+ # assert style_ref_wav_path is not None
837
+ assert timbre_ref_wav_path is not None
838
+
839
+ ## Text Tokens ##
840
+ if display_audio:
841
+ print("-" * 20)
842
+ print("Target text: \n", target_text)
843
+
844
+ input_text = style_ref_wav_text + " " + target_text
845
+ llm_prompt_text = self.get_llm_prompt_text(
846
+ input_text, use_prosody_code, logging=display_audio
847
+ )
848
+
849
+ ## Whether to use shifted chromagram for timbre_ref_wav ##
850
+ if use_pitch_shift:
851
+ if prosody_wav_path is not None and prosody_wav_shifted_steps == 0:
852
+ prosody_wav_shifted_steps = self.get_shifted_steps(
853
+ prosody_wav_path, timbre_ref_wav_path
854
+ )
855
+ if style_ref_wav_path is not None and style_ref_wav_shifted_steps == 0:
856
+ style_ref_wav_shifted_steps = self.get_shifted_steps(
857
+ style_ref_wav_path, timbre_ref_wav_path
858
+ )
859
+
860
+ if display_audio:
861
+ print("-" * 20)
862
+ print(f"prosody_wav_shifted_steps: {prosody_wav_shifted_steps}")
863
+ print(f"style_ref_wav_shifted_steps: {style_ref_wav_shifted_steps}")
864
+
865
+ ## Prosody Tokens ##
866
+ llm_prompt_prosody = self.get_llm_prompt_prosody(
867
+ use_prosody_code,
868
+ predict_target_prosody,
869
+ prosody_wav_path=prosody_wav_path,
870
+ style_ref_wav_path=style_ref_wav_path,
871
+ use_pitch_shift=use_pitch_shift,
872
+ prosody_wav_pitch_shift_steps=prosody_wav_shifted_steps,
873
+ style_ref_wav_pitch_shift_steps=style_ref_wav_shifted_steps,
874
+ target_duration=target_duration,
875
+ logging=display_audio,
876
+ )
877
+
878
+ ## Content-Style Tokens ##
879
+ llm_prompt_contentstyle = self.get_llm_prompt_contentstyle(
880
+ style_ref_wav_path,
881
+ use_pitch_shift=use_pitch_shift,
882
+ pitch_shift_steps=style_ref_wav_shifted_steps,
883
+ logging=display_audio,
884
+ )
885
+
886
+ ## AR ##
887
+ llm_prompt = llm_prompt_text + llm_prompt_prosody + llm_prompt_contentstyle
888
+ llm_input_ids = self.ar_tokenizer.encode(llm_prompt, add_special_tokens=True)
889
+ llm_input_ids = (
890
+ torch.tensor(llm_input_ids, dtype=torch.long).to(self.device).unsqueeze(0)
891
+ ) # [1, T]
892
+
893
+ if self.use_vllm:
894
+ sampling_params = SamplingParams(
895
+ max_tokens=500,
896
+ top_k=top_k,
897
+ top_p=top_p,
898
+ temperature=temperature,
899
+ stop_token_ids=[self.ar_tokenizer.eos_token_id],
900
+ skip_special_tokens=False,
901
+ )
902
+ outputs = self.ar_model.generate([llm_prompt], sampling_params)
903
+
904
+ assert len(outputs) == 1
905
+ output = outputs[0]
906
+ prompt = output.prompt
907
+ predicted_coco_text = output.outputs[0].text
908
+ # print("vllm predicted_coco_text: ", predicted_coco_text)
909
+
910
+ predicted_coco_codecs = extract_special_ids(
911
+ predicted_coco_text, prefix="content_style"
912
+ )
913
+ predicted_coco_codecs = (
914
+ torch.tensor(predicted_coco_codecs, dtype=torch.long)
915
+ .to(self.device)
916
+ .unsqueeze(0)
917
+ ) # [1, T]
918
+ else:
919
+ generate_ids = self.ar_model.generate(
920
+ input_ids=llm_input_ids,
921
+ min_new_tokens=15,
922
+ max_new_tokens=500,
923
+ eos_token_id=self.ar_tokenizer.eos_token_id,
924
+ do_sample=True,
925
+ top_k=top_k,
926
+ top_p=top_p,
927
+ temperature=temperature,
928
+ ) # [1, T]
929
+
930
+ predicted_coco_codecs = self.parse_llm_generated_ids(
931
+ generate_ids, llm_input_ids, logging=display_audio
932
+ ) # [1, T]
933
+
934
+ ## Diffusion ##
935
+ predict_mel_feat = self.code2mel(
936
+ predicted_coco_codecs,
937
+ timbre_ref_wav_path,
938
+ used_duration_of_timbre_ref_wav_path=used_duration_of_timbre_ref_wav_path,
939
+ flow_matching_steps=flow_matching_steps,
940
+ logging=display_audio,
941
+ ) # [1, T, D]
942
+
943
+ ## Vocoder ##
944
+ synthesized_audio = self.mel2audio(
945
+ predict_mel_feat, logging=display_audio
946
+ ) # [1, T]
947
+ return synthesized_audio
948
+
949
+ @torch.no_grad()
950
+ def inference_vocoder_resynthesis(self, wav_path, display_audio=False):
951
+ speech, speech24k, speech16k = load_wav(wav_path, self.device)
952
+ if display_audio:
953
+ print("Ground Truth audio:")
954
+ display_audio_in_notebook(speech, rate=24000)
955
+
956
+ mel = self.extract_mel_feature(speech24k) # [1, T, D]
957
+ audio = self.vocoder_model(mel.transpose(1, 2)).detach().cpu()[0]
958
+ if display_audio:
959
+ print("Resynthesized audio:")
960
+ display_audio_in_notebook(audio, rate=24000)
961
+ return audio
models/vc/flow_matching_transformer/llama_nar.py ADDED
@@ -0,0 +1,593 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from transformers import LlamaConfig, LlamaModel
7
+ import torch
8
+ import torch.nn as nn
9
+ from typing import List, Optional, Tuple, Union
10
+ import math
11
+ import torch.nn.functional as F
12
+
13
+ from transformers.models.llama.modeling_llama import (
14
+ LlamaDecoderLayer,
15
+ Cache,
16
+ apply_rotary_pos_emb,
17
+ repeat_kv,
18
+ BaseModelOutputWithPast,
19
+ LlamaRotaryEmbedding,
20
+ )
21
+
22
+ import logging
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ # sinusoidal positional encoding
28
+ class SinusoidalPosEmb(nn.Module):
29
+ def __init__(self, dim):
30
+ super().__init__()
31
+ self.dim = dim
32
+
33
+ def forward(self, x):
34
+ device = x.device
35
+ half_dim = self.dim // 2
36
+ emb = math.log(10000) / (half_dim - 1)
37
+ emb = torch.exp(torch.arange(half_dim, device=device) * -emb)
38
+ emb = x[:, None] * emb[None, :] * 1.0
39
+ emb = torch.cat((emb.sin(), emb.cos()), dim=-1)
40
+ return emb
41
+
42
+
43
+ class LlamaAdaptiveRMSNorm(nn.Module):
44
+ def __init__(self, hidden_size=1024, eps=1e-6, dim_cond=1024):
45
+ super().__init__()
46
+ self.to_weight = nn.Linear(dim_cond, hidden_size)
47
+ nn.init.zeros_(self.to_weight.weight)
48
+ nn.init.ones_(self.to_weight.bias)
49
+ self.variance_epsilon = eps
50
+ self._is_hf_initialized = True # disable automatic init
51
+
52
+ def forward(self, hidden_states, cond_embedding):
53
+ input_dtype = hidden_states.dtype
54
+ variance = hidden_states.to(torch.float32).pow(2).mean(-1, keepdim=True)
55
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
56
+
57
+ weight = self.to_weight(cond_embedding)
58
+ if len(weight.shape) == 2:
59
+ weight = weight.unsqueeze(1)
60
+
61
+ return (weight * hidden_states).to(input_dtype)
62
+
63
+
64
+ class OldLlamaAttention(nn.Module):
65
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
66
+
67
+ def __init__(self, config: LlamaConfig, layer_idx: Optional[int] = None):
68
+ super().__init__()
69
+ self.config = config
70
+ self.layer_idx = layer_idx
71
+ if layer_idx is None:
72
+ logger.warning_once(
73
+ f"Instantiating {self.__class__.__name__} without passing a `layer_idx` is not recommended and will "
74
+ "lead to errors during the forward call if caching is used. Please make sure to provide a `layer_idx` "
75
+ "when creating this class."
76
+ )
77
+
78
+ self.attention_dropout = config.attention_dropout
79
+ self.hidden_size = config.hidden_size
80
+ self.num_heads = config.num_attention_heads
81
+ self.head_dim = getattr(config, "head_dim", self.hidden_size // self.num_heads)
82
+ self.num_key_value_heads = config.num_key_value_heads
83
+ self.num_key_value_groups = self.num_heads // self.num_key_value_heads
84
+ self.max_position_embeddings = config.max_position_embeddings
85
+ self.rope_theta = config.rope_theta
86
+ self.is_causal = True
87
+
88
+ self.q_proj = nn.Linear(
89
+ self.hidden_size, self.num_heads * self.head_dim, bias=config.attention_bias
90
+ )
91
+ self.k_proj = nn.Linear(
92
+ self.hidden_size,
93
+ self.num_key_value_heads * self.head_dim,
94
+ bias=config.attention_bias,
95
+ )
96
+ self.v_proj = nn.Linear(
97
+ self.hidden_size,
98
+ self.num_key_value_heads * self.head_dim,
99
+ bias=config.attention_bias,
100
+ )
101
+ self.o_proj = nn.Linear(
102
+ self.num_heads * self.head_dim, self.hidden_size, bias=config.attention_bias
103
+ )
104
+
105
+ # TODO (joao): remove in v4.46 (RoPE is computed in the model, not in the decoder layers)
106
+ self.rotary_emb = LlamaRotaryEmbedding(config=self.config)
107
+
108
+ def forward(
109
+ self,
110
+ hidden_states: torch.Tensor,
111
+ attention_mask: Optional[torch.Tensor] = None,
112
+ position_ids: Optional[torch.LongTensor] = None,
113
+ past_key_value: Optional[Cache] = None,
114
+ output_attentions: bool = False,
115
+ use_cache: bool = False,
116
+ cache_position: Optional[torch.LongTensor] = None,
117
+ position_embeddings: Optional[
118
+ Tuple[torch.Tensor, torch.Tensor]
119
+ ] = None, # will become mandatory in v4.46
120
+ **kwargs,
121
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
122
+ bsz, q_len, _ = hidden_states.size()
123
+
124
+ if self.config.pretraining_tp > 1:
125
+ key_value_slicing = (
126
+ self.num_key_value_heads * self.head_dim
127
+ ) // self.config.pretraining_tp
128
+ query_slices = self.q_proj.weight.split(
129
+ (self.num_heads * self.head_dim) // self.config.pretraining_tp, dim=0
130
+ )
131
+ key_slices = self.k_proj.weight.split(key_value_slicing, dim=0)
132
+ value_slices = self.v_proj.weight.split(key_value_slicing, dim=0)
133
+
134
+ query_states = [
135
+ F.linear(hidden_states, query_slices[i])
136
+ for i in range(self.config.pretraining_tp)
137
+ ]
138
+ query_states = torch.cat(query_states, dim=-1)
139
+
140
+ key_states = [
141
+ F.linear(hidden_states, key_slices[i])
142
+ for i in range(self.config.pretraining_tp)
143
+ ]
144
+ key_states = torch.cat(key_states, dim=-1)
145
+
146
+ value_states = [
147
+ F.linear(hidden_states, value_slices[i])
148
+ for i in range(self.config.pretraining_tp)
149
+ ]
150
+ value_states = torch.cat(value_states, dim=-1)
151
+
152
+ else:
153
+ query_states = self.q_proj(hidden_states)
154
+ key_states = self.k_proj(hidden_states)
155
+ value_states = self.v_proj(hidden_states)
156
+
157
+ query_states = query_states.view(
158
+ bsz, q_len, self.num_heads, self.head_dim
159
+ ).transpose(1, 2)
160
+ key_states = key_states.view(
161
+ bsz, q_len, self.num_key_value_heads, self.head_dim
162
+ ).transpose(1, 2)
163
+ value_states = value_states.view(
164
+ bsz, q_len, self.num_key_value_heads, self.head_dim
165
+ ).transpose(1, 2)
166
+
167
+ if position_embeddings is None:
168
+ logger.warning_once(
169
+ "The attention layers in this model are transitioning from computing the RoPE embeddings internally "
170
+ "through `position_ids` (2D tensor with the indexes of the tokens), to using externally computed "
171
+ "`position_embeddings` (Tuple of tensors, containing cos and sin). In v4.46 `position_ids` will be "
172
+ "removed and `position_embeddings` will be mandatory."
173
+ )
174
+ cos, sin = self.rotary_emb(value_states, position_ids)
175
+ else:
176
+ cos, sin = position_embeddings
177
+ query_states, key_states = apply_rotary_pos_emb(
178
+ query_states, key_states, cos, sin
179
+ )
180
+
181
+ if past_key_value is not None:
182
+ # sin and cos are specific to RoPE models; cache_position needed for the static cache
183
+ cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position}
184
+ key_states, value_states = past_key_value.update(
185
+ key_states, value_states, self.layer_idx, cache_kwargs
186
+ )
187
+
188
+ key_states = repeat_kv(key_states, self.num_key_value_groups)
189
+ value_states = repeat_kv(value_states, self.num_key_value_groups)
190
+ attn_weights = torch.matmul(
191
+ query_states, key_states.transpose(2, 3)
192
+ ) / math.sqrt(self.head_dim)
193
+
194
+ if attention_mask is not None: # no matter the length, we just slice it
195
+ causal_mask = attention_mask[:, :, :, : key_states.shape[-2]]
196
+ attn_weights = attn_weights + causal_mask
197
+
198
+ # upcast attention to fp32
199
+ attn_weights = nn.functional.softmax(
200
+ attn_weights, dim=-1, dtype=torch.float32
201
+ ).to(query_states.dtype)
202
+ attn_weights = nn.functional.dropout(
203
+ attn_weights, p=self.attention_dropout, training=self.training
204
+ )
205
+ attn_output = torch.matmul(attn_weights, value_states)
206
+
207
+ if attn_output.size() != (bsz, self.num_heads, q_len, self.head_dim):
208
+ raise ValueError(
209
+ f"`attn_output` should be of size {(bsz, self.num_heads, q_len, self.head_dim)}, but is"
210
+ f" {attn_output.size()}"
211
+ )
212
+
213
+ attn_output = attn_output.transpose(1, 2).contiguous()
214
+
215
+ attn_output = attn_output.reshape(bsz, q_len, -1)
216
+
217
+ if self.config.pretraining_tp > 1:
218
+ attn_output = attn_output.split(
219
+ self.hidden_size // self.config.pretraining_tp, dim=2
220
+ )
221
+ o_proj_slices = self.o_proj.weight.split(
222
+ self.hidden_size // self.config.pretraining_tp, dim=1
223
+ )
224
+ attn_output = sum(
225
+ [
226
+ F.linear(attn_output[i], o_proj_slices[i])
227
+ for i in range(self.config.pretraining_tp)
228
+ ]
229
+ )
230
+ else:
231
+ attn_output = self.o_proj(attn_output)
232
+
233
+ if not output_attentions:
234
+ attn_weights = None
235
+
236
+ return attn_output, attn_weights, past_key_value
237
+
238
+
239
+ class LlamaNARDecoderLayer(LlamaDecoderLayer):
240
+ def __init__(self, config: LlamaConfig, layer_idx: int):
241
+ """Override to adaptive layer norm"""
242
+ super().__init__(config, layer_idx) # init attention, mlp, etc.
243
+ self.input_layernorm = LlamaAdaptiveRMSNorm(
244
+ config.hidden_size, eps=config.rms_norm_eps, dim_cond=config.hidden_size
245
+ )
246
+ self.post_attention_layernorm = LlamaAdaptiveRMSNorm(
247
+ config.hidden_size, eps=config.rms_norm_eps, dim_cond=config.hidden_size
248
+ )
249
+
250
+ # For transformers v4.46 (added by Xueyao)
251
+ self.self_attn = OldLlamaAttention(config=config, layer_idx=layer_idx)
252
+
253
+ # add `cond` in forward function
254
+ def forward(
255
+ self,
256
+ hidden_states: torch.Tensor,
257
+ cond_embedding: torch.Tensor,
258
+ attention_mask: Optional[torch.Tensor] = None,
259
+ position_ids: Optional[torch.LongTensor] = None,
260
+ past_key_value: Optional[Tuple[torch.Tensor]] = None,
261
+ output_attentions: Optional[bool] = False,
262
+ use_cache: Optional[bool] = False,
263
+ ) -> Tuple[
264
+ torch.FloatTensor, Optional[Tuple[torch.FloatTensor, torch.FloatTensor]]
265
+ ]:
266
+ """
267
+ Args:
268
+ hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, embed_dim)`
269
+ attention_mask (`torch.FloatTensor`, *optional*): attention mask of size
270
+ `(batch, 1, tgt_len, src_len)` where padding elements are indicated by very large negative values.
271
+ output_attentions (`bool`, *optional*):
272
+ Whether or not to return the attentions tensors of all attention layers. See `attentions` under
273
+ returned tensors for more detail.
274
+ use_cache (`bool`, *optional*):
275
+ If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding
276
+ (see `past_key_values`).
277
+ past_key_value (`Tuple(torch.FloatTensor)`, *optional*): cached past key and value projection states
278
+ """
279
+
280
+ residual = hidden_states
281
+
282
+ hidden_states = self.input_layernorm(
283
+ hidden_states, cond_embedding=cond_embedding
284
+ )
285
+
286
+ # Self Attention
287
+ hidden_states, self_attn_weights, present_key_value = self.self_attn(
288
+ hidden_states=hidden_states,
289
+ attention_mask=attention_mask,
290
+ position_ids=position_ids,
291
+ past_key_value=past_key_value,
292
+ output_attentions=output_attentions,
293
+ use_cache=use_cache,
294
+ )
295
+ hidden_states = residual + hidden_states
296
+
297
+ # Fully Connected
298
+ residual = hidden_states
299
+ hidden_states = self.post_attention_layernorm(
300
+ hidden_states, cond_embedding=cond_embedding
301
+ )
302
+ hidden_states = self.mlp(hidden_states)
303
+ hidden_states = residual + hidden_states
304
+
305
+ outputs = (hidden_states,)
306
+
307
+ if output_attentions:
308
+ outputs += (self_attn_weights,)
309
+
310
+ if use_cache:
311
+ outputs += (present_key_value,)
312
+
313
+ return outputs
314
+
315
+
316
+ class DiffLlama(LlamaModel):
317
+ def __init__(
318
+ self,
319
+ mel_dim=100,
320
+ hidden_size=1024,
321
+ num_heads=16,
322
+ num_layers=16,
323
+ dropout=0.1,
324
+ ffn_dropout=0.1,
325
+ attention_dropout=0.0,
326
+ # deviation: newer transformers rejects positional args, so kwargs
327
+ config=LlamaConfig(
328
+ vocab_size=0,
329
+ hidden_size=256,
330
+ intermediate_size=1024,
331
+ num_hidden_layers=1,
332
+ num_attention_heads=1,
333
+ ),
334
+ ):
335
+ super().__init__(config)
336
+
337
+ self.layers = nn.ModuleList(
338
+ [
339
+ LlamaNARDecoderLayer(
340
+ LlamaConfig(
341
+ hidden_size=hidden_size,
342
+ num_attention_heads=num_heads,
343
+ max_position_embeddings=4096,
344
+ intermediate_size=hidden_size * 4,
345
+ ),
346
+ layer_idx=i,
347
+ )
348
+ for i in range(num_layers)
349
+ ]
350
+ )
351
+
352
+ self.norm = LlamaAdaptiveRMSNorm(hidden_size, dim_cond=hidden_size)
353
+
354
+ self.diff_step_embedding = SinusoidalPosEmb(hidden_size)
355
+ self.diff_step_mlp = nn.Sequential(
356
+ nn.Linear(hidden_size, hidden_size * 4),
357
+ nn.SiLU(),
358
+ nn.Linear(hidden_size * 4, hidden_size),
359
+ )
360
+
361
+ self.cond_mlp = nn.Sequential(
362
+ nn.Linear(hidden_size, hidden_size * 4),
363
+ nn.SiLU(),
364
+ nn.Linear(hidden_size * 4, hidden_size),
365
+ )
366
+
367
+ self.mel_mlp = nn.Sequential(
368
+ nn.Linear(mel_dim, hidden_size * 4),
369
+ nn.SiLU(),
370
+ nn.Linear(hidden_size * 4, hidden_size),
371
+ )
372
+
373
+ self.mel_out_mlp = nn.Sequential(
374
+ nn.Linear(hidden_size, hidden_size * 4),
375
+ nn.SiLU(),
376
+ nn.Linear(hidden_size * 4, mel_dim),
377
+ )
378
+
379
+ for layer in self.layers:
380
+ layer.input_layernorm = LlamaAdaptiveRMSNorm(
381
+ hidden_size, dim_cond=hidden_size
382
+ )
383
+ layer.post_attention_layernorm = LlamaAdaptiveRMSNorm(
384
+ hidden_size, dim_cond=hidden_size
385
+ )
386
+
387
+ self.embed_tokens = None
388
+
389
+ self.post_init()
390
+
391
+ # self.reset_parameters()
392
+
393
+ def _prepare_decoder_attention_mask(
394
+ self, attention_mask, input_shape, inputs_embeds, past_key_values_length
395
+ ):
396
+ # create noncausal mask
397
+ # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len]
398
+ combined_attention_mask = None
399
+
400
+ def _expand_mask(
401
+ mask: torch.Tensor, dtype: torch.dtype, tgt_len: Optional[int] = None
402
+ ):
403
+ """
404
+ Expands attention_mask from `[bsz, seq_len]` to `[bsz, 1, tgt_seq_len, src_seq_len]`.
405
+ """
406
+ bsz, src_len = mask.size()
407
+ tgt_len = tgt_len if tgt_len is not None else src_len
408
+
409
+ expanded_mask = (
410
+ mask[:, None, None, :].expand(bsz, 1, tgt_len, src_len).to(dtype)
411
+ )
412
+
413
+ inverted_mask = 1.0 - expanded_mask
414
+
415
+ return inverted_mask.masked_fill(
416
+ inverted_mask.to(torch.bool), torch.finfo(dtype).min
417
+ )
418
+
419
+ if attention_mask is not None:
420
+ # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len]
421
+ expanded_attn_mask = _expand_mask(
422
+ attention_mask, inputs_embeds.dtype, tgt_len=input_shape[-1]
423
+ ).to(inputs_embeds.device)
424
+ combined_attention_mask = (
425
+ expanded_attn_mask
426
+ if combined_attention_mask is None
427
+ else expanded_attn_mask + combined_attention_mask
428
+ )
429
+
430
+ return combined_attention_mask
431
+
432
+ def forward(
433
+ self,
434
+ x,
435
+ diffusion_step,
436
+ cond,
437
+ x_mask,
438
+ input_ids: torch.LongTensor = None, # [num_quant, B, T]
439
+ attention_mask: Optional[torch.Tensor] = None,
440
+ position_ids: Optional[torch.LongTensor] = None,
441
+ past_key_values: Optional[List[torch.FloatTensor]] = None,
442
+ inputs_embeds: Optional[torch.FloatTensor] = None,
443
+ use_cache: Optional[bool] = None,
444
+ output_attentions: Optional[bool] = None,
445
+ output_hidden_states: Optional[bool] = None,
446
+ return_dict: Optional[bool] = False,
447
+ ) -> Union[Tuple, BaseModelOutputWithPast]:
448
+
449
+ # retrieve some shape info
450
+ batch_size, seq_length, _ = x.shape
451
+
452
+ # condtion mlp
453
+ cond_embedding = self.cond_mlp(cond) # (B, T, C)
454
+
455
+ # condition mel
456
+ x = self.mel_mlp(x)
457
+
458
+ # diffusion step embedding
459
+ diffusion_step = self.diff_step_embedding(diffusion_step).to(x.device)
460
+ diffusion_step = self.diff_step_mlp(diffusion_step) # (B, C)
461
+ x = x + cond_embedding
462
+
463
+ inputs_embeds = x
464
+ attention_mask = x_mask
465
+
466
+ output_attentions = (
467
+ output_attentions
468
+ if output_attentions is not None
469
+ else self.config.output_attentions
470
+ )
471
+ output_hidden_states = (
472
+ output_hidden_states
473
+ if output_hidden_states is not None
474
+ else self.config.output_hidden_states
475
+ )
476
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
477
+
478
+ seq_length_with_past = seq_length
479
+ past_key_values_length = 0
480
+
481
+ if past_key_values is not None:
482
+ past_key_values_length = past_key_values[0][0].shape[2]
483
+ seq_length_with_past = seq_length_with_past + past_key_values_length
484
+
485
+ if position_ids is None:
486
+ device = input_ids.device if input_ids is not None else inputs_embeds.device
487
+ position_ids = torch.arange(
488
+ past_key_values_length,
489
+ seq_length + past_key_values_length,
490
+ dtype=torch.long,
491
+ device=device,
492
+ )
493
+ position_ids = position_ids.unsqueeze(0).view(-1, seq_length)
494
+ else:
495
+ position_ids = position_ids.view(-1, seq_length).long()
496
+
497
+ # embed positions
498
+ if attention_mask is None:
499
+ attention_mask = torch.ones(
500
+ (batch_size, seq_length_with_past),
501
+ dtype=torch.bool,
502
+ device=inputs_embeds.device,
503
+ )
504
+ attention_mask = self._prepare_decoder_attention_mask(
505
+ attention_mask,
506
+ (batch_size, seq_length),
507
+ inputs_embeds,
508
+ past_key_values_length,
509
+ )
510
+
511
+ hidden_states = inputs_embeds
512
+
513
+ if self.gradient_checkpointing and self.training:
514
+ if use_cache:
515
+ use_cache = False
516
+
517
+ # decoder layers
518
+ all_hidden_states = () if output_hidden_states else None
519
+ all_self_attns = () if output_attentions else None
520
+ next_decoder_cache = () if use_cache else None
521
+
522
+ all_layer_hidden_states = []
523
+
524
+ for idx, decoder_layer in enumerate(self.layers):
525
+ if output_hidden_states:
526
+ all_hidden_states += (hidden_states,)
527
+
528
+ past_key_value = (
529
+ past_key_values[idx] if past_key_values is not None else None
530
+ )
531
+
532
+ if self.gradient_checkpointing and self.training:
533
+ raise NotImplementedError
534
+
535
+ def create_custom_forward(module):
536
+ def custom_forward(*inputs):
537
+ # None for past_key_value
538
+ return module(*inputs, output_attentions, None)
539
+
540
+ return custom_forward
541
+
542
+ layer_outputs = torch.utils.checkpoint.checkpoint(
543
+ create_custom_forward(decoder_layer),
544
+ hidden_states,
545
+ attention_mask,
546
+ position_ids,
547
+ None,
548
+ )
549
+ else:
550
+ layer_outputs = decoder_layer(
551
+ hidden_states,
552
+ attention_mask=attention_mask,
553
+ position_ids=position_ids,
554
+ past_key_value=past_key_value,
555
+ output_attentions=output_attentions,
556
+ use_cache=use_cache,
557
+ cond_embedding=diffusion_step,
558
+ )
559
+
560
+ hidden_states = layer_outputs[0]
561
+ all_layer_hidden_states.append(hidden_states.clone())
562
+
563
+ if use_cache:
564
+ next_decoder_cache += (layer_outputs[2 if output_attentions else 1],)
565
+
566
+ if output_attentions:
567
+ all_self_attns += (layer_outputs[1],)
568
+
569
+ hidden_states = self.norm(hidden_states, cond_embedding=diffusion_step)
570
+
571
+ # add hidden states from the last decoder layer
572
+ if output_hidden_states:
573
+ all_hidden_states += (hidden_states,)
574
+
575
+ next_cache = next_decoder_cache if use_cache else None
576
+
577
+ hidden_states = self.mel_out_mlp(hidden_states)
578
+
579
+ # if not return_dict:
580
+ # return tuple(v for v in [hidden_states, next_cache, all_hidden_states, all_self_attns] if v is not None)
581
+ # return BaseModelOutputWithPast(
582
+ # last_hidden_state=hidden_states,
583
+ # past_key_values=next_cache,
584
+ # hidden_states=all_hidden_states,
585
+ # attentions=all_self_attns,
586
+ # )
587
+ if return_dict:
588
+ return {
589
+ "output": hidden_states,
590
+ "hidden_states": all_layer_hidden_states,
591
+ }
592
+
593
+ return hidden_states
requirements.txt ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ git+https://github.com/TEAMuP-dev/pyharp.git@develop
2
+ # model-specific deps below:
3
+ spaces
4
+ torch==2.8.0
5
+ torchaudio==2.8.0
6
+ accelerate
7
+ # newest 4.x: reads the checkpoint's tokenizer.json but keeps pre-5.0 Llama internals
8
+ transformers==4.57.6
9
+ torchvision
10
+ einops
11
+ librosa
12
+ huggingface_hub
13
+ ruamel.yaml
14
+ pyyaml
15
+ setuptools<81
16
+ pyworld
17
+ praat-parselmouth
18
+ torchcrepe
19
+ openai-whisper
20
+ json5
21
+ ipython
22
+ soundfile
utils/__init__.py ADDED
File without changes
utils/f0.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import librosa
7
+ import numpy as np
8
+ import torch
9
+ import parselmouth
10
+ import torchcrepe
11
+ import pyworld as pw
12
+
13
+
14
+ def f0_to_coarse(f0, pitch_bin, f0_min, f0_max):
15
+ """
16
+ Convert f0 (Hz) to pitch (mel scale), and then quantize the mel-scale pitch to the
17
+ range from [1, 2, 3, ..., pitch_bin-1]
18
+
19
+ Reference: https://en.wikipedia.org/wiki/Mel_scale
20
+
21
+ Args:
22
+ f0 (array or Tensor): Hz
23
+ pitch_bin (int): the vocabulary size
24
+ f0_min (int): the minimum f0 (Hz)
25
+ f0_max (int): the maximum f0 (Hz)
26
+
27
+ Returns:
28
+ quantized f0 (array or Tensor)
29
+ """
30
+ f0_mel_min = 1127 * np.log(1 + f0_min / 700)
31
+ f0_mel_max = 1127 * np.log(1 + f0_max / 700)
32
+
33
+ is_torch = isinstance(f0, torch.Tensor)
34
+ f0_mel = 1127 * (1 + f0 / 700).log() if is_torch else 1127 * np.log(1 + f0 / 700)
35
+ f0_mel[f0_mel > 0] = (f0_mel[f0_mel > 0] - f0_mel_min) * (pitch_bin - 2) / (
36
+ f0_mel_max - f0_mel_min
37
+ ) + 1
38
+
39
+ f0_mel[f0_mel <= 1] = 1
40
+ f0_mel[f0_mel > pitch_bin - 1] = pitch_bin - 1
41
+ f0_coarse = (f0_mel + 0.5).long() if is_torch else np.rint(f0_mel).astype(np.int32)
42
+ assert f0_coarse.max() <= 255 and f0_coarse.min() >= 1, (
43
+ f0_coarse.max(),
44
+ f0_coarse.min(),
45
+ )
46
+ return f0_coarse
47
+
48
+
49
+ def interpolate(f0):
50
+ """Interpolate the unvoiced part. Thus the f0 can be passed to a subtractive synthesizer.
51
+ Args:
52
+ f0: A numpy array of shape (seq_len,)
53
+ Returns:
54
+ f0: Interpolated f0 of shape (seq_len,)
55
+ uv: Unvoiced part of shape (seq_len,)
56
+ """
57
+ uv = f0 == 0
58
+ if len(f0[~uv]) > 0:
59
+ # interpolate the unvoiced f0
60
+ f0[uv] = np.interp(np.where(uv)[0], np.where(~uv)[0], f0[~uv])
61
+ uv = uv.astype("float")
62
+ uv = np.min(np.array([uv[:-2], uv[1:-1], uv[2:]]), axis=0)
63
+ uv = np.pad(uv, (1, 1))
64
+ return f0, uv
65
+
66
+
67
+ def get_log_f0(f0):
68
+ f0[np.where(f0 == 0)] = 1
69
+ log_f0 = np.log(f0)
70
+ return log_f0
71
+
72
+
73
+ def get_f0_features_using_pyin(audio, cfg):
74
+ """Using pyin to extract the f0 feature.
75
+ Args:
76
+ audio
77
+ fs
78
+ win_length
79
+ hop_length
80
+ f0_min
81
+ f0_max
82
+ Returns:
83
+ f0: numpy array of shape (frame_len,)
84
+ """
85
+ f0, voiced_flag, voiced_probs = librosa.pyin(
86
+ y=audio,
87
+ fmin=cfg.f0_min,
88
+ fmax=cfg.f0_max,
89
+ sr=cfg.sample_rate,
90
+ win_length=cfg.win_size,
91
+ hop_length=cfg.hop_size,
92
+ )
93
+ # Set nan to 0
94
+ f0[voiced_flag == False] = 0
95
+ return f0
96
+
97
+
98
+ def get_f0_features_using_parselmouth(audio, cfg, speed=1):
99
+ """Using parselmouth to extract the f0 feature.
100
+ Args:
101
+ audio
102
+ mel_len
103
+ hop_length
104
+ fs
105
+ f0_min
106
+ f0_max
107
+ speed(default=1)
108
+ Returns:
109
+ f0: numpy array of shape (frame_len,)
110
+ pitch_coarse: numpy array of shape (frame_len,)
111
+ """
112
+ hop_size = int(np.round(cfg.hop_size * speed))
113
+
114
+ # Calculate the time step for pitch extraction
115
+ time_step = hop_size / cfg.sample_rate * 1000
116
+
117
+ f0 = (
118
+ parselmouth.Sound(audio, cfg.sample_rate)
119
+ .to_pitch_ac(
120
+ time_step=time_step / 1000,
121
+ voicing_threshold=0.6,
122
+ pitch_floor=cfg.f0_min,
123
+ pitch_ceiling=cfg.f0_max,
124
+ )
125
+ .selected_array["frequency"]
126
+ )
127
+ return f0
128
+
129
+
130
+ def get_f0_features_using_dio(audio, cfg):
131
+ """Using dio to extract the f0 feature.
132
+ Args:
133
+ audio
134
+ mel_len
135
+ fs
136
+ hop_length
137
+ f0_min
138
+ f0_max
139
+ Returns:
140
+ f0: numpy array of shape (frame_len,)
141
+ """
142
+ # Get the raw f0
143
+ _f0, t = pw.dio(
144
+ audio.astype("double"),
145
+ cfg.sample_rate,
146
+ f0_floor=cfg.f0_min,
147
+ f0_ceil=cfg.f0_max,
148
+ channels_in_octave=2,
149
+ frame_period=(1000 * cfg.hop_size / cfg.sample_rate),
150
+ )
151
+ # Get the f0
152
+ f0 = pw.stonemask(audio.astype("double"), _f0, t, cfg.sample_rate)
153
+ return f0
154
+
155
+
156
+ def get_f0_features_using_harvest(audio, mel_len, fs, hop_length, f0_min, f0_max):
157
+ """Using harvest to extract the f0 feature.
158
+ Args:
159
+ audio
160
+ mel_len
161
+ fs
162
+ hop_length
163
+ f0_min
164
+ f0_max
165
+ Returns:
166
+ f0: numpy array of shape (frame_len,)
167
+ """
168
+ f0, _ = pw.harvest(
169
+ audio.astype("double"),
170
+ fs,
171
+ f0_floor=f0_min,
172
+ f0_ceil=f0_max,
173
+ frame_period=(1000 * hop_length / fs),
174
+ )
175
+ f0 = f0.astype("float")[:mel_len]
176
+ return f0
177
+
178
+
179
+ def get_f0_features_using_crepe(
180
+ audio, mel_len, fs, hop_length, hop_length_new, f0_min, f0_max, threshold=0.3
181
+ ):
182
+ """Using torchcrepe to extract the f0 feature.
183
+ Args:
184
+ audio
185
+ mel_len
186
+ fs
187
+ hop_length
188
+ hop_length_new
189
+ f0_min
190
+ f0_max
191
+ threshold(default=0.3)
192
+ Returns:
193
+ f0: numpy array of shape (frame_len,)
194
+ """
195
+ # Currently, crepe only supports 16khz audio
196
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
197
+ audio_16k = librosa.resample(audio, orig_sr=fs, target_sr=16000)
198
+ audio_16k_torch = torch.FloatTensor(audio_16k).unsqueeze(0).to(device)
199
+
200
+ # Get the raw pitch
201
+ f0, pd = torchcrepe.predict(
202
+ audio_16k_torch,
203
+ 16000,
204
+ hop_length_new,
205
+ f0_min,
206
+ f0_max,
207
+ pad=True,
208
+ model="full",
209
+ batch_size=1024,
210
+ device=device,
211
+ return_periodicity=True,
212
+ )
213
+
214
+ # Filter, de-silence, set up threshold for unvoiced part
215
+ pd = torchcrepe.filter.median(pd, 3)
216
+ pd = torchcrepe.threshold.Silence(-60.0)(pd, audio_16k_torch, 16000, hop_length_new)
217
+ f0 = torchcrepe.threshold.At(threshold)(f0, pd)
218
+ f0 = torchcrepe.filter.mean(f0, 3)
219
+
220
+ # Convert unvoiced part to 0hz
221
+ f0 = torch.where(torch.isnan(f0), torch.full_like(f0, 0), f0)
222
+
223
+ # Interpolate f0
224
+ nzindex = torch.nonzero(f0[0]).squeeze()
225
+ f0 = torch.index_select(f0[0], dim=0, index=nzindex).cpu().numpy()
226
+ time_org = 0.005 * nzindex.cpu().numpy()
227
+ time_frame = np.arange(mel_len) * hop_length / fs
228
+ f0 = np.interp(time_frame, time_org, f0, left=f0[0], right=f0[-1])
229
+ return f0
230
+
231
+
232
+ def get_f0(audio, cfg, use_interpolate=False, return_uv=False):
233
+ if cfg.pitch_extractor == "dio":
234
+ f0 = get_f0_features_using_dio(audio, cfg)
235
+ elif cfg.pitch_extractor == "pyin":
236
+ f0 = get_f0_features_using_pyin(audio, cfg)
237
+ elif cfg.pitch_extractor == "parselmouth":
238
+ f0 = get_f0_features_using_parselmouth(audio, cfg)
239
+
240
+ if use_interpolate:
241
+ f0, uv = interpolate(f0)
242
+ else:
243
+ uv = f0 == 0
244
+
245
+ if return_uv:
246
+ return f0, uv
247
+
248
+ return f0
249
+
250
+
251
+ def get_cents(f0_hz):
252
+ """
253
+ F_{cent} = 1200 * log2 (F/440)
254
+
255
+ Reference:
256
+ APSIPA'17, Perceptual Evaluation of Singing Quality
257
+ """
258
+ voiced_f0 = f0_hz[f0_hz != 0]
259
+ return 1200 * np.log2(voiced_f0 / 440)
260
+
261
+
262
+ def get_pitch_derivatives(f0_hz):
263
+ """
264
+ f0_hz: (,T)
265
+ """
266
+ f0_cent = get_cents(f0_hz)
267
+ return f0_cent[1:] - f0_cent[:-1]
268
+
269
+
270
+ def get_pitch_sub_median(f0_hz):
271
+ """
272
+ f0_hz: (,T)
273
+ """
274
+ f0_cent = get_cents(f0_hz)
275
+ return f0_cent - np.median(f0_cent)
utils/hparam.py ADDED
@@ -0,0 +1,660 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ # This code is modified from https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/training/python/training/hparam.py pylint: disable=line-too-long
7
+ """Hyperparameter values."""
8
+
9
+ from __future__ import absolute_import
10
+ from __future__ import division
11
+ from __future__ import print_function
12
+
13
+ import json
14
+ import numbers
15
+ import re
16
+ import six
17
+
18
+ # Define the regular expression for parsing a single clause of the input
19
+ # (delimited by commas). A legal clause looks like:
20
+ # <variable name>[<index>]? = <rhs>
21
+ # where <rhs> is either a single token or [] enclosed list of tokens.
22
+ # For example: "var[1] = a" or "x = [1,2,3]"
23
+ PARAM_RE = re.compile(
24
+ r"""
25
+ (?P<name>[a-zA-Z][\w\.]*) # variable name: "var" or "x"
26
+ (\[\s*(?P<index>\d+)\s*\])? # (optional) index: "1" or None
27
+ \s*=\s*
28
+ ((?P<val>[^,\[]*) # single value: "a" or None
29
+ |
30
+ \[(?P<vals>[^\]]*)\]) # list of values: None or "1,2,3"
31
+ ($|,\s*)""",
32
+ re.VERBOSE,
33
+ )
34
+
35
+
36
+ def _parse_fail(name, var_type, value, values):
37
+ """Helper function for raising a value error for bad assignment."""
38
+ raise ValueError(
39
+ "Could not parse hparam '%s' of type '%s' with value '%s' in %s"
40
+ % (name, var_type.__name__, value, values)
41
+ )
42
+
43
+
44
+ def _reuse_fail(name, values):
45
+ """Helper function for raising a value error for reuse of name."""
46
+ raise ValueError("Multiple assignments to variable '%s' in %s" % (name, values))
47
+
48
+
49
+ def _process_scalar_value(name, parse_fn, var_type, m_dict, values, results_dictionary):
50
+ """Update results_dictionary with a scalar value.
51
+
52
+ Used to update the results_dictionary to be returned by parse_values when
53
+ encountering a clause with a scalar RHS (e.g. "s=5" or "arr[0]=5".)
54
+
55
+ Mutates results_dictionary.
56
+
57
+ Args:
58
+ name: Name of variable in assignment ("s" or "arr").
59
+ parse_fn: Function for parsing the actual value.
60
+ var_type: Type of named variable.
61
+ m_dict: Dictionary constructed from regex parsing.
62
+ m_dict['val']: RHS value (scalar)
63
+ m_dict['index']: List index value (or None)
64
+ values: Full expression being parsed
65
+ results_dictionary: The dictionary being updated for return by the parsing
66
+ function.
67
+
68
+ Raises:
69
+ ValueError: If the name has already been used.
70
+ """
71
+ try:
72
+ parsed_value = parse_fn(m_dict["val"])
73
+ except ValueError:
74
+ _parse_fail(name, var_type, m_dict["val"], values)
75
+
76
+ # If no index is provided
77
+ if not m_dict["index"]:
78
+ if name in results_dictionary:
79
+ _reuse_fail(name, values)
80
+ results_dictionary[name] = parsed_value
81
+ else:
82
+ if name in results_dictionary:
83
+ # The name has already been used as a scalar, then it
84
+ # will be in this dictionary and map to a non-dictionary.
85
+ if not isinstance(results_dictionary.get(name), dict):
86
+ _reuse_fail(name, values)
87
+ else:
88
+ results_dictionary[name] = {}
89
+
90
+ index = int(m_dict["index"])
91
+ # Make sure the index position hasn't already been assigned a value.
92
+ if index in results_dictionary[name]:
93
+ _reuse_fail("{}[{}]".format(name, index), values)
94
+ results_dictionary[name][index] = parsed_value
95
+
96
+
97
+ def _process_list_value(name, parse_fn, var_type, m_dict, values, results_dictionary):
98
+ """Update results_dictionary from a list of values.
99
+
100
+ Used to update results_dictionary to be returned by parse_values when
101
+ encountering a clause with a list RHS (e.g. "arr=[1,2,3]".)
102
+
103
+ Mutates results_dictionary.
104
+
105
+ Args:
106
+ name: Name of variable in assignment ("arr").
107
+ parse_fn: Function for parsing individual values.
108
+ var_type: Type of named variable.
109
+ m_dict: Dictionary constructed from regex parsing.
110
+ m_dict['val']: RHS value (scalar)
111
+ values: Full expression being parsed
112
+ results_dictionary: The dictionary being updated for return by the parsing
113
+ function.
114
+
115
+ Raises:
116
+ ValueError: If the name has an index or the values cannot be parsed.
117
+ """
118
+ if m_dict["index"] is not None:
119
+ raise ValueError("Assignment of a list to a list index.")
120
+ elements = filter(None, re.split("[ ,]", m_dict["vals"]))
121
+ # Make sure the name hasn't already been assigned a value
122
+ if name in results_dictionary:
123
+ raise _reuse_fail(name, values)
124
+ try:
125
+ results_dictionary[name] = [parse_fn(e) for e in elements]
126
+ except ValueError:
127
+ _parse_fail(name, var_type, m_dict["vals"], values)
128
+
129
+
130
+ def _cast_to_type_if_compatible(name, param_type, value):
131
+ """Cast hparam to the provided type, if compatible.
132
+
133
+ Args:
134
+ name: Name of the hparam to be cast.
135
+ param_type: The type of the hparam.
136
+ value: The value to be cast, if compatible.
137
+
138
+ Returns:
139
+ The result of casting `value` to `param_type`.
140
+
141
+ Raises:
142
+ ValueError: If the type of `value` is not compatible with param_type.
143
+ * If `param_type` is a string type, but `value` is not.
144
+ * If `param_type` is a boolean, but `value` is not, or vice versa.
145
+ * If `param_type` is an integer type, but `value` is not.
146
+ * If `param_type` is a float type, but `value` is not a numeric type.
147
+ """
148
+ fail_msg = "Could not cast hparam '%s' of type '%s' from value %r" % (
149
+ name,
150
+ param_type,
151
+ value,
152
+ )
153
+
154
+ # Some callers use None, for which we can't do any casting/checking. :(
155
+ if issubclass(param_type, type(None)):
156
+ return value
157
+
158
+ # Avoid converting a non-string type to a string.
159
+ if issubclass(param_type, (six.string_types, six.binary_type)) and not isinstance(
160
+ value, (six.string_types, six.binary_type)
161
+ ):
162
+ raise ValueError(fail_msg)
163
+
164
+ # Avoid converting a number or string type to a boolean or vice versa.
165
+ if issubclass(param_type, bool) != isinstance(value, bool):
166
+ raise ValueError(fail_msg)
167
+
168
+ # Avoid converting float to an integer (the reverse is fine).
169
+ if issubclass(param_type, numbers.Integral) and not isinstance(
170
+ value, numbers.Integral
171
+ ):
172
+ raise ValueError(fail_msg)
173
+
174
+ # Avoid converting a non-numeric type to a numeric type.
175
+ if issubclass(param_type, numbers.Number) and not isinstance(value, numbers.Number):
176
+ raise ValueError(fail_msg)
177
+
178
+ return param_type(value)
179
+
180
+
181
+ def parse_values(values, type_map, ignore_unknown=False):
182
+ """Parses hyperparameter values from a string into a python map.
183
+
184
+ `values` is a string containing comma-separated `name=value` pairs.
185
+ For each pair, the value of the hyperparameter named `name` is set to
186
+ `value`.
187
+
188
+ If a hyperparameter name appears multiple times in `values`, a ValueError
189
+ is raised (e.g. 'a=1,a=2', 'a[1]=1,a[1]=2').
190
+
191
+ If a hyperparameter name in both an index assignment and scalar assignment,
192
+ a ValueError is raised. (e.g. 'a=[1,2,3],a[0] = 1').
193
+
194
+ The hyperparameter name may contain '.' symbols, which will result in an
195
+ attribute name that is only accessible through the getattr and setattr
196
+ functions. (And must be first explicit added through add_hparam.)
197
+
198
+ WARNING: Use of '.' in your variable names is allowed, but is not well
199
+ supported and not recommended.
200
+
201
+ The `value` in `name=value` must follows the syntax according to the
202
+ type of the parameter:
203
+
204
+ * Scalar integer: A Python-parsable integer point value. E.g.: 1,
205
+ 100, -12.
206
+ * Scalar float: A Python-parsable floating point value. E.g.: 1.0,
207
+ -.54e89.
208
+ * Boolean: Either true or false.
209
+ * Scalar string: A non-empty sequence of characters, excluding comma,
210
+ spaces, and square brackets. E.g.: foo, bar_1.
211
+ * List: A comma separated list of scalar values of the parameter type
212
+ enclosed in square brackets. E.g.: [1,2,3], [1.0,1e-12], [high,low].
213
+
214
+ When index assignment is used, the corresponding type_map key should be the
215
+ list name. E.g. for "arr[1]=0" the type_map must have the key "arr" (not
216
+ "arr[1]").
217
+
218
+ Args:
219
+ values: String. Comma separated list of `name=value` pairs where
220
+ 'value' must follow the syntax described above.
221
+ type_map: A dictionary mapping hyperparameter names to types. Note every
222
+ parameter name in values must be a key in type_map. The values must
223
+ conform to the types indicated, where a value V is said to conform to a
224
+ type T if either V has type T, or V is a list of elements of type T.
225
+ Hence, for a multidimensional parameter 'x' taking float values,
226
+ 'x=[0.1,0.2]' will parse successfully if type_map['x'] = float.
227
+ ignore_unknown: Bool. Whether values that are missing a type in type_map
228
+ should be ignored. If set to True, a ValueError will not be raised for
229
+ unknown hyperparameter type.
230
+
231
+ Returns:
232
+ A python map mapping each name to either:
233
+ * A scalar value.
234
+ * A list of scalar values.
235
+ * A dictionary mapping index numbers to scalar values.
236
+ (e.g. "x=5,L=[1,2],arr[1]=3" results in {'x':5,'L':[1,2],'arr':{1:3}}")
237
+
238
+ Raises:
239
+ ValueError: If there is a problem with input.
240
+ * If `values` cannot be parsed.
241
+ * If a list is assigned to a list index (e.g. 'a[1] = [1,2,3]').
242
+ * If the same rvalue is assigned two different values (e.g. 'a=1,a=2',
243
+ 'a[1]=1,a[1]=2', or 'a=1,a=[1]')
244
+ """
245
+ results_dictionary = {}
246
+ pos = 0
247
+ while pos < len(values):
248
+ m = PARAM_RE.match(values, pos)
249
+ if not m:
250
+ raise ValueError("Malformed hyperparameter value: %s" % values[pos:])
251
+ # Check that there is a comma between parameters and move past it.
252
+ pos = m.end()
253
+ # Parse the values.
254
+ m_dict = m.groupdict()
255
+ name = m_dict["name"]
256
+ if name not in type_map:
257
+ if ignore_unknown:
258
+ continue
259
+ raise ValueError("Unknown hyperparameter type for %s" % name)
260
+ type_ = type_map[name]
261
+
262
+ # Set up correct parsing function (depending on whether type_ is a bool)
263
+ if type_ == bool:
264
+
265
+ def parse_bool(value):
266
+ if value in ["true", "True"]:
267
+ return True
268
+ elif value in ["false", "False"]:
269
+ return False
270
+ else:
271
+ try:
272
+ return bool(int(value))
273
+ except ValueError:
274
+ _parse_fail(name, type_, value, values)
275
+
276
+ parse = parse_bool
277
+ else:
278
+ parse = type_
279
+
280
+ # If a singe value is provided
281
+ if m_dict["val"] is not None:
282
+ _process_scalar_value(
283
+ name, parse, type_, m_dict, values, results_dictionary
284
+ )
285
+
286
+ # If the assigned value is a list:
287
+ elif m_dict["vals"] is not None:
288
+ _process_list_value(name, parse, type_, m_dict, values, results_dictionary)
289
+
290
+ else: # Not assigned a list or value
291
+ _parse_fail(name, type_, "", values)
292
+
293
+ return results_dictionary
294
+
295
+
296
+ class HParams(object):
297
+ """Class to hold a set of hyperparameters as name-value pairs.
298
+
299
+ A `HParams` object holds hyperparameters used to build and train a model,
300
+ such as the number of hidden units in a neural net layer or the learning rate
301
+ to use when training.
302
+
303
+ You first create a `HParams` object by specifying the names and values of the
304
+ hyperparameters.
305
+
306
+ To make them easily accessible the parameter names are added as direct
307
+ attributes of the class. A typical usage is as follows:
308
+
309
+ ```python
310
+ # Create a HParams object specifying names and values of the model
311
+ # hyperparameters:
312
+ hparams = HParams(learning_rate=0.1, num_hidden_units=100)
313
+
314
+ # The hyperparameter are available as attributes of the HParams object:
315
+ hparams.learning_rate ==> 0.1
316
+ hparams.num_hidden_units ==> 100
317
+ ```
318
+
319
+ Hyperparameters have type, which is inferred from the type of their value
320
+ passed at construction type. The currently supported types are: integer,
321
+ float, boolean, string, and list of integer, float, boolean, or string.
322
+
323
+ You can override hyperparameter values by calling the
324
+ [`parse()`](#HParams.parse) method, passing a string of comma separated
325
+ `name=value` pairs. This is intended to make it possible to override
326
+ any hyperparameter values from a single command-line flag to which
327
+ the user passes 'hyper-param=value' pairs. It avoids having to define
328
+ one flag for each hyperparameter.
329
+
330
+ The syntax expected for each value depends on the type of the parameter.
331
+ See `parse()` for a description of the syntax.
332
+
333
+ Example:
334
+
335
+ ```python
336
+ # Define a command line flag to pass name=value pairs.
337
+ # For example using argparse:
338
+ import argparse
339
+ parser = argparse.ArgumentParser(description='Train my model.')
340
+ parser.add_argument('--hparams', type=str,
341
+ help='Comma separated list of "name=value" pairs.')
342
+ args = parser.parse_args()
343
+ ...
344
+ def my_program():
345
+ # Create a HParams object specifying the names and values of the
346
+ # model hyperparameters:
347
+ hparams = tf.HParams(learning_rate=0.1, num_hidden_units=100,
348
+ activations=['relu', 'tanh'])
349
+
350
+ # Override hyperparameters values by parsing the command line
351
+ hparams.parse(args.hparams)
352
+
353
+ # If the user passed `--hparams=learning_rate=0.3` on the command line
354
+ # then 'hparams' has the following attributes:
355
+ hparams.learning_rate ==> 0.3
356
+ hparams.num_hidden_units ==> 100
357
+ hparams.activations ==> ['relu', 'tanh']
358
+
359
+ # If the hyperparameters are in json format use parse_json:
360
+ hparams.parse_json('{"learning_rate": 0.3, "activations": "relu"}')
361
+ ```
362
+ """
363
+
364
+ _HAS_DYNAMIC_ATTRIBUTES = True # Required for pytype checks.
365
+
366
+ def __init__(self, model_structure=None, **kwargs):
367
+ """Create an instance of `HParams` from keyword arguments.
368
+
369
+ The keyword arguments specify name-values pairs for the hyperparameters.
370
+ The parameter types are inferred from the type of the values passed.
371
+
372
+ The parameter names are added as attributes of `HParams` object, so they
373
+ can be accessed directly with the dot notation `hparams._name_`.
374
+
375
+ Example:
376
+
377
+ ```python
378
+ # Define 3 hyperparameters: 'learning_rate' is a float parameter,
379
+ # 'num_hidden_units' an integer parameter, and 'activation' a string
380
+ # parameter.
381
+ hparams = tf.HParams(
382
+ learning_rate=0.1, num_hidden_units=100, activation='relu')
383
+
384
+ hparams.activation ==> 'relu'
385
+ ```
386
+
387
+ Note that a few names are reserved and cannot be used as hyperparameter
388
+ names. If you use one of the reserved name the constructor raises a
389
+ `ValueError`.
390
+
391
+ Args:
392
+ model_structure: An instance of ModelStructure, defining the feature
393
+ crosses to be used in the Trial.
394
+ **kwargs: Key-value pairs where the key is the hyperparameter name and
395
+ the value is the value for the parameter.
396
+
397
+ Raises:
398
+ ValueError: If both `hparam_def` and initialization values are provided,
399
+ or if one of the arguments is invalid.
400
+
401
+ """
402
+ # Register the hyperparameters and their type in _hparam_types.
403
+ # This simplifies the implementation of parse().
404
+ # _hparam_types maps the parameter name to a tuple (type, bool).
405
+ # The type value is the type of the parameter for scalar hyperparameters,
406
+ # or the type of the list elements for multidimensional hyperparameters.
407
+ # The bool value is True if the value is a list, False otherwise.
408
+ self._hparam_types = {}
409
+ self._model_structure = model_structure
410
+ for name, value in six.iteritems(kwargs):
411
+ self.add_hparam(name, value)
412
+
413
+ def add_hparam(self, name, value):
414
+ """Adds {name, value} pair to hyperparameters.
415
+
416
+ Args:
417
+ name: Name of the hyperparameter.
418
+ value: Value of the hyperparameter. Can be one of the following types:
419
+ int, float, string, int list, float list, or string list.
420
+
421
+ Raises:
422
+ ValueError: if one of the arguments is invalid.
423
+ """
424
+ # Keys in kwargs are unique, but 'name' could the name of a pre-existing
425
+ # attribute of this object. In that case we refuse to use it as a
426
+ # hyperparameter name.
427
+ if getattr(self, name, None) is not None:
428
+ raise ValueError("Hyperparameter name is reserved: %s" % name)
429
+ if isinstance(value, (list, tuple)):
430
+ if not value:
431
+ raise ValueError(
432
+ "Multi-valued hyperparameters cannot be empty: %s" % name
433
+ )
434
+ self._hparam_types[name] = (type(value[0]), True)
435
+ else:
436
+ self._hparam_types[name] = (type(value), False)
437
+ setattr(self, name, value)
438
+
439
+ def set_hparam(self, name, value):
440
+ """Set the value of an existing hyperparameter.
441
+
442
+ This function verifies that the type of the value matches the type of the
443
+ existing hyperparameter.
444
+
445
+ Args:
446
+ name: Name of the hyperparameter.
447
+ value: New value of the hyperparameter.
448
+
449
+ Raises:
450
+ KeyError: If the hyperparameter doesn't exist.
451
+ ValueError: If there is a type mismatch.
452
+ """
453
+ param_type, is_list = self._hparam_types[name]
454
+ if isinstance(value, list):
455
+ if not is_list:
456
+ raise ValueError(
457
+ "Must not pass a list for single-valued parameter: %s" % name
458
+ )
459
+ setattr(
460
+ self,
461
+ name,
462
+ [_cast_to_type_if_compatible(name, param_type, v) for v in value],
463
+ )
464
+ else:
465
+ if is_list:
466
+ raise ValueError(
467
+ "Must pass a list for multi-valued parameter: %s." % name
468
+ )
469
+ setattr(self, name, _cast_to_type_if_compatible(name, param_type, value))
470
+
471
+ def del_hparam(self, name):
472
+ """Removes the hyperparameter with key 'name'.
473
+
474
+ Does nothing if it isn't present.
475
+
476
+ Args:
477
+ name: Name of the hyperparameter.
478
+ """
479
+ if hasattr(self, name):
480
+ delattr(self, name)
481
+ del self._hparam_types[name]
482
+
483
+ def parse(self, values):
484
+ """Override existing hyperparameter values, parsing new values from a string.
485
+
486
+ See parse_values for more detail on the allowed format for values.
487
+
488
+ Args:
489
+ values: String. Comma separated list of `name=value` pairs where 'value'
490
+ must follow the syntax described above.
491
+
492
+ Returns:
493
+ The `HParams` instance.
494
+
495
+ Raises:
496
+ ValueError: If `values` cannot be parsed or a hyperparameter in `values`
497
+ doesn't exist.
498
+ """
499
+ type_map = {}
500
+ for name, t in self._hparam_types.items():
501
+ param_type, _ = t
502
+ type_map[name] = param_type
503
+
504
+ values_map = parse_values(values, type_map)
505
+ return self.override_from_dict(values_map)
506
+
507
+ def override_from_dict(self, values_dict):
508
+ """Override existing hyperparameter values, parsing new values from a dictionary.
509
+
510
+ Args:
511
+ values_dict: Dictionary of name:value pairs.
512
+
513
+ Returns:
514
+ The `HParams` instance.
515
+
516
+ Raises:
517
+ KeyError: If a hyperparameter in `values_dict` doesn't exist.
518
+ ValueError: If `values_dict` cannot be parsed.
519
+ """
520
+ for name, value in values_dict.items():
521
+ self.set_hparam(name, value)
522
+ return self
523
+
524
+ def set_model_structure(self, model_structure):
525
+ self._model_structure = model_structure
526
+
527
+ def get_model_structure(self):
528
+ return self._model_structure
529
+
530
+ def to_json(self, indent=None, separators=None, sort_keys=False):
531
+ """Serializes the hyperparameters into JSON.
532
+
533
+ Args:
534
+ indent: If a non-negative integer, JSON array elements and object members
535
+ will be pretty-printed with that indent level. An indent level of 0, or
536
+ negative, will only insert newlines. `None` (the default) selects the
537
+ most compact representation.
538
+ separators: Optional `(item_separator, key_separator)` tuple. Default is
539
+ `(', ', ': ')`.
540
+ sort_keys: If `True`, the output dictionaries will be sorted by key.
541
+
542
+ Returns:
543
+ A JSON string.
544
+ """
545
+
546
+ def remove_callables(x):
547
+ """Omit callable elements from input with arbitrary nesting."""
548
+ if isinstance(x, dict):
549
+ return {
550
+ k: remove_callables(v)
551
+ for k, v in six.iteritems(x)
552
+ if not callable(v)
553
+ }
554
+ elif isinstance(x, list):
555
+ return [remove_callables(i) for i in x if not callable(i)]
556
+ return x
557
+
558
+ return json.dumps(
559
+ remove_callables(self.values()),
560
+ indent=indent,
561
+ separators=separators,
562
+ sort_keys=sort_keys,
563
+ )
564
+
565
+ def parse_json(self, values_json):
566
+ """Override existing hyperparameter values, parsing new values from a json object.
567
+
568
+ Args:
569
+ values_json: String containing a json object of name:value pairs.
570
+
571
+ Returns:
572
+ The `HParams` instance.
573
+
574
+ Raises:
575
+ KeyError: If a hyperparameter in `values_json` doesn't exist.
576
+ ValueError: If `values_json` cannot be parsed.
577
+ """
578
+ values_map = json.loads(values_json)
579
+ return self.override_from_dict(values_map)
580
+
581
+ def values(self):
582
+ """Return the hyperparameter values as a Python dictionary.
583
+
584
+ Returns:
585
+ A dictionary with hyperparameter names as keys. The values are the
586
+ hyperparameter values.
587
+ """
588
+ return {n: getattr(self, n) for n in self._hparam_types.keys()}
589
+
590
+ def get(self, key, default=None):
591
+ """Returns the value of `key` if it exists, else `default`."""
592
+ if key in self._hparam_types:
593
+ # Ensure that default is compatible with the parameter type.
594
+ if default is not None:
595
+ param_type, is_param_list = self._hparam_types[key]
596
+ type_str = "list<%s>" % param_type if is_param_list else str(param_type)
597
+ fail_msg = (
598
+ "Hparam '%s' of type '%s' is incompatible with "
599
+ "default=%s" % (key, type_str, default)
600
+ )
601
+
602
+ is_default_list = isinstance(default, list)
603
+ if is_param_list != is_default_list:
604
+ raise ValueError(fail_msg)
605
+
606
+ try:
607
+ if is_default_list:
608
+ for value in default:
609
+ _cast_to_type_if_compatible(key, param_type, value)
610
+ else:
611
+ _cast_to_type_if_compatible(key, param_type, default)
612
+ except ValueError as e:
613
+ raise ValueError("%s. %s" % (fail_msg, e))
614
+
615
+ return getattr(self, key)
616
+
617
+ return default
618
+
619
+ def __contains__(self, key):
620
+ return key in self._hparam_types
621
+
622
+ def __str__(self):
623
+ return str(sorted(self.values().items()))
624
+
625
+ def __repr__(self):
626
+ return "%s(%s)" % (type(self).__name__, self.__str__())
627
+
628
+ @staticmethod
629
+ def _get_kind_name(param_type, is_list):
630
+ """Returns the field name given parameter type and is_list.
631
+
632
+ Args:
633
+ param_type: Data type of the hparam.
634
+ is_list: Whether this is a list.
635
+
636
+ Returns:
637
+ A string representation of the field name.
638
+
639
+ Raises:
640
+ ValueError: If parameter type is not recognized.
641
+ """
642
+ if issubclass(param_type, bool):
643
+ # This check must happen before issubclass(param_type, six.integer_types),
644
+ # since Python considers bool to be a subclass of int.
645
+ typename = "bool"
646
+ elif issubclass(param_type, six.integer_types):
647
+ # Setting 'int' and 'long' types to be 'int64' to ensure the type is
648
+ # compatible with both Python2 and Python3.
649
+ typename = "int64"
650
+ elif issubclass(param_type, (six.string_types, six.binary_type)):
651
+ # Setting 'string' and 'bytes' types to be 'bytes' to ensure the type is
652
+ # compatible with both Python2 and Python3.
653
+ typename = "bytes"
654
+ elif issubclass(param_type, float):
655
+ typename = "float"
656
+ else:
657
+ raise ValueError("Unsupported parameter type: %s" % str(param_type))
658
+
659
+ suffix = "list" if is_list else "value"
660
+ return "_".join([typename, suffix])
utils/util.py ADDED
@@ -0,0 +1,689 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 Amphion.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+
7
+ import collections
8
+ import glob
9
+ import os
10
+ import random
11
+ import time
12
+ import argparse
13
+ from collections import OrderedDict
14
+
15
+ import json5
16
+ import numpy as np
17
+ import glob
18
+ from torch.nn import functional as F
19
+
20
+ try:
21
+ from ruamel.yaml import YAML as yaml
22
+ except:
23
+ from ruamel_yaml import YAML as yaml
24
+
25
+ import torch
26
+
27
+ from utils.hparam import HParams
28
+ import logging
29
+ from logging import handlers
30
+
31
+
32
+ def str2bool(v):
33
+ """Used in argparse.ArgumentParser.add_argument to indicate
34
+ that a type is a bool type and user can enter
35
+
36
+ - yes, true, t, y, 1, to represent True
37
+ - no, false, f, n, 0, to represent False
38
+
39
+ See https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse # noqa
40
+ """
41
+ if isinstance(v, bool):
42
+ return v
43
+ if v.lower() in ("yes", "true", "t", "y", "1"):
44
+ return True
45
+ elif v.lower() in ("no", "false", "f", "n", "0"):
46
+ return False
47
+ else:
48
+ raise argparse.ArgumentTypeError("Boolean value expected.")
49
+
50
+
51
+ def find_checkpoint_of_mapper(mapper_ckpt_dir):
52
+ mapper_ckpts = glob.glob(os.path.join(mapper_ckpt_dir, "ckpts/*.pt"))
53
+
54
+ # Select the max steps
55
+ mapper_ckpts.sort()
56
+ mapper_weights_file = mapper_ckpts[-1]
57
+ return mapper_weights_file
58
+
59
+
60
+ def pad_f0_to_tensors(f0s, batched=None):
61
+ # Initialize
62
+ tensors = []
63
+
64
+ if batched == None:
65
+ # Get the max frame for padding
66
+ size = -1
67
+ for f0 in f0s:
68
+ size = max(size, f0.shape[-1])
69
+
70
+ tensor = torch.zeros(len(f0s), size)
71
+
72
+ for i, f0 in enumerate(f0s):
73
+ tensor[i, : f0.shape[-1]] = f0[:]
74
+
75
+ tensors.append(tensor)
76
+ else:
77
+ start = 0
78
+ while start + batched - 1 < len(f0s):
79
+ end = start + batched - 1
80
+
81
+ # Get the max frame for padding
82
+ size = -1
83
+ for i in range(start, end + 1):
84
+ size = max(size, f0s[i].shape[-1])
85
+
86
+ tensor = torch.zeros(batched, size)
87
+
88
+ for i in range(start, end + 1):
89
+ tensor[i - start, : f0s[i].shape[-1]] = f0s[i][:]
90
+
91
+ tensors.append(tensor)
92
+
93
+ start = start + batched
94
+
95
+ if start != len(f0s):
96
+ end = len(f0s)
97
+
98
+ # Get the max frame for padding
99
+ size = -1
100
+ for i in range(start, end):
101
+ size = max(size, f0s[i].shape[-1])
102
+
103
+ tensor = torch.zeros(len(f0s) - start, size)
104
+
105
+ for i in range(start, end):
106
+ tensor[i - start, : f0s[i].shape[-1]] = f0s[i][:]
107
+
108
+ tensors.append(tensor)
109
+
110
+ return tensors
111
+
112
+
113
+ def pad_mels_to_tensors(mels, batched=None):
114
+ """
115
+ Args:
116
+ mels: A list of mel-specs
117
+ Returns:
118
+ tensors: A list of tensors containing the batched mel-specs
119
+ mel_frames: A list of tensors containing the frames of the original mel-specs
120
+ """
121
+ # Initialize
122
+ tensors = []
123
+ mel_frames = []
124
+
125
+ # Split mel-specs into batches to avoid cuda memory exceed
126
+ if batched == None:
127
+ # Get the max frame for padding
128
+ size = -1
129
+ for mel in mels:
130
+ size = max(size, mel.shape[-1])
131
+
132
+ tensor = torch.zeros(len(mels), mels[0].shape[0], size)
133
+ mel_frame = torch.zeros(len(mels), dtype=torch.int32)
134
+
135
+ for i, mel in enumerate(mels):
136
+ tensor[i, :, : mel.shape[-1]] = mel[:]
137
+ mel_frame[i] = mel.shape[-1]
138
+
139
+ tensors.append(tensor)
140
+ mel_frames.append(mel_frame)
141
+ else:
142
+ start = 0
143
+ while start + batched - 1 < len(mels):
144
+ end = start + batched - 1
145
+
146
+ # Get the max frame for padding
147
+ size = -1
148
+ for i in range(start, end + 1):
149
+ size = max(size, mels[i].shape[-1])
150
+
151
+ tensor = torch.zeros(batched, mels[0].shape[0], size)
152
+ mel_frame = torch.zeros(batched, dtype=torch.int32)
153
+
154
+ for i in range(start, end + 1):
155
+ tensor[i - start, :, : mels[i].shape[-1]] = mels[i][:]
156
+ mel_frame[i - start] = mels[i].shape[-1]
157
+
158
+ tensors.append(tensor)
159
+ mel_frames.append(mel_frame)
160
+
161
+ start = start + batched
162
+
163
+ if start != len(mels):
164
+ end = len(mels)
165
+
166
+ # Get the max frame for padding
167
+ size = -1
168
+ for i in range(start, end):
169
+ size = max(size, mels[i].shape[-1])
170
+
171
+ tensor = torch.zeros(len(mels) - start, mels[0].shape[0], size)
172
+ mel_frame = torch.zeros(len(mels) - start, dtype=torch.int32)
173
+
174
+ for i in range(start, end):
175
+ tensor[i - start, :, : mels[i].shape[-1]] = mels[i][:]
176
+ mel_frame[i - start] = mels[i].shape[-1]
177
+
178
+ tensors.append(tensor)
179
+ mel_frames.append(mel_frame)
180
+
181
+ return tensors, mel_frames
182
+
183
+
184
+ def load_model_config(args):
185
+ """Load model configurations (in args.json under checkpoint directory)
186
+
187
+ Args:
188
+ args (ArgumentParser): arguments to run bins/preprocess.py
189
+
190
+ Returns:
191
+ dict: dictionary that stores model configurations
192
+ """
193
+ if args.checkpoint_dir is None:
194
+ assert args.checkpoint_file is not None
195
+ checkpoint_dir = os.path.split(args.checkpoint_file)[0]
196
+ else:
197
+ checkpoint_dir = args.checkpoint_dir
198
+ config_path = os.path.join(checkpoint_dir, "args.json")
199
+ print("config_path: ", config_path)
200
+
201
+ config = load_config(config_path)
202
+ return config
203
+
204
+
205
+ def remove_and_create(dir):
206
+ if os.path.exists(dir):
207
+ os.system("rm -r {}".format(dir))
208
+ os.makedirs(dir, exist_ok=True)
209
+
210
+
211
+ def has_existed(path, warning=False):
212
+ if not warning:
213
+ return os.path.exists(path)
214
+
215
+ if os.path.exists(path):
216
+ answer = input(
217
+ "The path {} has existed. \nInput 'y' (or hit Enter) to skip it, and input 'n' to re-write it [y/n]\n".format(
218
+ path
219
+ )
220
+ )
221
+ if not answer == "n":
222
+ return True
223
+
224
+ return False
225
+
226
+
227
+ def remove_older_ckpt(saved_model_name, checkpoint_dir, max_to_keep=5):
228
+ if os.path.exists(os.path.join(checkpoint_dir, "checkpoint")):
229
+ with open(os.path.join(checkpoint_dir, "checkpoint"), "r") as f:
230
+ ckpts = [x.strip() for x in f.readlines()]
231
+ else:
232
+ ckpts = []
233
+ ckpts.append(saved_model_name)
234
+ for item in ckpts[:-max_to_keep]:
235
+ if os.path.exists(os.path.join(checkpoint_dir, item)):
236
+ os.remove(os.path.join(checkpoint_dir, item))
237
+ with open(os.path.join(checkpoint_dir, "checkpoint"), "w") as f:
238
+ for item in ckpts[-max_to_keep:]:
239
+ f.write("{}\n".format(item))
240
+
241
+
242
+ def set_all_random_seed(seed: int):
243
+ random.seed(seed)
244
+ np.random.seed(seed)
245
+ torch.random.manual_seed(seed)
246
+
247
+
248
+ def save_checkpoint(
249
+ args,
250
+ generator,
251
+ g_optimizer,
252
+ step,
253
+ discriminator=None,
254
+ d_optimizer=None,
255
+ max_to_keep=5,
256
+ ):
257
+ saved_model_name = "model.ckpt-{}.pt".format(step)
258
+ checkpoint_path = os.path.join(args.checkpoint_dir, saved_model_name)
259
+
260
+ if discriminator and d_optimizer:
261
+ torch.save(
262
+ {
263
+ "generator": generator.state_dict(),
264
+ "discriminator": discriminator.state_dict(),
265
+ "g_optimizer": g_optimizer.state_dict(),
266
+ "d_optimizer": d_optimizer.state_dict(),
267
+ "global_step": step,
268
+ },
269
+ checkpoint_path,
270
+ )
271
+ else:
272
+ torch.save(
273
+ {
274
+ "generator": generator.state_dict(),
275
+ "g_optimizer": g_optimizer.state_dict(),
276
+ "global_step": step,
277
+ },
278
+ checkpoint_path,
279
+ )
280
+
281
+ print("Saved checkpoint: {}".format(checkpoint_path))
282
+
283
+ if os.path.exists(os.path.join(args.checkpoint_dir, "checkpoint")):
284
+ with open(os.path.join(args.checkpoint_dir, "checkpoint"), "r") as f:
285
+ ckpts = [x.strip() for x in f.readlines()]
286
+ else:
287
+ ckpts = []
288
+ ckpts.append(saved_model_name)
289
+ for item in ckpts[:-max_to_keep]:
290
+ if os.path.exists(os.path.join(args.checkpoint_dir, item)):
291
+ os.remove(os.path.join(args.checkpoint_dir, item))
292
+ with open(os.path.join(args.checkpoint_dir, "checkpoint"), "w") as f:
293
+ for item in ckpts[-max_to_keep:]:
294
+ f.write("{}\n".format(item))
295
+
296
+
297
+ def attempt_to_restore(
298
+ generator, g_optimizer, checkpoint_dir, discriminator=None, d_optimizer=None
299
+ ):
300
+ checkpoint_list = os.path.join(checkpoint_dir, "checkpoint")
301
+ if os.path.exists(checkpoint_list):
302
+ checkpoint_filename = open(checkpoint_list).readlines()[-1].strip()
303
+ checkpoint_path = os.path.join(checkpoint_dir, "{}".format(checkpoint_filename))
304
+ print("Restore from {}".format(checkpoint_path))
305
+ checkpoint = torch.load(checkpoint_path, map_location="cpu")
306
+ if generator:
307
+ if not list(generator.state_dict().keys())[0].startswith("module."):
308
+ raw_dict = checkpoint["generator"]
309
+ clean_dict = OrderedDict()
310
+ for k, v in raw_dict.items():
311
+ if k.startswith("module."):
312
+ clean_dict[k[7:]] = v
313
+ else:
314
+ clean_dict[k] = v
315
+ generator.load_state_dict(clean_dict)
316
+ else:
317
+ generator.load_state_dict(checkpoint["generator"])
318
+ if g_optimizer:
319
+ g_optimizer.load_state_dict(checkpoint["g_optimizer"])
320
+ global_step = 100000
321
+ if discriminator and "discriminator" in checkpoint.keys():
322
+ discriminator.load_state_dict(checkpoint["discriminator"])
323
+ global_step = checkpoint["global_step"]
324
+ print("restore discriminator")
325
+ if d_optimizer and "d_optimizer" in checkpoint.keys():
326
+ d_optimizer.load_state_dict(checkpoint["d_optimizer"])
327
+ print("restore d_optimizer...")
328
+ else:
329
+ global_step = 0
330
+ return global_step
331
+
332
+
333
+ class ExponentialMovingAverage(object):
334
+ def __init__(self, decay):
335
+ self.decay = decay
336
+ self.shadow = {}
337
+
338
+ def register(self, name, val):
339
+ self.shadow[name] = val.clone()
340
+
341
+ def update(self, name, x):
342
+ assert name in self.shadow
343
+ update_delta = self.shadow[name] - x
344
+ self.shadow[name] -= (1.0 - self.decay) * update_delta
345
+
346
+
347
+ def apply_moving_average(model, ema):
348
+ for name, param in model.named_parameters():
349
+ if name in ema.shadow:
350
+ ema.update(name, param.data)
351
+
352
+
353
+ def register_model_to_ema(model, ema):
354
+ for name, param in model.named_parameters():
355
+ if param.requires_grad:
356
+ ema.register(name, param.data)
357
+
358
+
359
+ class YParams(HParams):
360
+ def __init__(self, yaml_file):
361
+ if not os.path.exists(yaml_file):
362
+ raise IOError("yaml file: {} is not existed".format(yaml_file))
363
+ super().__init__()
364
+ self.d = collections.OrderedDict()
365
+ with open(yaml_file) as fp:
366
+ for _, v in yaml().load(fp).items():
367
+ for k1, v1 in v.items():
368
+ try:
369
+ if self.get(k1):
370
+ self.set_hparam(k1, v1)
371
+ else:
372
+ self.add_hparam(k1, v1)
373
+ self.d[k1] = v1
374
+ except Exception:
375
+ import traceback
376
+
377
+ print(traceback.format_exc())
378
+
379
+ # @property
380
+ def get_elements(self):
381
+ return self.d.items()
382
+
383
+
384
+ def override_config(base_config, new_config):
385
+ """Update new configurations in the original dict with the new dict
386
+
387
+ Args:
388
+ base_config (dict): original dict to be overridden
389
+ new_config (dict): dict with new configurations
390
+
391
+ Returns:
392
+ dict: updated configuration dict
393
+ """
394
+ for k, v in new_config.items():
395
+ if type(v) == dict:
396
+ if k not in base_config.keys():
397
+ base_config[k] = {}
398
+ base_config[k] = override_config(base_config[k], v)
399
+ else:
400
+ base_config[k] = v
401
+ return base_config
402
+
403
+
404
+ def get_lowercase_keys_config(cfg):
405
+ """Change all keys in cfg to lower case
406
+
407
+ Args:
408
+ cfg (dict): dictionary that stores configurations
409
+
410
+ Returns:
411
+ dict: dictionary that stores configurations
412
+ """
413
+ updated_cfg = dict()
414
+ for k, v in cfg.items():
415
+ if type(v) == dict:
416
+ v = get_lowercase_keys_config(v)
417
+ updated_cfg[k.lower()] = v
418
+ return updated_cfg
419
+
420
+
421
+ def _load_config(config_fn, lowercase=False):
422
+ """Load configurations into a dictionary
423
+
424
+ Args:
425
+ config_fn (str): path to configuration file
426
+ lowercase (bool, optional): whether changing keys to lower case. Defaults to False.
427
+
428
+ Returns:
429
+ dict: dictionary that stores configurations
430
+ """
431
+ with open(config_fn, "r") as f:
432
+ data = f.read()
433
+ config_ = json5.loads(data)
434
+ if "base_config" in config_:
435
+ # load configurations from new path
436
+ try:
437
+ p_config_path = os.path.join(os.getenv("WORK_DIR"), config_["base_config"])
438
+ except:
439
+ p_config_path = config_["base_config"]
440
+ p_config_ = _load_config(p_config_path)
441
+ config_ = override_config(p_config_, config_)
442
+ if lowercase:
443
+ # change keys in config_ to lower case
444
+ config_ = get_lowercase_keys_config(config_)
445
+ return config_
446
+
447
+
448
+ def load_config(config_fn, lowercase=False):
449
+ """Load configurations into a dictionary
450
+
451
+ Args:
452
+ config_fn (str): path to configuration file
453
+ lowercase (bool, optional): _description_. Defaults to False.
454
+
455
+ Returns:
456
+ JsonHParams: an object that stores configurations
457
+ """
458
+ config_ = _load_config(config_fn, lowercase=lowercase)
459
+ # create an JsonHParams object with configuration dict
460
+ cfg = JsonHParams(**config_)
461
+ return cfg
462
+
463
+
464
+ def save_config(save_path, cfg):
465
+ """Save configurations into a json file
466
+
467
+ Args:
468
+ save_path (str): path to save configurations
469
+ cfg (dict): dictionary that stores configurations
470
+ """
471
+ with open(save_path, "w") as f:
472
+ json5.dump(
473
+ cfg, f, ensure_ascii=False, indent=4, quote_keys=True, sort_keys=True
474
+ )
475
+
476
+
477
+ class JsonHParams:
478
+ def __init__(self, **kwargs):
479
+ for k, v in kwargs.items():
480
+ if type(v) == dict:
481
+ v = JsonHParams(**v)
482
+ self[k] = v
483
+
484
+ def keys(self):
485
+ return self.__dict__.keys()
486
+
487
+ def items(self):
488
+ return self.__dict__.items()
489
+
490
+ def values(self):
491
+ return self.__dict__.values()
492
+
493
+ def __len__(self):
494
+ return len(self.__dict__)
495
+
496
+ def __getitem__(self, key):
497
+ return getattr(self, key)
498
+
499
+ def __setitem__(self, key, value):
500
+ return setattr(self, key, value)
501
+
502
+ def __contains__(self, key):
503
+ return key in self.__dict__
504
+
505
+ def __repr__(self):
506
+ return self.__dict__.__repr__()
507
+
508
+
509
+ class ValueWindow:
510
+ def __init__(self, window_size=100):
511
+ self._window_size = window_size
512
+ self._values = []
513
+
514
+ def append(self, x):
515
+ self._values = self._values[-(self._window_size - 1) :] + [x]
516
+
517
+ @property
518
+ def sum(self):
519
+ return sum(self._values)
520
+
521
+ @property
522
+ def count(self):
523
+ return len(self._values)
524
+
525
+ @property
526
+ def average(self):
527
+ return self.sum / max(1, self.count)
528
+
529
+ def reset(self):
530
+ self._values = []
531
+
532
+
533
+ class Logger(object):
534
+ def __init__(
535
+ self,
536
+ filename,
537
+ level="info",
538
+ when="D",
539
+ backCount=10,
540
+ fmt="%(asctime)s : %(message)s",
541
+ ):
542
+ self.level_relations = {
543
+ "debug": logging.DEBUG,
544
+ "info": logging.INFO,
545
+ "warning": logging.WARNING,
546
+ "error": logging.ERROR,
547
+ "crit": logging.CRITICAL,
548
+ }
549
+ if level == "debug":
550
+ fmt = "%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s"
551
+ self.logger = logging.getLogger(filename)
552
+ format_str = logging.Formatter(fmt)
553
+ self.logger.setLevel(self.level_relations.get(level))
554
+ sh = logging.StreamHandler()
555
+ sh.setFormatter(format_str)
556
+ th = handlers.TimedRotatingFileHandler(
557
+ filename=filename, when=when, backupCount=backCount, encoding="utf-8"
558
+ )
559
+ th.setFormatter(format_str)
560
+ self.logger.addHandler(sh)
561
+ self.logger.addHandler(th)
562
+ self.logger.info(
563
+ "==========================New Starting Here=============================="
564
+ )
565
+
566
+
567
+ def init_weights(m, mean=0.0, std=0.01):
568
+ classname = m.__class__.__name__
569
+ if classname.find("Conv") != -1:
570
+ m.weight.data.normal_(mean, std)
571
+
572
+
573
+ def get_padding(kernel_size, dilation=1):
574
+ return int((kernel_size * dilation - dilation) / 2)
575
+
576
+
577
+ def slice_segments(x, ids_str, segment_size=4):
578
+ ret = torch.zeros_like(x[:, :, :segment_size])
579
+ for i in range(x.size(0)):
580
+ idx_str = ids_str[i]
581
+ idx_end = idx_str + segment_size
582
+ ret[i] = x[i, :, idx_str:idx_end]
583
+ return ret
584
+
585
+
586
+ def rand_slice_segments(x, x_lengths=None, segment_size=4):
587
+ b, d, t = x.size()
588
+ if x_lengths is None:
589
+ x_lengths = t
590
+ ids_str_max = x_lengths - segment_size + 1
591
+ ids_str = (torch.rand([b]).to(device=x.device) * ids_str_max).to(dtype=torch.long)
592
+ ret = slice_segments(x, ids_str, segment_size)
593
+ return ret, ids_str
594
+
595
+
596
+ def subsequent_mask(length):
597
+ mask = torch.tril(torch.ones(length, length)).unsqueeze(0).unsqueeze(0)
598
+ return mask
599
+
600
+
601
+ @torch.jit.script
602
+ def fused_add_tanh_sigmoid_multiply(input_a, input_b, n_channels):
603
+ n_channels_int = n_channels[0]
604
+ in_act = input_a + input_b
605
+ t_act = torch.tanh(in_act[:, :n_channels_int, :])
606
+ s_act = torch.sigmoid(in_act[:, n_channels_int:, :])
607
+ acts = t_act * s_act
608
+ return acts
609
+
610
+
611
+ def convert_pad_shape(pad_shape):
612
+ l = pad_shape[::-1]
613
+ pad_shape = [item for sublist in l for item in sublist]
614
+ return pad_shape
615
+
616
+
617
+ def sequence_mask(length, max_length=None):
618
+ if max_length is None:
619
+ max_length = length.max()
620
+ x = torch.arange(max_length, dtype=length.dtype, device=length.device)
621
+ return x.unsqueeze(0) < length.unsqueeze(1)
622
+
623
+
624
+ def generate_path(duration, mask):
625
+ """
626
+ duration: [b, 1, t_x]
627
+ mask: [b, 1, t_y, t_x]
628
+ """
629
+ device = duration.device
630
+
631
+ b, _, t_y, t_x = mask.shape
632
+ cum_duration = torch.cumsum(duration, -1)
633
+
634
+ cum_duration_flat = cum_duration.view(b * t_x)
635
+ path = sequence_mask(cum_duration_flat, t_y).to(mask.dtype)
636
+ path = path.view(b, t_x, t_y)
637
+ path = path - F.pad(path, convert_pad_shape([[0, 0], [1, 0], [0, 0]]))[:, :-1]
638
+ path = path.unsqueeze(1).transpose(2, 3) * mask
639
+ return path
640
+
641
+
642
+ def clip_grad_value_(parameters, clip_value, norm_type=2):
643
+ if isinstance(parameters, torch.Tensor):
644
+ parameters = [parameters]
645
+ parameters = list(filter(lambda p: p.grad is not None, parameters))
646
+ norm_type = float(norm_type)
647
+ if clip_value is not None:
648
+ clip_value = float(clip_value)
649
+
650
+ total_norm = 0
651
+ for p in parameters:
652
+ param_norm = p.grad.data.norm(norm_type)
653
+ total_norm += param_norm.item() ** norm_type
654
+ if clip_value is not None:
655
+ p.grad.data.clamp_(min=-clip_value, max=clip_value)
656
+ total_norm = total_norm ** (1.0 / norm_type)
657
+ return total_norm
658
+
659
+
660
+ def get_current_time():
661
+ pass
662
+
663
+
664
+ def make_pad_mask(lengths: torch.Tensor, max_len: int = 0) -> torch.Tensor:
665
+ """
666
+ Args:
667
+ lengths:
668
+ A 1-D tensor containing sentence lengths.
669
+ max_len:
670
+ The length of masks.
671
+ Returns:
672
+ Return a 2-D bool tensor, where masked positions
673
+ are filled with `True` and non-masked positions are
674
+ filled with `False`.
675
+
676
+ >>> lengths = torch.tensor([1, 3, 2, 5])
677
+ >>> make_pad_mask(lengths)
678
+ tensor([[False, True, True, True, True],
679
+ [False, False, False, True, True],
680
+ [False, False, True, True, True],
681
+ [False, False, False, False, False]])
682
+ """
683
+ assert lengths.ndim == 1, lengths.ndim
684
+ max_len = max(max_len, lengths.max())
685
+ n = lengths.size(0)
686
+ seq_range = torch.arange(0, max_len, device=lengths.device)
687
+ expaned_lengths = seq_range.unsqueeze(0).expand(n, max_len)
688
+
689
+ return expaned_lengths >= lengths.unsqueeze(-1)