导出库:C ABI、回调式流式与多语言示例
除了 CLI 和 HTTP 服务,s2.cpp 还可以作为库被其他语言/程序嵌入。s2_shared / s2_static 两个目标(见第 03 章)通过 s2_export_api.h 暴露一套 extern “C” 接口,759 行实现在 s2_export_api.cpp。本章说明这套 ABI 的设计约定、两类合成入口,以及 Python/C#/Go 示例。
1. 为什么需要专门的 C ABI
C++ 的 name mangling、类布局、异常机制跨编译器/跨语言都不稳定。直接导出 C++ 类意味着调用方必须用相同编译器、相同 STL,且一旦抛异常就可能直接崩溃。导出层的应对原则是:
- 不透明指针(opaque pointer):调用方只拿到
Pipeline*、GenerateParams*等裸指针,所有对象由库内new、由对应 Releasedelete,调用方不接触布局; - 不跨边界抛异常:C ABI 函数全部 noexcept 边界,内部捕获后用整数返回码表达错误;
- 参数用 POD/基础类型:字符串用
const char*,配置用 C 风格 struct(S2StreamingParams)。
跨语言的 S2_Export 宏处理共享库符号可见性(Windows dllexport/import)。
2. Alloc / Release / Initialize 三件套
每个对象都遵循”分配 → 初始化 → 使用 → 释放”的固定生命周期,全部声明在头文件 :43-95:
| 对象 | Alloc | Initialize | Release |
|---|---|---|---|
| Pipeline | AllocS2Pipeline(:366) | InitializeS2PipelineFromFiles(:395) | ReleaseS2Pipeline |
| GenerateParams | AllocS2GenerateParams | InitializeS2GenerateParams(-1 表示保持默认) | ReleaseS2GenerateParams |
| Model | AllocS2Model(:440) | InitializeS2ModelWithGpuLayers(:453) | ReleaseS2Model |
| Tokenizer | AllocS2Tokenizer | InitializeS2Tokenizer | ReleaseS2Tokenizer |
| AudioCodec | AllocS2AudioCodec | InitializeS2AudioCodecModelShared(:496) | ReleaseS2AudioCodec |
| Prompt codes | AllocS2AudioPromptCodes | InitializeAudioPromptCodes(:524,一步完成编码) | ReleaseS2AudioPromptCodes |
| Audio buffer | AllocS2AudioBuffer | — | ReleaseS2AudioBuffer |
一个工程细节:InitializeS2GenerateParams 等函数对每个字段用 -1 哨兵区分”未提供”与”显式设 0”(:58-65),只覆盖调用方真正指定的项,其余保留 C++ 侧默认值。整数后端码(-1/0/1/2)由 to_backend_type() 翻译回枚举,非法值一律落到 CPU。
3. S2Synthesize:一次性合成
S2Synthesize() 是一次性合成的统一入口,签名覆盖了全部输入方式。它接受三种参考来源(任选):
ReferenceAudioPath:现场编码参考音频文件(:597-602);ReferenceAudioPromptCodes+ReferenceAudioTPrompt:调用方预先编好的码;- 两者都无:无参考默认音色。
输出也有两种去向:用户提供的 AudioBuffer(float 采样),和/或 OutputAudioPath(直接存成音频文件,:621-626)。
3.1 返回码约定
| 码 | 含义 |
|---|---|
| 1 | 成功 |
| 0 | 参数/管线无效(未初始化等) |
| -1 | 参考音频编码失败 |
| -4 | 合成失败 |
| -6 | 音频文件保存失败 |
| -7 | 有参考音频/码但缺少参考转写(:590-592) |
| -8 | 提供了预置码但没有 T_prompt 指针(:593-595) |
-7 和 -8 体现了克隆的硬约束:码必须与转写配对、长度必须可知,否则 prompt 无法构造。这些码让调用方能区分”输入问题”和”引擎内部失败”。
4. 回调式流式
流式不能用返回值(音频是持续产生的),于是导出层用回调集合 S2StreamingCallbacks:
on_wav_chunk(data, size, user_data) 每块音频(先 44 字节头,后 PCM16 块)
on_done(user_data) 正常结束
on_error(message, user_data) 错误
is_cancelled(user_data) 调用方请求取消(返回非 0)
入口有两个:S2SynthesizeStreaming()(只传 stride)与 S2SynthesizeStreamingEx()(传完整的 S2StreamingParams:stride/holdback/context/low_latency/分句/voice)。前者只是包装后者。
4.1 CallbackStreamingSink
CallbackStreamingSink 是 Pipeline 与 C 回调之间的适配器:
on_header:直接把 44 字节 WAV 头作为第一个 chunk 推出(:289-291);on_pcm_data:float → audio_to_pcm16,以 PCM16 字节推出(:283-287);is_cancelled:每帧查询调用方回调,返回非 0 即令 Pipeline 中止;- chunk 尺寸钳在 int32 以内(:336-343),回调返回 0 被记为
aborted_by_callback。
这与 HTTP 的 HttpStreamSink 行为对齐,所以同一条流式管线能无差别地接三种出口。
4.2 voice-only 解析与分句
StreamingEx 比一次性函数多两段逻辑:
- 仅凭 voice id 解析档案:当既无参考文件、也无预置码、但指定了 voice 时,调
resolve_prompt_reference从.s2voice载入(:707-722),失败经 on_error 报错; - 分句合成:
segment_sentences开启时切句、逐段经 sink 拼接(:733-745)。
流式特有返回码:-9 缺少必需的 on_wav_chunk 回调(:665-667);-10 合成因取消而中止(:751-754),区别于 -4 的真失败。
5. 参数应用辅助
两个内部辅助函数把 C 结构翻译成 C++ 参数:
- apply_streaming_params():只覆盖显式提供的字段(stride>0、holdback≥0),并处理 low_latency 预设;
- apply_voice_selection():处理 voice id 与 voice_dir,包括
.s2voice路径形式; - sync_tokenizer_config():以模型实际配置反哺分词器,与 Pipeline 初始化时的同步一致。
6. 多语言示例
仓库为三种语言提供了可直接参考的调用样例,全部基于这套 C ABI:
| 语言 | 示例文件 | 说明 |
|---|---|---|
| Python | ctypes_export_api.py | 用标准库 ctypes 加载 libs2,无需任何第三方包 |
| C# | Program.cs、.csproj | P/Invoke 声明结构体与回调 |
| Go | main.go、go.mod | cgo 调用,含回调注册 |
Python 路径尤其能体现”零 Python 运行时依赖”的另一层含义:不是”用 Python 跑模型”,而是”Python 只当作调用方”通过 ctypes 驱动 C++ 引擎——计算仍全部发生在原生库中。
7. 全系列总结
至此,s2.cpp 的完整面貌已经展开:
- 架构上,它以 11 行 token 矩阵统一文本与多码本音频,用带 KV cache 的 Slow-AR(36 层 4.13B)和无 cache 的 Fast-AR(4 层 0.42B)组成双 AR,再接卷积 codec(10×4096 RVQ,44.1 kHz,帧=2048 样本);
- 工程上,它把权重放置(CUDA 留嵌入、Vulkan/Metal 可全卸载、按驱动上限切多 buffer)、图缓存(step 复用、prefill 分块、codec 按帧数缓存 fused 图)、流式窗口(stride/holdback/context 三参数)等问题都给出了显式而鲁棒的处理;
- 交付上,CLI、单端点 HTTP 服务、C ABI 三种形态覆盖了本地使用、网络服务和语言嵌入三类场景。
它仍是一个标注 alpha、不承诺稳定的社区项目,且权重商业使用需另行授权——但作为”如何用纯 C++/ggml 完整落地一个现代 Dual-AR TTS 模型”的参考,其代码组织和工程取舍都值得细读。
关键文件
| 位置 | 职责 |
|---|---|
| include/s2_export_api.h | C ABI 全部声明 |
| s2_export_api.cpp:278 | CallbackStreamingSink |
| s2_export_api.cpp:395 | 从文件初始化 Pipeline |
| s2_export_api.cpp:561 | S2Synthesize |
| s2_export_api.cpp:653 | S2SynthesizeStreamingEx |
| examples/python/ctypes_export_api.py | Python ctypes 示例 |