Files
openvpi--diffsinger/deployment/modules/fastspeech2.py
T
2026-07-13 12:35:17 +08:00

236 lines
9.8 KiB
Python

import copy
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from modules.commons.common_layers import NormalInitEmbedding as Embedding
from modules.fastspeech.acoustic_encoder import FastSpeech2Acoustic
from modules.fastspeech.variance_encoder import FastSpeech2Variance
from utils.hparams import hparams
from utils.phoneme_utils import PAD_INDEX
f0_bin = 256
f0_max = 1100.0
f0_min = 50.0
f0_mel_min = 1127 * np.log(1 + f0_min / 700)
f0_mel_max = 1127 * np.log(1 + f0_max / 700)
def uniform_attention_pooling(spk_embed, durations):
_, T_mel, _ = spk_embed.shape
ph_starts = torch.cumsum(torch.cat([torch.zeros_like(durations[:, :1]), durations[:, :-1]], dim=1), dim=1)
ph_ends = ph_starts + durations
mel_indices = torch.arange(T_mel, device=spk_embed.device).view(1, 1, T_mel)
phoneme_to_mel_mask = (mel_indices >= ph_starts.unsqueeze(-1)) & (mel_indices < ph_ends.unsqueeze(-1))
uniform_scores = phoneme_to_mel_mask.float()
sum_scores = uniform_scores.sum(dim=2, keepdim=True)
attn_weights = uniform_scores / (sum_scores + (sum_scores == 0).float()) # [B, T_ph, T_mel]
ph_spk_embed = torch.bmm(attn_weights, spk_embed)
return ph_spk_embed
def f0_to_coarse(f0):
f0_mel = 1127 * (1 + f0 / 700).log()
a = (f0_bin - 2) / (f0_mel_max - f0_mel_min)
b = f0_mel_min * a - 1.
f0_mel = torch.where(f0_mel > 0, f0_mel * a - b, f0_mel)
torch.clip_(f0_mel, min=1., max=float(f0_bin - 1))
f0_coarse = torch.round(f0_mel).long()
return f0_coarse
class LengthRegulator(nn.Module):
# noinspection PyMethodMayBeStatic
def forward(self, dur):
token_idx = torch.arange(1, dur.shape[1] + 1, device=dur.device)[None, :, None]
dur_cumsum = torch.cumsum(dur, dim=1)
dur_cumsum_prev = F.pad(dur_cumsum, (1, -1), mode='constant', value=0)
pos_idx = torch.arange(dur.sum(dim=1).max(), device=dur.device)[None, None]
token_mask = (pos_idx >= dur_cumsum_prev[:, :, None]) & (pos_idx < dur_cumsum[:, :, None])
mel2ph = (token_idx * token_mask).sum(dim=1)
return mel2ph
class FastSpeech2AcousticONNX(FastSpeech2Acoustic):
def __init__(self, vocab_size, cross_lingual_token_idx=None):
super().__init__(vocab_size=vocab_size)
self.register_buffer(
'cross_lingual_token_idx',
torch.LongTensor(cross_lingual_token_idx),
persistent=False
) # [N,]
if len(cross_lingual_token_idx) == 0:
self.use_lang_id = False
# for temporary compatibility; will be completely removed in the future
self.f0_embed_type = hparams.get('f0_embed_type', 'continuous')
if self.f0_embed_type == 'discrete':
self.pitch_embed = Embedding(300, hparams['hidden_size'], PAD_INDEX)
self.lr = LengthRegulator()
if hparams['use_key_shift_embed']:
self.shift_min, self.shift_max = hparams['augmentation_args']['random_pitch_shifting']['range']
if hparams['use_speed_embed']:
self.speed_min, self.speed_max = hparams['augmentation_args']['random_time_stretching']['range']
# noinspection PyMethodOverriding
def forward(
self, tokens, durations,
f0, variances: dict,
gender=None, velocity=None,
spk_embed=None,
languages=None
):
txt_embed = self.txt_embed(tokens)
durations = durations * (tokens > 0)
mel2ph = self.lr(durations)
_mel2ph = mel2ph
f0 = f0 * (mel2ph > 0)
mel2ph = mel2ph[..., None].repeat((1, 1, hparams['hidden_size']))
if self.use_variance_scaling:
dur_embed = self.dur_embed(torch.log(1 + durations.float())[:, :, None])
else:
dur_embed = self.dur_embed(durations.float()[:, :, None])
if self.use_lang_id:
lang_mask = torch.any(
tokens[..., None] == self.cross_lingual_token_idx[None, None],
dim=-1
)
lang_embed = self.lang_embed(languages * lang_mask)
extra_embed = dur_embed + lang_embed
else:
extra_embed = dur_embed
if hparams.get('use_mix_ln', False):
if hasattr(self, 'frozen_spk_embed'):
ph_spk_embed = self.frozen_spk_embed.repeat(1, tokens.shape[1], 1)
else:
ph_spk_embed = uniform_attention_pooling(spk_embed, durations)
else:
ph_spk_embed = None
encoded = self.encoder(txt_embed, extra_embed, tokens == PAD_INDEX, spk_embed=ph_spk_embed)
encoded = F.pad(encoded, (0, 0, 1, 0))
condition = torch.gather(encoded, 1, mel2ph)
if self.use_stretch_embed:
stretch = torch.round(1000 * self.sr(_mel2ph, durations))
table = self.stretch_embed(torch.arange(0, 1001, device=stretch.device))
stretch_embed = torch.index_select(table, 0, stretch.view(-1).long()).view_as(condition)
condition += stretch_embed
stretch_embed_rnn_out, _ = self.stretch_embed_rnn(condition)
condition += stretch_embed_rnn_out
if self.f0_embed_type == 'discrete':
pitch = f0_to_coarse(f0)
pitch_embed = self.pitch_embed(pitch)
else:
f0_mel = (1 + f0 / 700).log()
pitch_embed = self.pitch_embed(f0_mel[:, :, None])
condition += pitch_embed
if self.use_variance_embeds:
variance_embeds = torch.stack([
self.variance_embeds[v_name](variances[v_name][:, :, None] * self.variance_scaling_factor[v_name])
for v_name in self.variance_embed_list
], dim=-1).sum(-1)
condition += variance_embeds
if hparams['use_key_shift_embed']:
if hasattr(self, 'frozen_key_shift'):
key_shift_embed = self.key_shift_embed(self.frozen_key_shift[:, None, None] * self.variance_scaling_factor['key_shift'])
else:
gender = torch.clip(gender, min=-1., max=1.)
gender_mask = (gender < 0.).float()
key_shift = gender * ((1. - gender_mask) * self.shift_max + gender_mask * abs(self.shift_min))
key_shift_embed = self.key_shift_embed(key_shift[:, :, None] * self.variance_scaling_factor['key_shift'])
condition += key_shift_embed
if hparams['use_speed_embed']:
if velocity is not None:
velocity = torch.clip(velocity, min=self.speed_min, max=self.speed_max)
speed_embed = self.speed_embed(velocity[:, :, None] * self.variance_scaling_factor['speed'])
else:
speed_embed = self.speed_embed(torch.FloatTensor([1.]).to(condition.device)[:, None, None] * self.variance_scaling_factor['speed'])
condition += speed_embed
if hparams['use_spk_id']:
if hasattr(self, 'frozen_spk_embed'):
condition += self.frozen_spk_embed
else:
condition += spk_embed
return condition
class FastSpeech2VarianceONNX(FastSpeech2Variance):
def __init__(self, vocab_size, cross_lingual_token_idx=None):
super().__init__(vocab_size=vocab_size)
self.register_buffer(
'cross_lingual_token_idx',
torch.LongTensor(cross_lingual_token_idx),
persistent=False
)
if len(cross_lingual_token_idx) == 0:
self.use_lang_id = False
self.lr = LengthRegulator()
def forward_encoder_word(self, tokens, word_div, word_dur, languages=None):
txt_embed = self.txt_embed(tokens)
ph2word = self.lr(word_div)
onset = ph2word > F.pad(ph2word, [1, -1])
onset_embed = self.onset_embed(onset.long())
ph_word_dur = torch.gather(F.pad(word_dur, [1, 0]), 1, ph2word)
word_dur_embed = self.word_dur_embed(ph_word_dur.float()[:, :, None])
extra_embed = onset_embed + word_dur_embed
if self.use_lang_id:
lang_mask = torch.any(
tokens[..., None] == self.cross_lingual_token_idx[None, None],
dim=-1
)
lang_embed = self.lang_embed(languages * lang_mask)
extra_embed += lang_embed
x_masks = tokens == PAD_INDEX
return self.encoder(txt_embed, extra_embed, x_masks), x_masks
def forward_encoder_phoneme(self, tokens, ph_dur, languages=None):
txt_embed = self.txt_embed(tokens)
if self.use_variance_scaling:
ph_dur_embed = self.ph_dur_embed(torch.log(1 + ph_dur.float())[:, :, None])
else:
ph_dur_embed = self.ph_dur_embed(ph_dur.float()[:, :, None])
if self.use_lang_id:
lang_mask = torch.any(
tokens[..., None] == self.cross_lingual_token_idx[None, None],
dim=-1
)
lang_embed = self.lang_embed(languages * lang_mask)
extra_embed = ph_dur_embed + lang_embed
else:
extra_embed = ph_dur_embed
x_masks = tokens == PAD_INDEX
return self.encoder(txt_embed, extra_embed, x_masks), x_masks
def forward_dur_predictor(self, encoder_out, x_masks, ph_midi, spk_embed=None):
midi_embed = self.midi_embed(ph_midi)
dur_cond = encoder_out + midi_embed
if hparams['use_spk_id'] and spk_embed is not None:
dur_cond += spk_embed
ph_dur = self.dur_predictor(dur_cond, x_masks=x_masks)
return ph_dur
def view_as_encoder(self):
model = copy.deepcopy(self)
if self.predict_dur:
del model.dur_predictor
model.forward = model.forward_encoder_word
else:
model.forward = model.forward_encoder_phoneme
return model
def view_as_dur_predictor(self):
model = copy.deepcopy(self)
del model.encoder
model.forward = model.forward_dur_predictor
return model