简介:RHVoice是一套免费开源的文本转语音(TTS)合成引擎,面向需要离线语音合成能力的开发者、语音技术研究者和嵌入式/桌面应用集成人员。它基于HTS/SPTK等统计参数合成技术,采用统计模型并仅将模型存于本地,支持俄语、美式英语、巴西葡萄牙语、世界语、乌克兰语、格鲁吉亚语、吉尔吉斯语、鞑靼语等语种,同时覆盖Windows、GNU/Linux与Android平台,可对接SAPI5、Android Text-to-Speech等标准接口。资源包共1673个文件,压缩后约179.54MB;文件以C/C++源码(cpp/hpp/h)、构建脚本、Android工程文件以及PDF/TXT技术文档为主,还包含语音模型、词典和FST状态转移等数据文件,展示了从源码编译到模型构建的完整技术栈。已有736人学习下载,查阅者可获得核心引擎源码、跨平台构建配置以及多语言语音模型相关工具链;这种轻量模型设计也使其适合在嵌入式或移动端部署,对二次开发和语音合成研究都有实际参考价值。
1. 免费开源语音合成器:多语言 TTS 落地前先看懂 RHVoice
如果你在找一款能离线跑、支持多语言、且不绑定商业 API 的语音合成引擎,RHVoice 是一个绕不开的名字。它基于 HTS(HMM-based Speech Synthesis System)架构,用统计参数合成路线生成语音,核心优势在于发音自然度和多语言扩展性——除了主打的俄语,它还覆盖英语、世界语、乌克兰语、格鲁吉亚语、吉尔吉斯语、鞑靼语和巴西葡萄牙语,而且 Android、Windows、Linux 三个平台都有正式的构建方案。我实际把它集成进一个语音朗读工具时,最大的感受是:它的开源生态比想象中完整,但坑也藏在编译链和音色调参里。这篇笔记就按「架构 → 编译 → 调参 → 排错 → 二次开发」的顺序,把能复现的步骤和踩过的坑一并写出来。
2. 基于 HTS 的统计参数合成:理解音色从哪里来
2.1 RHVoice 的合成流水线与 HTS 的关系
RHVoice 不是简单的波形拼接,它走的是统计参数合成路线。整个流程分为离线训练和在线合成两个阶段。离线阶段,用一批录音语料提取声学特征(基频 F0、频谱包络、时长等),训练出 HMM 模型;在线合成时,输入文本先做文本归一化和音素转换,再交给 HMM 模型预测声学参数,最后通过声码器将参数还原成波形。这套路线的优势是模型体积小、音色稳定,弱点是音质上限受声码器质量和训练数据影响。
我拆过它的源码结构,核心模块大致分为:
- 文本前端(text-processing):负责把输入文本转成音素序列和韵律标注,每种语言一套规则;
- 声学模型(voice library):存放 HTS 训练好的模型文件,RHVoice 的 voice 文件本质是编译后的 HMM 模型包;
- 合成引擎(synthesis core):执行参数预测和波形生成,内部调用声码器;
- 音频输出(audio backends):支持 PulseAudio、ALSA、WinMM、Android AudioTrack 等后端。
2.2 音库文件结构与 voice 格式
RHVoice 的 voice 文件不是一个裸数据文件。标准训练流程会生成多个数据文件——持续时间模型、频谱模型、基频模型——然后用一条命令把它们打包成一个 .htsvoice 文件。RHVoice 在运行时读取的就是这种打包后的 voice。以俄语为例,默认的 voice 文件名类似 RHVoice_voice_ru_RU_v3.0.htsvoice,里面包含了韵律参数和声学模型参数。
如果你打算自己训练音库,需要准备好三样东西:分段好的录音 WAV(16kHz 单声道)、对应的文本标注(与录音逐句对齐)、以及用于特征提取的配置文件。训练工具链用的是 HTS 的 demo 脚本改版,而不是 RHVoice 仓库自带的代码。这一步门槛较高,大多数情况下直接下载现成 voice 文件是更现实的选择。
2.3 多语言支持的设计思路:每个语言一套前端
RHVoice 多语言并存的底气在于它把「语言相关」和「语言无关」的部分剥离开了。语言相关的是文本前端——怎么分词、怎么注音、怎么处理重音;语言无关的是声学模型和合成引擎。这意味着新增语言的成本集中在前端规则和录音语料上。实际使用中,切换语言只需加载对应的 voice 文件即可,引擎本身不需要重编。
有个细节比较关键:RHVoice 对重音符号的处理是有形式的,俄语和乌克兰语这类重音语言尤其依赖文本里的重音标注。如果输入文本不带重音符号,合成结果的重音位置可能不准确,听感上会显得生硬。
3. 编译与部署:Linux 和 Windows 下的可复现步骤
3.1 Linux 下从源码编译
RHVoice 在 Linux 下的编译依赖很少,核心库只需要 CMake 和 C++ 编译器。我以 Ubuntu 系的系统为例,完整命令如下:
# 安装编译依赖 sudo apt-get install cmake g++ libpulse-dev # 克隆源码 git clone https://github.com/RHVoice/RHVoice.git cd RHVoice # 创建构建目录 mkdir build && cd build # 配置构建选项 cmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_PULSEAUDIO=ON # 编译并安装 make -j$(nproc) sudo make install # 配置动态库路径 sudo ldconfig逻辑说明:libpulse-dev是 PulseAudio 的开发库,RHVoice 在 Linux 下默认通过它输出声音;WITH_PULSEAUDIO开关控制是否编译 PulseAudio 后端。如果目标环境没有音频输出需求(比如只在服务器上做离线合成写 WAV),可以把这个开关关掉。命令行合成工具和共享库会随make install一并装好。
装完之后还需要做一步:安装 voice 文件。跑sudo make install之后,默认的 voice 数据文件会被复制到/usr/local/share/RHVoice/voices/目录下。可以用find检查一下:
find /usr/local/share/RHVoice -name "*.htsvoice"文件存在不代表能正常加载,还要看运行用户有没有读权限。
3.2 Windows 下用 MSYS2 编译
Windows 上我推荐 MSYS2 环境,因为依赖管理比直接拉源码省心得多。用 MSYS2 UCRT64 终端执行:
pacman -S mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-cmake git clone https://github.com/RHVoice/RHVoice.git cd RHVoice mkdir build && cd build cmake .. -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release mingw32-make -j$(nproc) mingw32-make install逻辑说明:这里用的是 MSYS2 自带的 MinGW-w64 工具链,生成的是原生 Windows 程序,不依赖 MSYS2 运行时。-G "MinGW Makefiles"指定生成器是 MinGW Makefiles 而不是 Visual Studio 工程,这样mingw32-make才能直接干活。
编译完成后,可执行文件会生成在build/src/下。那个rhvoice命令行工具可以直接跑,它会加载系统路径下的 voice 文件。Windows 下如果想要一个带界面的程序,仓库里还有一个独立的 GUI 前端,但那是单独的项目,不在主要源码树里。
3.3 命令行工具基本用法
安装完成后,RHVoice 提供了一个名为rhvoice的命令行工具,基本调用方式:
# 合成一段文本,输出到文件 echo "Привет, мир!" | rhvoice -o /tmp/output.wav -p ru_RU # 查看可用音库 rhvoice -l参数说明:-o指定输出 WAV 路径,-p指定语言和音色代码(比如ru_RU表示俄语),-l列出所有可用的 voice。这个工具的底层会调用 RhVoice 核心库,把文本合成为 PCM,再封装为 WAV 文件。
这里有一个容易被忽略的点:rhvoice -o输出的采样率默认是 22050Hz,但某些 voice 文件是用 16kHz 录音训练的。如果后端播放设备只支持 48000Hz,最好在应用中主动处理采样率转换。
4. 参数配置与音色调优:从默认值开始微调
4.1 voice 配置文件的核心参数
RHVoice 的 voice 文件内部有一个配置段,包含了对合成效果影响最直接的几类参数。RHVoice.ini或 voice 文件内嵌配置主要涉及以下参数:
| 参数 | 作用域 | 默认值 | 说明 |
|---|---|---|---|
voice.prosody.speed | 全局 | 1.0 | 语速倍率,0.5 表示慢一倍,大于 1 表示加快 |
voice.prosody.pitch | 全局 | 1.0 | 基频缩放,改的是整体音高 |
voice.prosody.pitch_range | 全局 | 1.0 | 基频动态范围,影响语调起伏 |
voice.domain | 全局 | text | 输入类型,text表示自由文本,ssml表示 SSML 标记 |
voice.language | 全局 | 自动 | 指定默认语言,用于多语言混排 |
配置方法有两种:一种是在运行时通过 API 设置,另一种是为每个 voice 写一个.conf文件放在 voices 目录下。实际操作中我更推荐后者,因为配置文件是明文的,改起来不用重编译。
4.2 命令行下调节语速和音高
在命令行工具中,语速和音高的调节入口不是直接的参数,而是通过RHVoice_voice_params结构体传入。但在命令行工具里,你可以用另一个方式实现——直接修改 voice 目录下的配置文件。假设我想让默认俄语音色语速放慢 20%,把音量调高一点,可以这样做:
# 找到 voices 目录 find /usr/local/share/RHVoice -name "*.conf" # 编辑对应的配置文件 cat /usr/local/share/RHVoice/voices/ru_RU.conf # 内容示例 voice.prosody.speed 0.8 voice.prosody.pitch 1.0 voice.prosody.pitch_range 1.0 volume 1.2改完配置后重启调用进程即可生效。注意这些参数是倍率关系,不是绝对值。voice.prosody.speed 0.8意味着语速是原速的 80%,也就是放慢了。如果想要更快的语速,设成 1.2 或 1.3,但超过 1.5 时音质会明显下降,因为参数合成路线对超线性倍率的鲁棒性不够强。
4.3 语音质量评估:什么时候该换音库
调参解决不了所有问题。当你把语速拉到 1.5 倍以上、音高改了两倍之后出现明显「机器味」,那不是参数没调对,而是声学模型的底子已经跟不上了。判断一个 voice 文件是否适合你的场景,我一般按这个顺序试:
- 先听默认参数的输出,确认发音是否准、有没有吞音;
- 再试语速 0.8 和 1.3 两个极端,看是否有明显破音;
- 最后试长句(比如含标点嵌套的复杂句式),看停顿是否自然;
- 如果长句明显发飘,优先换 voice 版本,而不是继续调参数。
RHVoice 的音库版本差异在实际听感中很大,尤其是俄语 voice 从 v3.0 到 v4.0 的提升是跨越式的。有条件就选新版音库。
5. 避坑指南:编译、运行时与集成中的常见问题
5.1 Linux 下运行时提示无法打开共享库
现象:rhvoice命令执行时报错error while loading shared libraries: librhvoice.so,文件明明已安装。
原因:RHVoice 的共享库默认安装到/usr/local/lib,某些 Linux 发行版的默认动态链接器搜索路径不包含这个目录。
解决:执行sudo ldconfig或者临时设置export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH。长期使用建议把/usr/local/lib写入/etc/ld.so.conf.d/下的一个.conf文件再跑一次ldconfig。
5.2 Windows 下输出 WAV 文件为空文件
现象:用命令行工具合成时进程正常结束,但生成的 WAV 大小为零或只有 44 字节的文件头。
原因:多数情况下是 voice 文件没有加载成功。Windows 下make install后的数据文件路径可能与程序运行时搜索的路径不一致,程序直接拿不到模型数据就静默返回。
解决:先跑rhvoice -l看列表。如果列表为空,把 voices 目录复制到程序所在目录下,或者用RHVOICE_VOICES_PATH环境变量指定路径。这算是我在 Windows 上踩过最深的一个坑——Linux 下不会出这个问题,因为安装路径是标准的。
5.3 合成时出现大量破音或「沙沙声」
现象:输出音频能听清内容,但背景有明显的嘶嘶声,某些元音上尤其严重。
原因:可能的因素有两个:一是输入音频或训练录音的采样率与声码器输出采样率不一致;二是声码器在合成高频段时数值不够稳定。后者在参数合成中是常见现象,但通常不会达到刺耳的程度。
解决:如果你只是在调用,尝试改输出采样率。命令行没有直接的采样率参数,需要通过 API 里的RhVoice_set_sample_rate设置。把采样率从 22050 降到 16000 往往能削弱高频噪声。如果是嵌入式场景,这一步值得写进集成代码里。
5.4 Android 集成时 voice 文件加载失败
现象:在RhVoiceEngine初始化时返回错误码,日志里没有明确提示。
原因:Android 上的文件路径和资源释放是两类常见问题。voice 文件如果放进了 assets,需要先解压到缓存目录再加载;另外,Android 的 JNI 层可能没有及时释放引用,连续初始化多个引擎实例会导致崩溃或加载失败。
解决:每次引擎使用完毕后,强制调用释放接口,而不是依赖 GC。voice 文件在 assets 里的话,首次运行复制到应用私有目录:
File voiceFile = new File(getFilesDir(), "voice.htsvoice"); if (!voiceFile.exists()) { copyAssetToFile("voice.htsvoice", voiceFile); }这么做不是最优解,但胜在稳定,适合大部分应用场景。
5.5 多语言混排时文本被识别成错误语言
现象:一段混有俄语和英语的文本,俄语单词被当作英语发音,或者反过来。
原因:RHVoice 的语言识别是基于 Unicode 字符范围判断的,对短文本尤其不可靠。默认情况下它会根据第一个可识别字符推断语言,中段出现的另一种语言不会触发切换,除非用显式标记。
解决:在应用层做语言分段,把不同语言的文本拆开分别调用对应 voice,而不是指望引擎自动切换。如果必须处理混合文本,建议关掉自动语言检测,用显式RHVoice_language参数控制。
6. 二次开发:把 RHVoice 嵌入到自己的程序
6.1 C API 核心调用流程
RHVoice 的 C API 非常直白。基本的调用流程是:创建引擎 → 设置参数 → 调用合成接口 → 释放资源。下面是一段完整的 C++ 示例代码:
#include <iostream> #include <string> #include "rhvoice.h" int main() { // 初始化引擎 RHVoice_engine engine = RHVoice_engine_new("/usr/local/share/RHVoice/voices"); if (!engine) { std::cerr << "Failed to create engine" << std::endl; return 1; } // 设置输出采样率 RHVoice_engine_set_sample_rate(engine, 22050); // 将文本转为 wide 字符串 std::string text = "Здравствуйте, это тестовое сообщение."; // 调用合成接口 RHVoice_tts_callbacks callbacks{}; callbacks.audio_buffer = [](const short* samples, size_t num_samples, void* user_data) { // 这里把 samples 写入文件或音频设备 fwrite(samples, sizeof(short), num_samples, (FILE*)user_data); }; FILE* out = fopen("output.raw", "wb"); RHVoice_engine_speak(engine, text.c_str(), text.size(), &callbacks, out); fclose(out); // 释放引擎 RHVoice_engine_delete(engine); return 0; }逻辑说明:RHVoice_engine_new需要传入 voices 目录的路径,在这个目录下放置 voice 文件和对应的.conf配置文件;RHVoice_engine_set_sample_rate可以设置输出采样率,这个必须在合成前设置;RHVoice_engine_speak是核心合成接口,文本会经过内部的前端处理和声学模型预测,最终通过回调函数输出 PCM 数据。
参数说明:回调函数的samples是 16-bit PCM 单声道数据,num_samples是短整型的数量。这里需要注意,回调是在合成线程内执行的,不能在这里做耗时操作,否则会阻塞合成流程。正确的做法是把数据拷贝出来放入缓冲区,由独立的线程去写文件或发给音频后端。
6.2 嵌入式场景的内存与资源优化
如果你在树莓派或类似低配硬件上跑,RHVoice 的内存占用是需要关注的。默认情况下,一个 voice 文件加载到内存后大约占 50~80MB,如果同时加载多个 voice(例如多语言混排需求),内存压力会翻倍。一个优化习惯是:只加载当前需要的 voice,切换语言时先释放再加载,而不是一次加载全部。
还有一个细节是RHVoice_engine_set_speed这类参数设置要在每次合成前重置。如果你之前设置了语速 1.5,之后切回默认语速时忘了改回来,合成出来的语速会保持 1.5,这不是引擎的问题,是调用方的状态没有管理好。从那以后我每次集成这类引擎时,都会强制走一遍「先重置参数 → 再设置本次参数 → 合成 → 释放」的流程,尤其是项目里多处代码都在调合成接口时,这个习惯帮我少查了很多玄学问题。希望这篇笔记能让你少踩几个我已经踩过的坑,直接把精力放到业务功能上。
本文还有配套的精品资源,点击获取