news 2026/9/23 3:21:03

PaddleSpeech 语音合成 Android Demo 实战:基于 Paddle Lite Java API 的端侧 TTS 部署指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PaddleSpeech 语音合成 Android Demo 实战:基于 Paddle Lite Java API 的端侧 TTS 部署指南

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 中的两个预测器:

  1. 声学模型(AM)fastspeech2_csmsc_arm.nb,将音素序列(phone_id)转换为梅尔频谱(Mel Spectrogram)。在代码中对应AMPredictor,其输出张量 shape 为[?, 80](80 维梅尔特征)。
  2. 声码器(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 主线程。

环境准备与运行部署

环境要求

  1. 本地安装Android Studio(官方 IDE,用于导入工程、编译与安装 APK)。
  2. 准备一部Android 手机,并开启USB 调试模式手机设置 -> 查找开发者选项 -> 打开开发者选项和 USB 调试模式

注意:如果 Android Studio 尚未配置 NDK,请预先按 Android Studio 用户指南中的"安装及配置 NDK 和 CMake"章节进行配置。可以选择最新的 NDK 版本,也可以使用与 Paddle Lite 预测库版本一致的 NDK(例如本 Demo 编译产物基于 arm64-v8a 架构,NDK r20b 一类版本即可满足需求)。

部署步骤

  1. 用 Android Studio 打开demos/TTSAndroid工程(即本仓库demos/TTSAndroid目录,工程入口为settings.gradle)。
  2. 手机连接电脑,打开USB 调试文件传输模式,在 Android Studio 中连接自己的手机设备(手机需开启"允许从 USB 安装软件"权限)。
  3. 点击Run按钮,Android Studio 会自动编译 APP 并安装到手机。该过程会自动下载 Paddle Lite 预测库和模型,需要联网

运行成功后:APP 安装到手机;打开 APP 后可在下拉框(Spinner)中选择待合成的文本;选择文本后自动开始合成,合成结束会显示推理耗时与 RTF(实时率),并出现播放/暂停/停止三个按钮用于播放音频。

常见 NDK 配置问题排查

导入项目、编译或运行过程中若遇到 NDK 配置错误,可按下述顺序排查:

  1. 打开File > Project Structure > SDK Location,将Android NDK location修改为本机 NDK 所在路径;
  2. 如果 NDK 是通过 Android Studio 的SDK Tools下载的,可直接在下拉框中选择默认路径;
  3. 也可以在工程根目录的local.properties文件中手动添加 NDK 路径配置,例如nkd.dir=/root/android-ndk-r20b
  4. 若上述方法均无法解决,可参考 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.nbmb_melgan_csmsc_arm.nb模型文件(opt 工具转化后的 Paddle Lite 模型),分别来自 FastSpeech2-CNNDecoder 与 MB MelGAN 的*_pdlite_*.zip发布包app/src/main/assets/models/cpu/(由 Gradle 自动下载)
libpaddle_lite_jni.soPaddlePredictor.jarPaddle 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.jarlibpaddle_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 的创建、运行、释放功能,核心是onLoadModelonRunModel两个函数,它们完成界面值传递和推理处理:

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 SettingsChoose pre-installed models(预置模型选择,ListPreference)、Enable custom settings(自定义设置开关,CheckBoxPreference)、Model Path(模型路径,EditTextPreference)。
  • CPU SettingsCPU Thread Num(线程数,ListPreference)与CPU Power Mode(功耗模式,ListPreference)。

参数默认值在 strings.xml 中声明:模型路径默认models/cpu,线程数默认1,功耗模式默认LITE_POWER_HIGH。每个元素的 ID 和 value 与settings.xmlstrings.xml中的键值一一对应。该部分不建议随意修改;若需新增属性,可按此格式在settings.xml中添加 Preference 并在SettingsActivity中注册监听。

SettingsActivity实现了OnSharedPreferenceChangeListener,在onSharedPreferenceChanged中监听参数变化并调用reloadPreferenceAndUpdateUI刷新界面;当选择预置模型时自动关闭自定义设置(ENABLE_CUSTOM_SETTINGS_KEY置为 false)。主界面 MainActivity.java 的onResume会读取SharedPreferences中模型路径、线程数、功耗模式并与当前值比对,若发生变化则自动重新加载模型

Predictor:Paddle Lite 推理核心

Predictor使用 Java API 实现语音合成模型的预测功能,重点关注initrunModel

// 初始化函数,完成预测器初始化 public boolean init(Context appCtx, String modelPath, String AMmodelName, String VOCmodelName, int cpuThreadNum, String cpuPowerMode); // 模型推理函数 public boolean runModel(float[] phones);

init依次为声学模型与声码器调用loadModel创建两个PaddlePredictor实例(AMPredictorVOCPredictor),全部加载成功后将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 预测库:

  1. 参考 Paddle Lite 源码编译文档,编译 Android 预测库(注意 TTS 模型依赖 Paddle Lite 的 TTS 算子支持,建议使用支持 TTS 模型的版本或 develop 分支,详见 examples/csmsc/tts3/README.md 中的说明)。
  2. 编译最终产物位于build.lite.xxx.xxx.xxx下的inference_lite_lib.xxx.xxx目录。
  3. 替换 Java 库 jar 包:将生成的build.lite.android.xxx.gcc/inference_lite_lib.android.xxx/java/jar/PaddlePredictor.jar替换 Demo 中的demos/TTSAndroid/app/libs/PaddlePredictor.jar
  4. 替换 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

更新模型与输入

更新模型

  1. 将优化后的 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 集合)。
  2. 如果新模型名字与工程中默认模型完全一致(即均为fastspeech2_csmsc_arm.nb,且声学模型的phone_id_map.txt也一样)和mb_melgan_csmsc_arm.nb,则代码无需修改;否则需要修改 MainActivity.java 中的AMmodelNameVOCmodelName字段为实际文件名。
  3. 如果新模型的输入/输出 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 Num1 threads / 2 threads / 4 threads / 8 threads
