设计一个可以控制音高的Vocoder

AI声明:本项目由Kimi K3,Devin SWE 2(研究讨论)和DeepSeek V4.1 Flash(运维)协助完成。其中Kimi K3通过Cursor调用,其基础设施提供商位于美国,不存在被路由到Claude等其他模型的可能性。

简介

vocoder是什么

TODO

Idea

对抗式移除

我的目标是训练一个encoder从mel中提取出 无关的features(encoder features ), ;希望通过对抗训练的形式,训练一个f0 discriminator() 从这个features里面尝试提取 ;然后再将“这个 无法从我的 里面提取出任何 信息”这个目标,以一定权重加入到训练Encoder使用的那个loss里面。

如何定义“无法提取出任何 信息”

是一个标量,所以最自然的方法是把检测f0定义为一个回归问题。但是现代 检测模型(RMVPE,FCPE)都不约而同地把f0检测定义为一个分类问题:把人类发声的可能f0按照一个公差进行等差数列分桶,每一个桶就是一个类别。

对于我们的任务,有明确的理由应该选择后者,因为我们想要把“没有任何 信息”定义为一个数学上的目标,此时回归式的定义就不可用了:

而“完全无信息”在概率分布上有良定义的最大熵形式:

按照这样,可以把 检测模块的输出定义为输出一个 分桶上的离散分布,通过交叉熵损失和KL散度的方式,分别进行 和Encoder的优化。

Idea验证:AutoEncoder

根据经验,Vocoder的训练是很慢的,如果直接在Vocoder上做实验会严重拖慢想法验证的速度。于是首先想到了进行一个如下AutoEncoder架构的小规模实验:

好处是:

在这个实验中,我主要尝试了以下选项,每个实验都跑至少30k steps:

最终的网络结构设计就是一个ConvNeXt架构[2] 的非压缩式Autoencoder; 部分则是直接照抄了FCPE[3] 的模型架构。

端到端Vocoder

Source-Filter理论和NSF架构

Source-Filter理论

由声学家 Gunnar Fant 在其经典著作[4] 中系统建立,该理论将复杂的语音发声机制解耦为两个线性无关的模块:

  1. Source模块
  1. Filter模块:声道(咽腔、口腔、鼻腔等)具有特定的物理共振几何形态,扮演一个线性时变时域/频域滤波器。它在特定频率产生共振峰(Formants),增强或衰减声源中的特定频率分量,赋予语音音素、音色与语义特征。

数学上,语音生成信号 可表示为声源频谱 与声道频响函数 的时域卷积/频域乘积:

基于S-F理论的Neural Vocoder

在很长一段时间内,S-F理论过于简化的模型与严格的线性卷积限制限制了合成的音质。现代模型在继承了“Excitation与Filter显式解耦”思想的基础上,对理论中过于理想化的数学假设进行了重构:

Vocoder结构

架构基于前面的Autoencoder设计,主要改动是将Decoder换成S-F架构的Vocoder,并且加上波形生成的对抗Loss。

Filter网络架构和对抗Loss的设计主要来自Pupu-Vocoder[5];Source注入的方式主要来自Coulin9的设计。此外,基于Coulin9的实验,Vocoder以WORLD features作为condition输入是一个听感合理的baseline,因此设计中Encoder features先和WORLD features融合之后,再作为condition提供给Source和Filter。以下是模型定义的节选:

class VocoderGenerator(nn.Module):
def __init__(
self,
sample_rate: int = 44100,
hop_size: int = 256,
encoder_hidden: int = 384,
encoder_blocks: int = 12,
feature_dim: int = 256,
condition_dim: int = 256,
source_stage_channels: list[int] = (80, 80, 64, 48, 32),
upsample_rates: list[int] = (4, 8, 2, 2, 2),
upsample_initial_channel: int = 896,
):
super().__init__()
self.encoder = MelEncoder(hidden_dim=encoder_hidden, feature_dim=feature_dim, num_blocks=encoder_blocks)
self.condition_mlp = ConditionalMLP(feature_dim + 144, 320, condition_dim)
self.source = NHSourceNetwork(
condition_channels=condition_dim,
upsample_rates=list(upsample_rates),
stage_channels=list(source_stage_channels),
sample_rate=sample_rate,
hop_size=hop_size,
)
self.filter = PupuFilter(
condition_channels=condition_dim,
upsample_initial_channel=upsample_initial_channel,
upsample_rates=list(upsample_rates),
source_channels=self.source.output_channels,
)

训练

TODO

效果展示

TODO

参考文献

  • [1] 苏剑林, 《Muon优化器指南:快速上手与关键细节》.
  • [2] Z. Liu, H. Mao, C.-Y. Wu, C. Feichtenhofer, T. Darrell, 和 S. Xie, 《A ConvNet for the 2020s》. [在线]. 载于: https://arxiv.org/abs/2201.03545
  • [3] Y. Luo, R. Zhang, L.-C. Liu, T. Li, 和 H. Liu, 《FCPE: A Fast Context-based Pitch Estimation Model》. [在线]. 载于: https://arxiv.org/abs/2509.15140
  • [4] G. Fant, Acoustic Theory of Speech Production. The Hague: Mouton, 1960.
  • [5] Y. Gu, J. Zhang, C. Wang, J. Li, Z. Wu, 和 L. Juvela, 《Aliasing-Free Neural Audio Synthesis》. [在线]. 载于: https://arxiv.org/abs/2512.20211