mlx-audio OmniVoice:基于双向 Qwen3-0.6B 与迭代掩码扩散的零样本多语言 TTS 全解
2026/9/16 19:40:53 网站建设 项目流程

mlx-audio OmniVoice:基于双向 Qwen3-0.6B 与迭代掩码扩散的零样本多语言 TTS 全解

【免费下载链接】mlx-audioA text-to-speech (TTS), speech-to-text (STT) and speech-to-speech (STS) library built on Apple's MLX framework, providing efficient speech analysis on Apple Silicon.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-audio

OmniVoice 是 mlx-audio 中一款支持 646+ 语言、可从参考音频做零样本音色克隆的非自回归 TTS 模型,本文以 模型 README 为骨架,结合仓库内 模型实现、生成循环 与 音色克隆预处理 的源码,完整讲解其架构原理、全部生成参数、Python API / CLI 用法与音色克隆工作流,读完即可在 Apple Silicon 上直接跑通合成与克隆。

1. 模型概览与架构

OmniVoice 的核心定位(来自 README):

  • 零样本多语言 TTS,支持 646+ 语言,可选参考音频音色克隆;
  • 主干为双向(bidirectional)Qwen3-0.6B,因果掩码被显式移除;
  • 解码采用迭代掩码扩散(iterative masked diffusion,非自回归),默认 32 步逐步去掩码;
  • 声学 tokenizer 为HiggsAudioV2:24 kHz 采样率、25 tokens/sec,输出经 HiggsAudioV2 解码器还原为 24 kHz WAV。

原文档给出的数据流如下:

Text tokens --> backbone (bidirectional Qwen3-0.6B) --> 8 prediction heads Ref tokens --> | MASK tokens --> iterative unmasking (32 steps) | HiggsAudioV2 decoder --> 24kHz WAV

从源码结构可以印证并扩展这张图的细节:

主干(backbone)。backbone.py 中BackboneConfig的默认值对应 Qwen3-0.6B 规格:hidden_size=1024num_hidden_layers=28num_attention_heads=16num_key_value_heads=8(GQA)、intermediate_size=3072vocab_size=151676rope_theta=1_000_000Attention的前向实现里有一行关键注释:

# NO causal mask — full bidirectional attention out = mx.fast.scaled_dot_product_attention(q, k, v, scale=self.scale, mask=None)

即整条序列(文本 token + 参考音频 token + MASK 占位 token)做全双向注意力。这是非自回归扩散解码成立的前提:目标区间的每个 MASK 位置可以同时看到全部上下文,因此不需要从左到右逐 token 生成,而是并行地"填空"。OmniVoiceBackbone.__call__还断言不支持 padding 掩码(assert attention_mask is None),说明该主干按定长批内对齐的方式工作,批内变长由上层_pack_batch的 MASK 填充解决。

音频 token 表达。config.py 的OmniVoiceConfig定义了音频词表的关键常量:

配置项默认值含义
num_audio_codebook88 个码本(8 层量化)
audio_vocab_size10250–1023 为真实 codec token,1024 为 MASK
audio_mask_id1024掩码 token id
audio_codebook_weights[8, 8, 6, 6, 4, 4, 2, 2]各码本权重
sample_rate24000输出采样率
llm_config原始 Qwen3 config dict延迟解析,只取BackboneConfig已知字段

对应地,omnivoice.py 的Model.__init__每个码本独立建一张嵌入表和一张预测头8× nn.Embedding(1025, hidden)+8× nn.Linear(hidden, 1025, bias=False)),前向时对 8 个码本嵌入求和后与文本嵌入按audio_mask二选一,输出再 stack 成[B, L, 8, 1025]的 logits——即 README 中"8 prediction heads"的落地形态。

token 速率。代码中tokens_per_sec = self.config.sample_rate / 960,即 24000/960 =25 tokens/sec,与 README 一致;每个 token 对应 960 个采样点(40 ms)。因此duration_s=5.0会展开为ceil(5.0 × 25) = 125个目标 token 帧,最终音频长度严格等于target_len × 960 / 24000秒——duration_s固定上限/精确时长而非"最长"。

2. 模型权重

README 给出两个可选权重源:

Repo ID说明
mlx-community/OmniVoice-bfloat16MLX-ready 权重(bfloat16),推荐
k2-fsa/OmniVoice原始权重(bfloat16),需经转换流程适配