CPU Power ModeLITE_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!并返回加载失败。

参数更新步骤:

  1. 打开 APP,点击右上角的:,选择Settings..打开设置界面;
  2. 勾选Enable custom settings(☑️)启用自定义设置,然后更新参数;
  3. 例如将CPU Thread Num设置为 4,更新后返回原界面,APP 会自动重新加载模型;在下拉框中选择文本会进行合成,合成结束后会打印 4 线程的推理耗时与结果(包括 RTF 与保存的音频路径)。

性能优化方向

若当前端侧性能不符合需求,可从以下方向入手:

  • 调大 CPU 线程数:通过 Setting 界面将线程数从默认 1 调整为 2/4/8,充分利用多核(大核LITE_POWER_HIGH模式对延迟敏感任务通常收益明显)。
  • 更换功耗模式:在发热/续航敏感场景使用LITE_POWER_LOW,在追求极致性能时使用LITE_POWER_FULLLITE_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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 3:17:06

Flutter测试迁移鸿蒙:test_process进程适配与CLI集成测试实践

我手头这个 Flutter 项目本来跑得好好的,CI 上一套集成测试每天都稳定执行。第一次把整套验证迁移到鸿蒙开发板上时,测试零零散散挂了一大片。我看日志还以为是打包脚本的问题,点进去才发现错误清一色集中在dart:io的进程相关调用上&#xff…

作者头像 李华
网站建设 2026/9/23 3:13:20

毛发检测仪核心技术解析与临床应用

1. 项目背景与产品定位在皮肤科与毛发医学领域,精准诊断一直是临床工作的核心难点。传统毛发检测主要依赖肉眼观察和普通光学设备,存在放大倍数有限、图像清晰度不足、数据难以量化等问题。这次在毛发专病医联体年会上亮相的"发觉星毛发拍摄仪"…

作者头像 李华
网站建设 2026/9/23 3:12:58

职场过劳危机:从能者多劳到健康管理

1. 现象背后的职场文化反思张雪峰老师的突然离世在职场圈引发广泛讨论,表面看是过劳导致的悲剧,实则折射出更深层的组织管理问题。作为在人力资源领域深耕十余年的从业者,我观察到"能者多劳"现象已成为许多企业的隐性管理逻辑——那…

作者头像 李华