Spaces:
Sleeping
Sleeping
Staticaliza
commited on
Upload 6 files
Browse files- modules/bigvgan/activations.py +120 -0
- modules/bigvgan/bigvgan.py +492 -0
- modules/bigvgan/config.json +63 -0
- modules/bigvgan/env.py +18 -0
- modules/bigvgan/meldataset.py +354 -0
- modules/bigvgan/utils.py +99 -0
modules/bigvgan/activations.py
ADDED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Implementation adapted from https://github.com/EdwardDixon/snake under the MIT license.
|
2 |
+
# LICENSE is in incl_licenses directory.
|
3 |
+
|
4 |
+
import torch
|
5 |
+
from torch import nn, sin, pow
|
6 |
+
from torch.nn import Parameter
|
7 |
+
|
8 |
+
|
9 |
+
class Snake(nn.Module):
|
10 |
+
'''
|
11 |
+
Implementation of a sine-based periodic activation function
|
12 |
+
Shape:
|
13 |
+
- Input: (B, C, T)
|
14 |
+
- Output: (B, C, T), same shape as the input
|
15 |
+
Parameters:
|
16 |
+
- alpha - trainable parameter
|
17 |
+
References:
|
18 |
+
- This activation function is from this paper by Liu Ziyin, Tilman Hartwig, Masahito Ueda:
|
19 |
+
https://arxiv.org/abs/2006.08195
|
20 |
+
Examples:
|
21 |
+
>>> a1 = snake(256)
|
22 |
+
>>> x = torch.randn(256)
|
23 |
+
>>> x = a1(x)
|
24 |
+
'''
|
25 |
+
def __init__(self, in_features, alpha=1.0, alpha_trainable=True, alpha_logscale=False):
|
26 |
+
'''
|
27 |
+
Initialization.
|
28 |
+
INPUT:
|
29 |
+
- in_features: shape of the input
|
30 |
+
- alpha: trainable parameter
|
31 |
+
alpha is initialized to 1 by default, higher values = higher-frequency.
|
32 |
+
alpha will be trained along with the rest of your model.
|
33 |
+
'''
|
34 |
+
super(Snake, self).__init__()
|
35 |
+
self.in_features = in_features
|
36 |
+
|
37 |
+
# initialize alpha
|
38 |
+
self.alpha_logscale = alpha_logscale
|
39 |
+
if self.alpha_logscale: # log scale alphas initialized to zeros
|
40 |
+
self.alpha = Parameter(torch.zeros(in_features) * alpha)
|
41 |
+
else: # linear scale alphas initialized to ones
|
42 |
+
self.alpha = Parameter(torch.ones(in_features) * alpha)
|
43 |
+
|
44 |
+
self.alpha.requires_grad = alpha_trainable
|
45 |
+
|
46 |
+
self.no_div_by_zero = 0.000000001
|
47 |
+
|
48 |
+
def forward(self, x):
|
49 |
+
'''
|
50 |
+
Forward pass of the function.
|
51 |
+
Applies the function to the input elementwise.
|
52 |
+
Snake ∶= x + 1/a * sin^2 (xa)
|
53 |
+
'''
|
54 |
+
alpha = self.alpha.unsqueeze(0).unsqueeze(-1) # line up with x to [B, C, T]
|
55 |
+
if self.alpha_logscale:
|
56 |
+
alpha = torch.exp(alpha)
|
57 |
+
x = x + (1.0 / (alpha + self.no_div_by_zero)) * pow(sin(x * alpha), 2)
|
58 |
+
|
59 |
+
return x
|
60 |
+
|
61 |
+
|
62 |
+
class SnakeBeta(nn.Module):
|
63 |
+
'''
|
64 |
+
A modified Snake function which uses separate parameters for the magnitude of the periodic components
|
65 |
+
Shape:
|
66 |
+
- Input: (B, C, T)
|
67 |
+
- Output: (B, C, T), same shape as the input
|
68 |
+
Parameters:
|
69 |
+
- alpha - trainable parameter that controls frequency
|
70 |
+
- beta - trainable parameter that controls magnitude
|
71 |
+
References:
|
72 |
+
- This activation function is a modified version based on this paper by Liu Ziyin, Tilman Hartwig, Masahito Ueda:
|
73 |
+
https://arxiv.org/abs/2006.08195
|
74 |
+
Examples:
|
75 |
+
>>> a1 = snakebeta(256)
|
76 |
+
>>> x = torch.randn(256)
|
77 |
+
>>> x = a1(x)
|
78 |
+
'''
|
79 |
+
def __init__(self, in_features, alpha=1.0, alpha_trainable=True, alpha_logscale=False):
|
80 |
+
'''
|
81 |
+
Initialization.
|
82 |
+
INPUT:
|
83 |
+
- in_features: shape of the input
|
84 |
+
- alpha - trainable parameter that controls frequency
|
85 |
+
- beta - trainable parameter that controls magnitude
|
86 |
+
alpha is initialized to 1 by default, higher values = higher-frequency.
|
87 |
+
beta is initialized to 1 by default, higher values = higher-magnitude.
|
88 |
+
alpha will be trained along with the rest of your model.
|
89 |
+
'''
|
90 |
+
super(SnakeBeta, self).__init__()
|
91 |
+
self.in_features = in_features
|
92 |
+
|
93 |
+
# initialize alpha
|
94 |
+
self.alpha_logscale = alpha_logscale
|
95 |
+
if self.alpha_logscale: # log scale alphas initialized to zeros
|
96 |
+
self.alpha = Parameter(torch.zeros(in_features) * alpha)
|
97 |
+
self.beta = Parameter(torch.zeros(in_features) * alpha)
|
98 |
+
else: # linear scale alphas initialized to ones
|
99 |
+
self.alpha = Parameter(torch.ones(in_features) * alpha)
|
100 |
+
self.beta = Parameter(torch.ones(in_features) * alpha)
|
101 |
+
|
102 |
+
self.alpha.requires_grad = alpha_trainable
|
103 |
+
self.beta.requires_grad = alpha_trainable
|
104 |
+
|
105 |
+
self.no_div_by_zero = 0.000000001
|
106 |
+
|
107 |
+
def forward(self, x):
|
108 |
+
'''
|
109 |
+
Forward pass of the function.
|
110 |
+
Applies the function to the input elementwise.
|
111 |
+
SnakeBeta ∶= x + 1/b * sin^2 (xa)
|
112 |
+
'''
|
113 |
+
alpha = self.alpha.unsqueeze(0).unsqueeze(-1) # line up with x to [B, C, T]
|
114 |
+
beta = self.beta.unsqueeze(0).unsqueeze(-1)
|
115 |
+
if self.alpha_logscale:
|
116 |
+
alpha = torch.exp(alpha)
|
117 |
+
beta = torch.exp(beta)
|
118 |
+
x = x + (1.0 / (beta + self.no_div_by_zero)) * pow(sin(x * alpha), 2)
|
119 |
+
|
120 |
+
return x
|
modules/bigvgan/bigvgan.py
ADDED
@@ -0,0 +1,492 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Copyright (c) 2024 NVIDIA CORPORATION.
|
2 |
+
# Licensed under the MIT license.
|
3 |
+
|
4 |
+
# Adapted from https://github.com/jik876/hifi-gan under the MIT license.
|
5 |
+
# LICENSE is in incl_licenses directory.
|
6 |
+
|
7 |
+
import os
|
8 |
+
import json
|
9 |
+
from pathlib import Path
|
10 |
+
from typing import Optional, Union, Dict
|
11 |
+
|
12 |
+
import torch
|
13 |
+
import torch.nn as nn
|
14 |
+
from torch.nn import Conv1d, ConvTranspose1d
|
15 |
+
from torch.nn.utils import weight_norm, remove_weight_norm
|
16 |
+
|
17 |
+
from . import activations
|
18 |
+
from .utils import init_weights, get_padding
|
19 |
+
from .alias_free_activation.torch.act import Activation1d as TorchActivation1d
|
20 |
+
from .env import AttrDict
|
21 |
+
|
22 |
+
from huggingface_hub import PyTorchModelHubMixin, hf_hub_download
|
23 |
+
|
24 |
+
|
25 |
+
def load_hparams_from_json(path) -> AttrDict:
|
26 |
+
with open(path) as f:
|
27 |
+
data = f.read()
|
28 |
+
return AttrDict(json.loads(data))
|
29 |
+
|
30 |
+
|
31 |
+
class AMPBlock1(torch.nn.Module):
|
32 |
+
"""
|
33 |
+
AMPBlock applies Snake / SnakeBeta activation functions with trainable parameters that control periodicity, defined for each layer.
|
34 |
+
AMPBlock1 has additional self.convs2 that contains additional Conv1d layers with a fixed dilation=1 followed by each layer in self.convs1
|
35 |
+
|
36 |
+
Args:
|
37 |
+
h (AttrDict): Hyperparameters.
|
38 |
+
channels (int): Number of convolution channels.
|
39 |
+
kernel_size (int): Size of the convolution kernel. Default is 3.
|
40 |
+
dilation (tuple): Dilation rates for the convolutions. Each dilation layer has two convolutions. Default is (1, 3, 5).
|
41 |
+
activation (str): Activation function type. Should be either 'snake' or 'snakebeta'. Default is None.
|
42 |
+
"""
|
43 |
+
|
44 |
+
def __init__(
|
45 |
+
self,
|
46 |
+
h: AttrDict,
|
47 |
+
channels: int,
|
48 |
+
kernel_size: int = 3,
|
49 |
+
dilation: tuple = (1, 3, 5),
|
50 |
+
activation: str = None,
|
51 |
+
):
|
52 |
+
super().__init__()
|
53 |
+
|
54 |
+
self.h = h
|
55 |
+
|
56 |
+
self.convs1 = nn.ModuleList(
|
57 |
+
[
|
58 |
+
weight_norm(
|
59 |
+
Conv1d(
|
60 |
+
channels,
|
61 |
+
channels,
|
62 |
+
kernel_size,
|
63 |
+
stride=1,
|
64 |
+
dilation=d,
|
65 |
+
padding=get_padding(kernel_size, d),
|
66 |
+
)
|
67 |
+
)
|
68 |
+
for d in dilation
|
69 |
+
]
|
70 |
+
)
|
71 |
+
self.convs1.apply(init_weights)
|
72 |
+
|
73 |
+
self.convs2 = nn.ModuleList(
|
74 |
+
[
|
75 |
+
weight_norm(
|
76 |
+
Conv1d(
|
77 |
+
channels,
|
78 |
+
channels,
|
79 |
+
kernel_size,
|
80 |
+
stride=1,
|
81 |
+
dilation=1,
|
82 |
+
padding=get_padding(kernel_size, 1),
|
83 |
+
)
|
84 |
+
)
|
85 |
+
for _ in range(len(dilation))
|
86 |
+
]
|
87 |
+
)
|
88 |
+
self.convs2.apply(init_weights)
|
89 |
+
|
90 |
+
self.num_layers = len(self.convs1) + len(
|
91 |
+
self.convs2
|
92 |
+
) # Total number of conv layers
|
93 |
+
|
94 |
+
# Select which Activation1d, lazy-load cuda version to ensure backward compatibility
|
95 |
+
if self.h.get("use_cuda_kernel", False):
|
96 |
+
from alias_free_activation.cuda.activation1d import (
|
97 |
+
Activation1d as CudaActivation1d,
|
98 |
+
)
|
99 |
+
|
100 |
+
Activation1d = CudaActivation1d
|
101 |
+
else:
|
102 |
+
Activation1d = TorchActivation1d
|
103 |
+
|
104 |
+
# Activation functions
|
105 |
+
if activation == "snake":
|
106 |
+
self.activations = nn.ModuleList(
|
107 |
+
[
|
108 |
+
Activation1d(
|
109 |
+
activation=activations.Snake(
|
110 |
+
channels, alpha_logscale=h.snake_logscale
|
111 |
+
)
|
112 |
+
)
|
113 |
+
for _ in range(self.num_layers)
|
114 |
+
]
|
115 |
+
)
|
116 |
+
elif activation == "snakebeta":
|
117 |
+
self.activations = nn.ModuleList(
|
118 |
+
[
|
119 |
+
Activation1d(
|
120 |
+
activation=activations.SnakeBeta(
|
121 |
+
channels, alpha_logscale=h.snake_logscale
|
122 |
+
)
|
123 |
+
)
|
124 |
+
for _ in range(self.num_layers)
|
125 |
+
]
|
126 |
+
)
|
127 |
+
else:
|
128 |
+
raise NotImplementedError(
|
129 |
+
"activation incorrectly specified. check the config file and look for 'activation'."
|
130 |
+
)
|
131 |
+
|
132 |
+
def forward(self, x):
|
133 |
+
acts1, acts2 = self.activations[::2], self.activations[1::2]
|
134 |
+
for c1, c2, a1, a2 in zip(self.convs1, self.convs2, acts1, acts2):
|
135 |
+
xt = a1(x)
|
136 |
+
xt = c1(xt)
|
137 |
+
xt = a2(xt)
|
138 |
+
xt = c2(xt)
|
139 |
+
x = xt + x
|
140 |
+
|
141 |
+
return x
|
142 |
+
|
143 |
+
def remove_weight_norm(self):
|
144 |
+
for l in self.convs1:
|
145 |
+
remove_weight_norm(l)
|
146 |
+
for l in self.convs2:
|
147 |
+
remove_weight_norm(l)
|
148 |
+
|
149 |
+
|
150 |
+
class AMPBlock2(torch.nn.Module):
|
151 |
+
"""
|
152 |
+
AMPBlock applies Snake / SnakeBeta activation functions with trainable parameters that control periodicity, defined for each layer.
|
153 |
+
Unlike AMPBlock1, AMPBlock2 does not contain extra Conv1d layers with fixed dilation=1
|
154 |
+
|
155 |
+
Args:
|
156 |
+
h (AttrDict): Hyperparameters.
|
157 |
+
channels (int): Number of convolution channels.
|
158 |
+
kernel_size (int): Size of the convolution kernel. Default is 3.
|
159 |
+
dilation (tuple): Dilation rates for the convolutions. Each dilation layer has two convolutions. Default is (1, 3, 5).
|
160 |
+
activation (str): Activation function type. Should be either 'snake' or 'snakebeta'. Default is None.
|
161 |
+
"""
|
162 |
+
|
163 |
+
def __init__(
|
164 |
+
self,
|
165 |
+
h: AttrDict,
|
166 |
+
channels: int,
|
167 |
+
kernel_size: int = 3,
|
168 |
+
dilation: tuple = (1, 3, 5),
|
169 |
+
activation: str = None,
|
170 |
+
):
|
171 |
+
super().__init__()
|
172 |
+
|
173 |
+
self.h = h
|
174 |
+
|
175 |
+
self.convs = nn.ModuleList(
|
176 |
+
[
|
177 |
+
weight_norm(
|
178 |
+
Conv1d(
|
179 |
+
channels,
|
180 |
+
channels,
|
181 |
+
kernel_size,
|
182 |
+
stride=1,
|
183 |
+
dilation=d,
|
184 |
+
padding=get_padding(kernel_size, d),
|
185 |
+
)
|
186 |
+
)
|
187 |
+
for d in dilation
|
188 |
+
]
|
189 |
+
)
|
190 |
+
self.convs.apply(init_weights)
|
191 |
+
|
192 |
+
self.num_layers = len(self.convs) # Total number of conv layers
|
193 |
+
|
194 |
+
# Select which Activation1d, lazy-load cuda version to ensure backward compatibility
|
195 |
+
if self.h.get("use_cuda_kernel", False):
|
196 |
+
from alias_free_activation.cuda.activation1d import (
|
197 |
+
Activation1d as CudaActivation1d,
|
198 |
+
)
|
199 |
+
|
200 |
+
Activation1d = CudaActivation1d
|
201 |
+
else:
|
202 |
+
Activation1d = TorchActivation1d
|
203 |
+
|
204 |
+
# Activation functions
|
205 |
+
if activation == "snake":
|
206 |
+
self.activations = nn.ModuleList(
|
207 |
+
[
|
208 |
+
Activation1d(
|
209 |
+
activation=activations.Snake(
|
210 |
+
channels, alpha_logscale=h.snake_logscale
|
211 |
+
)
|
212 |
+
)
|
213 |
+
for _ in range(self.num_layers)
|
214 |
+
]
|
215 |
+
)
|
216 |
+
elif activation == "snakebeta":
|
217 |
+
self.activations = nn.ModuleList(
|
218 |
+
[
|
219 |
+
Activation1d(
|
220 |
+
activation=activations.SnakeBeta(
|
221 |
+
channels, alpha_logscale=h.snake_logscale
|
222 |
+
)
|
223 |
+
)
|
224 |
+
for _ in range(self.num_layers)
|
225 |
+
]
|
226 |
+
)
|
227 |
+
else:
|
228 |
+
raise NotImplementedError(
|
229 |
+
"activation incorrectly specified. check the config file and look for 'activation'."
|
230 |
+
)
|
231 |
+
|
232 |
+
def forward(self, x):
|
233 |
+
for c, a in zip(self.convs, self.activations):
|
234 |
+
xt = a(x)
|
235 |
+
xt = c(xt)
|
236 |
+
x = xt + x
|
237 |
+
|
238 |
+
def remove_weight_norm(self):
|
239 |
+
for l in self.convs:
|
240 |
+
remove_weight_norm(l)
|
241 |
+
|
242 |
+
|
243 |
+
class BigVGAN(
|
244 |
+
torch.nn.Module,
|
245 |
+
PyTorchModelHubMixin,
|
246 |
+
library_name="bigvgan",
|
247 |
+
repo_url="https://github.com/NVIDIA/BigVGAN",
|
248 |
+
docs_url="https://github.com/NVIDIA/BigVGAN/blob/main/README.md",
|
249 |
+
pipeline_tag="audio-to-audio",
|
250 |
+
license="mit",
|
251 |
+
tags=["neural-vocoder", "audio-generation", "arxiv:2206.04658"],
|
252 |
+
):
|
253 |
+
"""
|
254 |
+
BigVGAN is a neural vocoder model that applies anti-aliased periodic activation for residual blocks (resblocks).
|
255 |
+
New in BigVGAN-v2: it can optionally use optimized CUDA kernels for AMP (anti-aliased multi-periodicity) blocks.
|
256 |
+
|
257 |
+
Args:
|
258 |
+
h (AttrDict): Hyperparameters.
|
259 |
+
use_cuda_kernel (bool): If set to True, loads optimized CUDA kernels for AMP. This should be used for inference only, as training is not supported with CUDA kernels.
|
260 |
+
|
261 |
+
Note:
|
262 |
+
- The `use_cuda_kernel` parameter should be used for inference only, as training with CUDA kernels is not supported.
|
263 |
+
- Ensure that the activation function is correctly specified in the hyperparameters (h.activation).
|
264 |
+
"""
|
265 |
+
|
266 |
+
def __init__(self, h: AttrDict, use_cuda_kernel: bool = False):
|
267 |
+
super().__init__()
|
268 |
+
self.h = h
|
269 |
+
self.h["use_cuda_kernel"] = use_cuda_kernel
|
270 |
+
|
271 |
+
# Select which Activation1d, lazy-load cuda version to ensure backward compatibility
|
272 |
+
if self.h.get("use_cuda_kernel", False):
|
273 |
+
from alias_free_activation.cuda.activation1d import (
|
274 |
+
Activation1d as CudaActivation1d,
|
275 |
+
)
|
276 |
+
|
277 |
+
Activation1d = CudaActivation1d
|
278 |
+
else:
|
279 |
+
Activation1d = TorchActivation1d
|
280 |
+
|
281 |
+
self.num_kernels = len(h.resblock_kernel_sizes)
|
282 |
+
self.num_upsamples = len(h.upsample_rates)
|
283 |
+
|
284 |
+
# Pre-conv
|
285 |
+
self.conv_pre = weight_norm(
|
286 |
+
Conv1d(h.num_mels, h.upsample_initial_channel, 7, 1, padding=3)
|
287 |
+
)
|
288 |
+
|
289 |
+
# Define which AMPBlock to use. BigVGAN uses AMPBlock1 as default
|
290 |
+
if h.resblock == "1":
|
291 |
+
resblock_class = AMPBlock1
|
292 |
+
elif h.resblock == "2":
|
293 |
+
resblock_class = AMPBlock2
|
294 |
+
else:
|
295 |
+
raise ValueError(
|
296 |
+
f"Incorrect resblock class specified in hyperparameters. Got {h.resblock}"
|
297 |
+
)
|
298 |
+
|
299 |
+
# Transposed conv-based upsamplers. does not apply anti-aliasing
|
300 |
+
self.ups = nn.ModuleList()
|
301 |
+
for i, (u, k) in enumerate(zip(h.upsample_rates, h.upsample_kernel_sizes)):
|
302 |
+
self.ups.append(
|
303 |
+
nn.ModuleList(
|
304 |
+
[
|
305 |
+
weight_norm(
|
306 |
+
ConvTranspose1d(
|
307 |
+
h.upsample_initial_channel // (2**i),
|
308 |
+
h.upsample_initial_channel // (2 ** (i + 1)),
|
309 |
+
k,
|
310 |
+
u,
|
311 |
+
padding=(k - u) // 2,
|
312 |
+
)
|
313 |
+
)
|
314 |
+
]
|
315 |
+
)
|
316 |
+
)
|
317 |
+
|
318 |
+
# Residual blocks using anti-aliased multi-periodicity composition modules (AMP)
|
319 |
+
self.resblocks = nn.ModuleList()
|
320 |
+
for i in range(len(self.ups)):
|
321 |
+
ch = h.upsample_initial_channel // (2 ** (i + 1))
|
322 |
+
for j, (k, d) in enumerate(
|
323 |
+
zip(h.resblock_kernel_sizes, h.resblock_dilation_sizes)
|
324 |
+
):
|
325 |
+
self.resblocks.append(
|
326 |
+
resblock_class(h, ch, k, d, activation=h.activation)
|
327 |
+
)
|
328 |
+
|
329 |
+
# Post-conv
|
330 |
+
activation_post = (
|
331 |
+
activations.Snake(ch, alpha_logscale=h.snake_logscale)
|
332 |
+
if h.activation == "snake"
|
333 |
+
else (
|
334 |
+
activations.SnakeBeta(ch, alpha_logscale=h.snake_logscale)
|
335 |
+
if h.activation == "snakebeta"
|
336 |
+
else None
|
337 |
+
)
|
338 |
+
)
|
339 |
+
if activation_post is None:
|
340 |
+
raise NotImplementedError(
|
341 |
+
"activation incorrectly specified. check the config file and look for 'activation'."
|
342 |
+
)
|
343 |
+
|
344 |
+
self.activation_post = Activation1d(activation=activation_post)
|
345 |
+
|
346 |
+
# Whether to use bias for the final conv_post. Default to True for backward compatibility
|
347 |
+
self.use_bias_at_final = h.get("use_bias_at_final", True)
|
348 |
+
self.conv_post = weight_norm(
|
349 |
+
Conv1d(ch, 1, 7, 1, padding=3, bias=self.use_bias_at_final)
|
350 |
+
)
|
351 |
+
|
352 |
+
# Weight initialization
|
353 |
+
for i in range(len(self.ups)):
|
354 |
+
self.ups[i].apply(init_weights)
|
355 |
+
self.conv_post.apply(init_weights)
|
356 |
+
|
357 |
+
# Final tanh activation. Defaults to True for backward compatibility
|
358 |
+
self.use_tanh_at_final = h.get("use_tanh_at_final", True)
|
359 |
+
|
360 |
+
def forward(self, x):
|
361 |
+
# Pre-conv
|
362 |
+
x = self.conv_pre(x)
|
363 |
+
|
364 |
+
for i in range(self.num_upsamples):
|
365 |
+
# Upsampling
|
366 |
+
for i_up in range(len(self.ups[i])):
|
367 |
+
x = self.ups[i][i_up](x)
|
368 |
+
# AMP blocks
|
369 |
+
xs = None
|
370 |
+
for j in range(self.num_kernels):
|
371 |
+
if xs is None:
|
372 |
+
xs = self.resblocks[i * self.num_kernels + j](x)
|
373 |
+
else:
|
374 |
+
xs += self.resblocks[i * self.num_kernels + j](x)
|
375 |
+
x = xs / self.num_kernels
|
376 |
+
|
377 |
+
# Post-conv
|
378 |
+
x = self.activation_post(x)
|
379 |
+
x = self.conv_post(x)
|
380 |
+
# Final tanh activation
|
381 |
+
if self.use_tanh_at_final:
|
382 |
+
x = torch.tanh(x)
|
383 |
+
else:
|
384 |
+
x = torch.clamp(x, min=-1.0, max=1.0) # Bound the output to [-1, 1]
|
385 |
+
|
386 |
+
return x
|
387 |
+
|
388 |
+
def remove_weight_norm(self):
|
389 |
+
try:
|
390 |
+
print("Removing weight norm...")
|
391 |
+
for l in self.ups:
|
392 |
+
for l_i in l:
|
393 |
+
remove_weight_norm(l_i)
|
394 |
+
for l in self.resblocks:
|
395 |
+
l.remove_weight_norm()
|
396 |
+
remove_weight_norm(self.conv_pre)
|
397 |
+
remove_weight_norm(self.conv_post)
|
398 |
+
except ValueError:
|
399 |
+
print("[INFO] Model already removed weight norm. Skipping!")
|
400 |
+
pass
|
401 |
+
|
402 |
+
# Additional methods for huggingface_hub support
|
403 |
+
def _save_pretrained(self, save_directory: Path) -> None:
|
404 |
+
"""Save weights and config.json from a Pytorch model to a local directory."""
|
405 |
+
|
406 |
+
model_path = save_directory / "bigvgan_generator.pt"
|
407 |
+
torch.save({"generator": self.state_dict()}, model_path)
|
408 |
+
|
409 |
+
config_path = save_directory / "config.json"
|
410 |
+
with open(config_path, "w") as config_file:
|
411 |
+
json.dump(self.h, config_file, indent=4)
|
412 |
+
|
413 |
+
@classmethod
|
414 |
+
def _from_pretrained(
|
415 |
+
cls,
|
416 |
+
*,
|
417 |
+
model_id: str,
|
418 |
+
revision: str,
|
419 |
+
cache_dir: str,
|
420 |
+
force_download: bool,
|
421 |
+
proxies: Optional[Dict],
|
422 |
+
resume_download: bool,
|
423 |
+
local_files_only: bool,
|
424 |
+
token: Union[str, bool, None],
|
425 |
+
map_location: str = "cpu", # Additional argument
|
426 |
+
strict: bool = False, # Additional argument
|
427 |
+
use_cuda_kernel: bool = False,
|
428 |
+
**model_kwargs,
|
429 |
+
):
|
430 |
+
"""Load Pytorch pretrained weights and return the loaded model."""
|
431 |
+
|
432 |
+
# Download and load hyperparameters (h) used by BigVGAN
|
433 |
+
if os.path.isdir(model_id):
|
434 |
+
print("Loading config.json from local directory")
|
435 |
+
config_file = os.path.join(model_id, "config.json")
|
436 |
+
else:
|
437 |
+
config_file = hf_hub_download(
|
438 |
+
repo_id=model_id,
|
439 |
+
filename="config.json",
|
440 |
+
revision=revision,
|
441 |
+
cache_dir=cache_dir,
|
442 |
+
force_download=force_download,
|
443 |
+
proxies=proxies,
|
444 |
+
resume_download=resume_download,
|
445 |
+
token=token,
|
446 |
+
local_files_only=local_files_only,
|
447 |
+
)
|
448 |
+
h = load_hparams_from_json(config_file)
|
449 |
+
|
450 |
+
# instantiate BigVGAN using h
|
451 |
+
if use_cuda_kernel:
|
452 |
+
print(
|
453 |
+
f"[WARNING] You have specified use_cuda_kernel=True during BigVGAN.from_pretrained(). Only inference is supported (training is not implemented)!"
|
454 |
+
)
|
455 |
+
print(
|
456 |
+
f"[WARNING] You need nvcc and ninja installed in your system that matches your PyTorch build is using to build the kernel. If not, the model will fail to initialize or generate incorrect waveform!"
|
457 |
+
)
|
458 |
+
print(
|
459 |
+
f"[WARNING] For detail, see the official GitHub repository: https://github.com/NVIDIA/BigVGAN?tab=readme-ov-file#using-custom-cuda-kernel-for-synthesis"
|
460 |
+
)
|
461 |
+
model = cls(h, use_cuda_kernel=use_cuda_kernel)
|
462 |
+
|
463 |
+
# Download and load pretrained generator weight
|
464 |
+
if os.path.isdir(model_id):
|
465 |
+
print("Loading weights from local directory")
|
466 |
+
model_file = os.path.join(model_id, "bigvgan_generator.pt")
|
467 |
+
else:
|
468 |
+
print(f"Loading weights from {model_id}")
|
469 |
+
model_file = hf_hub_download(
|
470 |
+
repo_id=model_id,
|
471 |
+
filename="bigvgan_generator.pt",
|
472 |
+
revision=revision,
|
473 |
+
cache_dir=cache_dir,
|
474 |
+
force_download=force_download,
|
475 |
+
proxies=proxies,
|
476 |
+
resume_download=resume_download,
|
477 |
+
token=token,
|
478 |
+
local_files_only=local_files_only,
|
479 |
+
)
|
480 |
+
|
481 |
+
checkpoint_dict = torch.load(model_file, map_location=map_location)
|
482 |
+
|
483 |
+
try:
|
484 |
+
model.load_state_dict(checkpoint_dict["generator"])
|
485 |
+
except RuntimeError:
|
486 |
+
print(
|
487 |
+
f"[INFO] the pretrained checkpoint does not contain weight norm. Loading the checkpoint after removing weight norm!"
|
488 |
+
)
|
489 |
+
model.remove_weight_norm()
|
490 |
+
model.load_state_dict(checkpoint_dict["generator"])
|
491 |
+
|
492 |
+
return model
|
modules/bigvgan/config.json
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"resblock": "1",
|
3 |
+
"num_gpus": 0,
|
4 |
+
"batch_size": 32,
|
5 |
+
"learning_rate": 0.0001,
|
6 |
+
"adam_b1": 0.8,
|
7 |
+
"adam_b2": 0.99,
|
8 |
+
"lr_decay": 0.9999996,
|
9 |
+
"seed": 1234,
|
10 |
+
|
11 |
+
"upsample_rates": [4,4,2,2,2,2],
|
12 |
+
"upsample_kernel_sizes": [8,8,4,4,4,4],
|
13 |
+
"upsample_initial_channel": 1536,
|
14 |
+
"resblock_kernel_sizes": [3,7,11],
|
15 |
+
"resblock_dilation_sizes": [[1,3,5], [1,3,5], [1,3,5]],
|
16 |
+
|
17 |
+
"use_tanh_at_final": false,
|
18 |
+
"use_bias_at_final": false,
|
19 |
+
|
20 |
+
"activation": "snakebeta",
|
21 |
+
"snake_logscale": true,
|
22 |
+
|
23 |
+
"use_cqtd_instead_of_mrd": true,
|
24 |
+
"cqtd_filters": 128,
|
25 |
+
"cqtd_max_filters": 1024,
|
26 |
+
"cqtd_filters_scale": 1,
|
27 |
+
"cqtd_dilations": [1, 2, 4],
|
28 |
+
"cqtd_hop_lengths": [512, 256, 256],
|
29 |
+
"cqtd_n_octaves": [9, 9, 9],
|
30 |
+
"cqtd_bins_per_octaves": [24, 36, 48],
|
31 |
+
|
32 |
+
"mpd_reshapes": [2, 3, 5, 7, 11],
|
33 |
+
"use_spectral_norm": false,
|
34 |
+
"discriminator_channel_mult": 1,
|
35 |
+
|
36 |
+
"use_multiscale_melloss": true,
|
37 |
+
"lambda_melloss": 15,
|
38 |
+
|
39 |
+
"clip_grad_norm": 500,
|
40 |
+
|
41 |
+
"segment_size": 65536,
|
42 |
+
"num_mels": 80,
|
43 |
+
"num_freq": 1025,
|
44 |
+
"n_fft": 1024,
|
45 |
+
"hop_size": 256,
|
46 |
+
"win_size": 1024,
|
47 |
+
|
48 |
+
"sampling_rate": 22050,
|
49 |
+
|
50 |
+
"fmin": 0,
|
51 |
+
"fmax": null,
|
52 |
+
"fmax_for_loss": null,
|
53 |
+
|
54 |
+
"normalize_volume": true,
|
55 |
+
|
56 |
+
"num_workers": 4,
|
57 |
+
|
58 |
+
"dist_config": {
|
59 |
+
"dist_backend": "nccl",
|
60 |
+
"dist_url": "tcp://localhost:54321",
|
61 |
+
"world_size": 1
|
62 |
+
}
|
63 |
+
}
|
modules/bigvgan/env.py
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Adapted from https://github.com/jik876/hifi-gan under the MIT license.
|
2 |
+
# LICENSE is in incl_licenses directory.
|
3 |
+
|
4 |
+
import os
|
5 |
+
import shutil
|
6 |
+
|
7 |
+
|
8 |
+
class AttrDict(dict):
|
9 |
+
def __init__(self, *args, **kwargs):
|
10 |
+
super(AttrDict, self).__init__(*args, **kwargs)
|
11 |
+
self.__dict__ = self
|
12 |
+
|
13 |
+
|
14 |
+
def build_env(config, config_name, path):
|
15 |
+
t_path = os.path.join(path, config_name)
|
16 |
+
if config != t_path:
|
17 |
+
os.makedirs(path, exist_ok=True)
|
18 |
+
shutil.copyfile(config, os.path.join(path, config_name))
|
modules/bigvgan/meldataset.py
ADDED
@@ -0,0 +1,354 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Copyright (c) 2024 NVIDIA CORPORATION.
|
2 |
+
# Licensed under the MIT license.
|
3 |
+
|
4 |
+
# Adapted from https://github.com/jik876/hifi-gan under the MIT license.
|
5 |
+
# LICENSE is in incl_licenses directory.
|
6 |
+
|
7 |
+
import math
|
8 |
+
import os
|
9 |
+
import random
|
10 |
+
import torch
|
11 |
+
import torch.utils.data
|
12 |
+
import numpy as np
|
13 |
+
from librosa.util import normalize
|
14 |
+
from scipy.io.wavfile import read
|
15 |
+
from librosa.filters import mel as librosa_mel_fn
|
16 |
+
import pathlib
|
17 |
+
from tqdm import tqdm
|
18 |
+
|
19 |
+
MAX_WAV_VALUE = 32767.0 # NOTE: 32768.0 -1 to prevent int16 overflow (results in popping sound in corner cases)
|
20 |
+
|
21 |
+
|
22 |
+
def load_wav(full_path, sr_target):
|
23 |
+
sampling_rate, data = read(full_path)
|
24 |
+
if sampling_rate != sr_target:
|
25 |
+
raise RuntimeError(
|
26 |
+
f"Sampling rate of the file {full_path} is {sampling_rate} Hz, but the model requires {sr_target} Hz"
|
27 |
+
)
|
28 |
+
return data, sampling_rate
|
29 |
+
|
30 |
+
|
31 |
+
def dynamic_range_compression(x, C=1, clip_val=1e-5):
|
32 |
+
return np.log(np.clip(x, a_min=clip_val, a_max=None) * C)
|
33 |
+
|
34 |
+
|
35 |
+
def dynamic_range_decompression(x, C=1):
|
36 |
+
return np.exp(x) / C
|
37 |
+
|
38 |
+
|
39 |
+
def dynamic_range_compression_torch(x, C=1, clip_val=1e-5):
|
40 |
+
return torch.log(torch.clamp(x, min=clip_val) * C)
|
41 |
+
|
42 |
+
|
43 |
+
def dynamic_range_decompression_torch(x, C=1):
|
44 |
+
return torch.exp(x) / C
|
45 |
+
|
46 |
+
|
47 |
+
def spectral_normalize_torch(magnitudes):
|
48 |
+
return dynamic_range_compression_torch(magnitudes)
|
49 |
+
|
50 |
+
|
51 |
+
def spectral_de_normalize_torch(magnitudes):
|
52 |
+
return dynamic_range_decompression_torch(magnitudes)
|
53 |
+
|
54 |
+
|
55 |
+
mel_basis_cache = {}
|
56 |
+
hann_window_cache = {}
|
57 |
+
|
58 |
+
|
59 |
+
def mel_spectrogram(
|
60 |
+
y: torch.Tensor,
|
61 |
+
n_fft: int,
|
62 |
+
num_mels: int,
|
63 |
+
sampling_rate: int,
|
64 |
+
hop_size: int,
|
65 |
+
win_size: int,
|
66 |
+
fmin: int,
|
67 |
+
fmax: int = None,
|
68 |
+
center: bool = False,
|
69 |
+
) -> torch.Tensor:
|
70 |
+
"""
|
71 |
+
Calculate the mel spectrogram of an input signal.
|
72 |
+
This function uses slaney norm for the librosa mel filterbank (using librosa.filters.mel) and uses Hann window for STFT (using torch.stft).
|
73 |
+
|
74 |
+
Args:
|
75 |
+
y (torch.Tensor): Input signal.
|
76 |
+
n_fft (int): FFT size.
|
77 |
+
num_mels (int): Number of mel bins.
|
78 |
+
sampling_rate (int): Sampling rate of the input signal.
|
79 |
+
hop_size (int): Hop size for STFT.
|
80 |
+
win_size (int): Window size for STFT.
|
81 |
+
fmin (int): Minimum frequency for mel filterbank.
|
82 |
+
fmax (int): Maximum frequency for mel filterbank. If None, defaults to half the sampling rate (fmax = sr / 2.0) inside librosa_mel_fn
|
83 |
+
center (bool): Whether to pad the input to center the frames. Default is False.
|
84 |
+
|
85 |
+
Returns:
|
86 |
+
torch.Tensor: Mel spectrogram.
|
87 |
+
"""
|
88 |
+
if torch.min(y) < -1.0:
|
89 |
+
print(f"[WARNING] Min value of input waveform signal is {torch.min(y)}")
|
90 |
+
if torch.max(y) > 1.0:
|
91 |
+
print(f"[WARNING] Max value of input waveform signal is {torch.max(y)}")
|
92 |
+
|
93 |
+
device = y.device
|
94 |
+
key = f"{n_fft}_{num_mels}_{sampling_rate}_{hop_size}_{win_size}_{fmin}_{fmax}_{device}"
|
95 |
+
|
96 |
+
if key not in mel_basis_cache:
|
97 |
+
mel = librosa_mel_fn(
|
98 |
+
sr=sampling_rate, n_fft=n_fft, n_mels=num_mels, fmin=fmin, fmax=fmax
|
99 |
+
)
|
100 |
+
mel_basis_cache[key] = torch.from_numpy(mel).float().to(device)
|
101 |
+
hann_window_cache[key] = torch.hann_window(win_size).to(device)
|
102 |
+
|
103 |
+
mel_basis = mel_basis_cache[key]
|
104 |
+
hann_window = hann_window_cache[key]
|
105 |
+
|
106 |
+
padding = (n_fft - hop_size) // 2
|
107 |
+
y = torch.nn.functional.pad(
|
108 |
+
y.unsqueeze(1), (padding, padding), mode="reflect"
|
109 |
+
).squeeze(1)
|
110 |
+
|
111 |
+
spec = torch.stft(
|
112 |
+
y,
|
113 |
+
n_fft,
|
114 |
+
hop_length=hop_size,
|
115 |
+
win_length=win_size,
|
116 |
+
window=hann_window,
|
117 |
+
center=center,
|
118 |
+
pad_mode="reflect",
|
119 |
+
normalized=False,
|
120 |
+
onesided=True,
|
121 |
+
return_complex=True,
|
122 |
+
)
|
123 |
+
spec = torch.sqrt(torch.view_as_real(spec).pow(2).sum(-1) + 1e-9)
|
124 |
+
|
125 |
+
mel_spec = torch.matmul(mel_basis, spec)
|
126 |
+
mel_spec = spectral_normalize_torch(mel_spec)
|
127 |
+
|
128 |
+
return mel_spec
|
129 |
+
|
130 |
+
|
131 |
+
def get_mel_spectrogram(wav, h):
|
132 |
+
"""
|
133 |
+
Generate mel spectrogram from a waveform using given hyperparameters.
|
134 |
+
|
135 |
+
Args:
|
136 |
+
wav (torch.Tensor): Input waveform.
|
137 |
+
h: Hyperparameters object with attributes n_fft, num_mels, sampling_rate, hop_size, win_size, fmin, fmax.
|
138 |
+
|
139 |
+
Returns:
|
140 |
+
torch.Tensor: Mel spectrogram.
|
141 |
+
"""
|
142 |
+
return mel_spectrogram(
|
143 |
+
wav,
|
144 |
+
h.n_fft,
|
145 |
+
h.num_mels,
|
146 |
+
h.sampling_rate,
|
147 |
+
h.hop_size,
|
148 |
+
h.win_size,
|
149 |
+
h.fmin,
|
150 |
+
h.fmax,
|
151 |
+
)
|
152 |
+
|
153 |
+
|
154 |
+
def get_dataset_filelist(a):
|
155 |
+
training_files = []
|
156 |
+
validation_files = []
|
157 |
+
list_unseen_validation_files = []
|
158 |
+
|
159 |
+
with open(a.input_training_file, "r", encoding="utf-8") as fi:
|
160 |
+
training_files = [
|
161 |
+
os.path.join(a.input_wavs_dir, x.split("|")[0] + ".wav")
|
162 |
+
for x in fi.read().split("\n")
|
163 |
+
if len(x) > 0
|
164 |
+
]
|
165 |
+
print(f"first training file: {training_files[0]}")
|
166 |
+
|
167 |
+
with open(a.input_validation_file, "r", encoding="utf-8") as fi:
|
168 |
+
validation_files = [
|
169 |
+
os.path.join(a.input_wavs_dir, x.split("|")[0] + ".wav")
|
170 |
+
for x in fi.read().split("\n")
|
171 |
+
if len(x) > 0
|
172 |
+
]
|
173 |
+
print(f"first validation file: {validation_files[0]}")
|
174 |
+
|
175 |
+
for i in range(len(a.list_input_unseen_validation_file)):
|
176 |
+
with open(a.list_input_unseen_validation_file[i], "r", encoding="utf-8") as fi:
|
177 |
+
unseen_validation_files = [
|
178 |
+
os.path.join(a.list_input_unseen_wavs_dir[i], x.split("|")[0] + ".wav")
|
179 |
+
for x in fi.read().split("\n")
|
180 |
+
if len(x) > 0
|
181 |
+
]
|
182 |
+
print(
|
183 |
+
f"first unseen {i}th validation fileset: {unseen_validation_files[0]}"
|
184 |
+
)
|
185 |
+
list_unseen_validation_files.append(unseen_validation_files)
|
186 |
+
|
187 |
+
return training_files, validation_files, list_unseen_validation_files
|
188 |
+
|
189 |
+
|
190 |
+
class MelDataset(torch.utils.data.Dataset):
|
191 |
+
def __init__(
|
192 |
+
self,
|
193 |
+
training_files,
|
194 |
+
hparams,
|
195 |
+
segment_size,
|
196 |
+
n_fft,
|
197 |
+
num_mels,
|
198 |
+
hop_size,
|
199 |
+
win_size,
|
200 |
+
sampling_rate,
|
201 |
+
fmin,
|
202 |
+
fmax,
|
203 |
+
split=True,
|
204 |
+
shuffle=True,
|
205 |
+
n_cache_reuse=1,
|
206 |
+
device=None,
|
207 |
+
fmax_loss=None,
|
208 |
+
fine_tuning=False,
|
209 |
+
base_mels_path=None,
|
210 |
+
is_seen=True,
|
211 |
+
):
|
212 |
+
self.audio_files = training_files
|
213 |
+
random.seed(1234)
|
214 |
+
if shuffle:
|
215 |
+
random.shuffle(self.audio_files)
|
216 |
+
self.hparams = hparams
|
217 |
+
self.is_seen = is_seen
|
218 |
+
if self.is_seen:
|
219 |
+
self.name = pathlib.Path(self.audio_files[0]).parts[0]
|
220 |
+
else:
|
221 |
+
self.name = "-".join(pathlib.Path(self.audio_files[0]).parts[:2]).strip("/")
|
222 |
+
|
223 |
+
self.segment_size = segment_size
|
224 |
+
self.sampling_rate = sampling_rate
|
225 |
+
self.split = split
|
226 |
+
self.n_fft = n_fft
|
227 |
+
self.num_mels = num_mels
|
228 |
+
self.hop_size = hop_size
|
229 |
+
self.win_size = win_size
|
230 |
+
self.fmin = fmin
|
231 |
+
self.fmax = fmax
|
232 |
+
self.fmax_loss = fmax_loss
|
233 |
+
self.cached_wav = None
|
234 |
+
self.n_cache_reuse = n_cache_reuse
|
235 |
+
self._cache_ref_count = 0
|
236 |
+
self.device = device
|
237 |
+
self.fine_tuning = fine_tuning
|
238 |
+
self.base_mels_path = base_mels_path
|
239 |
+
|
240 |
+
print("[INFO] checking dataset integrity...")
|
241 |
+
for i in tqdm(range(len(self.audio_files))):
|
242 |
+
assert os.path.exists(
|
243 |
+
self.audio_files[i]
|
244 |
+
), f"{self.audio_files[i]} not found"
|
245 |
+
|
246 |
+
def __getitem__(self, index):
|
247 |
+
filename = self.audio_files[index]
|
248 |
+
if self._cache_ref_count == 0:
|
249 |
+
audio, sampling_rate = load_wav(filename, self.sampling_rate)
|
250 |
+
audio = audio / MAX_WAV_VALUE
|
251 |
+
if not self.fine_tuning:
|
252 |
+
audio = normalize(audio) * 0.95
|
253 |
+
self.cached_wav = audio
|
254 |
+
if sampling_rate != self.sampling_rate:
|
255 |
+
raise ValueError(
|
256 |
+
f"{sampling_rate} SR doesn't match target {self.sampling_rate} SR"
|
257 |
+
)
|
258 |
+
self._cache_ref_count = self.n_cache_reuse
|
259 |
+
else:
|
260 |
+
audio = self.cached_wav
|
261 |
+
self._cache_ref_count -= 1
|
262 |
+
|
263 |
+
audio = torch.FloatTensor(audio)
|
264 |
+
audio = audio.unsqueeze(0)
|
265 |
+
|
266 |
+
if not self.fine_tuning:
|
267 |
+
if self.split:
|
268 |
+
if audio.size(1) >= self.segment_size:
|
269 |
+
max_audio_start = audio.size(1) - self.segment_size
|
270 |
+
audio_start = random.randint(0, max_audio_start)
|
271 |
+
audio = audio[:, audio_start : audio_start + self.segment_size]
|
272 |
+
else:
|
273 |
+
audio = torch.nn.functional.pad(
|
274 |
+
audio, (0, self.segment_size - audio.size(1)), "constant"
|
275 |
+
)
|
276 |
+
|
277 |
+
mel = mel_spectrogram(
|
278 |
+
audio,
|
279 |
+
self.n_fft,
|
280 |
+
self.num_mels,
|
281 |
+
self.sampling_rate,
|
282 |
+
self.hop_size,
|
283 |
+
self.win_size,
|
284 |
+
self.fmin,
|
285 |
+
self.fmax,
|
286 |
+
center=False,
|
287 |
+
)
|
288 |
+
else: # Validation step
|
289 |
+
# Match audio length to self.hop_size * n for evaluation
|
290 |
+
if (audio.size(1) % self.hop_size) != 0:
|
291 |
+
audio = audio[:, : -(audio.size(1) % self.hop_size)]
|
292 |
+
mel = mel_spectrogram(
|
293 |
+
audio,
|
294 |
+
self.n_fft,
|
295 |
+
self.num_mels,
|
296 |
+
self.sampling_rate,
|
297 |
+
self.hop_size,
|
298 |
+
self.win_size,
|
299 |
+
self.fmin,
|
300 |
+
self.fmax,
|
301 |
+
center=False,
|
302 |
+
)
|
303 |
+
assert (
|
304 |
+
audio.shape[1] == mel.shape[2] * self.hop_size
|
305 |
+
), f"audio shape {audio.shape} mel shape {mel.shape}"
|
306 |
+
|
307 |
+
else:
|
308 |
+
mel = np.load(
|
309 |
+
os.path.join(
|
310 |
+
self.base_mels_path,
|
311 |
+
os.path.splitext(os.path.split(filename)[-1])[0] + ".npy",
|
312 |
+
)
|
313 |
+
)
|
314 |
+
mel = torch.from_numpy(mel)
|
315 |
+
|
316 |
+
if len(mel.shape) < 3:
|
317 |
+
mel = mel.unsqueeze(0)
|
318 |
+
|
319 |
+
if self.split:
|
320 |
+
frames_per_seg = math.ceil(self.segment_size / self.hop_size)
|
321 |
+
|
322 |
+
if audio.size(1) >= self.segment_size:
|
323 |
+
mel_start = random.randint(0, mel.size(2) - frames_per_seg - 1)
|
324 |
+
mel = mel[:, :, mel_start : mel_start + frames_per_seg]
|
325 |
+
audio = audio[
|
326 |
+
:,
|
327 |
+
mel_start
|
328 |
+
* self.hop_size : (mel_start + frames_per_seg)
|
329 |
+
* self.hop_size,
|
330 |
+
]
|
331 |
+
else:
|
332 |
+
mel = torch.nn.functional.pad(
|
333 |
+
mel, (0, frames_per_seg - mel.size(2)), "constant"
|
334 |
+
)
|
335 |
+
audio = torch.nn.functional.pad(
|
336 |
+
audio, (0, self.segment_size - audio.size(1)), "constant"
|
337 |
+
)
|
338 |
+
|
339 |
+
mel_loss = mel_spectrogram(
|
340 |
+
audio,
|
341 |
+
self.n_fft,
|
342 |
+
self.num_mels,
|
343 |
+
self.sampling_rate,
|
344 |
+
self.hop_size,
|
345 |
+
self.win_size,
|
346 |
+
self.fmin,
|
347 |
+
self.fmax_loss,
|
348 |
+
center=False,
|
349 |
+
)
|
350 |
+
|
351 |
+
return (mel.squeeze(), audio.squeeze(0), filename, mel_loss.squeeze())
|
352 |
+
|
353 |
+
def __len__(self):
|
354 |
+
return len(self.audio_files)
|
modules/bigvgan/utils.py
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Adapted from https://github.com/jik876/hifi-gan under the MIT license.
|
2 |
+
# LICENSE is in incl_licenses directory.
|
3 |
+
|
4 |
+
import glob
|
5 |
+
import os
|
6 |
+
import matplotlib
|
7 |
+
import torch
|
8 |
+
from torch.nn.utils import weight_norm
|
9 |
+
|
10 |
+
matplotlib.use("Agg")
|
11 |
+
import matplotlib.pylab as plt
|
12 |
+
from .meldataset import MAX_WAV_VALUE
|
13 |
+
from scipy.io.wavfile import write
|
14 |
+
|
15 |
+
|
16 |
+
def plot_spectrogram(spectrogram):
|
17 |
+
fig, ax = plt.subplots(figsize=(10, 2))
|
18 |
+
im = ax.imshow(spectrogram, aspect="auto", origin="lower", interpolation="none")
|
19 |
+
plt.colorbar(im, ax=ax)
|
20 |
+
|
21 |
+
fig.canvas.draw()
|
22 |
+
plt.close()
|
23 |
+
|
24 |
+
return fig
|
25 |
+
|
26 |
+
|
27 |
+
def plot_spectrogram_clipped(spectrogram, clip_max=2.0):
|
28 |
+
fig, ax = plt.subplots(figsize=(10, 2))
|
29 |
+
im = ax.imshow(
|
30 |
+
spectrogram,
|
31 |
+
aspect="auto",
|
32 |
+
origin="lower",
|
33 |
+
interpolation="none",
|
34 |
+
vmin=1e-6,
|
35 |
+
vmax=clip_max,
|
36 |
+
)
|
37 |
+
plt.colorbar(im, ax=ax)
|
38 |
+
|
39 |
+
fig.canvas.draw()
|
40 |
+
plt.close()
|
41 |
+
|
42 |
+
return fig
|
43 |
+
|
44 |
+
|
45 |
+
def init_weights(m, mean=0.0, std=0.01):
|
46 |
+
classname = m.__class__.__name__
|
47 |
+
if classname.find("Conv") != -1:
|
48 |
+
m.weight.data.normal_(mean, std)
|
49 |
+
|
50 |
+
|
51 |
+
def apply_weight_norm(m):
|
52 |
+
classname = m.__class__.__name__
|
53 |
+
if classname.find("Conv") != -1:
|
54 |
+
weight_norm(m)
|
55 |
+
|
56 |
+
|
57 |
+
def get_padding(kernel_size, dilation=1):
|
58 |
+
return int((kernel_size * dilation - dilation) / 2)
|
59 |
+
|
60 |
+
|
61 |
+
def load_checkpoint(filepath, device):
|
62 |
+
assert os.path.isfile(filepath)
|
63 |
+
print(f"Loading '{filepath}'")
|
64 |
+
checkpoint_dict = torch.load(filepath, map_location=device)
|
65 |
+
print("Complete.")
|
66 |
+
return checkpoint_dict
|
67 |
+
|
68 |
+
|
69 |
+
def save_checkpoint(filepath, obj):
|
70 |
+
print(f"Saving checkpoint to {filepath}")
|
71 |
+
torch.save(obj, filepath)
|
72 |
+
print("Complete.")
|
73 |
+
|
74 |
+
|
75 |
+
def scan_checkpoint(cp_dir, prefix, renamed_file=None):
|
76 |
+
# Fallback to original scanning logic first
|
77 |
+
pattern = os.path.join(cp_dir, prefix + "????????")
|
78 |
+
cp_list = glob.glob(pattern)
|
79 |
+
|
80 |
+
if len(cp_list) > 0:
|
81 |
+
last_checkpoint_path = sorted(cp_list)[-1]
|
82 |
+
print(f"[INFO] Resuming from checkpoint: '{last_checkpoint_path}'")
|
83 |
+
return last_checkpoint_path
|
84 |
+
|
85 |
+
# If no pattern-based checkpoints are found, check for renamed file
|
86 |
+
if renamed_file:
|
87 |
+
renamed_path = os.path.join(cp_dir, renamed_file)
|
88 |
+
if os.path.isfile(renamed_path):
|
89 |
+
print(f"[INFO] Resuming from renamed checkpoint: '{renamed_file}'")
|
90 |
+
return renamed_path
|
91 |
+
|
92 |
+
return None
|
93 |
+
|
94 |
+
|
95 |
+
def save_audio(audio, path, sr):
|
96 |
+
# wav: torch with 1d shape
|
97 |
+
audio = audio * MAX_WAV_VALUE
|
98 |
+
audio = audio.cpu().numpy().astype("int16")
|
99 |
+
write(path, sr, audio)
|