MLX 版目录由 convert.py 从原始仓库生成:它把llm.*前缀改名为backbone.*、把合并存储的[8*V, H]音频嵌入/头矩阵切分为audio_embeddings.{i}.weight/audio_heads.{i}.weightV=1025),丢弃codebook_layer_offsets,并把audio_tokenizer/子目录权重完整保留(MLX 端解码走 MLX,而音色克隆的 encode 走 PyTorch 加载同一目录)。该脚本本身也可独立运行:

# 将原始权重转换为 MLX 目录(可选 --dtype float32/float16/bfloat16) python -m mlx_audio.tts.models.omnivoice.convert \ --model k2-fsa/OmniVoice \ --output ./models/omnivoice-mlx

Model.sanitize()convert.remap_backbone()执行同一套键名重映射,前者供"直接加载原始model.safetensors"的用法(见下节示例),这也是该模型类暴露sanitize的原因。

3. 快速上手:Python API

README 的完整加载与合成流程(可直接复制运行,前提已pip install mlx-audio transformers huggingface_hub):

from transformers import AutoTokenizer from huggingface_hub import snapshot_download from mlx_audio.codec.models.higgs_audio.higgs_audio import HiggsAudioTokenizer from mlx_audio.tts.models.omnivoice.omnivoice import Model, OmniVoiceConfig from mlx_audio.audio_io import write as audio_write import mlx.core as mx, json, numpy as np model_path = snapshot_download("mlx-community/OmniVoice-bfloat16") # 加载模型 with open(f"{model_path}/config.json") as f: raw = json.load(f) config = OmniVoiceConfig.from_dict(raw) model = Model(config) weights = model.sanitize(dict(mx.load(f"{model_path}/model.safetensors"))) model.load_weights(list(weights.items())) mx.eval(model.parameters()) # 加载 tokenizer tokenizer = HiggsAudioTokenizer.from_pretrained(model_path) text_tokenizer = AutoTokenizer.from_pretrained(f"{model_path}/tokenizer") # 生成语音 result = next(model.generate( text="Hello, this is OmniVoice running on Apple Silicon.", language="en", duration_s=5.0, tokenizer=tokenizer, text_tokenizer=text_tokenizer, )) audio_write("output.wav", np.array(result.audio), result.sample_rate)

几个源码层面的要点:

  • model.generate()生成器Generator[GenerationResult, None, None]),next()取出唯一的GenerationResult,其中含audiomx.array)、sample_rate=24000real_time_factor(RTF)、processing_time_secondspeak_memory_usage等统计量,便于对比num_steps等超参对速度/质量的影响;
  • 若不想手工管理两个 tokenizer,可直接用库级加载器load_tts("mlx-community/OmniVoice-bfloat16"),其底层触发Model.post_load_hook(见 omnivoice.py)自动加载text_tokenizeraudio_tokenizer,之后model.generate(...)可省略这两个参数;
  • 输入序列的拼装(_prepare_inference_inputs)为[style 段, text 段, (ref 音频 token 段), T 个 MASK 帧]。style 段由_tokenize_style_and_text生成:
style_text += "<|denoise|>" # 仅在提供参考音频时 style_text += f"<|lang_start|>{language}<|lang_end|>" style_text += f"<|instruct_start|>{instruct}<|instruct_end|>" # 正文包裹为 <|text_start|>{full_text}<|text_end|>

即语言标签、风格指令(instruct)和"降噪"标记(<|denoise|>,有ref_audio时自动开启)都以特殊 token 形式前置,参与双向注意力。

4. 快速上手:CLI

README 给出的命令行用法:

python -m mlx_audio.tts.generate \ --model mlx-community/OmniVoice-bfloat16 \ --text "Hello, this is OmniVoice." \ --duration_s 5.0 \ --language en

结合 generate.py 的入口实现,补充以下使用说明:

  • --duration_s--language--num_steps--guidance_scale等模型专属参数不在 argparse 显式声明,而是经generate_audio(**kwargs)**kwargs透传机制转发给model.generate()extra_kwargs只保留非 None 值);
  • CLI 另有一组通用参数对 OmniVoice 同样有效:--ref_audio(可重复传入多条参考)、--ref_text(参考音频文本)、--stt_model(未提供--ref_text时用它自动转写参考音频,默认mlx-community/whisper-large-v3-turbo-asr-fp16)、--output_path--play--verbose
  • 批量合成可多次传入--ref_audio/--ref_text(二者必须等长),底层走generate_batchmax_batch_size默认 8,批内不同长度的条件序列用 MASK 填充对齐。

