任意到任意(any-to-any)零样本语音转换工具包。
voxmorph 把一段语音拆解为“语言内容”和“说话人音色”两部分,转换时保留源语音的 内容表示,替换成目标说话人的音色统计量,再重新合成波形。整个流程无需训练, 对未见过的说话人也能直接工作,因此是零样本(zero-shot)的。
- 内容 / 音色解耦:对数幅度谱做实例归一化(instance normalization), 去掉逐频带的全局统计量得到说话人无关的内容表示。
- 说话人嵌入:把每个频带的均值与标准差拼接、归一化,作为训练无关的说话人表示。
- AdaIN 音色重组:用目标说话人的统计量对内容做仿射变换,实现音色替换。
- 离线优先:核心只依赖 NumPy;WAV 读写走标准库,无需任何音频库即可运行。
- 可选 PyTorch 后端:FFT 可切换到
torch.fft,未安装时自动降级为 NumPy。 - 客观评价:内置梅尔倒谱失真(MCD)、SNR、对数谱距离、谱收敛度,以及基于 说话人嵌入余弦相似度与等错误率(EER)的相似度评估。
pip install voxmorph # 核心(仅 NumPy)
pip install "voxmorph[torch]" # 附带可选 PyTorch 后端从源码开发:
uv sync --extra dev
uv run pytest无需任何音频素材,直接用内置的合成说话人做一次零样本转换:
from voxmorph import VoiceConverter, SpeakerEncoder
from voxmorph.datasets import synth_speaker
# 同样的“内容种子”配不同音色 = 两个不同说话人说同一句话
source = synth_speaker("alice", content_seed=1, duration=1.0) # 提供内容
target = synth_speaker("bob", content_seed=7, duration=1.0) # 提供音色
converter = VoiceConverter()
converted = converter.convert(source, target)
# 转换后语音的说话人身份更接近目标
spk = SpeakerEncoder()
print("转换后 vs 目标:", float(spk.embed(converted) @ spk.embed(target)))
print("转换前 vs 目标:", float(spk.embed(source) @ spk.embed(target)))处理真实文件:
from voxmorph import VoiceConverter, read_wav, write_wav
source, sr = read_wav("source.wav")
target, _ = read_wav("target.wav")
converted = VoiceConverter().convert(source, target)
write_wav("converted.wav", converted, sr)voxmorph convert --source a.wav --target b.wav --output out.wav
voxmorph embed --input a.wav --output spk.npy
voxmorph features --input a.wav --output mel.npy --kind mel
voxmorph evaluate --reference ref.wav --test out.wav # 打印 MCD / LSD / SNR / 相似度- docs/architecture.md —— 整体架构与数据流
- docs/usage.md —— 详细用法
- docs/design-notes.md —— 解耦与重组的原理和取舍
- docs/api-reference.md —— 公开 API 速查
- examples/ —— 可直接运行的离线示例