Text-to-Speech
Transformers
Safetensors
hifi_gan
feature-extraction
audio
vocoder
hifi-gan
bigvgan
neural-vocoder
speaker-conditioning
audio-watermarking
custom_code
Instructions to use mlr2000/vocoder-large with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use mlr2000/vocoder-large with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-to-speech", model="mlr2000/vocoder-large", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("mlr2000/vocoder-large", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Upload folder using huggingface_hub
Browse files- README.md +114 -0
- __init__.py +4 -0
- alias_free_act.py +30 -0
- alias_free_filter.py +101 -0
- alias_free_resample.py +58 -0
- bigvgan_activations.py +129 -0
- bigvgan_model.py +361 -0
- config.json +193 -0
- configuration_hifigan.py +192 -0
- example_roundtrip.ipynb +125 -0
- model.safetensors +3 -0
- modeling_hifigan.py +131 -0
- speaker_embedding_config.py +31 -0
- temporal_adapter.py +100 -0
README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
library_name: transformers
|
| 3 |
+
tags:
|
| 4 |
+
- audio
|
| 5 |
+
- text-to-speech
|
| 6 |
+
- vocoder
|
| 7 |
+
- hifi-gan
|
| 8 |
+
- bigvgan
|
| 9 |
+
- neural-vocoder
|
| 10 |
+
- speaker-conditioning
|
| 11 |
+
- audio-watermarking
|
| 12 |
+
pipeline_tag: text-to-speech
|
| 13 |
+
license: cc-by-4.0
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# VocBulwark HiFi-GAN β watermarking neural vocoder (inference export)
|
| 17 |
+
|
| 18 |
+
Speaker-conditioned **BigVGAN / HiFi-GAN** neural vocoder. It turns an input mel-spectrogram (*what* is said) into a 24 kHz waveform, conditioned on a **precomputed 768-d speaker embedding** (*whose* voice). Every clip it generates carries a **fixed 50-bit provenance watermark** that identifies this specific model instance, see [Watermark](#watermark).
|
| 19 |
+
|
| 20 |
+
This is the **lean, inference-only** vocoder: the frozen perceptual-loss base models (Whisper / WavLM / Wav2Vec2), the training discriminators, **and the speaker encoder** have all been stripped, you pass the speaker embedding in. Use the companion **speaker-encoder** repo to turn a reference clip into that embedding. The modeling code is bundled, so it loads with `trust_remote_code=True` **without** the training repo.
|
| 21 |
+
|
| 22 |
+
## Model summary
|
| 23 |
+
|
| 24 |
+
| | |
|
| 25 |
+
|---|---|
|
| 26 |
+
| Architecture | `HiFiGANArchitecture` (BigVGAN generator, snakebeta activation) |
|
| 27 |
+
| Inputs | log-mel spectrogram (**96 mel channels**) + speaker embedding (**768-d**) |
|
| 28 |
+
| Output | mono waveform, **24 kHz** |
|
| 29 |
+
| Speaker conditioning | precomputed embedding (from the companion speaker encoder) |
|
| 30 |
+
| Generator | initial channels 1536, upsample rates [4, 4, 2, 2, 2, 2] |
|
| 31 |
+
| Watermark | 50-bit fixed VocBulwark signature, always embedded |
|
| 32 |
+
| Framework | π€ Transformers, PyTorch, safetensors |
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
## Companion Models
|
| 36 |
+
|
| 37 |
+
This model is part of a set of 6 repositories:
|
| 38 |
+
|
| 39 |
+
| Repo | Role |
|
| 40 |
+
|------|------|
|
| 41 |
+
| `mlr2000/vocoder-large` | Large vocoder (this repo) |
|
| 42 |
+
| `mlr2000/vocoder-large-watermark-detector` | Watermark detector for the large model |
|
| 43 |
+
| `mlr2000/vocoder-large-speaker-encoder` | Speaker encoder for the large model |
|
| 44 |
+
| `mlr2000/vocoder-small` | Small vocoder |
|
| 45 |
+
| `mlr2000/vocoder-small-watermark-detector` | Watermark detector for the small model |
|
| 46 |
+
| `mlr2000/vocoder-small-speaker-encoder` | Speaker encoder for the small model |
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
## Usage
|
| 50 |
+
|
| 51 |
+
```python
|
| 52 |
+
import torch
|
| 53 |
+
from transformers import AutoModel
|
| 54 |
+
|
| 55 |
+
model = AutoModel.from_pretrained("mlr2000/vocoder-large", trust_remote_code=True).eval()
|
| 56 |
+
|
| 57 |
+
mel = torch.randn(1, model.config.hifigan_in_channels, 200) # [B, mel, T]
|
| 58 |
+
emb = torch.randn(1, model.config.speaker_embedding_size) # [B, 768] from the speaker encoder
|
| 59 |
+
with torch.no_grad():
|
| 60 |
+
audio = model(mel_spectrogram=mel, speaker_embedding=emb).audio
|
| 61 |
+
# audio: [B, 1, samples] @ model.config.target_sample_rate
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
See **`example_roundtrip.ipynb`** in this repo for the full pipeline (reference
|
| 65 |
+
clip β speaker encoder β embedding β vocode β verify watermark).
|
| 66 |
+
|
| 67 |
+
## Training
|
| 68 |
+
|
| 69 |
+
| | |
|
| 70 |
+
|---|---|
|
| 71 |
+
| Training data | Multilingual LibriSpeech (8 languages, ~22,200h) and Common Voice (14 languages, ~3,000h) |
|
| 72 |
+
| Training steps | 1,000,000 |
|
| 73 |
+
| Hardware | 2 Γ NVIDIA H200 GPUs |
|
| 74 |
+
| Training objective | Discriminator-free: mel spectrogram + WavLM + wav2vec 2.0 + Whisper encoder losses |
|
| 75 |
+
| Effective batch size | 32 |
|
| 76 |
+
| Learning rate | 1e-4 |
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
## Watermark
|
| 80 |
+
|
| 81 |
+
Every clip this model generates carries a **fixed 50-bit provenance watermark** (`config.fixed_watermark`) that identifies this specific model instance. It is embedded automatically inside `forward` and **cannot be disabled or changed** through this interface β there is deliberately no watermark argument to override.
|
| 82 |
+
|
| 83 |
+
To verify whether a given audio clip was generated by this model, use the companion **detector** repo (`mlr2000/vocoder-large-watermark-detector`), which extracts the embedded bits and compares them to the known fixed code.
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
## Notes
|
| 87 |
+
|
| 88 |
+
- Inputs: log-mel spectrogram (`config.hifigan_in_channels` channels) and a
|
| 89 |
+
`[B, config.speaker_embedding_size]` speaker embedding.
|
| 90 |
+
- Output: mono waveform at `config.target_sample_rate`.
|
| 91 |
+
- Use the embedding from the speaker encoder this vocoder was **trained with**, a
|
| 92 |
+
mismatched encoder will not condition it correctly.
|
| 93 |
+
- Not intended for voice cloning of real individuals without consent, or any
|
| 94 |
+
deceptive / impersonation use.
|
| 95 |
+
|
| 96 |
+
## Citation
|
| 97 |
+
|
| 98 |
+
If you use this model, please cite:
|
| 99 |
+
|
| 100 |
+
```bibtex
|
| 101 |
+
@misc{muletta2026,
|
| 102 |
+
title = {Training a Discriminator-Free Foundation Vocoder
|
| 103 |
+
with Integrated Audio Watermarking},
|
| 104 |
+
author = {Muletta, Romolo and Deriu, Jan},
|
| 105 |
+
year = {2026},
|
| 106 |
+
note = {VT2 Project Report, ZHAW School of Engineering}
|
| 107 |
+
}
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
## License
|
| 111 |
+
|
| 112 |
+
`cc-by-4.0`. Trained on MLS (CC-BY-4.0) and Common Voice (CC0); builds on
|
| 113 |
+
BigVGAN (MIT) and wav2vec 2.0 (Apache-2.0). Please retain attribution when
|
| 114 |
+
redistributing or building on this model.
|
__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .configuration_hifigan import HiFiGANConfig
|
| 2 |
+
from .modeling_hifigan import HiFiGANArchitecture, HiFiGANOutput
|
| 3 |
+
|
| 4 |
+
__all__ = ["HiFiGANConfig", "HiFiGANArchitecture", "HiFiGANOutput"]
|
alias_free_act.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Adapted from https://github.com/junjun3518/alias-free-torch under the Apache License 2.0
|
| 2 |
+
# LICENSE is in incl_licenses directory.
|
| 3 |
+
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
from .alias_free_resample import UpSample1d, DownSample1d
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class Activation1d(nn.Module):
|
| 9 |
+
def __init__(
|
| 10 |
+
self,
|
| 11 |
+
activation,
|
| 12 |
+
up_ratio: int = 2,
|
| 13 |
+
down_ratio: int = 2,
|
| 14 |
+
up_kernel_size: int = 12,
|
| 15 |
+
down_kernel_size: int = 12,
|
| 16 |
+
):
|
| 17 |
+
super().__init__()
|
| 18 |
+
self.up_ratio = up_ratio
|
| 19 |
+
self.down_ratio = down_ratio
|
| 20 |
+
self.act = activation
|
| 21 |
+
self.upsample = UpSample1d(up_ratio, up_kernel_size)
|
| 22 |
+
self.downsample = DownSample1d(down_ratio, down_kernel_size)
|
| 23 |
+
|
| 24 |
+
# x: [B,C,T]
|
| 25 |
+
def forward(self, x):
|
| 26 |
+
x = self.upsample(x)
|
| 27 |
+
x = self.act(x)
|
| 28 |
+
x = self.downsample(x)
|
| 29 |
+
|
| 30 |
+
return x
|
alias_free_filter.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Adapted from https://github.com/junjun3518/alias-free-torch under the Apache License 2.0
|
| 2 |
+
# LICENSE is in incl_licenses directory.
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
import torch.nn as nn
|
| 6 |
+
import torch.nn.functional as F
|
| 7 |
+
import math
|
| 8 |
+
|
| 9 |
+
if "sinc" in dir(torch):
|
| 10 |
+
sinc = torch.sinc
|
| 11 |
+
else:
|
| 12 |
+
# This code is adopted from adefossez's julius.core.sinc under the MIT License
|
| 13 |
+
# https://adefossez.github.io/julius/julius/core.html
|
| 14 |
+
# LICENSE is in incl_licenses directory.
|
| 15 |
+
def sinc(x: torch.Tensor):
|
| 16 |
+
"""
|
| 17 |
+
Implementation of sinc, i.e. sin(pi * x) / (pi * x)
|
| 18 |
+
__Warning__: Different to julius.sinc, the input is multiplied by `pi`!
|
| 19 |
+
"""
|
| 20 |
+
return torch.where(
|
| 21 |
+
x == 0,
|
| 22 |
+
torch.tensor(1.0, device=x.device, dtype=x.dtype),
|
| 23 |
+
torch.sin(math.pi * x) / math.pi / x,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
# This code is adopted from adefossez's julius.lowpass.LowPassFilters under the MIT License
|
| 28 |
+
# https://adefossez.github.io/julius/julius/lowpass.html
|
| 29 |
+
# LICENSE is in incl_licenses directory.
|
| 30 |
+
def kaiser_sinc_filter1d(
|
| 31 |
+
cutoff, half_width, kernel_size
|
| 32 |
+
): # return filter [1,1,kernel_size]
|
| 33 |
+
even = kernel_size % 2 == 0
|
| 34 |
+
half_size = kernel_size // 2
|
| 35 |
+
|
| 36 |
+
# For kaiser window
|
| 37 |
+
delta_f = 4 * half_width
|
| 38 |
+
A = 2.285 * (half_size - 1) * math.pi * delta_f + 7.95
|
| 39 |
+
if A > 50.0:
|
| 40 |
+
beta = 0.1102 * (A - 8.7)
|
| 41 |
+
elif A >= 21.0:
|
| 42 |
+
beta = 0.5842 * (A - 21) ** 0.4 + 0.07886 * (A - 21.0)
|
| 43 |
+
else:
|
| 44 |
+
beta = 0.0
|
| 45 |
+
window = torch.kaiser_window(kernel_size, beta=beta, periodic=False)
|
| 46 |
+
|
| 47 |
+
# ratio = 0.5/cutoff -> 2 * cutoff = 1 / ratio
|
| 48 |
+
if even:
|
| 49 |
+
time = torch.arange(-half_size, half_size) + 0.5
|
| 50 |
+
else:
|
| 51 |
+
time = torch.arange(kernel_size) - half_size
|
| 52 |
+
if cutoff == 0:
|
| 53 |
+
filter_ = torch.zeros_like(time)
|
| 54 |
+
else:
|
| 55 |
+
filter_ = 2 * cutoff * window * sinc(2 * cutoff * time)
|
| 56 |
+
"""
|
| 57 |
+
Normalize filter to have sum = 1, otherwise we will have a small leakage of the constant component in the input signal.
|
| 58 |
+
"""
|
| 59 |
+
filter_ /= filter_.sum()
|
| 60 |
+
filter = filter_.view(1, 1, kernel_size)
|
| 61 |
+
|
| 62 |
+
return filter
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class LowPassFilter1d(nn.Module):
|
| 66 |
+
def __init__(
|
| 67 |
+
self,
|
| 68 |
+
cutoff=0.5,
|
| 69 |
+
half_width=0.6,
|
| 70 |
+
stride: int = 1,
|
| 71 |
+
padding: bool = True,
|
| 72 |
+
padding_mode: str = "replicate",
|
| 73 |
+
kernel_size: int = 12,
|
| 74 |
+
):
|
| 75 |
+
"""
|
| 76 |
+
kernel_size should be even number for stylegan3 setup, in this implementation, odd number is also possible.
|
| 77 |
+
"""
|
| 78 |
+
super().__init__()
|
| 79 |
+
if cutoff < -0.0:
|
| 80 |
+
raise ValueError("Minimum cutoff must be larger than zero.")
|
| 81 |
+
if cutoff > 0.5:
|
| 82 |
+
raise ValueError("A cutoff above 0.5 does not make sense.")
|
| 83 |
+
self.kernel_size = kernel_size
|
| 84 |
+
self.even = kernel_size % 2 == 0
|
| 85 |
+
self.pad_left = kernel_size // 2 - int(self.even)
|
| 86 |
+
self.pad_right = kernel_size // 2
|
| 87 |
+
self.stride = stride
|
| 88 |
+
self.padding = padding
|
| 89 |
+
self.padding_mode = padding_mode
|
| 90 |
+
filter = kaiser_sinc_filter1d(cutoff, half_width, kernel_size)
|
| 91 |
+
self.register_buffer("filter", filter)
|
| 92 |
+
|
| 93 |
+
# Input [B, C, T]
|
| 94 |
+
def forward(self, x):
|
| 95 |
+
_, C, _ = x.shape
|
| 96 |
+
|
| 97 |
+
if self.padding:
|
| 98 |
+
x = F.pad(x, (self.pad_left, self.pad_right), mode=self.padding_mode)
|
| 99 |
+
out = F.conv1d(x, self.filter.expand(C, -1, -1), stride=self.stride, groups=C)
|
| 100 |
+
|
| 101 |
+
return out
|
alias_free_resample.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Adapted from https://github.com/junjun3518/alias-free-torch under the Apache License 2.0
|
| 2 |
+
# LICENSE is in incl_licenses directory.
|
| 3 |
+
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
from torch.nn import functional as F
|
| 6 |
+
from .alias_free_filter import LowPassFilter1d
|
| 7 |
+
from .alias_free_filter import kaiser_sinc_filter1d
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class UpSample1d(nn.Module):
|
| 11 |
+
def __init__(self, ratio=2, kernel_size=None):
|
| 12 |
+
super().__init__()
|
| 13 |
+
self.ratio = ratio
|
| 14 |
+
self.kernel_size = (
|
| 15 |
+
int(6 * ratio // 2) * 2 if kernel_size is None else kernel_size
|
| 16 |
+
)
|
| 17 |
+
self.stride = ratio
|
| 18 |
+
self.pad = self.kernel_size // ratio - 1
|
| 19 |
+
self.pad_left = self.pad * self.stride + (self.kernel_size - self.stride) // 2
|
| 20 |
+
self.pad_right = (
|
| 21 |
+
self.pad * self.stride + (self.kernel_size - self.stride + 1) // 2
|
| 22 |
+
)
|
| 23 |
+
filter = kaiser_sinc_filter1d(
|
| 24 |
+
cutoff=0.5 / ratio, half_width=0.6 / ratio, kernel_size=self.kernel_size
|
| 25 |
+
)
|
| 26 |
+
self.register_buffer("filter", filter)
|
| 27 |
+
|
| 28 |
+
# x: [B, C, T]
|
| 29 |
+
def forward(self, x):
|
| 30 |
+
_, C, _ = x.shape
|
| 31 |
+
|
| 32 |
+
x = F.pad(x, (self.pad, self.pad), mode="replicate")
|
| 33 |
+
x = self.ratio * F.conv_transpose1d(
|
| 34 |
+
x, self.filter.expand(C, -1, -1), stride=self.stride, groups=C
|
| 35 |
+
)
|
| 36 |
+
x = x[..., self.pad_left : -self.pad_right]
|
| 37 |
+
|
| 38 |
+
return x
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class DownSample1d(nn.Module):
|
| 42 |
+
def __init__(self, ratio=2, kernel_size=None):
|
| 43 |
+
super().__init__()
|
| 44 |
+
self.ratio = ratio
|
| 45 |
+
self.kernel_size = (
|
| 46 |
+
int(6 * ratio // 2) * 2 if kernel_size is None else kernel_size
|
| 47 |
+
)
|
| 48 |
+
self.lowpass = LowPassFilter1d(
|
| 49 |
+
cutoff=0.5 / ratio,
|
| 50 |
+
half_width=0.6 / ratio,
|
| 51 |
+
stride=ratio,
|
| 52 |
+
kernel_size=self.kernel_size,
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
def forward(self, x):
|
| 56 |
+
xx = self.lowpass(x)
|
| 57 |
+
|
| 58 |
+
return xx
|
bigvgan_activations.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2024 NVIDIA CORPORATION.
|
| 2 |
+
# Licensed under the MIT license.
|
| 3 |
+
|
| 4 |
+
# Adapted from https://github.com/NVIDIA/BigVGAN under the MIT license.
|
| 5 |
+
# LICENSE is in incl_licenses directory.
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
from torch import nn, sin, pow
|
| 9 |
+
from torch.nn import Parameter
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class Snake(nn.Module):
|
| 13 |
+
"""
|
| 14 |
+
Implementation of a sine-based periodic activation function
|
| 15 |
+
Shape:
|
| 16 |
+
- Input: (B, C, T)
|
| 17 |
+
- Output: (B, C, T), same shape as the input
|
| 18 |
+
Parameters:
|
| 19 |
+
- alpha - trainable parameter
|
| 20 |
+
References:
|
| 21 |
+
- This activation function is from this paper by Liu Ziyin, Tilman Hartwig, Masahito Ueda:
|
| 22 |
+
https://arxiv.org/abs/2006.08195
|
| 23 |
+
Examples:
|
| 24 |
+
>>> a1 = snake(256)
|
| 25 |
+
>>> x = torch.randn(256)
|
| 26 |
+
>>> x = a1(x)
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
def __init__(
|
| 30 |
+
self, in_features, alpha=1.0, alpha_trainable=True, alpha_logscale=False
|
| 31 |
+
):
|
| 32 |
+
"""
|
| 33 |
+
Initialization.
|
| 34 |
+
INPUT:
|
| 35 |
+
- in_features: shape of the input
|
| 36 |
+
- alpha: trainable parameter
|
| 37 |
+
alpha is initialized to 1 by default, higher values = higher-frequency.
|
| 38 |
+
alpha will be trained along with the rest of your model.
|
| 39 |
+
"""
|
| 40 |
+
super(Snake, self).__init__()
|
| 41 |
+
self.in_features = in_features
|
| 42 |
+
|
| 43 |
+
# Initialize alpha
|
| 44 |
+
self.alpha_logscale = alpha_logscale
|
| 45 |
+
if self.alpha_logscale: # Log scale alphas initialized to zeros
|
| 46 |
+
self.alpha = Parameter(torch.zeros(in_features) * alpha)
|
| 47 |
+
else: # Linear scale alphas initialized to ones
|
| 48 |
+
self.alpha = Parameter(torch.ones(in_features) * alpha)
|
| 49 |
+
|
| 50 |
+
self.alpha.requires_grad = alpha_trainable
|
| 51 |
+
|
| 52 |
+
self.no_div_by_zero = 0.000000001
|
| 53 |
+
|
| 54 |
+
def forward(self, x):
|
| 55 |
+
"""
|
| 56 |
+
Forward pass of the function.
|
| 57 |
+
Applies the function to the input elementwise.
|
| 58 |
+
Snake βΆ= x + 1/a * sin^2 (xa)
|
| 59 |
+
"""
|
| 60 |
+
alpha = self.alpha.unsqueeze(0).unsqueeze(-1) # Line up with x to [B, C, T]
|
| 61 |
+
if self.alpha_logscale:
|
| 62 |
+
alpha = torch.exp(alpha)
|
| 63 |
+
x = x + (1.0 / (alpha + self.no_div_by_zero)) * pow(sin(x * alpha), 2)
|
| 64 |
+
|
| 65 |
+
return x
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class SnakeBeta(nn.Module):
|
| 69 |
+
"""
|
| 70 |
+
A modified Snake function which uses separate parameters for the magnitude of the periodic components
|
| 71 |
+
Shape:
|
| 72 |
+
- Input: (B, C, T)
|
| 73 |
+
- Output: (B, C, T), same shape as the input
|
| 74 |
+
Parameters:
|
| 75 |
+
- alpha - trainable parameter that controls frequency
|
| 76 |
+
- beta - trainable parameter that controls magnitude
|
| 77 |
+
References:
|
| 78 |
+
- This activation function is a modified version based on this paper by Liu Ziyin, Tilman Hartwig, Masahito Ueda:
|
| 79 |
+
https://arxiv.org/abs/2006.08195
|
| 80 |
+
Examples:
|
| 81 |
+
>>> a1 = snakebeta(256)
|
| 82 |
+
>>> x = torch.randn(256)
|
| 83 |
+
>>> x = a1(x)
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
def __init__(
|
| 87 |
+
self, in_features, alpha=1.0, alpha_trainable=True, alpha_logscale=False
|
| 88 |
+
):
|
| 89 |
+
"""
|
| 90 |
+
Initialization.
|
| 91 |
+
INPUT:
|
| 92 |
+
- in_features: shape of the input
|
| 93 |
+
- alpha - trainable parameter that controls frequency
|
| 94 |
+
- beta - trainable parameter that controls magnitude
|
| 95 |
+
alpha is initialized to 1 by default, higher values = higher-frequency.
|
| 96 |
+
beta is initialized to 1 by default, higher values = higher-magnitude.
|
| 97 |
+
alpha will be trained along with the rest of your model.
|
| 98 |
+
"""
|
| 99 |
+
super(SnakeBeta, self).__init__()
|
| 100 |
+
self.in_features = in_features
|
| 101 |
+
|
| 102 |
+
# Initialize alpha
|
| 103 |
+
self.alpha_logscale = alpha_logscale
|
| 104 |
+
if self.alpha_logscale: # Log scale alphas initialized to zeros
|
| 105 |
+
self.alpha = Parameter(torch.zeros(in_features) * alpha)
|
| 106 |
+
self.beta = Parameter(torch.zeros(in_features) * alpha)
|
| 107 |
+
else: # Linear scale alphas initialized to ones
|
| 108 |
+
self.alpha = Parameter(torch.ones(in_features) * alpha)
|
| 109 |
+
self.beta = Parameter(torch.ones(in_features) * alpha)
|
| 110 |
+
|
| 111 |
+
self.alpha.requires_grad = alpha_trainable
|
| 112 |
+
self.beta.requires_grad = alpha_trainable
|
| 113 |
+
|
| 114 |
+
self.no_div_by_zero = 0.000000001
|
| 115 |
+
|
| 116 |
+
def forward(self, x):
|
| 117 |
+
"""
|
| 118 |
+
Forward pass of the function.
|
| 119 |
+
Applies the function to the input elementwise.
|
| 120 |
+
SnakeBeta βΆ= x + 1/b * sin^2 (xa)
|
| 121 |
+
"""
|
| 122 |
+
alpha = self.alpha.unsqueeze(0).unsqueeze(-1) # Line up with x to [B, C, T]
|
| 123 |
+
beta = self.beta.unsqueeze(0).unsqueeze(-1)
|
| 124 |
+
if self.alpha_logscale:
|
| 125 |
+
alpha = torch.exp(alpha)
|
| 126 |
+
beta = torch.exp(beta)
|
| 127 |
+
x = x + (1.0 / (beta + self.no_div_by_zero)) * pow(sin(x * alpha), 2)
|
| 128 |
+
|
| 129 |
+
return x
|
bigvgan_model.py
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2024 NVIDIA CORPORATION.
|
| 2 |
+
# Licensed under the MIT license.
|
| 3 |
+
|
| 4 |
+
# Adapted from https://github.com/NVIDIA/BigVGAN under the MIT license.
|
| 5 |
+
# LICENSE is in incl_licenses directory.
|
| 6 |
+
|
| 7 |
+
from typing import *
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from torch.nn import Conv1d, ConvTranspose1d
|
| 12 |
+
from torch.nn.utils import weight_norm, remove_weight_norm
|
| 13 |
+
|
| 14 |
+
from . import bigvgan_activations as activations
|
| 15 |
+
from .alias_free_act import Activation1d as TorchActivation1d
|
| 16 |
+
from .temporal_adapter import TemporalAdapterBlock
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def init_weights(m, mean=0.0, std=0.01):
|
| 20 |
+
classname = m.__class__.__name__
|
| 21 |
+
if classname.find("Conv") != -1:
|
| 22 |
+
m.weight.data.normal_(mean, std)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def apply_weight_norm(m):
|
| 26 |
+
classname = m.__class__.__name__
|
| 27 |
+
if classname.find("Conv") != -1:
|
| 28 |
+
weight_norm(m)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def get_padding(kernel_size, dilation=1):
|
| 32 |
+
return int((kernel_size * dilation - dilation) / 2)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class FiLMBlock(nn.Module):
|
| 36 |
+
"""Feature-wise Linear Modulation for watermark conditioning.
|
| 37 |
+
|
| 38 |
+
Maps a binary watermark vector to per-channel scale (Ξ³) and shift (Ξ²)
|
| 39 |
+
parameters that modulate intermediate feature maps.
|
| 40 |
+
"""
|
| 41 |
+
|
| 42 |
+
def __init__(self, watermark_bits: int, channels: int, hidden_dim: int = 128):
|
| 43 |
+
super().__init__()
|
| 44 |
+
self.scale_net = nn.Sequential(
|
| 45 |
+
nn.Linear(watermark_bits, hidden_dim),
|
| 46 |
+
nn.ReLU(),
|
| 47 |
+
nn.Linear(hidden_dim, channels),
|
| 48 |
+
)
|
| 49 |
+
self.shift_net = nn.Sequential(
|
| 50 |
+
nn.Linear(watermark_bits, hidden_dim),
|
| 51 |
+
nn.ReLU(),
|
| 52 |
+
nn.Linear(hidden_dim, channels),
|
| 53 |
+
)
|
| 54 |
+
# Initialize close to identity: Ξ³β1, Ξ²β0
|
| 55 |
+
nn.init.zeros_(self.scale_net[-1].weight)
|
| 56 |
+
nn.init.zeros_(self.scale_net[-1].bias)
|
| 57 |
+
nn.init.zeros_(self.shift_net[-1].weight)
|
| 58 |
+
nn.init.zeros_(self.shift_net[-1].bias)
|
| 59 |
+
|
| 60 |
+
def forward(self, x, watermark):
|
| 61 |
+
"""
|
| 62 |
+
Args:
|
| 63 |
+
x: [B, C, T] feature tensor
|
| 64 |
+
watermark: [B, watermark_bits] float tensor
|
| 65 |
+
Returns:
|
| 66 |
+
[B, C, T] modulated features
|
| 67 |
+
"""
|
| 68 |
+
gamma = self.scale_net(watermark).unsqueeze(-1) # [B, C, 1]
|
| 69 |
+
beta = self.shift_net(watermark).unsqueeze(-1) # [B, C, 1]
|
| 70 |
+
return (1 + gamma) * x + beta # Ξ³β0 β identity
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
class AMPBlock1(torch.nn.Module):
|
| 74 |
+
"""
|
| 75 |
+
AMPBlock applies Snake / SnakeBeta activation functions with trainable parameters that control periodicity, defined for each layer.
|
| 76 |
+
AMPBlock1 has additional self.convs2 that contains additional Conv1d layers with a fixed dilation=1 followed by each layer in self.convs1
|
| 77 |
+
|
| 78 |
+
Args:
|
| 79 |
+
h (AttrDict): Hyperparameters.
|
| 80 |
+
channels (int): Number of convolution channels.
|
| 81 |
+
kernel_size (int): Size of the convolution kernel. Default is 3.
|
| 82 |
+
dilation (tuple): Dilation rates for the convolutions. Each dilation layer has two convolutions. Default is (1, 3, 5).
|
| 83 |
+
activation (str): Activation function type. Should be either 'snake' or 'snakebeta'. Default is None.
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
def __init__(
|
| 87 |
+
self,
|
| 88 |
+
channels: int,
|
| 89 |
+
kernel_size: int = 3,
|
| 90 |
+
dilation: tuple = (1, 3, 5),
|
| 91 |
+
activation: str = None,
|
| 92 |
+
snake_logscale: bool = True,
|
| 93 |
+
):
|
| 94 |
+
super().__init__()
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
self.convs1 = nn.ModuleList(
|
| 98 |
+
[
|
| 99 |
+
weight_norm(
|
| 100 |
+
Conv1d(
|
| 101 |
+
channels,
|
| 102 |
+
channels,
|
| 103 |
+
kernel_size,
|
| 104 |
+
stride=1,
|
| 105 |
+
dilation=d,
|
| 106 |
+
padding=get_padding(kernel_size, d),
|
| 107 |
+
)
|
| 108 |
+
)
|
| 109 |
+
for d in dilation
|
| 110 |
+
]
|
| 111 |
+
)
|
| 112 |
+
self.convs1.apply(init_weights)
|
| 113 |
+
|
| 114 |
+
self.convs2 = nn.ModuleList(
|
| 115 |
+
[
|
| 116 |
+
weight_norm(
|
| 117 |
+
Conv1d(
|
| 118 |
+
channels,
|
| 119 |
+
channels,
|
| 120 |
+
kernel_size,
|
| 121 |
+
stride=1,
|
| 122 |
+
dilation=1,
|
| 123 |
+
padding=get_padding(kernel_size, 1),
|
| 124 |
+
)
|
| 125 |
+
)
|
| 126 |
+
for _ in range(len(dilation))
|
| 127 |
+
]
|
| 128 |
+
)
|
| 129 |
+
self.convs2.apply(init_weights)
|
| 130 |
+
|
| 131 |
+
self.num_layers = len(self.convs1) + len(
|
| 132 |
+
self.convs2
|
| 133 |
+
) # Total number of conv layers
|
| 134 |
+
|
| 135 |
+
Activation1d = TorchActivation1d
|
| 136 |
+
|
| 137 |
+
# Activation functions
|
| 138 |
+
if activation == "snake":
|
| 139 |
+
self.activations = nn.ModuleList(
|
| 140 |
+
[
|
| 141 |
+
Activation1d(
|
| 142 |
+
activation=activations.Snake(
|
| 143 |
+
channels, alpha_logscale=snake_logscale
|
| 144 |
+
)
|
| 145 |
+
)
|
| 146 |
+
for _ in range(self.num_layers)
|
| 147 |
+
]
|
| 148 |
+
)
|
| 149 |
+
elif activation == "snakebeta":
|
| 150 |
+
self.activations = nn.ModuleList(
|
| 151 |
+
[
|
| 152 |
+
Activation1d(
|
| 153 |
+
activation=activations.SnakeBeta(
|
| 154 |
+
channels, alpha_logscale=snake_logscale
|
| 155 |
+
)
|
| 156 |
+
)
|
| 157 |
+
for _ in range(self.num_layers)
|
| 158 |
+
]
|
| 159 |
+
)
|
| 160 |
+
else:
|
| 161 |
+
raise NotImplementedError(
|
| 162 |
+
"activation incorrectly specified. check the config file and look for 'activation'."
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
def forward(self, x):
|
| 166 |
+
acts1, acts2 = self.activations[::2], self.activations[1::2]
|
| 167 |
+
for c1, c2, a1, a2 in zip(self.convs1, self.convs2, acts1, acts2):
|
| 168 |
+
xt = a1(x)
|
| 169 |
+
xt = c1(xt)
|
| 170 |
+
xt = a2(xt)
|
| 171 |
+
xt = c2(xt)
|
| 172 |
+
x = xt + x
|
| 173 |
+
|
| 174 |
+
return x
|
| 175 |
+
|
| 176 |
+
def remove_weight_norm(self):
|
| 177 |
+
for l in self.convs1:
|
| 178 |
+
remove_weight_norm(l)
|
| 179 |
+
for l in self.convs2:
|
| 180 |
+
remove_weight_norm(l)
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
class BigVGAN(torch.nn.Module):
|
| 185 |
+
"""
|
| 186 |
+
BigVGAN is a neural vocoder model that applies anti-aliased periodic activation for residual blocks (resblocks).
|
| 187 |
+
New in BigVGAN-v2: it can optionally use optimized CUDA kernels for AMP (anti-aliased multi-periodicity) blocks.
|
| 188 |
+
|
| 189 |
+
Args:
|
| 190 |
+
h (AttrDict): Hyperparameters.
|
| 191 |
+
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.
|
| 192 |
+
|
| 193 |
+
Note:
|
| 194 |
+
- The `use_cuda_kernel` parameter should be used for inference only, as training with CUDA kernels is not supported.
|
| 195 |
+
- Ensure that the activation function is correctly specified in the hyperparameters (h.activation).
|
| 196 |
+
"""
|
| 197 |
+
|
| 198 |
+
def __init__(
|
| 199 |
+
self,
|
| 200 |
+
num_mels: int = 96,
|
| 201 |
+
global_channels: int = -1,
|
| 202 |
+
upsample_initial_channel: int = 1536,
|
| 203 |
+
resblock_kernel_sizes: List[int] = [3, 7, 11],
|
| 204 |
+
resblock_dilation_sizes: List[Tuple[int]] = [(1, 3, 5), (1, 3, 5), (1, 3, 5)],
|
| 205 |
+
upsample_rates: List[int] = [4,4,2,2,2,2],
|
| 206 |
+
upsample_kernel_sizes: List[int] = [8,8,4,4,4,4],
|
| 207 |
+
snake_logscale: bool = True,
|
| 208 |
+
activation: str = 'snakebeta',
|
| 209 |
+
use_bias_at_final:bool = False,
|
| 210 |
+
use_tanh_at_final:bool = False,
|
| 211 |
+
# Watermark FiLM conditioning
|
| 212 |
+
watermark_bits: int = 0,
|
| 213 |
+
watermark_film_hidden: int = 128,
|
| 214 |
+
# VocBulwark Temporal Adapter
|
| 215 |
+
vocbulwark_bits: int = 0,
|
| 216 |
+
vocbulwark_ta_hidden: int = 128,
|
| 217 |
+
vocbulwark_zero_conv_std: float = 0.02,
|
| 218 |
+
):
|
| 219 |
+
super().__init__()
|
| 220 |
+
|
| 221 |
+
Activation1d = TorchActivation1d
|
| 222 |
+
|
| 223 |
+
self.num_kernels = len(resblock_kernel_sizes)
|
| 224 |
+
self.num_upsamples = len(upsample_rates)
|
| 225 |
+
|
| 226 |
+
# Pre-conv
|
| 227 |
+
self.conv_pre = weight_norm(
|
| 228 |
+
Conv1d(num_mels, upsample_initial_channel, 7, 1, padding=3)
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
if global_channels > 0:
|
| 232 |
+
self.global_conv = torch.nn.Conv1d(global_channels, upsample_initial_channel, 1)
|
| 233 |
+
|
| 234 |
+
# Define which AMPBlock to use. BigVGAN uses AMPBlock1 as default
|
| 235 |
+
resblock_class = AMPBlock1
|
| 236 |
+
|
| 237 |
+
# Transposed conv-based upsamplers. does not apply anti-aliasing
|
| 238 |
+
self.ups = nn.ModuleList()
|
| 239 |
+
for i, (u, k) in enumerate(zip(upsample_rates, upsample_kernel_sizes)):
|
| 240 |
+
self.ups.append(
|
| 241 |
+
nn.ModuleList(
|
| 242 |
+
[
|
| 243 |
+
weight_norm(
|
| 244 |
+
ConvTranspose1d(
|
| 245 |
+
upsample_initial_channel // (2 ** i),
|
| 246 |
+
upsample_initial_channel // (2 ** (i + 1)),
|
| 247 |
+
k,
|
| 248 |
+
u,
|
| 249 |
+
padding=(k - u) // 2,
|
| 250 |
+
)
|
| 251 |
+
)
|
| 252 |
+
]
|
| 253 |
+
)
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
# Residual blocks using anti-aliased multi-periodicity composition modules (AMP)
|
| 257 |
+
self.resblocks = nn.ModuleList()
|
| 258 |
+
for i in range(len(self.ups)):
|
| 259 |
+
ch = upsample_initial_channel // (2 ** (i + 1))
|
| 260 |
+
for j, (k, d) in enumerate(
|
| 261 |
+
zip(resblock_kernel_sizes, resblock_dilation_sizes)
|
| 262 |
+
):
|
| 263 |
+
self.resblocks.append(
|
| 264 |
+
resblock_class(ch, k, d, activation=activation, snake_logscale=snake_logscale)
|
| 265 |
+
)
|
| 266 |
+
|
| 267 |
+
# Watermark FiLM layers (one per upsample stage)
|
| 268 |
+
self.watermark_bits = watermark_bits
|
| 269 |
+
if watermark_bits > 0:
|
| 270 |
+
self.film_layers = nn.ModuleList()
|
| 271 |
+
for i in range(len(self.ups)):
|
| 272 |
+
ch_film = upsample_initial_channel // (2 ** (i + 1))
|
| 273 |
+
self.film_layers.append(
|
| 274 |
+
FiLMBlock(watermark_bits, ch_film, watermark_film_hidden)
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
# VocBulwark Temporal Adapter layers (one per upsample stage)
|
| 278 |
+
self.vocbulwark_bits = vocbulwark_bits
|
| 279 |
+
if vocbulwark_bits > 0:
|
| 280 |
+
self.ta_layers = nn.ModuleList()
|
| 281 |
+
for i in range(len(self.ups)):
|
| 282 |
+
ch_ta = upsample_initial_channel // (2 ** (i + 1))
|
| 283 |
+
self.ta_layers.append(
|
| 284 |
+
TemporalAdapterBlock(vocbulwark_bits, ch_ta, vocbulwark_ta_hidden,
|
| 285 |
+
zero_conv_std=vocbulwark_zero_conv_std)
|
| 286 |
+
)
|
| 287 |
+
|
| 288 |
+
# Post-conv
|
| 289 |
+
activation_post = (
|
| 290 |
+
activations.Snake(ch, alpha_logscale=snake_logscale)
|
| 291 |
+
if activation == "snake"
|
| 292 |
+
else (
|
| 293 |
+
activations.SnakeBeta(ch, alpha_logscale=snake_logscale)
|
| 294 |
+
if activation == "snakebeta"
|
| 295 |
+
else None
|
| 296 |
+
)
|
| 297 |
+
)
|
| 298 |
+
if activation_post is None:
|
| 299 |
+
raise NotImplementedError(
|
| 300 |
+
"activation incorrectly specified. check the config file and look for 'activation'."
|
| 301 |
+
)
|
| 302 |
+
|
| 303 |
+
self.activation_post = Activation1d(activation=activation_post)
|
| 304 |
+
|
| 305 |
+
# Whether to use bias for the final conv_post. Default to True for backward compatibility
|
| 306 |
+
self.use_bias_at_final = use_bias_at_final
|
| 307 |
+
self.conv_post = weight_norm(
|
| 308 |
+
Conv1d(ch, 1, 7, 1, padding=3, bias=self.use_bias_at_final)
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
# Weight initialization
|
| 312 |
+
for i in range(len(self.ups)):
|
| 313 |
+
self.ups[i].apply(init_weights)
|
| 314 |
+
self.conv_post.apply(init_weights)
|
| 315 |
+
|
| 316 |
+
# Final tanh activation. Defaults to True for backward compatibility
|
| 317 |
+
self.use_tanh_at_final = use_tanh_at_final
|
| 318 |
+
|
| 319 |
+
def forward(
|
| 320 |
+
self,
|
| 321 |
+
x: torch.Tensor,
|
| 322 |
+
g: Optional[torch.Tensor] = None,
|
| 323 |
+
watermark: Optional[torch.Tensor] = None,
|
| 324 |
+
):
|
| 325 |
+
# Pre-conv
|
| 326 |
+
x = self.conv_pre(x)
|
| 327 |
+
if g is not None:
|
| 328 |
+
x = x + self.global_conv(g)
|
| 329 |
+
|
| 330 |
+
for i in range(self.num_upsamples):
|
| 331 |
+
# Upsampling
|
| 332 |
+
for i_up in range(len(self.ups[i])):
|
| 333 |
+
x = self.ups[i][i_up](x)
|
| 334 |
+
# AMP blocks
|
| 335 |
+
xs = None
|
| 336 |
+
for j in range(self.num_kernels):
|
| 337 |
+
if xs is None:
|
| 338 |
+
xs = self.resblocks[i * self.num_kernels + j](x)
|
| 339 |
+
else:
|
| 340 |
+
xs += self.resblocks[i * self.num_kernels + j](x)
|
| 341 |
+
x = xs / self.num_kernels
|
| 342 |
+
|
| 343 |
+
# Watermark FiLM conditioning
|
| 344 |
+
if watermark is not None and self.watermark_bits > 0:
|
| 345 |
+
x = self.film_layers[i](x, watermark)
|
| 346 |
+
|
| 347 |
+
# VocBulwark Temporal Adapter conditioning
|
| 348 |
+
if watermark is not None and self.vocbulwark_bits > 0:
|
| 349 |
+
x = self.ta_layers[i](x, watermark)
|
| 350 |
+
|
| 351 |
+
# Post-conv
|
| 352 |
+
x = self.activation_post(x)
|
| 353 |
+
x = self.conv_post(x)
|
| 354 |
+
# Final tanh activation
|
| 355 |
+
if self.use_tanh_at_final:
|
| 356 |
+
x = torch.tanh(x)
|
| 357 |
+
else:
|
| 358 |
+
x = torch.clamp(x, min=-1.0, max=1.0) # Bound the output to [-1, 1]
|
| 359 |
+
|
| 360 |
+
return x
|
| 361 |
+
|
config.json
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_film_watermark": false,
|
| 3 |
+
"add_mel_loss": false,
|
| 4 |
+
"add_watermark_vocbulwark": true,
|
| 5 |
+
"add_wav2vec_loss": false,
|
| 6 |
+
"add_wavlm_loss": false,
|
| 7 |
+
"add_whisper_encoder_loss": false,
|
| 8 |
+
"add_whisper_feature_loss": false,
|
| 9 |
+
"add_whisper_kl_loss": false,
|
| 10 |
+
"architectures": [
|
| 11 |
+
"HiFiGANArchitecture"
|
| 12 |
+
],
|
| 13 |
+
"block_out_channel_mults": [
|
| 14 |
+
1,
|
| 15 |
+
2,
|
| 16 |
+
2,
|
| 17 |
+
3
|
| 18 |
+
],
|
| 19 |
+
"dtype": "float32",
|
| 20 |
+
"hifigan_bias": false,
|
| 21 |
+
"hifigan_channels": 1536,
|
| 22 |
+
"hifigan_global_channels": 256,
|
| 23 |
+
"hifigan_in_channels": 96,
|
| 24 |
+
"hifigan_kernel_size": 7,
|
| 25 |
+
"hifigan_nonlinear_activation": "snakebeta",
|
| 26 |
+
"hifigan_nonlinear_activation_params": {
|
| 27 |
+
"negative_slope": 0.1
|
| 28 |
+
},
|
| 29 |
+
"hifigan_out_channels": 1,
|
| 30 |
+
"hifigan_resblock_dilations": [
|
| 31 |
+
[
|
| 32 |
+
1,
|
| 33 |
+
3,
|
| 34 |
+
5
|
| 35 |
+
],
|
| 36 |
+
[
|
| 37 |
+
1,
|
| 38 |
+
3,
|
| 39 |
+
5
|
| 40 |
+
],
|
| 41 |
+
[
|
| 42 |
+
1,
|
| 43 |
+
3,
|
| 44 |
+
5
|
| 45 |
+
]
|
| 46 |
+
],
|
| 47 |
+
"hifigan_resblock_kernel_sizes": [
|
| 48 |
+
3,
|
| 49 |
+
7,
|
| 50 |
+
11
|
| 51 |
+
],
|
| 52 |
+
"hifigan_upsample_kernel_sizes": [
|
| 53 |
+
8,
|
| 54 |
+
8,
|
| 55 |
+
4,
|
| 56 |
+
4,
|
| 57 |
+
4,
|
| 58 |
+
4
|
| 59 |
+
],
|
| 60 |
+
"hifigan_upsample_scales": [
|
| 61 |
+
4,
|
| 62 |
+
4,
|
| 63 |
+
2,
|
| 64 |
+
2,
|
| 65 |
+
2,
|
| 66 |
+
2
|
| 67 |
+
],
|
| 68 |
+
"hifigan_use_additional_convs": true,
|
| 69 |
+
"hifigan_use_weight_norm": true,
|
| 70 |
+
"hop_length": 256,
|
| 71 |
+
"input_type": "mel",
|
| 72 |
+
"lambda_adv": 1.0,
|
| 73 |
+
"lambda_feat_match": 10.0,
|
| 74 |
+
"lambda_mel": 15.0,
|
| 75 |
+
"lambda_vocbulwark_ext": 1.0,
|
| 76 |
+
"lambda_vocbulwark_mel": 0.1,
|
| 77 |
+
"lambda_vocbulwark_mstft": 0.1,
|
| 78 |
+
"lambda_vocbulwark_wavlm": 0.0,
|
| 79 |
+
"lambda_watermark": 1.0,
|
| 80 |
+
"lambda_wav2vec": 15.0,
|
| 81 |
+
"lambda_wavlm": 15.0,
|
| 82 |
+
"lambda_whisper_encoder": 15.0,
|
| 83 |
+
"lambda_whisper_features": 15.0,
|
| 84 |
+
"lambda_whisper_kl": 1.0,
|
| 85 |
+
"mel_channels": 96,
|
| 86 |
+
"model_type": "hifi_gan",
|
| 87 |
+
"mpd_reshapes": [
|
| 88 |
+
2,
|
| 89 |
+
3,
|
| 90 |
+
5,
|
| 91 |
+
7,
|
| 92 |
+
11
|
| 93 |
+
],
|
| 94 |
+
"mrd_resolutions": [
|
| 95 |
+
[
|
| 96 |
+
1024,
|
| 97 |
+
120,
|
| 98 |
+
600
|
| 99 |
+
],
|
| 100 |
+
[
|
| 101 |
+
2048,
|
| 102 |
+
240,
|
| 103 |
+
1200
|
| 104 |
+
],
|
| 105 |
+
[
|
| 106 |
+
512,
|
| 107 |
+
50,
|
| 108 |
+
240
|
| 109 |
+
]
|
| 110 |
+
],
|
| 111 |
+
"n_fft": 1024,
|
| 112 |
+
"raw_sample_rate": 22050,
|
| 113 |
+
"snake_logscale": true,
|
| 114 |
+
"speaker_embed_checkpoint": null,
|
| 115 |
+
"speaker_embed_config": null,
|
| 116 |
+
"target_sample_rate": 24000,
|
| 117 |
+
"train_discriminator": false,
|
| 118 |
+
"transformers_version": "4.57.3",
|
| 119 |
+
"update_speaker_embedding": true,
|
| 120 |
+
"use_wavlm_features": false,
|
| 121 |
+
"vocbulwark_acc_tau1": 0.9,
|
| 122 |
+
"vocbulwark_acc_tau2": 0.95,
|
| 123 |
+
"vocbulwark_bits": 50,
|
| 124 |
+
"vocbulwark_bootstrap_threshold": 0.6,
|
| 125 |
+
"vocbulwark_fidelity_vs_clean": false,
|
| 126 |
+
"vocbulwark_ta_hidden": 128,
|
| 127 |
+
"vocbulwark_zero_conv_std": 0.02,
|
| 128 |
+
"watermark_bits": 32,
|
| 129 |
+
"watermark_film_hidden": 128,
|
| 130 |
+
"wav2vec_cnn_frozen": true,
|
| 131 |
+
"wav2vec_cnn_model_name": "facebook/wav2vec2-base",
|
| 132 |
+
"wav2vec_cnn_sample_rate": 16000,
|
| 133 |
+
"wavlm_layer_idx": 6,
|
| 134 |
+
"wavlm_model_name": "microsoft/wavlm-base-plus",
|
| 135 |
+
"win_length": 1024,
|
| 136 |
+
"speaker_embedding_size": 768,
|
| 137 |
+
"auto_map": {
|
| 138 |
+
"AutoConfig": "configuration_hifigan.HiFiGANConfig",
|
| 139 |
+
"AutoModel": "modeling_hifigan.HiFiGANArchitecture"
|
| 140 |
+
},
|
| 141 |
+
"fixed_watermark": [
|
| 142 |
+
0,
|
| 143 |
+
1,
|
| 144 |
+
0,
|
| 145 |
+
0,
|
| 146 |
+
1,
|
| 147 |
+
1,
|
| 148 |
+
1,
|
| 149 |
+
0,
|
| 150 |
+
0,
|
| 151 |
+
0,
|
| 152 |
+
0,
|
| 153 |
+
1,
|
| 154 |
+
0,
|
| 155 |
+
1,
|
| 156 |
+
0,
|
| 157 |
+
1,
|
| 158 |
+
1,
|
| 159 |
+
0,
|
| 160 |
+
1,
|
| 161 |
+
1,
|
| 162 |
+
1,
|
| 163 |
+
1,
|
| 164 |
+
1,
|
| 165 |
+
0,
|
| 166 |
+
1,
|
| 167 |
+
0,
|
| 168 |
+
1,
|
| 169 |
+
1,
|
| 170 |
+
1,
|
| 171 |
+
0,
|
| 172 |
+
1,
|
| 173 |
+
0,
|
| 174 |
+
1,
|
| 175 |
+
1,
|
| 176 |
+
1,
|
| 177 |
+
1,
|
| 178 |
+
0,
|
| 179 |
+
1,
|
| 180 |
+
1,
|
| 181 |
+
0,
|
| 182 |
+
1,
|
| 183 |
+
1,
|
| 184 |
+
1,
|
| 185 |
+
1,
|
| 186 |
+
1,
|
| 187 |
+
1,
|
| 188 |
+
0,
|
| 189 |
+
0,
|
| 190 |
+
0,
|
| 191 |
+
0
|
| 192 |
+
]
|
| 193 |
+
}
|
configuration_hifigan.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transformers import PretrainedConfig
|
| 2 |
+
from .speaker_embedding_config import SpeakerEmbeddingConfig
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class HiFiGANConfig(PretrainedConfig):
|
| 6 |
+
model_type = "hifi_gan"
|
| 7 |
+
|
| 8 |
+
def __init__(
|
| 9 |
+
self,
|
| 10 |
+
# Audio settings
|
| 11 |
+
raw_sample_rate=22050,
|
| 12 |
+
target_sample_rate=24000,
|
| 13 |
+
n_fft=1024,
|
| 14 |
+
hop_length=256,
|
| 15 |
+
win_length=1024,
|
| 16 |
+
mel_channels=96,
|
| 17 |
+
|
| 18 |
+
# Speaker embedding
|
| 19 |
+
speaker_embed_config: SpeakerEmbeddingConfig = None,
|
| 20 |
+
speaker_embed_checkpoint: str = None,
|
| 21 |
+
update_speaker_embedding: bool = False,
|
| 22 |
+
|
| 23 |
+
# Input feature type: "mel" (default) or "wav2vec_cnn"
|
| 24 |
+
input_type: str = "mel",
|
| 25 |
+
wav2vec_cnn_model_name: str = "facebook/wav2vec2-base",
|
| 26 |
+
wav2vec_cnn_frozen: bool = True,
|
| 27 |
+
wav2vec_cnn_sample_rate: int = 16000,
|
| 28 |
+
|
| 29 |
+
# HiFi-GAN Generator config
|
| 30 |
+
hifigan_in_channels=96,
|
| 31 |
+
hifigan_out_channels=1,
|
| 32 |
+
hifigan_channels=512,
|
| 33 |
+
hifigan_global_channels=256, # Speaker conditioning
|
| 34 |
+
hifigan_kernel_size=7,
|
| 35 |
+
#hifigan_upsample_scales=[8, 8, 2, 2],
|
| 36 |
+
hifigan_upsample_kernel_sizes=[8,8,4,4,4,4],
|
| 37 |
+
#hifigan_upsample_kernel_sizes=[16, 16, 4, 4],
|
| 38 |
+
hifigan_upsample_scales=[4,4,2,2,2,2],
|
| 39 |
+
hifigan_resblock_kernel_sizes=[3, 7, 11],
|
| 40 |
+
hifigan_resblock_dilations=[[1, 3, 5], [1, 3, 5], [1, 3, 5]],
|
| 41 |
+
hifigan_use_additional_convs=True,
|
| 42 |
+
hifigan_bias=False,
|
| 43 |
+
hifigan_nonlinear_activation="snakebeta",
|
| 44 |
+
snake_logscale=True,
|
| 45 |
+
hifigan_nonlinear_activation_params={"negative_slope": 0.1},
|
| 46 |
+
hifigan_use_weight_norm=True,
|
| 47 |
+
|
| 48 |
+
mpd_reshapes=[2, 3, 5, 7, 11],
|
| 49 |
+
mrd_resolutions=[
|
| 50 |
+
(1024, 120, 600),
|
| 51 |
+
(2048, 240, 1200),
|
| 52 |
+
(512, 50, 240),
|
| 53 |
+
],
|
| 54 |
+
|
| 55 |
+
# VAE config
|
| 56 |
+
block_out_channel_mults=[1, 2, 2, 3],
|
| 57 |
+
|
| 58 |
+
# Loss weights
|
| 59 |
+
lambda_adv=1.0, # Adversarial loss weight
|
| 60 |
+
lambda_feat_match=10.0, # Feature matching loss weight
|
| 61 |
+
add_mel_loss=True,
|
| 62 |
+
lambda_mel=15.0, # Mel spectrogram loss weight
|
| 63 |
+
lambda_wavlm=15.0, # WavLM perceptual loss weight
|
| 64 |
+
add_wavlm_loss=False,
|
| 65 |
+
add_wav2vec_loss=False,
|
| 66 |
+
lambda_wav2vec=15.0, # Wav2Vec perceptual loss weight
|
| 67 |
+
add_whisper_encoder_loss=False,
|
| 68 |
+
add_whisper_kl_loss=False,
|
| 69 |
+
lambda_whisper_encoder=15.0, # Whisper encoder hidden states L1 weight
|
| 70 |
+
lambda_whisper_kl=1.0, # Whisper decoder KL divergence weight
|
| 71 |
+
add_whisper_feature_loss=False,
|
| 72 |
+
lambda_whisper_features=15.0, # Whisper conv feature extractor L1 weight
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
use_wavlm_features: bool = False, # Use WavLM instead of MEL
|
| 76 |
+
wavlm_model_name: str = "microsoft/wavlm-base-plus",
|
| 77 |
+
wavlm_layer_idx: int = 6, # Which layer to extract features from (0-11 for base)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
train_discriminator: bool = True,
|
| 81 |
+
|
| 82 |
+
# Watermark (FiLM)
|
| 83 |
+
add_film_watermark: bool = False,
|
| 84 |
+
watermark_bits: int = 32,
|
| 85 |
+
lambda_watermark: float = 1.0,
|
| 86 |
+
watermark_film_hidden: int = 128,
|
| 87 |
+
|
| 88 |
+
# VocBulwark watermarking
|
| 89 |
+
add_watermark_vocbulwark: bool = False,
|
| 90 |
+
vocbulwark_bits: int = 100,
|
| 91 |
+
vocbulwark_ta_hidden: int = 128,
|
| 92 |
+
lambda_vocbulwark_ext: float = 1.0,
|
| 93 |
+
lambda_vocbulwark_mel: float = 0.1,
|
| 94 |
+
lambda_vocbulwark_mstft: float = 0.1,
|
| 95 |
+
lambda_vocbulwark_wavlm: float = 0.0,
|
| 96 |
+
vocbulwark_acc_tau1: float = 0.9,
|
| 97 |
+
vocbulwark_acc_tau2: float = 0.95,
|
| 98 |
+
vocbulwark_bootstrap_threshold: float = 0.0,
|
| 99 |
+
vocbulwark_zero_conv_std: float = 0.02,
|
| 100 |
+
vocbulwark_fidelity_vs_clean: bool = False,
|
| 101 |
+
|
| 102 |
+
**kwargs
|
| 103 |
+
):
|
| 104 |
+
super().__init__(**kwargs)
|
| 105 |
+
|
| 106 |
+
# Audio
|
| 107 |
+
self.raw_sample_rate = raw_sample_rate
|
| 108 |
+
self.target_sample_rate = target_sample_rate
|
| 109 |
+
self.n_fft = n_fft
|
| 110 |
+
self.hop_length = hop_length
|
| 111 |
+
self.win_length = win_length
|
| 112 |
+
|
| 113 |
+
# Mel
|
| 114 |
+
self.mel_channels = mel_channels
|
| 115 |
+
|
| 116 |
+
# Speaker
|
| 117 |
+
self.speaker_embed_config = speaker_embed_config
|
| 118 |
+
self.speaker_embed_checkpoint = speaker_embed_checkpoint
|
| 119 |
+
self.update_speaker_embedding = update_speaker_embedding
|
| 120 |
+
|
| 121 |
+
# Input type
|
| 122 |
+
self.input_type = input_type
|
| 123 |
+
self.wav2vec_cnn_model_name = wav2vec_cnn_model_name
|
| 124 |
+
self.wav2vec_cnn_frozen = wav2vec_cnn_frozen
|
| 125 |
+
self.wav2vec_cnn_sample_rate = wav2vec_cnn_sample_rate
|
| 126 |
+
|
| 127 |
+
# VAE parameters
|
| 128 |
+
self.block_out_channel_mults = block_out_channel_mults
|
| 129 |
+
|
| 130 |
+
# HiFi-GAN parameters
|
| 131 |
+
self.hifigan_in_channels = hifigan_in_channels
|
| 132 |
+
self.hifigan_out_channels = hifigan_out_channels
|
| 133 |
+
self.hifigan_channels = hifigan_channels
|
| 134 |
+
self.hifigan_global_channels = hifigan_global_channels
|
| 135 |
+
self.hifigan_kernel_size = hifigan_kernel_size
|
| 136 |
+
self.hifigan_upsample_scales = hifigan_upsample_scales
|
| 137 |
+
self.hifigan_upsample_kernel_sizes = hifigan_upsample_kernel_sizes
|
| 138 |
+
self.hifigan_resblock_kernel_sizes = hifigan_resblock_kernel_sizes
|
| 139 |
+
self.hifigan_resblock_dilations = hifigan_resblock_dilations
|
| 140 |
+
self.hifigan_use_additional_convs = hifigan_use_additional_convs
|
| 141 |
+
self.hifigan_bias = hifigan_bias
|
| 142 |
+
self.hifigan_nonlinear_activation = hifigan_nonlinear_activation
|
| 143 |
+
self.hifigan_nonlinear_activation_params = hifigan_nonlinear_activation_params
|
| 144 |
+
self.hifigan_use_weight_norm = hifigan_use_weight_norm
|
| 145 |
+
self.snake_logscale = snake_logscale
|
| 146 |
+
|
| 147 |
+
self.mpd_reshapes = mpd_reshapes
|
| 148 |
+
self.mrd_resolutions = mrd_resolutions
|
| 149 |
+
|
| 150 |
+
# Loss weights
|
| 151 |
+
self.lambda_adv = lambda_adv
|
| 152 |
+
self.lambda_feat_match = lambda_feat_match
|
| 153 |
+
self.add_mel_loss = add_mel_loss
|
| 154 |
+
self.lambda_mel = lambda_mel
|
| 155 |
+
self.lambda_wavlm = lambda_wavlm
|
| 156 |
+
self.add_wavlm_loss = add_wavlm_loss
|
| 157 |
+
self.add_wav2vec_loss = add_wav2vec_loss
|
| 158 |
+
self.lambda_wav2vec = lambda_wav2vec
|
| 159 |
+
self.add_whisper_encoder_loss = add_whisper_encoder_loss
|
| 160 |
+
self.add_whisper_kl_loss = add_whisper_kl_loss
|
| 161 |
+
self.lambda_whisper_encoder = lambda_whisper_encoder
|
| 162 |
+
self.lambda_whisper_kl = lambda_whisper_kl
|
| 163 |
+
self.add_whisper_feature_loss = add_whisper_feature_loss
|
| 164 |
+
self.lambda_whisper_features = lambda_whisper_features
|
| 165 |
+
|
| 166 |
+
self.use_wavlm_features = use_wavlm_features
|
| 167 |
+
self.wavlm_model_name = wavlm_model_name
|
| 168 |
+
self.wavlm_layer_idx = wavlm_layer_idx
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# train discrimnator or not
|
| 172 |
+
self.train_discriminator = train_discriminator
|
| 173 |
+
|
| 174 |
+
# Watermark (FiLM)
|
| 175 |
+
self.add_film_watermark = add_film_watermark
|
| 176 |
+
self.watermark_bits = watermark_bits
|
| 177 |
+
self.lambda_watermark = lambda_watermark
|
| 178 |
+
self.watermark_film_hidden = watermark_film_hidden
|
| 179 |
+
|
| 180 |
+
# VocBulwark
|
| 181 |
+
self.add_watermark_vocbulwark = add_watermark_vocbulwark
|
| 182 |
+
self.vocbulwark_bits = vocbulwark_bits
|
| 183 |
+
self.vocbulwark_ta_hidden = vocbulwark_ta_hidden
|
| 184 |
+
self.lambda_vocbulwark_ext = lambda_vocbulwark_ext
|
| 185 |
+
self.lambda_vocbulwark_mel = lambda_vocbulwark_mel
|
| 186 |
+
self.lambda_vocbulwark_mstft = lambda_vocbulwark_mstft
|
| 187 |
+
self.lambda_vocbulwark_wavlm = lambda_vocbulwark_wavlm
|
| 188 |
+
self.vocbulwark_acc_tau1 = vocbulwark_acc_tau1
|
| 189 |
+
self.vocbulwark_acc_tau2 = vocbulwark_acc_tau2
|
| 190 |
+
self.vocbulwark_bootstrap_threshold = vocbulwark_bootstrap_threshold
|
| 191 |
+
self.vocbulwark_zero_conv_std = vocbulwark_zero_conv_std
|
| 192 |
+
self.vocbulwark_fidelity_vs_clean = vocbulwark_fidelity_vs_clean
|
example_roundtrip.ipynb
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"# VocBulwark watermark round-trip\n",
|
| 8 |
+
"\n",
|
| 9 |
+
"The full pipeline, in three self-contained models loaded from the Hub:\n",
|
| 10 |
+
"\n",
|
| 11 |
+
"1. **Speaker encoder** β a reference clip β a fixed-size speaker embedding (*whose* voice).\n",
|
| 12 |
+
"2. **Vocoder** β a mel spectrogram (*what* is said) + that embedding β a 24 kHz waveform, with the fixed provenance watermark embedded automatically.\n",
|
| 13 |
+
"3. **Detector** β the waveform β how many watermark bits match.\n",
|
| 14 |
+
"\n",
|
| 15 |
+
"In a real TTS system you usually already have the mel (from an acoustic model) and the speaker embedding (precomputed once per speaker), so step 1 is only needed to *make* an embedding from audio. Here we reconstruct one clip (use it as both content and speaker reference) to exercise the watermark.\n",
|
| 16 |
+
"\n",
|
| 17 |
+
"Needs only `torch`, `transformers`, `soundfile`, `torchaudio`. Put one or more `.wav` files in an `example_audio/` folder next to this notebook. If the repos are private, run `huggingface-cli login` first."
|
| 18 |
+
]
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"cell_type": "code",
|
| 22 |
+
"execution_count": null,
|
| 23 |
+
"metadata": {},
|
| 24 |
+
"outputs": [],
|
| 25 |
+
"source": [
|
| 26 |
+
"from pathlib import Path\n",
|
| 27 |
+
"\n",
|
| 28 |
+
"SPK_REPO = \"mlr2000/vocoder-large-speaker-encoder\" # <- speaker encoder\n",
|
| 29 |
+
"GEN_REPO = \"mlr2000/vocoder-large\" # <- vocoder (paired with SPK_REPO)\n",
|
| 30 |
+
"DET_REPO = \"mlr2000/vocoder-large-watermark-detector\" # <- matching detector\n",
|
| 31 |
+
"TGT_SR = 24000 # vocoder output SR\n",
|
| 32 |
+
"MAX_SAMPLES = 16 * TGT_SR\n",
|
| 33 |
+
"\n",
|
| 34 |
+
"AUDIO_DIR = next(p for p in [Path(\"example_audio\"), Path(\"../example_audio\")] if p.exists())"
|
| 35 |
+
]
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"cell_type": "code",
|
| 39 |
+
"execution_count": null,
|
| 40 |
+
"metadata": {},
|
| 41 |
+
"outputs": [],
|
| 42 |
+
"source": [
|
| 43 |
+
"import torch\n",
|
| 44 |
+
"from transformers import AutoModel\n",
|
| 45 |
+
"\n",
|
| 46 |
+
"enc = AutoModel.from_pretrained(SPK_REPO, trust_remote_code=True).eval()\n",
|
| 47 |
+
"gen = AutoModel.from_pretrained(GEN_REPO, trust_remote_code=True).eval()\n",
|
| 48 |
+
"det = AutoModel.from_pretrained(DET_REPO, trust_remote_code=True).eval()\n",
|
| 49 |
+
"RAW_SR = enc.config.raw_sample_rate # SR the speaker encoder expects\n",
|
| 50 |
+
"print(f\"embedding dim: {enc.config.embedding_size} | watermark: {len(gen.config.fixed_watermark)} bits\")"
|
| 51 |
+
]
|
| 52 |
+
},
|
| 53 |
+
{
|
| 54 |
+
"cell_type": "code",
|
| 55 |
+
"execution_count": null,
|
| 56 |
+
"metadata": {},
|
| 57 |
+
"outputs": [],
|
| 58 |
+
"source": [
|
| 59 |
+
"import soundfile as sf\n",
|
| 60 |
+
"import torchaudio.functional as AF\n",
|
| 61 |
+
"from transformers import WhisperFeatureExtractor\n",
|
| 62 |
+
"\n",
|
| 63 |
+
"# Mel front-end β must match the trained vocoder (n_fft=1024, hop=256, mel channels from config).\n",
|
| 64 |
+
"N_MELS = gen.config.hifigan_in_channels\n",
|
| 65 |
+
"mel_fe = WhisperFeatureExtractor(sampling_rate=TGT_SR, n_fft=1024, feature_size=N_MELS, hop_length=256)\n",
|
| 66 |
+
"mel_fe.n_samples = MAX_SAMPLES\n",
|
| 67 |
+
"mel_fe.chunk_length = MAX_SAMPLES / TGT_SR\n",
|
| 68 |
+
"\n",
|
| 69 |
+
"def load_inputs(path):\n",
|
| 70 |
+
" \"\"\"A clip -> (mel = content, raw = speaker reference @ RAW_SR).\"\"\"\n",
|
| 71 |
+
" w, sr = sf.read(str(path))\n",
|
| 72 |
+
" w = torch.tensor(w, dtype=torch.float32)\n",
|
| 73 |
+
" if w.dim() == 2:\n",
|
| 74 |
+
" w = w.mean(1) # mono\n",
|
| 75 |
+
" raw = AF.resample(w, sr, RAW_SR) if sr != RAW_SR else w # speaker ref @ RAW_SR\n",
|
| 76 |
+
" tgt = AF.resample(raw, RAW_SR, TGT_SR) # content @ 24 kHz\n",
|
| 77 |
+
" mel = mel_fe(tgt.numpy(), sampling_rate=TGT_SR, padding=\"longest\",\n",
|
| 78 |
+
" return_tensors=\"pt\")[\"input_features\"]\n",
|
| 79 |
+
" return mel, raw.unsqueeze(0)"
|
| 80 |
+
]
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"cell_type": "code",
|
| 84 |
+
"execution_count": null,
|
| 85 |
+
"metadata": {},
|
| 86 |
+
"outputs": [],
|
| 87 |
+
"source": [
|
| 88 |
+
"from IPython.display import Audio, display\n",
|
| 89 |
+
"\n",
|
| 90 |
+
"for path in sorted(AUDIO_DIR.glob(\"*.wav\")):\n",
|
| 91 |
+
" mel, raw = load_inputs(path)\n",
|
| 92 |
+
" with torch.no_grad():\n",
|
| 93 |
+
" emb = enc.embed(raw) # [1, embedding_size]\n",
|
| 94 |
+
" # The vocoder embeds its fixed watermark automatically.\n",
|
| 95 |
+
" audio = gen(mel_spectrogram=mel, speaker_embedding=emb).audio.squeeze(1)\n",
|
| 96 |
+
" r = det.detect(audio)\n",
|
| 97 |
+
" print(f\"{path.name}: {r['matches']}/{r['n_bits']} bits matched p={r['p_value']:.1e}\")\n",
|
| 98 |
+
" display(Audio(audio[0].numpy(), rate=TGT_SR))"
|
| 99 |
+
]
|
| 100 |
+
},
|
| 101 |
+
{
|
| 102 |
+
"cell_type": "markdown",
|
| 103 |
+
"metadata": {},
|
| 104 |
+
"source": [
|
| 105 |
+
"Sanity check: an **unrelated** clip (here, random noise) is not from our model, so it matches only about half the bits with a large `p_value`."
|
| 106 |
+
]
|
| 107 |
+
},
|
| 108 |
+
{
|
| 109 |
+
"cell_type": "code",
|
| 110 |
+
"execution_count": null,
|
| 111 |
+
"metadata": {},
|
| 112 |
+
"outputs": [],
|
| 113 |
+
"source": [
|
| 114 |
+
"print(\"random noise:\", det.detect(torch.randn(1, TGT_SR)))"
|
| 115 |
+
]
|
| 116 |
+
}
|
| 117 |
+
],
|
| 118 |
+
"metadata": {
|
| 119 |
+
"language_info": {
|
| 120 |
+
"name": "python"
|
| 121 |
+
}
|
| 122 |
+
},
|
| 123 |
+
"nbformat": 4,
|
| 124 |
+
"nbformat_minor": 5
|
| 125 |
+
}
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:626e334b29898f1b4840552666fd72f431baef10b4386fba4853a3310718e9e7
|
| 3 |
+
size 474989104
|
modeling_hifigan.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Self-contained inference model for the VocBulwark HiFi-GAN / BigVGAN vocoder.
|
| 2 |
+
|
| 3 |
+
Loadable with `AutoModel.from_pretrained(path, trust_remote_code=True)` without
|
| 4 |
+
the training repository. This is the lean vocoder: it takes a mel-spectrogram and
|
| 5 |
+
a precomputed speaker embedding, and does NOT include the speaker encoder (that
|
| 6 |
+
is a separate model), the training losses, discriminators, or watermark heads.
|
| 7 |
+
"""
|
| 8 |
+
from dataclasses import dataclass
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
import torch.nn.utils.parametrize as parametrize
|
| 12 |
+
from torch.nn.utils import remove_weight_norm
|
| 13 |
+
from transformers import PreTrainedModel
|
| 14 |
+
from transformers.modeling_outputs import ModelOutput
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _strip_weight_norm(root):
|
| 18 |
+
"""Remove every weight-norm reparametrization (both conventions), leaving
|
| 19 |
+
plain ``.weight`` parameters that match the folded export weights."""
|
| 20 |
+
for m in root.modules():
|
| 21 |
+
if parametrize.is_parametrized(m, "weight"):
|
| 22 |
+
parametrize.remove_parametrizations(m, "weight", leave_parametrized=True)
|
| 23 |
+
elif hasattr(m, "weight_g"):
|
| 24 |
+
try:
|
| 25 |
+
remove_weight_norm(m)
|
| 26 |
+
except (ValueError, RuntimeError):
|
| 27 |
+
pass
|
| 28 |
+
|
| 29 |
+
from .configuration_hifigan import HiFiGANConfig
|
| 30 |
+
from .bigvgan_model import BigVGAN
|
| 31 |
+
|
| 32 |
+
# transformers' trust_remote_code loader copies only modules *directly* imported
|
| 33 |
+
# by this entry file, and recognizes the `from .X import Y` form. Import a real
|
| 34 |
+
# symbol from every bundled leaf module so all files get copied into the
|
| 35 |
+
# dynamic-module cache (they are used transitively by the imports above).
|
| 36 |
+
from .bigvgan_activations import Snake as _Snake # noqa: F401
|
| 37 |
+
from .alias_free_act import Activation1d as _Activation1d # noqa: F401
|
| 38 |
+
from .alias_free_resample import UpSample1d as _UpSample1d # noqa: F401
|
| 39 |
+
from .alias_free_filter import LowPassFilter1d as _LowPassFilter1d # noqa: F401
|
| 40 |
+
from .temporal_adapter import TemporalAdapterBlock as _TemporalAdapterBlock # noqa: F401
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
@dataclass
|
| 44 |
+
class HiFiGANOutput(ModelOutput):
|
| 45 |
+
audio: torch.Tensor = None
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class HiFiGANArchitecture(PreTrainedModel):
|
| 49 |
+
config_class = HiFiGANConfig
|
| 50 |
+
|
| 51 |
+
def __init__(self, config):
|
| 52 |
+
super().__init__(config)
|
| 53 |
+
|
| 54 |
+
# Lean vocoder: the speaker embedding is supplied at inference time, so the
|
| 55 |
+
# generator only needs its dimension to build the conditioning conv.
|
| 56 |
+
global_channels = int(getattr(config, "speaker_embedding_size", 0)) or -1
|
| 57 |
+
self.hifi_gan = BigVGAN(
|
| 58 |
+
num_mels=config.hifigan_in_channels,
|
| 59 |
+
upsample_initial_channel=config.hifigan_channels,
|
| 60 |
+
resblock_kernel_sizes=config.hifigan_resblock_kernel_sizes,
|
| 61 |
+
resblock_dilation_sizes=config.hifigan_resblock_dilations,
|
| 62 |
+
upsample_kernel_sizes=config.hifigan_upsample_kernel_sizes,
|
| 63 |
+
upsample_rates=config.hifigan_upsample_scales,
|
| 64 |
+
snake_logscale=config.snake_logscale,
|
| 65 |
+
activation=config.hifigan_nonlinear_activation,
|
| 66 |
+
use_bias_at_final=config.hifigan_bias,
|
| 67 |
+
global_channels=global_channels,
|
| 68 |
+
watermark_bits=config.watermark_bits if config.add_film_watermark else 0,
|
| 69 |
+
watermark_film_hidden=config.watermark_film_hidden,
|
| 70 |
+
vocbulwark_bits=config.vocbulwark_bits if config.add_watermark_vocbulwark else 0,
|
| 71 |
+
vocbulwark_ta_hidden=config.vocbulwark_ta_hidden,
|
| 72 |
+
vocbulwark_zero_conv_std=getattr(config, "vocbulwark_zero_conv_std", 0.02),
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
# Export folds weight-norm into plain .weight; strip the reparametrization
|
| 76 |
+
# here so module keys match (and inference is independent of torch's
|
| 77 |
+
# weight_norm convention).
|
| 78 |
+
_strip_weight_norm(self)
|
| 79 |
+
|
| 80 |
+
@torch.no_grad()
|
| 81 |
+
def forward(
|
| 82 |
+
self,
|
| 83 |
+
mel_spectrogram=None,
|
| 84 |
+
speaker_embedding=None,
|
| 85 |
+
input_features=None,
|
| 86 |
+
return_loss=False,
|
| 87 |
+
**kwargs,
|
| 88 |
+
):
|
| 89 |
+
"""Vocode a (log-)mel spectrogram into a waveform.
|
| 90 |
+
|
| 91 |
+
Args:
|
| 92 |
+
mel_spectrogram: [B, mel_channels, T] input features.
|
| 93 |
+
speaker_embedding: [B, speaker_embedding_size] speaker conditioning,
|
| 94 |
+
produced by the companion speaker-encoder model.
|
| 95 |
+
Returns:
|
| 96 |
+
HiFiGANOutput with `.audio` of shape [B, 1, samples] @ target_sample_rate.
|
| 97 |
+
|
| 98 |
+
The model's fixed provenance watermark is embedded in every generated clip
|
| 99 |
+
and cannot be changed or disabled through this interface.
|
| 100 |
+
"""
|
| 101 |
+
hifi_gan_device = self.hifi_gan.conv_pre.bias.device
|
| 102 |
+
if torch.is_autocast_enabled():
|
| 103 |
+
target_dtype = torch.get_autocast_gpu_dtype()
|
| 104 |
+
else:
|
| 105 |
+
target_dtype = self.hifi_gan.conv_pre.bias.dtype
|
| 106 |
+
|
| 107 |
+
if input_features is not None:
|
| 108 |
+
features = input_features.to(device=hifi_gan_device, dtype=target_dtype)
|
| 109 |
+
else:
|
| 110 |
+
features = mel_spectrogram.to(device=hifi_gan_device, dtype=target_dtype)
|
| 111 |
+
|
| 112 |
+
if speaker_embedding is None:
|
| 113 |
+
raise ValueError(
|
| 114 |
+
"speaker_embedding is required: pass a "
|
| 115 |
+
"[B, config.speaker_embedding_size] tensor from the companion "
|
| 116 |
+
"speaker-encoder model.")
|
| 117 |
+
g = speaker_embedding.to(device=hifi_gan_device, dtype=target_dtype)
|
| 118 |
+
if g.dim() == 2: # [B, E] -> [B, E, 1]
|
| 119 |
+
g = g.unsqueeze(2)
|
| 120 |
+
|
| 121 |
+
# Provenance watermark: the model's fixed signature is embedded in every
|
| 122 |
+
# generated clip. It is intentionally not overridable through this
|
| 123 |
+
# interface β there is no way to disable or change it here.
|
| 124 |
+
watermark = None
|
| 125 |
+
fw = getattr(self.config, "fixed_watermark", None)
|
| 126 |
+
if fw is not None:
|
| 127 |
+
watermark = torch.tensor(fw, dtype=target_dtype, device=hifi_gan_device)
|
| 128 |
+
watermark = watermark.unsqueeze(0).expand(features.shape[0], -1)
|
| 129 |
+
|
| 130 |
+
audio = self.hifi_gan(features, g=g, watermark=watermark)
|
| 131 |
+
return HiFiGANOutput(audio=audio)
|
speaker_embedding_config.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transformers.models.wav2vec2.configuration_wav2vec2 import Wav2Vec2Config
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class SpeakerEmbeddingConfig(Wav2Vec2Config):
|
| 5 |
+
|
| 6 |
+
def __init__(
|
| 7 |
+
self,
|
| 8 |
+
embedding_size: int = 768,
|
| 9 |
+
train_batch_speakers: int = 64,
|
| 10 |
+
train_batch_per_device: int = 64,
|
| 11 |
+
train_batch_samples_per_speaker: int = 10,
|
| 12 |
+
disable_positional_embeddings: bool = False,
|
| 13 |
+
loss_margin: float = 0.2,
|
| 14 |
+
loss_scale: float = 30.0,
|
| 15 |
+
use_layer_weights: bool = True,
|
| 16 |
+
n_projection_layers: int = 3,
|
| 17 |
+
**kwargs,
|
| 18 |
+
):
|
| 19 |
+
super().__init__(**kwargs)
|
| 20 |
+
self.embedding_size = embedding_size
|
| 21 |
+
self.train_batch_per_device = train_batch_per_device
|
| 22 |
+
self.train_batch_speakers = train_batch_speakers
|
| 23 |
+
self.train_batch_samples_per_speaker = train_batch_samples_per_speaker
|
| 24 |
+
self.disable_positional_embeddings = disable_positional_embeddings
|
| 25 |
+
self.loss_margin = loss_margin
|
| 26 |
+
self.loss_scale = loss_scale
|
| 27 |
+
self.use_layer_weights = use_layer_weights
|
| 28 |
+
self.n_projection_layers = n_projection_layers
|
| 29 |
+
#self.conv_stride = (7, 2, 2, 2, 2, 2, 2)
|
| 30 |
+
#self.conv_kernel = (15, 5, 3, 3, 3, 2, 2)
|
| 31 |
+
#self.conv_dim = (512, 512, 512, 512, 512, 512, 512)
|
temporal_adapter.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Temporal Adapter (TA) from VocBulwark (Appendix B.1).
|
| 3 |
+
|
| 4 |
+
Lightweight module injected at each upsample stage of the frozen vocoder.
|
| 5 |
+
Embeds watermark bits into acoustic features via:
|
| 6 |
+
1. Acoustic Feature Alignment: Emb(w) β PFP β w_proj β [B, C, 1]
|
| 7 |
+
2. Frame-level Temporal Broadcasting: w_proj β w_latent β [B, C, T]
|
| 8 |
+
3. Adaptive Injection: concat(w_latent, h) β downsample β DSC β zero_conv + h (residual)
|
| 9 |
+
|
| 10 |
+
Architecture from paper:
|
| 11 |
+
Embedding: Linear(bits, 2*C) β LeakyReLU β Linear(2*C, 512) β LeakyReLU
|
| 12 |
+
PFP: Linear(512, rank) β SiLU β Linear(rank, C)
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
import torch
|
| 16 |
+
import torch.nn as nn
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class TemporalAdapterBlock(nn.Module):
|
| 20 |
+
"""Single TA block for one upsample stage."""
|
| 21 |
+
|
| 22 |
+
def __init__(self, watermark_bits: int, channels: int, hidden_dim: int = 128,
|
| 23 |
+
zero_conv_std: float = 0.02):
|
| 24 |
+
super().__init__()
|
| 25 |
+
self.channels = channels
|
| 26 |
+
|
| 27 |
+
# Acoustic Feature Alignment (paper B.1):
|
| 28 |
+
# Two FC layers with two LeakyReLU activations.
|
| 29 |
+
# First FC projects to 2 * hidden_feature_channels, second to 512.
|
| 30 |
+
self.embedding = nn.Sequential(
|
| 31 |
+
nn.Linear(watermark_bits, channels * 2),
|
| 32 |
+
nn.LeakyReLU(0.2),
|
| 33 |
+
nn.Linear(channels * 2, 512),
|
| 34 |
+
nn.LeakyReLU(0.2),
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
# Progressive Feature Projection (PFP) (paper B.1):
|
| 38 |
+
# Two FC layers with SiLU activation between them.
|
| 39 |
+
# First FC reduces to pre-defined Rank, second aligns to C.
|
| 40 |
+
rank = hidden_dim // 2 # pre-defined rank value
|
| 41 |
+
self.pfp = nn.Sequential(
|
| 42 |
+
nn.Linear(512, rank),
|
| 43 |
+
nn.SiLU(),
|
| 44 |
+
nn.Linear(rank, channels),
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
# Adaptive Injection: concat [w_latent, h] β downsample β DSC β zero_conv
|
| 48 |
+
# Downsample from 2C to C
|
| 49 |
+
self.downsample = nn.Conv1d(channels * 2, channels, kernel_size=1)
|
| 50 |
+
|
| 51 |
+
# Depth-wise Separable Convolution (DSC)
|
| 52 |
+
# BatchNorm is load-bearing: it normalizes the DSC output to ~unit variance
|
| 53 |
+
# before the small-init zero_conv, giving the injected watermark residual a
|
| 54 |
+
# meaningful scale. Without any norm the residual is ~0, the watermark barely
|
| 55 |
+
# perturbs the audio, extraction gradients vanish (grad_norm ~0.03) and
|
| 56 |
+
# training sticks at exactly random (acc 0.5, loss_ext = ln 2). InstanceNorm
|
| 57 |
+
# also fails (it cancels the time-constant watermark). So: keep BatchNorm.
|
| 58 |
+
self.dsc = nn.Sequential(
|
| 59 |
+
# Depth-wise conv
|
| 60 |
+
nn.Conv1d(channels, channels, kernel_size=3, padding=1, groups=channels),
|
| 61 |
+
nn.BatchNorm1d(channels),
|
| 62 |
+
nn.LeakyReLU(0.2),
|
| 63 |
+
# Point-wise conv
|
| 64 |
+
nn.Conv1d(channels, channels, kernel_size=1),
|
| 65 |
+
nn.BatchNorm1d(channels),
|
| 66 |
+
nn.LeakyReLU(0.2),
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
# Small-init convolution (not zero-init) to break the bootstrap deadlock.
|
| 70 |
+
# Zero-init prevents any watermark signal from reaching the audio,
|
| 71 |
+
# so the extractor can never learn. Small random init ensures different
|
| 72 |
+
# watermarks produce slightly different audio from the start.
|
| 73 |
+
self.zero_conv = nn.Conv1d(channels, channels, kernel_size=1)
|
| 74 |
+
nn.init.normal_(self.zero_conv.weight, std=zero_conv_std)
|
| 75 |
+
nn.init.zeros_(self.zero_conv.bias)
|
| 76 |
+
|
| 77 |
+
def forward(self, h: torch.Tensor, watermark: torch.Tensor) -> torch.Tensor:
|
| 78 |
+
"""
|
| 79 |
+
Args:
|
| 80 |
+
h: [B, C, T] hidden features from vocoder upsample stage
|
| 81 |
+
watermark: [B, watermark_bits] binary watermark vector (float)
|
| 82 |
+
Returns:
|
| 83 |
+
[B, C, T] watermarked features (h + residual)
|
| 84 |
+
"""
|
| 85 |
+
B, C, T = h.shape
|
| 86 |
+
|
| 87 |
+
# 1. Acoustic Feature Alignment
|
| 88 |
+
w_emb = self.embedding(watermark) # [B, 512]
|
| 89 |
+
w_proj = self.pfp(w_emb) # [B, C]
|
| 90 |
+
|
| 91 |
+
# 2. Frame-level Temporal Broadcasting
|
| 92 |
+
w_latent = w_proj.unsqueeze(-1).expand(B, C, T) # [B, C, T]
|
| 93 |
+
|
| 94 |
+
# 3. Adaptive Injection
|
| 95 |
+
h_cat = torch.cat([w_latent, h], dim=1) # [B, 2C, T]
|
| 96 |
+
h_down = self.downsample(h_cat) # [B, C, T]
|
| 97 |
+
h_dsc = self.dsc(h_down) # [B, C, T]
|
| 98 |
+
residual = self.zero_conv(h_dsc) # [B, C, T]
|
| 99 |
+
|
| 100 |
+
return h + residual
|