vocoder-small / modeling_hifigan.py
mlr2000's picture
Upload folder using huggingface_hub
1fdc661 verified
Raw
History Blame Contribute Delete
5.91 kB
"""Self-contained inference model for the VocBulwark HiFi-GAN / BigVGAN vocoder.
Loadable with `AutoModel.from_pretrained(path, trust_remote_code=True)` without
the training repository. This is the lean vocoder: it takes a mel-spectrogram and
a precomputed speaker embedding, and does NOT include the speaker encoder (that
is a separate model), the training losses, discriminators, or watermark heads.
"""
from dataclasses import dataclass
import torch
import torch.nn.utils.parametrize as parametrize
from torch.nn.utils import remove_weight_norm
from transformers import PreTrainedModel
from transformers.modeling_outputs import ModelOutput
def _strip_weight_norm(root):
"""Remove every weight-norm reparametrization (both conventions), leaving
plain ``.weight`` parameters that match the folded export weights."""
for m in root.modules():
if parametrize.is_parametrized(m, "weight"):
parametrize.remove_parametrizations(m, "weight", leave_parametrized=True)
elif hasattr(m, "weight_g"):
try:
remove_weight_norm(m)
except (ValueError, RuntimeError):
pass
from .configuration_hifigan import HiFiGANConfig
from .bigvgan_model import BigVGAN
# transformers' trust_remote_code loader copies only modules *directly* imported
# by this entry file, and recognizes the `from .X import Y` form. Import a real
# symbol from every bundled leaf module so all files get copied into the
# dynamic-module cache (they are used transitively by the imports above).
from .bigvgan_activations import Snake as _Snake # noqa: F401
from .alias_free_act import Activation1d as _Activation1d # noqa: F401
from .alias_free_resample import UpSample1d as _UpSample1d # noqa: F401
from .alias_free_filter import LowPassFilter1d as _LowPassFilter1d # noqa: F401
from .temporal_adapter import TemporalAdapterBlock as _TemporalAdapterBlock # noqa: F401
@dataclass
class HiFiGANOutput(ModelOutput):
audio: torch.Tensor = None
class HiFiGANArchitecture(PreTrainedModel):
config_class = HiFiGANConfig
def __init__(self, config):
super().__init__(config)
# Lean vocoder: the speaker embedding is supplied at inference time, so the
# generator only needs its dimension to build the conditioning conv.
global_channels = int(getattr(config, "speaker_embedding_size", 0)) or -1
self.hifi_gan = BigVGAN(
num_mels=config.hifigan_in_channels,
upsample_initial_channel=config.hifigan_channels,
resblock_kernel_sizes=config.hifigan_resblock_kernel_sizes,
resblock_dilation_sizes=config.hifigan_resblock_dilations,
upsample_kernel_sizes=config.hifigan_upsample_kernel_sizes,
upsample_rates=config.hifigan_upsample_scales,
snake_logscale=config.snake_logscale,
activation=config.hifigan_nonlinear_activation,
use_bias_at_final=config.hifigan_bias,
global_channels=global_channels,
watermark_bits=config.watermark_bits if config.add_film_watermark else 0,
watermark_film_hidden=config.watermark_film_hidden,
vocbulwark_bits=config.vocbulwark_bits if config.add_watermark_vocbulwark else 0,
vocbulwark_ta_hidden=config.vocbulwark_ta_hidden,
vocbulwark_zero_conv_std=getattr(config, "vocbulwark_zero_conv_std", 0.02),
)
# Export folds weight-norm into plain .weight; strip the reparametrization
# here so module keys match (and inference is independent of torch's
# weight_norm convention).
_strip_weight_norm(self)
@torch.no_grad()
def forward(
self,
mel_spectrogram=None,
speaker_embedding=None,
input_features=None,
return_loss=False,
**kwargs,
):
"""Vocode a (log-)mel spectrogram into a waveform.
Args:
mel_spectrogram: [B, mel_channels, T] input features.
speaker_embedding: [B, speaker_embedding_size] speaker conditioning,
produced by the companion speaker-encoder model.
Returns:
HiFiGANOutput with `.audio` of shape [B, 1, samples] @ target_sample_rate.
The model's fixed provenance watermark is embedded in every generated clip
and cannot be changed or disabled through this interface.
"""
hifi_gan_device = self.hifi_gan.conv_pre.bias.device
if torch.is_autocast_enabled():
target_dtype = torch.get_autocast_gpu_dtype()
else:
target_dtype = self.hifi_gan.conv_pre.bias.dtype
if input_features is not None:
features = input_features.to(device=hifi_gan_device, dtype=target_dtype)
else:
features = mel_spectrogram.to(device=hifi_gan_device, dtype=target_dtype)
if speaker_embedding is None:
raise ValueError(
"speaker_embedding is required: pass a "
"[B, config.speaker_embedding_size] tensor from the companion "
"speaker-encoder model.")
g = speaker_embedding.to(device=hifi_gan_device, dtype=target_dtype)
if g.dim() == 2: # [B, E] -> [B, E, 1]
g = g.unsqueeze(2)
# Provenance watermark: the model's fixed signature is embedded in every
# generated clip. It is intentionally not overridable through this
# interface — there is no way to disable or change it here.
watermark = None
fw = getattr(self.config, "fixed_watermark", None)
if fw is not None:
watermark = torch.tensor(fw, dtype=target_dtype, device=hifi_gan_device)
watermark = watermark.unsqueeze(0).expand(features.shape[0], -1)
audio = self.hifi_gan(features, g=g, watermark=watermark)
return HiFiGANOutput(audio=audio)