5. 音色克隆:参考音频与预处理流水线

README 的克隆用法只需在generate()中加一个参数:

result = next(model.generate( text="Hello, this is OmniVoice.", language="en", duration_s=5.0, ref_audio="reference.wav", # 任意采样率,截取前 10s tokenizer=tokenizer, text_tokenizer=text_tokenizer, )) audio_write("output_cloned.wav", np.array(result.audio), result.sample_rate)

ref_audio传文件路径时,utils.py 的create_voice_clone_prompt()会执行与原版 k2-fsa/OmniVoice 对齐的预处理链,再调用tokenizer.encode(wav)得到[T_ref, 8]的参考 token:

  1. 重采样:非 24 kHz 的音频用 torchaudio 兼容的 sinc 插值重采样到 24 kHz(复用 HiggsAudioTokenizer 的_sinc_resample);
  2. RMS 归一化:RMS 在 (0, 0.1) 的安静音频增益到RMS=0.1
  3. 静音切除(依赖-free 的 pydub 风格实现):中段 >300 ms 静音、边缘 >100 ms 静音按 -50 dBFS 阈值切除;
  4. 长音频裁剪:仅当未提供ref_text时,>20 s 的音频在最大静音间隙处裁到 15 s 以内(max_duration_screate_voice_clone_prompt内默认 15.0,而generate()ref_audio_max_duration_s默认 10.0 s,二者以调用链实际传入值为准,README 对外表述为"截取前 10 s");
  5. 若直接传入mx.array/numpy 波形(非路径),则跳过预处理直接encode

重要实践:voice cloning 指南 特别强调,OmniVoice 的预处理会先去静音再编码——如果你需要同时提供ref_text(参考音频转写文本),必须转写预处理之后的音频而不是原始录音,否则转写会比实际参考内容多出被裁掉的文字,导致多余文本泄漏进生成结果。完整正确工作流可参考 示例脚本:先预处理、把预处理音频解码回波形、用任意 mlx-audio STT 模型(示例默认mlx-community/Qwen3-ASR-0.6B-8bit)转写、再同时把ref_tokensref_text交给generate()

另外,模型注册表 中"omnivoice"已登记在MODEL_REMAPPINGmodel_type="omnivoice"可被load_tts/load_model按仓库 ID 自动识别;测试 也校验了该注册项。

6. 生成参数详解

README 的参数表(默认值与generate()函数签名完全一致,omnivoice.py):

参数默认值说明
text要合成的文本
language"None"BCP-47 语言标签:"en""ru""zh"、``"ja"…;"None"` = 自动检测
instruct"None"风格指令标签(模型特有)
duration_s5.0输出时长上限(秒);实际为精确目标时长
ref_audioNone音色克隆参考 WAV 路径(任意采样率)
ref_audio_max_duration_s10.0参考音频裁剪时长(秒)
num_steps32迭代去掩码步数(4–64;越少越快、质量越低)
guidance_scale2.0CFG 强度(0关闭 CFG)
class_temperature0.0token 采样温度(0= 贪心)
position_temperature5.0位置置信度上的 Gumbel 噪声

签名中还有两个 README 未列出、但可透传的超参:

  • layer_penalty_factor(默认5.0):置信度打分时按码本层号施加的线性惩罚;
  • t_shift(默认0.1):时间步调度的 cosine-shift 参数。

源码级解读(generation.py):

num_steps与去掩码调度_get_time_steps生成[0, 1]均匀采样后做 warp:t' = t_shift·t / (1 + (t_shift-1)·t)t_shift=0.1 < 1使早期步长更大(先确定大轮廓、后精修细节)。每步揭示数量k = ceil(total_mask · Δt)total_mask = T×8),最后一步强制揭示全部剩余位置;测试 验证了调度的单调性(33 个点、首 0 末 1)以及 5 步解码后零 MASK 残留、token 全部落在 [0, 1023]

guidance_scale(CFG)。每步对"条件输入"与"仅目标段"的无输入各做一次前向,在log 概率空间做引导:lp = softmax( lp_c + scale·(lp_c − lp_u) )guidance_scale=0时跳过无条件下向,省一次前向——这是该模型最直接的提速开关。

class_temperature(token 级随机性)>0时先_filter_top_k保留概率最高的 10% 词表项(其余置-inf),再加 Gumbel 噪声取 argmax;=0则纯贪心。由于 MASK token(1024)在任何一步都被置-inf,解码永远不会输出掩码本身。

position_temperature(位置级随机性)。对每个待揭示位置的置信度confidence = max(log_probs) − layer_id × layer_penalty_factor叠加 Gumbel 噪声后排序,取前k个位置揭示。layer_penalty_factor使高层码本(layer_id大)更难被优先揭示,配合各码本权重实现"先粗后细";position_temperature=5.0默认带随机性,调小可得到更稳定的揭示顺序。

duration_s=None的自动时长。未指定时长时,generate()调用 duration.py 的RuleDurationEstimator——一个覆盖 600+ 语言的规则估算器:按 Unicode 区段给字符分配发音权重(CJK 记号文字 3.0、谚文 2.5、拉丁 1.0、标点 0.5、空格 0.2、数字 3.5 等,基准为拉丁字母 ≈ 40–50 ms),以参考句 "Nice to meet you." 的 25 tokens 为基准换算 token 数,再乘 1.15 余量、下限 10 token:

raw_tokens = estimator.estimate_duration(text, "Nice to meet you.", 25) target_len = max(10, int(raw_tokens * 1.15))

非语言标签_NONVERBAL_PATTERN识别[laughter][sigh][question-ah][surprise-oh][dissatisfaction-hnn]等 13 种标签,_tokenize_with_nonverbal_tags把它们作为原子片段单独分词(避免被切成子词),可在text中直接书写以表达笑声、叹息、疑问等非语言发声;_combine_text还会对 CJK 边界去空格、压平换行。

7. 支持的语言

通过language标签可覆盖 646+ 语言;README 标注质量最佳的语言为:enzhrujadefreskoarptitplnltrlanguage="None"时走模型自身的自动检测。

8. 运行依赖与工程注意事项

README Notes 部分与源码相互印证:

  • 音色克隆需要torchaudiotransformerspip install torchaudio transformers):参考音频的encode()路径运行在 PyTorchCPU上(README 给出量级:约 0.5 s / 1 s 音频);decode()在 MLX GPU 上完成——这也是convert.py刻意保留 HuBERT/semantic 权重供 PyTorch 侧加载的原因;
  • 纯合成路径(无ref_audio)不需要 torchaudio,只依赖 MLX 与transformers文本 tokenizer;
  • 步数取舍num_steps越大质量越好,16步是 README 推荐的速度/质量平衡点,下限 4、上限 64;由于每步需要 1–2 次全序列前向(有条件 + 无 CFG 条件),num_stepsguidance_scale=0是控制时延的两个主要旋钮,real_time_factor会写入GenerationResult便于实测对比;
  • 输出长度由duration_s精确决定,长文本请相应放大该值,或干脆置None交给规则估算器。

9. 测试与可验证点

tests/test_models.py 中围绕该模型有多组可直接运行的单测(使用小型随机初始化配置,无需下载权重):

  • TestOmniVoiceConfigOmniVoiceConfig.from_dict对未知键的容忍;
  • TestOmniVoiceRegistrationMODEL_REMAPPING"omnivoice" → "omnivoice"
  • TestOmniVoiceBackbone/TestOmniVoiceModel/TestOmniVoicePrepareInputs:双向主干前向、输入拼装(含 MASK 布局与audio_mask边界);
  • TestOmniVoiceGeneration/TestOmniVoiceIterativeUnmaskRefactor:时间步单调性、去掩码完整性、冻结 token 不变性;
  • TestOmniVoiceSanitizellm.*backbone.*及 8 码本切分重映射的正确性。
# 在仓库根目录运行该模型相关测试(无需权重文件) python -m pytest mlx_audio/tts/tests/test_models.py -k OmniVoice

10. 小结与延伸阅读

OmniVoice 在 mlx-audio 中的实现,展示了"小主干 + 多码本 + 迭代掩码扩散"这条非自回归 TTS 路线的完整工程形态:双向 Qwen3-0.6B 一次前向并行打分全部待揭示位置,HiggsAudioV2(24 kHz、25 tokens/sec)负责 token 与波形的双向转换,num_steps/CFG/双温度参数提供从速度到质量的连续调节空间,而ref_audio一条参数即打通零样本克隆。继续深入可看:音色克隆指南、TTS 模型索引、克隆演示脚本 以及同目录下的 backbone 实现 与 权重转换脚本。

【免费下载链接】mlx-audioA text-to-speech (TTS), speech-to-text (STT) and speech-to-speech (STS) library built on Apple's MLX framework, providing efficient speech analysis on Apple Silicon.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-audio

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询