共计 2317 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
在移动开发中,离线语音合成(TTS)的需求越来越普遍。尤其是在以下场景:

- 弱网环境:如车载导航、野外作业等网络不稳定的场合
- 隐私保护:医疗、金融等敏感领域需避免语音数据上传
- 海外发布:某些地区无法使用 Google 服务(如华为海外机型)
商业方案的主要局限性:
- Google TTS 必须联网且依赖 GMS
- 阿里云 / 讯飞等收费方案成本高(按调用次数计费)
- 厂商预装 TTS 引擎普遍存在语音生硬、不支持自定义模型等问题
方案对比
测试设备:Redmi Note 11 Pro(Android 12)
| 引擎 | APK 体积增量 | 中文支持 | 最低 API | CPU 占用 (单句) |
|---|---|---|---|---|
| MaryTTS | 28MB | 需插件 | 21 | 12% |
| Flite | 6.5MB | 内置 | 16 | 8% |
| RHVoice | 41MB | 需模型 | 19 | 15% |
关键结论:
- 轻量级首选:Flite(适合嵌入式设备)
- 多语言需求:RHVoice(支持 50+ 语言但体积大)
- 高音质场景:MaryTTS(支持 HMM 合成但延迟较高)
集成实战(Flite 为例)
第一步:Gradle 配置
android {
defaultConfig {
ndk {abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'}
}
}
dependencies {
implementation 'edu.cmu.cs.flite:flite:2.2.0'
implementation 'edu.cmu.cs.flite:flite_voices:2.2.0'
}
注意:
- 必须指定 abiFilters 避免打包所有 CPU 架构导致 APK 膨胀
- flite_voices 包含默认中文语音模型(cmu_us_slt.flitevox)
第二步:NDK 兼容处理
在 src/main/jniLibs 下按架构放置.so 文件:
jniLibs/
├── arm64-v8a
│ └── libflite.so
├── armeabi-v7a
│ └── libflite.so
└── x86_64
└── libflite.so
代码示例(Kotlin)
初始化引擎
class FliteEngine(context: Context) {private val handler = Handler(Looper.getMainLooper())
// 使用 16bit PCM 保证兼容性(8bit 可能产生噪音)private val audioFormat = AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(16000) // 16kHz 平衡质量与体积
.build()
init {
// 异步加载避免 ANR
Thread {System.loadLibrary("flite")
handler.post {/* 初始化完成回调 */}
}.start()}
}
播放队列管理
private val playQueue = LinkedBlockingQueue<String>()
private var isPlaying = false
fun speak(text: String) {playQueue.put(text)
if (!isPlaying) {processNext()
}
}
private fun processNext() {if (playQueue.isEmpty()) return
isPlaying = true
val text = playQueue.take()
// 实际合成代码省略...
audioTrack?.setPlaybackPositionUpdateListener(
object : AudioTrack.OnPlaybackPositionUpdateListener {override fun onMarkerReached(track: AudioTrack) {
isPlaying = false
processNext() // 播完下一句}
}
)
}
性能优化
预加载模型
在 Application.onCreate 预初始化:
class MyApp : Application() {val ttsEngine by lazy { FliteEngine(this) }
override fun onCreate() {super.onCreate()
// 触发懒加载
ttsEngine.hashCode()}
}
采样率对比测试
| 采样率 | 内存占用 | 延迟 | 主观音质 |
|---|---|---|---|
| 8000Hz | 12MB | 120ms | 机器人声 |
| 16000Hz | 18MB | 200ms | 可接受 |
| 22050Hz | 25MB | 350ms | 较自然 |
推荐:
- 导航类应用:16000Hz(延迟敏感)
- 朗读类应用:22050Hz(音质优先)
避坑指南
- 版权合规
- 使用 Flite 自带的 cmu_us_slt 模型可商用
-
第三方模型需确认 LICENSE(如 VITS 模型需单独授权)
-
线程安全
- 合成操作必须放在子线程
-
使用 AtomicBoolean 控制播放状态
-
Android 12 限制
- 后台播放需添加权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> - 启动前台 Service 时传递 NOTIFICATION_ID
延伸思考
进阶优化方向:
- 使用 ONNX Runtime 替换原始模型推理
- 将 Flite 模型转换为 ONNX 格式
-
实测可降低 20% CPU 占用(但增加 3MB 体积)
-
混合合成策略
- 常用短句使用 Concatenative 合成(拼接录音片段)
-
长文本使用 Parametric 合成(参数生成更灵活)
-
动态降采样
- 根据设备剩余内存自动调整采样率
- 低内存设备切换为 8000Hz 模式
通过合理选型和优化,完全可以在免费方案上实现接近商业产品的 TTS 体验。关键在于根据实际场景权衡音质、延迟和资源消耗这三要素。
正文完
