Prompt48 commited on
Commit
1cb6a6e
·
verified ·
1 Parent(s): 21fdc32

Upload edit\Qwen3-TTS-test\qwen_tts\core\tokenizer_25hz\vq\speech_vq.py with huggingface_hub

Browse files
edit//Qwen3-TTS-test//qwen_tts//core//tokenizer_25hz//vq//speech_vq.py ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2026 The Alibaba Qwen team.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ import sox
17
+ import copy
18
+ import torch
19
+ import operator
20
+ import onnxruntime
21
+
22
+ import torch.nn as nn
23
+ import torch.nn.functional as F
24
+ import torchaudio.compliance.kaldi as kaldi
25
+
26
+ from librosa.filters import mel as librosa_mel_fn
27
+ from itertools import accumulate
28
+ from typing import List
29
+ from torch import Tensor
30
+
31
+ from .core_vq import DistributedGroupResidualVectorQuantization
32
+ from .whisper_encoder import WhisperEncoder, Conv1d, ConvTranspose1d
33
+
34
+
35
+ def dynamic_range_compression_torch(x, C=1, clip_val=1e-5):
36
+ return torch.log(torch.clamp(x, min=clip_val) * C)
37
+
38
+ def spectral_normalize_torch(magnitudes):
39
+ output = dynamic_range_compression_torch(magnitudes)
40
+ return output
41
+
42
+ class MelSpectrogramFeatures(nn.Module):
43
+ """
44
+ Calculate the BigVGAN style mel spectrogram of an input signal.
45
+ Args:
46
+ filter_length (int): The number of samples in the filter window, used for the Fourier Transform. Default is 1024.
47
+ hop_length (int): The number of samples between successive frames (stride of the STFT). Default is 160.
48
+ win_length (int): The length of the window function applied to each frame, usually less than or equal to the filter length. Default is 640.
49
+ n_mel_channels (int): The number of Mel-frequency channels to output from the Mel-scale spectrogram. Default is 80.
50
+ mel_fmin (int): The minimum frequency (in Hz) of the Mel-scale spectrogram. Default is 0.
51
+ mel_fmax (int): The maximum frequency (in Hz) of the Mel-scale spectrogram. Default is 8000.
52
+ sampling_rate (int): The sampling rate of the audio data (in Hz). Default is 16000.
53
+ sampling_rate_org (int, optional): The original sampling rate of the audio data before any resampling (in Hz), if applicable. Default is None.
54
+ padding (str): The padding mode for the input signal. 'center' pads the signal symmetrically around its center. Default is 'center'.
55
+
56
+ Returns:
57
+ torch.Tensor: Mel spectrogram.
58
+ """
59
+ def __init__(self,
60
+ filter_length=1024,
61
+ hop_length=160,
62
+ win_length=640,
63
+ n_mel_channels=80,
64
+ mel_fmin=0,
65
+ mel_fmax=8000,
66
+ sampling_rate=16000,
67
+ sampling_rate_org=None,
68
+ padding='center',
69
+ use_db = False,
70
+ ):
71
+ super().__init__()
72
+ if padding not in ["center", "same"]:
73
+ raise ValueError("Padding must be 'center' or 'same'.")
74
+ self.padding = padding
75
+
76
+ self.filter_length = filter_length
77
+ self.hop_length = hop_length
78
+ self.win_length = win_length
79
+ self.n_mel_channels = n_mel_channels
80
+ self.mel_fmin = mel_fmin
81
+ self.mel_fmax = mel_fmax
82
+ self.sampling_rate = sampling_rate
83
+ self.sampling_rate_org = sampling_rate_org if sampling_rate_org is not None else sampling_rate
84
+ self.mel_basis = {}
85
+ self.hann_window = {}
86
+
87
+ def forward(self, audio: torch.Tensor, **kwargs) -> torch.Tensor:
88
+ with torch.no_grad():
89
+ feats = self.extract(audio, **kwargs)
90
+ return feats
91
+
92
+ def extract(self, audio, **kwargs):
93
+
94
+ if len(audio.shape) == 3:
95
+ audio = audio.squeeze(1) if audio.shape[1] == 1 else audio.squeeze(2)
96
+ assert len(audio.shape) == 2
97
+
98
+ y = audio
99
+ if len(list(self.mel_basis.keys())) == 0:
100
+ mel = librosa_mel_fn(sr=self.sampling_rate, n_fft=self.filter_length, n_mels=self.n_mel_channels, fmin=self.mel_fmin, fmax=self.mel_fmax)
101
+ self.mel_basis[str(self.mel_fmax)+'_'+str(y.device)] = torch.from_numpy(mel).float().to(y.device)
102
+ self.hann_window[str(y.device)] = torch.hann_window(self.win_length).to(y.device)
103
+
104
+ y = torch.nn.functional.pad(y.unsqueeze(1), (int((self.filter_length-self.hop_length)/2), int((self.filter_length-self.hop_length)/2)), mode='reflect')
105
+ y = y.squeeze(1)
106
+
107
+ spec = torch.stft(y, self.filter_length, hop_length=self.hop_length, win_length=self.win_length, window=self.hann_window[str(y.device)],
108
+ center=False, pad_mode='reflect', normalized=False, onesided=True, return_complex=True)
109
+ spec = torch.view_as_real(spec)
110
+ spec = torch.sqrt(spec.pow(2).sum(-1)+(1e-9))
111
+
112
+ spec = torch.matmul(self.mel_basis[str(self.mel_fmax)+'_'+str(y.device)], spec)
113
+ spec = spectral_normalize_torch(spec)
114
+
115
+ return spec
116
+
117
+
118
+ class XVectorExtractor(nn.Module):
119
+ def __init__(self, audio_codec_with_xvector):
120
+ super().__init__()
121
+ option = onnxruntime.SessionOptions()
122
+ option.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL
123
+ option.intra_op_num_threads = 1
124
+ providers = ["CPUExecutionProvider"]
125
+ self.ort_session = onnxruntime.InferenceSession(audio_codec_with_xvector, sess_options=option, providers=providers)
126
+
127
+ self.tfm = sox.Transformer()
128
+ self.tfm.norm(db_level=-6)
129
+
130
+ self.mel_ext = MelSpectrogramFeatures(
131
+ filter_length=1024,
132
+ hop_length=160,
133
+ win_length=640,
134
+ n_mel_channels=80,
135
+ mel_fmin=0,
136
+ mel_fmax=8000,
137
+ sampling_rate=16000
138
+ )
139
+
140
+ def extract_code(self, audio):
141
+ with torch.no_grad():
142
+ norm_audio = self.sox_norm(audio)
143
+
144
+ norm_audio = torch.from_numpy(copy.deepcopy(norm_audio)).unsqueeze(0)
145
+ feat = kaldi.fbank(norm_audio,
146
+ num_mel_bins=80,
147
+ dither=0,
148
+ sample_frequency=16000)
149
+ feat = feat - feat.mean(dim=0, keepdim=True)
150
+ norm_embedding = self.ort_session.run(None, {self.ort_session.get_inputs()[0].name: feat.unsqueeze(dim=0).cpu().numpy()})[0].flatten()
151
+ norm_embedding = F.normalize(torch.from_numpy(norm_embedding), dim=0)
152
+
153
+ ref_mel = self.mel_ext.extract(audio=norm_audio)
154
+
155
+ return norm_embedding.numpy(), ref_mel.permute(0,2,1).squeeze(0).numpy()
156
+
157
+ def sox_norm(self, audio):
158
+ wav_norm = self.tfm.build_array(input_array=audio, sample_rate_in=16000)
159
+ return wav_norm
160
+
161
+
162
+ class WhisperEncoderVQ(WhisperEncoder):
163
+ def __init__(
164
+ self,
165
+ n_mels: int,
166
+ n_ctx: int,
167
+ n_state: int,
168
+ n_head: int,
169
+ n_layer: int,
170
+ n_window: int = 1500,
171
+ output_dim: int = 512,
172
+ grad_checkpointing: bool = False,
173
+ enable_mp: bool = False,
174
+ audio_sequence_parallel: bool = False,
175
+ audio_vq_layers: int = -1,
176
+ audio_vq_type: str = "NULL",
177
+ audio_vq_codebook_size: int = 4096,
178
+ audio_vq_pe: bool = False,
179
+ audio_vq_commit_loss: float = 0.0,
180
+ audio_vq_out_commit_loss: float = 0.0,
181
+ audio_vq_no_quantize: bool = False,
182
+ audio_vq_ff_layer: int = 0,
183
+ audio_vq_threshold_ema_dead_code: float = 0.1,
184
+ audio_vq_codebook_dim: int = None,
185
+ audio_vq_ds_rate: int = None,
186
+ ):
187
+ super().__init__(n_mels, n_ctx, n_state, n_head, n_layer, n_window, output_dim, grad_checkpointing, enable_mp, audio_sequence_parallel)
188
+
189
+ self.audio_vq_layers = audio_vq_layers
190
+ self.audio_vq_type = audio_vq_type
191
+ self.audio_vq_codebook_size = audio_vq_codebook_size
192
+ self.audio_vq_pe = audio_vq_pe
193
+ self.audio_vq_commit_loss = audio_vq_commit_loss
194
+ self.audio_vq_out_commit_loss = audio_vq_out_commit_loss
195
+ self.audio_vq_no_quantize = audio_vq_no_quantize
196
+ self.audio_vq_ff_layer = audio_vq_ff_layer
197
+
198
+ if audio_vq_layers > 0:
199
+ self.vq_feature_dim = self.n_state
200
+ self.audio_vq_ds_rate = 1
201
+ else:
202
+ raise NotImplementedError(f"Unsupported audio_vq_layers: {audio_vq_layers}")
203
+
204
+ if self.audio_vq_ds_rate == audio_vq_ds_rate:
205
+ self.audio_vq_downsample = nn.Identity()
206
+ self.audio_vq_upsample = nn.Identity()
207
+ else:
208
+ assert audio_vq_ds_rate % self.audio_vq_ds_rate == 0
209
+ stride = audio_vq_ds_rate // self.audio_vq_ds_rate
210
+ self.audio_vq_downsample = Conv1d(self.vq_feature_dim, self.vq_feature_dim, kernel_size=stride, stride=stride)
211
+ self.audio_vq_upsample = ConvTranspose1d(self.vq_feature_dim, self.vq_feature_dim, kernel_size=stride, stride=stride)
212
+ self.audio_vq_ds_rate = audio_vq_ds_rate
213
+
214
+ if audio_vq_type == "GRVQ":
215
+ self.audio_quantizer = DistributedGroupResidualVectorQuantization(
216
+ codebook_size = audio_vq_codebook_size,
217
+ dim = self.vq_feature_dim,
218
+ codebook_dim = self.vq_codebook_dim if audio_vq_codebook_dim is None else audio_vq_codebook_dim,
219
+ num_groups=1,
220
+ num_quantizers=1,
221
+ kmeans_init=False,
222
+ threshold_ema_dead_code = audio_vq_threshold_ema_dead_code
223
+ )
224
+ else:
225
+ raise NotImplementedError(f"Unsupported audio_vq_type: {audio_vq_type}")
226
+
227
+ if self.audio_vq_pe:
228
+ self.project_after_vq_pe = nn.Linear(self.n_state, self.n_state)
229
+
230
+ def _calc_quantize_activities(self, indices):
231
+ indices_onehot = F.one_hot(indices.long().flatten(), self.audio_vq_codebook_size).sum(dim=0)
232
+ vq_num_activities = sum(indices_onehot>0)
233
+ vq_num_tokens = sum(indices_onehot)
234
+ return {
235
+ "vq_num_activities": vq_num_activities,
236
+ "vq_num_tokens": vq_num_tokens,
237
+ }
238
+
239
+ def _do_quantize(self, x, pe=None, y=None):
240
+ """
241
+ x: torch.Tensor, shape = (T, D)
242
+ q: torch.Tensor, shape = (T, D)
243
+ i: torch.Tensor, shape = (T)
244
+ """
245
+ if self.audio_vq_out_commit_loss > 0:
246
+ x_teacher = x.clone()
247
+ x = x.unsqueeze(0)
248
+
249
+ x = self.audio_vq_downsample(x.transpose(1, 2))
250
+ x = x.transpose(1, 2)
251
+
252
+ vq_stats = {}
253
+
254
+ if self.audio_vq_type == "GRVQ":
255
+ if self.training:
256
+ raise NotImplementedError
257
+ else:
258
+ indices = self.audio_quantizer.encode(x)
259
+ x = self.audio_quantizer.decode(indices)
260
+ indices = indices.squeeze(2).squeeze(1)
261
+
262
+ vq_stats.update(self._calc_quantize_activities(indices))
263
+
264
+ x, indices = x.squeeze(0), indices.squeeze(0)
265
+ if self.audio_vq_pe:
266
+ x = x + pe
267
+ x = self.project_after_vq_pe(x)
268
+
269
+ x = self.audio_vq_upsample(x.unsqueeze(0).transpose(1, 2))
270
+ x = x.transpose(1, 2).squeeze(0)
271
+
272
+ if self.audio_vq_out_commit_loss > 0:
273
+ vq_out_commit_loss = F.mse_loss(x_teacher.detach(), x)
274
+ vq_stats["vq_out_commit_loss"] = vq_out_commit_loss * self.audio_vq_out_commit_loss
275
+
276
+ return x, indices, vq_stats
277
+
278
+ def forward(self, x_list: List[Tensor], audio_mellens:List[int], audio_aftercnnlens:List[int], audio_seqlens:List[int], return_indices=False, audio_pitchs=None):
279
+ """
280
+ x : torch.Tensor, shape = (n_mels, n_ctx)
281
+ the mel spectrogram of the audio
282
+ """
283
+
284
+ aftercnn_x_list = []
285
+ pe_for_vq_list = []
286
+ for each_x in x_list:
287
+ each_x_split_list = each_x.split(self.n_window * 2, dim=1)
288
+ for each_x_split in each_x_split_list:
289
+ each_x_split = F.gelu(self.conv1(each_x_split))
290
+ each_x_split = F.gelu(self.conv2(each_x_split))
291
+ each_x_split = each_x_split.permute(1, 0) # L,D
292
+
293
+ each_positional_embedding_split = self.positional_embedding[:each_x_split.shape[0]]
294
+ aftercnn_x_list.append(each_x_split+each_positional_embedding_split.to(each_x_split.dtype))
295
+
296
+ pe_for_vq_split = self.positional_embedding[:each_x_split.shape[0] // self.audio_vq_ds_rate]
297
+ pe_for_vq_list.append(pe_for_vq_split.to(each_x_split.dtype))
298
+
299
+ pe_for_vq = torch.cat(pe_for_vq_list, dim=0)
300
+ x = torch.cat(aftercnn_x_list, dim=0)
301
+ src_len = x.size(0)
302
+
303
+ output_list = []
304
+ for item in audio_aftercnnlens:
305
+ while item > self.n_window:
306
+ output_list.append(self.n_window)
307
+ item -= self.n_window
308
+ output_list.append(item)
309
+
310
+ cu_seqlens = list(accumulate(output_list, func=operator.add,initial=0))
311
+ cu_seqlens = torch.Tensor(cu_seqlens).to(device=x.device, dtype=torch.int32)
312
+
313
+ layer_id = 0
314
+
315
+ for block in self.blocks:
316
+ layer_id+=1
317
+
318
+ x = block(x, cu_seqlens=cu_seqlens)
319
+
320
+ if self.audio_vq_layers == layer_id: # vq inside encoder
321
+ x, indices, vq_stats = self._do_quantize(x, pe_for_vq)
322
+ if return_indices:
323
+ return x, indices
324
+
325
+ if self.avg_pooler:
326
+ x_list = x.split(audio_aftercnnlens, dim=0)
327
+ token_x_list = []
328
+ for x in x_list:
329
+ x = x.permute(1, 0)
330
+ x = self.avg_pooler(x)
331
+ x = x.permute(1, 0)
332
+ token_x_list.append(x)
333
+ x = torch.cat(token_x_list, dim=0)
334
+
335
+ x = self.ln_post(x)
336
+
337
+ x = self.proj(x)
338
+
339
+ output = torch.zeros(
340
+ (x.size(0) + len(audio_seqlens) * 2, x.size(1)),
341
+ device=x.device, dtype=x.dtype
342
+ )
343
+
344
+ audio_seqlens_acc = list(accumulate(audio_seqlens, func=operator.add, initial=0))
345
+ start_ids = torch.tensor(audio_seqlens_acc[:-1], device=x.device, dtype=torch.int32)
346
+ end_ids = torch.tensor(audio_seqlens_acc[1:], device=x.device, dtype=torch.int32) - 1
347
+
348
+ audio_tokens_mask = torch.ones(output.size(0), device=x.device, dtype=torch.bool)
349
+ audio_tokens_mask[start_ids] = False
350
+ audio_tokens_mask[end_ids] = False
351
+ output[start_ids] = self.audio_bos_eos_token.weight[0].to(x.dtype)
352
+ output[end_ids] = self.audio_bos_eos_token.weight[1].to(x.dtype)
353
+ output[audio_tokens_mask] = x
354
+
355
+ if self.audio_vq_type != "NULL":
356
+ return output, vq_stats
357
+ return output