GitHub - pwilkin/openmoss: 基于GGML的OpenMOSS纯C++管道

Reddit r/LocalLLaMA 工具

摘要

OpenMOSS 是 MOSS-TTS 的独立 C++/GGML 移植版本,提供了一个自包含的二进制文件,用于文本转语音和语音克隆,基于 Qwen3-8B 骨干网络和 32 个 RVQ 音频码本。它包含用于一次性合成的 CLI 和用于重复生成的 HTTP 服务器。

我正在上传一个完整的基于GGML的OpenMOSS管道(https://huggingface.co/OpenMOSS-Team/MOSS-TTS),这是我为自己凭感觉编写的,以防其他人觉得有用。由于整个Python生态系统的原因,TTS模型的设置众所周知很麻烦,所以我决定让它变得简单一些。这里同时支持服务器模式和单次CLI模式。为什么选择OpenMOSS?对我来说,原因是它是少数能够很好地处理典型“英语/中文”组合之外的语言的TTS模型之一——即波兰语。也许其他人也会觉得它有用。
查看原文
查看缓存全文

缓存时间: 2026/05/15 15:02

pwilkin/openmoss 来源: https://github.com/pwilkin/openmoss

openmoss-ggml

一个独立的 C++/GGML (https://www.github.com/ggml-org/ggml) 移植版,基于 MOSS-TTS-Delay (https://huggingface.co/OpenMOSS-Team/MOSS-TTS) —— 包含 Qwen3-8B 语言骨干网络 + 32 个 RVQ 音频码本 + 一个 1.6 B 参数的纯 Transformer 音频编解码器,所有功能均可从单个自包含二进制文件运行。Qwen3 骨干网络由 libllama 托管(因此您可以免费获得 llama.cpp 的所有量化以及 CUDA / CPU / Vulkan 后端);嵌入层、语言模型头以及编解码器的编码器/解码器是我们自行构建的 GGML 计算图。

提供两个入口点:

  • moss-tts-cli — 一次性合成。加载模型,合成一段语音,写入 WAV 文件后退出。
  • moss-tts-server — 模型常驻内存,并暴露 HTTP 接口,用于重复生成(TTS 和语音克隆)。

在单个 16 GB GPU(RTX 5060 Ti)上,Q8_0 骨干网络生成约 10 秒语音的实际耗时约 4 秒;编解码器运行速度约为实时的 50 倍。有关详细的功能矩阵、流水线示意图和基准测试数据,请参阅 docs/STATUS.md

快速开始

# 1. 构建(假设已有一个构建好的 llama.cpp 目录)
cmake -B build -DLLAMA_CPP_DIR=/path/to/llama.cpp -DGGML_CUDA=ON
cmake --build build -j

# 2. 转换权重(仅需要执行一次)
python scripts/convert_hf_to_gguf.py \
    --moss-tts OpenMOSS-Team/MOSS-TTS \
    --codec OpenMOSS-Team/MOSS-Audio-Tokenizer \
    --output weights/moss-tts.gguf

# 3. (可选)量化骨干网络 —— 保持嵌入表为 f16
/path/to/llama.cpp/build/bin/llama-quantize \
    --token-embedding-type f16 \
    weights/moss-tts.gguf weights/moss-tts-q8_0.gguf Q8_0

# 4. 合成语音
./build/moss-tts-cli \
    --model weights/moss-tts-q8_0.gguf \
    --text "Hello, world!" \
    --output out.wav

构建

前置条件:

  • 一个已构建的 llama.cpp 目录(包含 libllama.solibggml*.so,以及 ggml/include/include/ 下的头文件)。使用您需要的后端进行编译(例如 -DGGML_CUDA=ON 用于 NVIDIA GPU)。
  • CMake ≥ 3.18,支持 C++17 的编译器。
  • nlohmann/json.hpp(仅需要头文件;在 Debian/Ubuntu 上:apt install nlohmann-json3-dev)。
  • HTTP 服务器依赖于内嵌的 cpp-httplib v0.18.7 副本(third_party/cpp-httplib/httplib.h);无需额外的系统依赖。
cmake -B build \
    -DLLAMA_CPP_DIR=/devel/tools/llama.cpp \
    -DGGML_CUDA=ON
cmake --build build -j

输出文件:

  • build/moss-tts-cli
  • build/moss-tts-server
  • build/moss-tts-infobuild/moss-tts-compute-testbuild/moss-codec-roundtrip(诊断工具)

转换权重

转换器会生成 两个 并存的 GGUF 文件:

  • moss-tts.gguf — Qwen3-8B 骨干网络,纯 Qwen3 布局,可直接由 libllama 加载。
  • moss-tts.extras.gguf — 辅助文件:32 个音频嵌入表、33 个 LM 头、编解码器编码器 / RVQ 码本 / 编解码器解码器,以及 moss.* 的 KV 命名空间(帧率、特殊 token ID 等)。由我们加载,而非 libllama。
pip install safetensors numpy huggingface_hub gguf
python scripts/convert_hf_to_gguf.py \
    --moss-tts OpenMOSS-Team/MOSS-TTS \
    --codec OpenMOSS-Team/MOSS-Audio-Tokenizer \
    --output weights/moss-tts.gguf \
    --llama-cpp-dir /path/to/llama.cpp \
    --backbone-dtype f16

有用的选项:--cache-dir 指定 HF 缓存目录,--scratch-dir/--keep-scratch 用于检查中间提取出的骨干网络,--skip-extract 可以在已经运行过转换的情况下重用先前提取的骨干网络。

量化

量化骨干网络的方式与量化任何 GGUF 文件相同,但需要传递 --token-embedding-type f16:嵌入表通过 ggml_get_rows 索引,而该操作在 CUDA 上不支持量化的 src0,因此必须保持 f16。

llama-quantize --token-embedding-type f16 \
    weights/moss-tts.gguf weights/moss-tts-q8_0.gguf Q8_0   # ~8.7 GB,已端到端验证
llama-quantize --token-embedding-type f16 \
    weights/moss-tts.gguf weights/moss-tts-q4km.gguf Q4_K_M # ~4.7 GB

辅助文件 进行量化 —— 将 moss-tts.extras.gguf 与您选择的骨干网络文件放在同一目录下。CLI 和服务器通过将骨干网络文件名的 .gguf 后缀替换为 .extras.gguf 来查找辅助文件。

CLI 用法

moss-tts-cli --model <模型文件> --text "<文本>" [选项]

常用选项:

参数含义
--text "..."要合成的文本(必填)
--output PATH输出 WAV 文件路径(默认为 out.wav,16 位单声道 @ 24 kHz)
--reference PATH用于语音克隆的参考 WAV 文件 —— 提取说话人特征并拼接到提示中
--instruction "..."语音/风格描述(语音生成模式,无参考音频时使用)
--language CODE语言提示(en / zh / …)
--tokens N近似音频长度(以 token 计;1 秒 ≈ 12.5 个 token)
--max-new-tokens N生成上限(默认 4096)
--main-gpu N将模型固定到索引为 N 的 GPU(默认:自动选择空闲显存最多的 GPU)
--n-gpu-layers N骨干网络 GPU 卸载层数(默认:全部)
--no-flash-attn禁用 flash attention
--skip-codec不加载编解码器张量;仅输出 codes(调试用,可节省约 3.4 GB)

普通 TTS

./build/moss-tts-cli \
    --model weights/moss-tts-q8_0.gguf \
    --text "The quick brown fox jumps over the lazy dog." \
    --max-new-tokens 600 \
    --output out.wav

语音克隆

通过 --reference 传入参考 WAV 文件。编解码器编码器将其转换为 32 码本的 codes,然后拼接到用户端音频块的聊天提示中,模型会以该语音继续生成。

./build/moss-tts-cli \
    --model weights/moss-tts-q8_0.gguf \
    --text "A different sentence in the cloned voice." \
    --reference samples/alice.wav \
    --max-new-tokens 600 \
    --output cloned.wav

支持任意采样率 —— 内部会将 WAV 线性重采样为 24 kHz。通常几秒钟的清晰语音就足够了。

服务器用法

./build/moss-tts-server --model weights/moss-tts-q8_0.gguf --host 0.0.0.0 --port 8080

moss-tts-server 支持与 CLI 相同的 --main-gpu--n-gpu-layers--no-flash-attn--skip-codec 参数。生成请求通过单个互斥锁串行化(libllama 状态不可重入);并发请求会优雅排队。

接口

  • GET /health — 健康检查,返回 ok
  • GET /info — JSON 格式返回模型维度、编解码器状态和请求计数器。
  • POST /tts — 输入 JSON,输出 audio/wav(失败时输出 text/plain 并包含错误信息)。

POST /tts 请求体

字段类型必填说明
textstring要合成的文本
instructionstring语音/风格描述(语音生成模式)
languagestring语言提示
tokensint近似长度(1 秒 ≈ 12.5 tokens)
max_new_tokensint默认 4096
reference_wav_b64string用于语音克隆的 base64 编码 WAV 数据
samplingobject包含 text_temperaturetext_top_ptext_top_kaudio_temperatureaudio_top_paudio_top_kaudio_repetition_penaltyseed

响应头中会携带时间信息:X-MOSS-Audio-FramesX-MOSS-Generate-SecondsX-MOSS-Decode-Seconds。请求体大小限制为 64 MB,大约可容纳 base64 编码后的约 60 秒参考 WAV。

示例

# 普通 TTS
curl -s -X POST http://localhost:8080/tts \
    -H 'Content-Type: application/json' \
    -d '{"text":"Hello from the persistent server.","max_new_tokens":300}' \
    -o out.wav

# 语音克隆
python3 -c "import base64,json,sys; \
print(json.dumps({'text': sys.argv[1], 'max_new_tokens': 600, \
'reference_wav_b64': base64.b64encode(open(sys.argv[2],'rb').read()).decode()}))" \
    "A different sentence in the cloned voice." samples/alice.wav > req.json

curl -s -X POST http://localhost:8080/tts \
    -H 'Content-Type: application/json' --data @req.json -o cloned.wav

GPU 分配

两个二进制文件都将完整模型 —— libllama 骨干网络 以及 辅助 GGML 后端(嵌入层、LM 头、编解码器) —— 通过 LLAMA_SPLIT_MODE_NONE 固定到单个设备上。如果未指定标志,程序在加载时会自动选择空闲显存最多的 GPU:

Model::load: pinning to GPU 1 (CUDA1, 15703/15850 MiB free)

可以通过 --main-gpu N 覆盖(N 是 ggml GPU 设备列表中的索引 —— 与 libllama 自身的 main_gpu 约定相同)。当前 不支持 将骨干网络拆分到多个 GPU,因为辅助后端仍必须落在同一个设备上,否则会与该设备上的骨干网络片段竞争显存。在 Q8_0 量化下(骨干网络约 9 GB + 辅助约 5 GB ≈ 14 GB),任何 ≥16 GB 的 GPU 都足以容纳。

架构

                     ┌────────────────────────────────────────────┐
 text ────►          │ BPE tokenizer (Qwen3, 155 648 vocab)      │
 ref.wav ──►         │ Codec encoder (audio → 32×T_a codes)      │
                     │                                            │ — 语音克隆
                     └────────────────────────────────────────────┘
                                      │
                     ┌─────────▼──────────────────────────────────┐
                     │ Embedding stack:                           │
                     │ text emb + Σi audio_emb_i(code_i)          │
                     │ (33 tables, 4096 dim)                     │
                     └─────────┬──────────────────────────────────┘
                              │
                    inputs_embeds (S, 4096) — fed via batch.embd
                              │
                     ┌─────────▼──────────────────────────────────┐
                     │ Qwen3-8B backbone (libllama)              │
                     │ 36 layers, hidden=4096, GQA 32/8          │
                     └─────────┬──────────────────────────────────┘
                              │
                    hidden_state (4096)
                              │
                     ┌─────────▼──────────────────────────────────┐
                     │ 33 LM heads (1 text + 32 audio×1025)      │
                     │ (GGML matmul, GPU)                        │
                     └─────────┬──────────────────────────────────┘
                              │
                           logits
                              │
                     ┌─────────▼──────────────────────────────────┐
                     │ Delay-pattern state machine + sampling     │
                     │ (CPU, deterministic)                      │
                     └─────────┬──────────────────────────────────┘
                              │
                      codes (32 × T_a)
                              │
                     ┌─────────▼──────────────────────────────────┐
                     │ Codec decoder (4-stage pure transformer)   │
                     │ (GGML)                                     │
                     │ 12.5 Hz → 24 000 Hz (×1920 upsample)       │
                     └─────────┬──────────────────────────────────┘
                              │
                    waveform (f32, 24 kHz) → 16-bit WAV

关于每个阶段的张量形状、调试过程中遇到的错误列表以及基准测试数据,请参阅 docs/STATUS.md

仓库布局

路径角色
scripts/convert_hf_to_gguf.pyHF → 骨干网络 GGUF + 辅助 GGUF
src/model.cpp, src/aux_internal.h两文件加载器、辅助后端、嵌入/LM 头计算图
src/codec.cppRLFQ + 4 阶段 Transformer 编码器/解码器计算图
src/tokenizer.cpplibllama BPE 封装
src/delay.cppDelayState + 采样(top-k/p, 重复惩罚)
src/pipeline.cpp提示构建器 + 自回归循环 + 编解码器调度
src/wav.cppRIFF/WAVE 输入/输出(无需 libsndfile 依赖)
src/cli/moss_tts_cli.cppCLI 入口点
src/server/moss_tts_server.cppHTTP 服务器
tests/诊断:模型信息、计算图冒烟测试、编解码器往返测试
third_party/cpp-httplib/httplib.h内嵌的单头 HTTP 库

许可证

Apache-2.0,与上游 MOSS-TTS 一致。

相似文章

OpenMOSS-Team/MOSS-TTS-Nano-100M

Hugging Face Models Trending

MOSS-TTS-Nano是一个开源的多语言语音生成模型,仅0.1B参数,专为实时TTS设计,可直接在CPU上运行而无需GPU。由OpenMOSS团队和MOSI.AI发布,它支持简单的本地部署,用于Web服务和产品集成。

OpenMOSS-Team/MOSS-TTS-v1.5 · Hugging Face

Reddit r/LocalLLaMA

MOSS-TTS v1.5是一个更新的开源文本转语音模型,具有改进的多语言合成(支持31种语言)、更稳定的零样本语音克隆以及显式的内联停顿控制。

openbmb/VoxCPM2

Hugging Face Models Trending

VoxCPM2 是一个开源的、无分词器的扩散自回归文本转语音模型,支持30种语言,拥有20亿参数,48kHz音频输出,并具备从自然语言描述进行语音设计、可控语音克隆以及实时流式处理等功能。