PaddleSpeech 语音合成 Android Demo 实战:基于 Paddle Lite Java API 的端侧 TTS 部署指南
【免费下载链接】PaddleSpeechEasy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword Spotting. Won NAACL2022 Best Demo Award.项目地址: https://gitcode.com/gh_mirrors/pa/PaddleSpeech
本文是 PaddleSpeech 开源仓库中demos/TTSAndroid语音合成 Android Demo 的完整技术指南。该 Demo 在 Android 手机上实现端侧语音合成(TTS),通过 Paddle Lite 预测库运行 FastSpeech2 声学模型与 MB MelGAN 声码器双模型管线,具备易用性与开放性——你可以在 Demo 中替换自己训练的模型。读完本文,你将掌握:Android Studio 环境搭建与工程运行、工程代码结构与 Java 端各模块职责、Paddle Lite Java API 五步推理流程、预测库与模型的更新替换方法,以及通过 Setting 界面调整 CPU 线程数与功耗模式的性能调优方案。
Demo 概览:端侧双模型 TTS 管线
demos/TTSAndroid是一个完整的 Android Studio 工程,目标是在手机端离线完成"文本 → 语音"的合成任务。从源码看,其推理链路由两个 Paddle Lite 模型串联组成,对应 Predictor.java 中的两个预测器:
- 声学模型(AM):
fastspeech2_csmsc_arm.nb,将音素序列(phone_id)转换为梅尔频谱(Mel Spectrogram)。在代码中对应AMPredictor,其输出张量 shape 为[?, 80](80 维梅尔特征)。 - 声码器(VOC):
mb_melgan_csmsc_arm.nb(Multi Band MelGAN),将梅尔频谱还原为波形。代码中对应VOCPredictor,其输出 shape 为[? x 300, 1]的原始波形数据。
两个模型的名字在 MainActivity.java 中硬编码:
private final String AMmodelName = "fastspeech2_csmsc_arm.nb"; private final String VOCmodelName = "mb_melgan_csmsc_arm.nb";合成出的波形以 24000 Hz 采样率(int sampleRate = 24000;)通过 Utils.java 的rawToWave方法写入tts_output.wav(保存在手机外部存储根目录),随后用 AndroidMediaPlayer播放。整个推理运行在HandlerThread工作线程中,避免阻塞 UI 主线程。
环境准备与运行部署
环境要求
- 本地安装Android Studio(官方 IDE,用于导入工程、编译与安装 APK)。
- 准备一部Android 手机,并开启USB 调试模式:
手机设置 -> 查找开发者选项 -> 打开开发者选项和 USB 调试模式。
注意:如果 Android Studio 尚未配置 NDK,请预先按 Android Studio 用户指南中的"安装及配置 NDK 和 CMake"章节进行配置。可以选择最新的 NDK 版本,也可以使用与 Paddle Lite 预测库版本一致的 NDK(例如本 Demo 编译产物基于 arm64-v8a 架构,NDK r20b 一类版本即可满足需求)。
部署步骤
- 用 Android Studio 打开
demos/TTSAndroid工程(即本仓库demos/TTSAndroid目录,工程入口为settings.gradle)。 - 手机连接电脑,打开USB 调试和文件传输模式,在 Android Studio 中连接自己的手机设备(手机需开启"允许从 USB 安装软件"权限)。
- 点击Run按钮,Android Studio 会自动编译 APP 并安装到手机。该过程会自动下载 Paddle Lite 预测库和模型,需要联网。
运行成功后:APP 安装到手机;打开 APP 后可在下拉框(Spinner)中选择待合成的文本;选择文本后自动开始合成,合成结束会显示推理耗时与 RTF(实时率),并出现播放/暂停/停止三个按钮用于播放音频。
常见 NDK 配置问题排查
导入项目、编译或运行过程中若遇到 NDK 配置错误,可按下述顺序排查:
- 打开
File > Project Structure > SDK Location,将Android NDK location修改为本机 NDK 所在路径; - 如果 NDK 是通过 Android Studio 的SDK Tools下载的,可直接在下拉框中选择默认路径;
- 也可以在工程根目录的
local.properties文件中手动添加 NDK 路径配置,例如nkd.dir=/root/android-ndk-r20b; - 若上述方法均无法解决,可参考 Android Studio 官方文档中"更新 Android Gradle plugin"章节,尝试更新 Android Gradle 插件版本。
工程结构与重点文件
demos/TTSAndroid是一个标准的 Gradle Android 工程,核心目录结构如下:
demos/TTSAndroid/ ├── app/ │ ├── build.gradle # Gradle 构建脚本(自动下载预测库与模型) │ ├── libs/ │ │ └── PaddlePredictor.jar # Paddle Lite Java 预测库 jar 包 │ └── src/ │ ├── main/ │ │ ├── assets/models/cpu/ # 模型文件目录(运行时由 Gradle 自动下载) │ │ ├── java/com/baidu/paddle/lite/demo/tts/ │ │ │ ├── MainActivity.java # 主界面:模型加载、推理、播放 │ │ │ ├── SettingsActivity.java # 设置界面:线程数、功耗模式等 │ │ │ ├── Predictor.java # 预测核心:Java API 推理封装 │ │ │ ├── Utils.java # 工具类:assets 拷贝、raw→wav 转换 │ │ │ └── AppCompatPreferenceActivity.java │ │ └── res/ │ │ ├── values/strings.xml # 参数默认值 │ │ ├── values/arrays.xml # 线程数/功耗模式候选项、示例文本 │ │ └── xml/settings.xml # 设置界面元素定义 │ └── test/、androidTest/ # 单元测试与仪器测试 ├── gradle/wrapper/ # Gradle Wrapper ├── settings.gradle └── gradlew / gradlew.bat四个重点关注内容
| 关注点 | 作用 | 仓库位置 |
|---|---|---|
Predictor.java | 预测代码,封装 Paddle Lite 模型加载与推理 | Predictor.java |
fastspeech2_csmsc_arm.nb、mb_melgan_csmsc_arm.nb | 模型文件(opt 工具转化后的 Paddle Lite 模型),分别来自 FastSpeech2-CNNDecoder 与 MB MelGAN 的*_pdlite_*.zip发布包 | app/src/main/assets/models/cpu/(由 Gradle 自动下载) |
libpaddle_lite_jni.so、PaddlePredictor.jar | Paddle Lite Java 预测库与 jar 包 | app/src/main/jniLibs/arm64-v8a/、app/libs/ |
build.gradle | 定义编译过程的 Gradle 脚本,内含自动下载预测库与模型的逻辑 | app/build.gradle |
关于模型文件:当前仓库并未直接存放.nb文件,而是由 build.gradle 中的downloadAndExtractPaddleLiteModels任务在构建时从fs2cnn_mbmelgan_cpu_v1.3.0.tar.gz下载并解压到src/main/assets/models目录。同理,downloadAndExtractPaddleLiteLibs任务会下载paddle_lite_libs_68b66fd3.tar.gz并解压出PaddlePredictor.jar与libpaddle_lite_jni.so,两个任务均通过preBuild.dependsOn挂到构建链上,且带有 MD5 缓存逻辑,避免重复下载。如需手动更新模型和预测库,可注释掉 Gradle 脚本中的download*相关任务,并将新文件直接放置到对应目录。
替换动态库与 jar 包的规则:新的libpaddle_lite_jni.so放到app/src/main/jniLibs/arm64-v8a/目录,新的PaddlePredictor.jar放到app/libs/目录。
Java 端模块详解
MainActivity:APP 生命周期与推理调度
MainActivity实现 APP 的创建、运行、释放功能,核心是onLoadModel和onRunModel两个函数,它们完成界面值传递和推理处理:
public boolean onLoadModel() { return predictor.init(MainActivity.this, modelPath, AMmodelName, VOCmodelName, cpuThreadNum, cpuPowerMode); } public boolean onRunModel() { return predictor.isLoaded() && predictor.runModel(phones); }其内部机制(对应 MainActivity.java):
- 双 Handler 线程模型:
sender(绑定HandlerThread("Predictor Worker")的 Looper)接收REQUEST_LOAD_MODEL/REQUEST_RUN_MODEL消息,在工作线程中执行耗时推理;receiver(主线程 Handler)接收RESPONSE_*结果消息并更新 UI,包括进度对话框ProgressDialog的显示与关闭。 - 示例文本与 phone_id 映射:
sentencesToChoose是一个float[][]数组,每个元素是对应句子的音素序列(phone_id 数组,与phone_id_map.txt对应);下拉框选择文本后,通过onItemSelected将对应数组赋给phones并触发runModel()。 - 结果展示:
onRunModelSuccessed中显示推理耗时、RTF(inferenceTime * sampleRate / (wav.length * 1000)),并将predictor.wav通过Utils.rawToWave写为tts_output.wav,随后初始化MediaPlayer播放。 - 生命周期释放:
onDestroy中调用predictor.releaseModel()、退出工作线程并释放MediaPlayer。 - 运行时权限:启动时请求
WRITE_EXTERNAL_STORAGE权限(用于写 wav 文件)。
SettingsActivity:设置界面
SettingsActivity实现设置界面各元素的更新与显示(模型地址、线程数、CPU 功耗模式等)。其界面元素由 settings.xml 定义:
- Model Settings:
Choose pre-installed models(预置模型选择,ListPreference)、Enable custom settings(自定义设置开关,CheckBoxPreference)、Model Path(模型路径,EditTextPreference)。 - CPU Settings:
CPU Thread Num(线程数,ListPreference)与CPU Power Mode(功耗模式,ListPreference)。
参数默认值在 strings.xml 中声明:模型路径默认models/cpu,线程数默认1,功耗模式默认LITE_POWER_HIGH。每个元素的 ID 和 value 与settings.xml及strings.xml中的键值一一对应。该部分不建议随意修改;若需新增属性,可按此格式在settings.xml中添加 Preference 并在SettingsActivity中注册监听。
SettingsActivity实现了OnSharedPreferenceChangeListener,在onSharedPreferenceChanged中监听参数变化并调用reloadPreferenceAndUpdateUI刷新界面;当选择预置模型时自动关闭自定义设置(ENABLE_CUSTOM_SETTINGS_KEY置为 false)。主界面 MainActivity.java 的onResume会读取SharedPreferences中模型路径、线程数、功耗模式并与当前值比对,若发生变化则自动重新加载模型。
Predictor:Paddle Lite 推理核心
Predictor使用 Java API 实现语音合成模型的预测功能,重点关注init与runModel:
// 初始化函数,完成预测器初始化 public boolean init(Context appCtx, String modelPath, String AMmodelName, String VOCmodelName, int cpuThreadNum, String cpuPowerMode); // 模型推理函数 public boolean runModel(float[] phones);init依次为声学模型与声码器调用loadModel创建两个PaddlePredictor实例(AMPredictor、VOCPredictor),全部加载成功后将isLoaded置为 true。loadModel内部完成:将 assets 中的模型目录拷贝到应用缓存目录(Utils.copyDirectoryFromAssets,若路径以/开头则视为自定义绝对路径),构造MobileConfig,通过config.setModelFromFile(realPath + File.separator + modelName)指定模型文件,setThreads(cpuThreadNum)设置线程数,并根据字符串匹配设置六种PowerMode之一,最后调用PaddlePredictor.createPaddlePredictor(config)创建预测器。
Paddle Lite Java API 五步推理流程
结合 Predictor.java 的实际代码,端侧执行一次 TTS 预测的完整流程如下:
第一步:初始化 MobileConfig 并创建预测器
MobileConfig config = new MobileConfig(); config.setModelFromFile(realPath + File.separator + modelName); config.setThreads(cpuThreadNum); config.setPowerMode(PowerMode.LITE_POWER_HIGH); // 按 cpuPowerMode 字符串映射 return PaddlePredictor.createPaddlePredictor(config);第二步:获取输入 Tensor 并填充数据(声学模型)
getAMOutput中取得am_predictor.getInput(0),将输入 resize 为{phones.length}(一维音素序列),setData(phones)写入 float 数组:
Tensor phones_handle = am_predictor.getInput(0); long[] dims = {phones.length}; phones_handle.resize(dims); phones_handle.setData(phones); am_predictor.run(); Tensor am_output_handle = am_predictor.getOutput(0); // [?, 80]第三步:声学模型推理,得到梅尔频谱:am_predictor.run()执行推理,输出张量 shape 为[?, 80](帧数 × 80 维梅尔特征)。代码中特别注释:因为声码器需要知道输入 shape,所以这里必须保留 Tensor 句柄(am_output_handle)而不能退化为扁平 float 数组。
第四步:将梅尔频谱作为输入喂给声码器
Tensor mel_handle = voc_predictor.getInput(0); long[] dims = input.shape(); // 复用声学模型输出的 [?, 80] shape mel_handle.resize(dims); mel_handle.setData(am_output_data); voc_predictor.run(); Tensor voc_output_handle = voc_predictor.getOutput(0); // [? x 300, 1]第五步:获取最终波形:voc_output_handle.getFloatData()得到一维 float 波形数组,存入predictor.wav,再经 Utils.java 的rawToWave写入标准 WAV 文件(写入 RIFF/WAVE 头、16bit PCM、24000Hz 单声道),并由FloatArray2ShortArray做归一化(按峰值归一化到 16bit 短整型范围)后落盘。
更新 Paddle Lite 预测库
如需使用更新版本的 Paddle Lite 预测库:
- 参考 Paddle Lite 源码编译文档,编译 Android 预测库(注意 TTS 模型依赖 Paddle Lite 的 TTS 算子支持,建议使用支持 TTS 模型的版本或 develop 分支,详见 examples/csmsc/tts3/README.md 中的说明)。
- 编译最终产物位于
build.lite.xxx.xxx.xxx下的inference_lite_lib.xxx.xxx目录。 - 替换 Java 库 jar 包:将生成的
build.lite.android.xxx.gcc/inference_lite_lib.android.xxx/java/jar/PaddlePredictor.jar替换 Demo 中的demos/TTSAndroid/app/libs/PaddlePredictor.jar。 - 替换 Java so 库(arm64-v8a):将生成的
build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/java/so/libpaddle_lite_jni.so替换 Demo 中的demos/TTSAndroid/app/src/main/jniLibs/arm64-v8a/libpaddle_lite_jni.so。
更新模型与输入
更新模型
- 将优化后的 Paddle Lite 模型(
.nb)存放到demos/TTSAndroid/app/src/main/assets/models/cpu/目录下。可以任意换成 released_model.md 中列出的*_pdlite_*.zip压缩包解压出的*_arm.nb格式声学模型和声码器(该文档给出了 FastSpeech2 系列、SpeedySpeech、Parallel WaveGAN、MB MelGAN、HiFiGAN 等多个语料库上的 Paddle Lite 模型下载清单)。注意:更换声学模型后,需要对应修改 MainActivity.java 中的sentencesToChoose数组(新模型的音素 id 集合)。 - 如果新模型名字与工程中默认模型完全一致(即均为
fastspeech2_csmsc_arm.nb,且声学模型的phone_id_map.txt也一样)和mb_melgan_csmsc_arm.nb,则代码无需修改;否则需要修改 MainActivity.java 中的AMmodelName和VOCmodelName字段为实际文件名。 - 如果新模型的输入/输出 Tensor 个数、shape 或 Dtype 发生变化,则需要同步更新 Predictor.java(如
getAMOutput/getVOCOutput中的resize维度与数据读写逻辑)。
更新输入
本 Demo 不包含文本前端模块:它通过下拉框选择预先设置好的文本,并在代码中把文本映射为对应的 phone_id 数组(即sentencesToChoose)。如需完整的文本前端(文本规范化、分词、G2P 等),需要自行集成,可参考社区提供的 C++ 中文前端与英文 g2p 实现。phone_id_map.txt(音素 id 映射表)包含在fastspeech2_cnndecoder_csmsc_pdlite_1.3.0.zip模型包中,用于将文本转写为音素序列。
通过 Setting 界面更新语音合成参数
APP 右上角点击:符号,选择Settings..可打开设置界面,目前支持 CPU 相关参数更新:
- power_mode:默认
LITE_POWER_HIGH - thread_num:默认
1
候选项定义在 arrays.xml 中:
| 参数 | 可选值 |
|---|---|
| CPU Thread Num | 1 threads / 2 threads / 4 threads / 8 threads |
| CPU Power Mode | LITE_POWER_HIGH(仅大核)/LITE_POWER_LOW(仅小核)/LITE_POWER_FULL(全部核)/LITE_POWER_NO_BIND(由系统决定)/LITE_POWER_RAND_HIGH/LITE_POWER_RAND_LOW |
这六种功耗模式与 Predictor.java 中loadModel的字符串映射一一对应,未知模式会打印Unknown cpu power mode!并返回加载失败。
参数更新步骤:
- 打开 APP,点击右上角的
:,选择Settings..打开设置界面; - 勾选
Enable custom settings(☑️)启用自定义设置,然后更新参数; - 例如将
CPU Thread Num设置为 4,更新后返回原界面,APP 会自动重新加载模型;在下拉框中选择文本会进行合成,合成结束后会打印 4 线程的推理耗时与结果(包括 RTF 与保存的音频路径)。
性能优化方向
若当前端侧性能不符合需求,可从以下方向入手:
- 调大 CPU 线程数:通过 Setting 界面将线程数从默认 1 调整为 2/4/8,充分利用多核(大核
LITE_POWER_HIGH模式对延迟敏感任务通常收益明显)。 - 更换功耗模式:在发热/续航敏感场景使用
LITE_POWER_LOW,在追求极致性能时使用LITE_POWER_FULL或LITE_POWER_RAND_HIGH。 - 更换更轻量的模型:例如在声学模型上使用轻量解码器版本(FastSpeech2-CNNDecoder),声码器侧选择 MB MelGAN 等参数更小的模型。
- 关注 RTF 指标:Demo 主界面会打印实时率(RTF = 推理耗时 × 采样率 / 波形长度),可作为端侧性能的量化评估基准。
Release 与参考
- 仓库为 Android 端 TTS Demo 提供了预编译 APK 发布包(
2022-11-29-app-release.apk),可直接安装体验。 - 模型的 Paddle Lite 导出与运行示例可参考 examples/csmsc/tts3(含
lite_predict.sh等端侧推理脚本);完整的 Paddle Lite 模型发布清单见 docs/source/released_model.md。 - 该 Demo 合并自社区的开源 TTSAndroid 项目,Java 侧推理代码(
Predictor.java等)可在本仓库demos/TTSAndroid下直接查看学习。
【免费下载链接】PaddleSpeechEasy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword Spotting. Won NAACL2022 Best Demo Award.项目地址: https://gitcode.com/gh_mirrors/pa/PaddleSpeech
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考