mlx-audio 中 LFM2.5-Audio 的 MLX 实现一文掌握 TTS / ASR / STS 多模态音频生成【免费下载链接】mlx-audioA text-to-speech (TTS), speech-to-text (STT) and speech-to-speech (STS) library built on Apples MLX framework, providing efficient speech analysis on Apple Silicon.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-audioLFM2.5-Audio 是 LiquidAI 发布的多模态基础模型能够在单一模型内完成文本转语音TTS、语音转文本ASR与语音转语音STS任务并支持文本与音频交错输出与逐 token 流式生成。本文以 mlx_audio/sts/models/lfm_audio/README.md 为主体结合 model.py、processor.py、config.py 等源码系统讲解其安装、三种任务的完整调用方式、交错生成与流式播放的实现细节、两种音频解码路径以及 Conformer LFM Depthformer Detokenizer 的整体架构。读完本文你将能够在 Apple Silicon 上直接运行 LFM2.5-Audio 的 MLX 量化版本并理解其底层采样与解码原理。模型概览与能力定位LFM2.5-Audio1.5B 参数是面向音频理解与生成的多模态基础模型。在 mlx-audio 仓库中它位于 STS语音到语音模块下但能力不止于 STS能力说明对应生成方法Text-to-Speech (TTS)从文本生成自然语音generate_sequentialSpeech-to-Text (ASR)将语音转录为文本generate_interleavedSpeech-to-Speech (STS)输入音频、输出带语音的对话generate_interleavedInterleaved Generation单轮内交错输出文本与音频generate_interleavedStreaming逐 token 生成支持实时播放两个方法均为生成器Generator在官方文档 docs/models/sts/index.md 中LFM2.5-Audio 与 SAM-Audio、Moshi、MossFormer2 SE、DeepFilterNet 并列为 STS 模型是其中唯一一个模型通吃 TTS / ASR / STS的通用语音助手类模型。模型以 24kHz 采样率输出音频使用 8 个音频 codebook音频词汇表大小为 20492048 个码 1 个填充位见 config.py。安装与环境要求LFM2.5-Audio 的 MLX 实现依赖 mlx-audio 包pip install mlx-audio由于 MLX 框架本身面向 Apple Silicon模型的本地推理运行在 MacApple Silicon环境。除核心依赖外以下能力需要额外依赖源码中做了惰性加载用到时才报错提示文本分词LFM2AudioProcessor通过transformers的AutoTokenizer加载分词器需要安装transformers见 processor.py音频编解码音频输入特征提取、Mimi 编解码与 Detokenizer 均由仓库内实现mlx_audio.dsp、mlx_audio.codec.models.mimi随包自带。模型权重通过 Hugging Face Hub 下载推荐使用社区已转换的量化权重4bit / 8bit权重仓库精度mlx-community/LFM2.5-Audio-1.5B-4bit4-bit 量化mlx-community/LFM2.5-Audio-1.5B-8bit8-bit 量化LFM2AudioModel.from_pretrained会读取权重目录下的config.json若其中含quantization字段则自动按 group_size / bits 对模型进行量化后加载见 model.py。核心 API 一览动手前先认识三个核心类全部从mlx_audio.sts.models.lfm_audio导出见init.pyLFM2AudioModel模型本体负责 prefill 与两种模式的 token 生成LFM2AudioProcessor处理器负责文本分词、音频预处理mel 特征、音频 token 化Mimi与音频解码Detokenizer / MimiChatState多轮对话状态容器并行维护文本 token、音频特征与模态标记三组张量。from mlx_audio.sts.models.lfm_audio import ( LFM2AudioModel, LFM2AudioProcessor, ChatState, LFMModality, )LFMModality是 IntEnum取值与 PyTorch 参考实现保持一致TEXT 1、AUDIO_IN 2、AUDIO_OUT 30 保留未用该值在 test_lfm_audio.py 中有断言测试。场景一Text-to-SpeechTTSTTS 使用generate_sequential模型在文本模式与音频模式之间自主切换——当采样到|audio_start|特殊 tokenID 128时进入音频模式遇到音频 EOS tokenID 2048时结束音频见 model.py。import mlx.core as mx from mlx_audio.sts.models.lfm_audio import ( LFM2AudioModel, LFM2AudioProcessor, ChatState, LFMModality, ) from mlx_audio.sts.models.lfm_audio.model import AUDIO_EOS_TOKEN # 加载模型与处理器可传本地目录或 Hub 仓库名 model LFM2AudioModel.from_pretrained(mlx-community/LFM2.5-Audio-1.5B-4bit) processor LFM2AudioProcessor.from_pretrained(mlx-community/LFM2.5-Audio-1.5B-4bit) # 构造多轮对话system 指令 - user 文本 - assistant待生成 chat ChatState(processor) chat.new_turn(system) chat.add_text(Perform TTS. Use a UK male voice.) chat.end_turn() chat.new_turn(user) chat.add_text(Hello, welcome to MLX Audio!) chat.end_turn() chat.new_turn(assistant) # 顺序生成文本与音频交错返回 audio_codes [] for token, modality in model.generate_sequential( **dict(chat), max_new_tokens2048, temperature0.8, ): mx.eval(token) # 触发 MLX 计算 if modality LFMModality.AUDIO_OUT: if token[0].item() AUDIO_EOS_TOKEN: break # 遇到音频 EOS 停止 audio_codes.append(token) # 解码音频(T, 8) - (1, 8, T) audio_codes mx.stack(audio_codes, axis0)[None, :].transpose(0, 2, 1) waveform processor.decode_audio(audio_codes) # 保存音频24kHz from mlx_audio.audio_io import write as audio_write audio_write(output.wav, waveform[0].tolist(), model.sample_rate)要点说明**dict(chat)展开为text_tokens、audio_features、modalities三个关键字参数ChatState.__iter__的实现见 processor.py每个音频 token 是一个形状为(8,)的完整帧包含全部 8 个 codebook 的取值mx.eval(token)是 MLX 惰性求值的关键生成器只是构图必须显式求值才能取到token[0].item()判断 EOS输出采样率使用model.sample_rate即config.sample_rate固定 24000。场景二Speech-to-TextASRASR 使用generate_interleaved并只收集 TEXT 模态的输出。音频输入通过chat.add_audio()加入对话import mlx.core as mx import numpy as np from mlx_audio.audio_io import read as audio_read from mlx_audio.sts.models.lfm_audio import ( LFM2AudioModel, LFM2AudioProcessor, ChatState, LFMModality, ) model LFM2AudioModel.from_pretrained(mlx-community/LFM2.5-Audio-1.5B-4bit) processor LFM2AudioProcessor.from_pretrained(mlx-community/LFM2.5-Audio-1.5B-4bit) # 读取音频输入采样率建议 24kHz内部会自动重采样 audio, sr audio_read(input.wav) audio mx.array(audio.astype(np.float32)) chat ChatState(processor) chat.new_turn(user) chat.add_audio(audio, sample_ratesr) # 音频作为用户输入 chat.add_text(Transcribe the audio.) chat.end_turn() chat.new_turn(assistant) # 生成文本响应 for token, modality in model.generate_interleaved(**dict(chat), max_new_tokens512): mx.eval(token) if modality LFMModality.TEXT: print(processor.decode_text(token[None]), end, flushTrue)输入音频的处理链路值得注意见 processor.pyadd_audio先将波形转成 mel 频谱特征AudioPreprocessor默认按 16kHz 配置提取 128 维 mel 特征窗口 25ms、步长 10ms参数见 config.py随后按 FastConformer 下采样公式3 个 stride-2 卷积kernel3, padding1共 8 倍下采样计算编码后的帧数把对应位置标记为AUDIO_IN模态见 processor.pygenerate_interleaved在 prefill 阶段根据模态标记把文本嵌入与音频嵌入按位置交错拼装_build_interleaved_embeddings见 model.py。场景三Speech-to-SpeechSTSSTS 把音频输入与交错输出能力组合起来模型既输出文本可在终端流式打印也输出语音帧import mlx.core as mx import numpy as np from mlx_audio.audio_io import read as audio_read, write as audio_write from mlx_audio.sts.models.lfm_audio import ( LFM2AudioModel, LFM2AudioProcessor, ChatState, LFMModality, ) model LFM2AudioModel.from_pretrained(mlx-community/LFM2.5-Audio-1.5B-4bit) processor LFM2AudioProcessor.from_pretrained(mlx-community/LFM2.5-Audio-1.5B-4bit) audio, sr audio_read(input.wav) audio mx.array(audio.astype(np.float32)) chat ChatState(processor) chat.new_turn(system) chat.add_text(Respond with interleaved text and audio.) chat.end_turn() chat.new_turn(user) chat.add_audio(audio, sample_ratesr) chat.end_turn() chat.new_turn(assistant) text_out, audio_out [], [] for token, modality in model.generate_interleaved(**dict(chat), max_new_tokens2048): mx.eval(token) if modality LFMModality.TEXT: text_out.append(token) print(processor.decode_text(token[None]), end, flushTrue) else: audio_out.append(token) if audio_out: # 丢弃最后一帧音频 EOS 帧剩余帧堆叠为 (1, 8, T) audio_codes mx.stack(audio_out[:-1], axis1)[None, :] # (1, 8, T) waveform processor.decode_with_detokenizer(audio_codes) audio_write(response.wav, waveform[0].tolist(), 24000)与 TTS 的关键差异这里使用generate_interleaved而非generate_sequential。两种模式的分工是——generate_sequential顺序模式由模型自主决定何时切换文本/音频依据|audio_start|token128进入音频、|im_end|7结束回合、|text_end|130标记文本完成特殊 token 定义见 model.pygenerate_interleaved交错模式按固定节奏交替生成文本块与音频块每组文本 6 个 token、每组音频 12 帧interleaved_n_text6、interleaved_n_audio12见 config.py文本未完成时在两种模态间轮换。Interleaved 交错生成详解generate_interleaved是 LFM2.5-Audio 最有特色的能力单轮回复中可以说一段话放一段语音再说一段话。其返回约定为TEXTtoken是标量 tokenmodality为LFMModality.TEXT用processor.decode_text(token[None])解码AUDIO_OUTtoken是形状(8,)的数组8 个 codebook 的完整一帧modality为LFMModality.AUDIO_OUT。from mlx_audio.sts.models.lfm_audio import LFMModality text_out, audio_out [], [] for token, modality in model.generate_interleaved(**dict(chat), max_new_tokens2048): mx.eval(token) if modality LFMModality.TEXT: text_out.append(token) print(processor.decode_text(token[None]), end, flushTrue) else: # LFMModality.AUDIO_OUT audio_out.append(token) # token shape: (8,) # 帧列表 [(8,), (8,), ...] - (1, 8, T) if audio_out: audio_codes mx.stack(audio_out[:-1], axis1)[None, :] # (1, 8, T) waveform processor.decode_with_detokenizer(audio_codes)底层循环逻辑model.py可以概括为文本阶段lfm.embed_tokens.as_linear(last_hidden)得到文本 logits采样后判断是否im_end结束整轮或text_end文本完成每生成n_text个 token 或文本完成即切到音频阶段音频阶段_sample_audio_frame逐 codebook 顺序采样一帧若第 0 个 codebook 采到音频 EOS2048则把 8 个 codebook 全部置为 EOS 并 yield一个值得注意的实现细节遇到音频 EOS 后代码会先把 EOS 帧重新嵌入回 LFM 状态再恢复文本生成否则下一个文本 token 会从过期的 pre-EOS 状态采样对应注释见 model.py并有专门的单元测试 test_lfm_audio.py 验证该行为。音频解码的两条路径LFM2AudioProcessor提供两种把音频 code 还原为波形的解码方式decode_audio方法见 processor.py1. 神经 DetokenizerTTS 推荐# Decode using detokenizer audio processor.decode_with_detokenizer(codes[None]) # (1, T_audio)LFM2AudioDetokenizerdetokenizer.py是 ISTFT 基神经声码器流水线为FusedEmbedding把 8 个 codebook 的 token 映射到带 codebook 偏移的共享嵌入表并按 codebook 取平均6 倍最近邻上采样LFM 主干8 层混合结构layer_types (conv, conv, sliding_attention, conv, ...)其中卷积层使用B-gate 输入门控 depthwise conv C-gate 输出门控结构见 detokenizer.py线性投影输出 1282 维641 维 log-magnitude 641 维 phaseISTFT 重建n_fft1280、hop_length320使用 hann 窗与normalizedTrueCOLA 归一化并按samepadding 模式裁剪。2. Mimi Codec# Decode using Mimi codec audio processor.decode_audio(codes) # (1, 1, T_audio)Mimi 是 Kyutai 的神经音频编解码器。仓库中的实现mlx_audio/codec/models/mimi说明了一个兼容性细节官方 checkpoint 的 Mimi 含 32 个 codebook而 LFM2.5-Audio 只用前 8 个见 processor.py。因此processor.mimi属性用mimi_202407(num_codebooks32)加载完整权重推理时只取前 8 路 code。两条路径的取舍Detokenizer 通常用于 TTS 等需要更自然音质的场景README 标注为 Recommended for TTSMimi 则作为备选解码路径如需要与 Mimi 编码链路对齐时。生成配置参数详解GenerationConfig集中了采样参数dataclass 定义见 model.pyfrom mlx_audio.sts.models.lfm_audio import GenerationConfig config GenerationConfig( max_new_tokens2048, # 最大生成 token 数 temperature0.9, # 文本采样温度 top_k50, # 文本 top-k 采样 top_p1.0, # 文本核采样nucleus sampling audio_temperature0.7, # 音频采样温度 audio_top_k30, # 音频 top-k 采样 )各参数的实际作用与源码对应关系参数作用于源码默认值说明max_new_tokens整体512生成 token 上限两个生成方法共用的终止条件temperature文本1.0文本 logits 除以温度后采样为 0 时退化为 argmax 贪心_sample_text_token见 model.pytop_k文本50仅保留 logits 前 k 大其余置-inftop_p文本1.0核采样阈值。注意GenerationConfig定义了该字段但当前两个生成方法的实现中实际只使用temperature与top_k进行采样audio_temperature音频1.0音频帧采样温度temperature 0或top_k 1时走贪心路径_sample_audio_frame见 model.pyaudio_top_k音频4音频逐 codebook 采样的 top-k音频帧采样还有一个重要的源码细节8 个 codebook不是独立并行采样而是按顺序条件逐路生成——第 i 个 codebook 的输入 Depthformer 输入 第 i-1 个 codebook 采样 token 的原始嵌入不加 normembed_raw与参考实现逐迭代条件化一致见 model.py。流式生成逐 chunk 实时播放由于两个生成方法都是 Python 生成器天然支持生成一帧、解码一帧的流式管线。README 给出的模式是攒够 10 帧再解码一个 chunk兼顾实时性与解码开销from mlx_audio.sts.models.lfm_audio import LFMModality FRAMES_PER_CHUNK 10 # 每攒够 10 帧解码一次 audio_buffer [] for token, modality in model.generate_interleaved(**dict(chat), max_new_tokens2048): mx.eval(token) if modality LFMModality.AUDIO_OUT: audio_buffer.append(token) # 攒够一帧块就解码 if len(audio_buffer) FRAMES_PER_CHUNK: codes mx.stack(audio_buffer, axis1)[None, :] # (1, 8, T) chunk processor.decode_with_detokenizer(codes) # 交给你的音频库播放 chunk ... audio_buffer [] elif modality LFMModality.TEXT: # 文本逐 token 流式输出 print(processor.decode_text(token[None]), end, flushTrue)实际工程中可把chunk交给sounddevice、soundfile等任意支持 float 波形的库写入输出流。FRAMES_PER_CHUNK是实时性越小越流畅与解码吞吐越大越省之间的调节旋钮。模型架构深度解析README 给出了四段式架构结合源码可以进一步确认每部分的内部结构1. Audio EncoderFastConformer输入 mel 特征128 维先经ConvSubsampling3 个 stride-2 卷积 depthwise 分离卷积8 倍下采样见 conformer.py再进入 17 层 Conformer 块。每个块遵循标准 Conformer 布局半残差前馈 → 相对位置多头注意力 → 卷积模块 → 半残差前馈 → 最终归一化见 conformer.py。注意力使用相对位置编码2T-1 个位置_rel_shift完成相对位置对齐默认xscaleFalse。2. LFM Backbone1.5B Liquid Foundation Model复用仓库的Lfm2Modellm/models/lfm2.py。它是一个混合架构注意力层使用 GQA RoPE卷积层使用门控深度卷积SwiGLU 式门控并支持 KV cachetransformer.py 中的TransformerBlock/ConvTransformerBlock。LFM2AudioModel.make_cache()会为每一层创建KVCache注意力层或ArraysCache(size1)卷积层见 model.py。3. Audio DecoderDepthformerAudioHead中的 Depthformer 负责把 LFM 的隐状态转成 8 个 codebook 的预测先由depth_linear投影到(B, L, 8×1024)拆成 8 路后各自过 6 层 Transformer 块32 个 Q 头、8 个 KV 头GQARoPE theta10000使用 bounded attention 的 Q/K LayerNorm最终由 8 个AudioEmbeddingWithNorm分别投影出各自词汇表的 logits。配置见DepthformerConfigconfig.py。4. DetokenizerISTFT 神经声码器即上文音频解码路径中的 Detokenizer把 8 路 code 还原为 24kHz 波形。模型整体装配关系如下model.pyConformerEncoder(17层) --MLP Adapter(512-2048-2048)-- Lfm2Model(1.5B) --depth_linear-- AudioHead(Depthformer, 8路) --depth_embeddings-- 8×codebook logits音频嵌入方面AudioEmbedding使用共享嵌入表 codebook 偏移策略8 个 codebook 的 token 加上各自的偏移[0, 2049, 4098, ...]后查同一张表并求和见 model.py与参考实现audio_embedding(next_token codebook_offsets).sum(0)对齐。量化加载与权重映射from_pretrained支持直接传 Hub 仓库名或本地路径model.py从 Hub 下载时allow_patterns[*.json, *.safetensors, *.bin]读取config.json后创建模型若存在quantization配置则调用nn.quantizegroup_size、bits、affine/其它模式均来自 checkpointsanitize()完成 PyTorch → MLX 的权重名映射与维度调整包括Conformer 各 norm 重命名、Depthformer 合并的 QKV 权重拆分为独立的 q/k/v 投影Q 1024 维 K 256 维 V 256 维、卷积权重转置NCHW → NHWC、非 conv/norm 的 float32 权重转为 float16见 model.py。常见问题与排查建议音频采样率不匹配音频输入建议按 24kHz 准备但add_audio内部会用多相滤波重采样到目标采样率_resample见 processor.py传入实际采样率即可输出固定为 24kHz。解码结果长度异常audio_out[:-1]丢弃最后一帧是有意为之——最后一行通常是全 8 路 EOS 的结束帧不应参与解码。top_p参数似乎无效GenerationConfig保留了top_p字段但当前版本generate_interleaved/generate_sequential的采样实现只使用temperature与top_k。transformers未安装文本分词是惰性加载的未安装时会抛出带安装提示的ImportError。量化权重加载失败确认 checkpoint 的config.json含quantization字段且*.safetensors与模型结构匹配from_pretrained权重加载为strictTrue。延伸阅读官方文档入口docs/models/sts/index.mdSTS 模型总览含 4bit/8bit 权重清单模型配置与参数mlx_audio/sts/models/lfm_audio/config.py处理器与对话状态mlx_audio/sts/models/lfm_audio/processor.py生成循环与采样实现mlx_audio/sts/models/lfm_audio/model.py神经声码器实现mlx_audio/sts/models/lfm_audio/detokenizer.py单元测试mlx_audio/sts/tests/test_lfm_audio.py覆盖配置默认值、模态枚举、特殊 token、交错生成状态更新等音频读写工具mlx_audio/audio_io.pyLFM 主干模型mlx_audio/lm/models/lfm2.pyLFM2.5-Audio 的 MLX 实现把 TTS、ASR、STS 三种能力收敛到一套对话式 API 中配合交错生成与逐 token 流式输出适合在 Apple Silicon 上搭建实时语音助手、有声内容合成与多模态语音交互应用。理解generate_sequential与generate_interleaved的分工、8-codebook 帧的结构约定以及 Detokenizer / Mimi 两条解码路径是把它用好的关键。【免费下载链接】mlx-audioA text-to-speech (TTS), speech-to-text (STT) and speech-to-speech (STS) library built on Apples MLX framework, providing efficient speech analysis on Apple Silicon.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-audio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考