第19篇:语音合成 - TTS语音播报
📚 本篇导读
在前面的教程中,我们已经在图像识别功能中简单使用了TTS语音播报。本篇教程将深入讲解HarmonyOS Core Speech Kit的TTS(Text-to-Speech)功能,并在智慧农业场景中实现多种语音播报应用,让应用更加智能和人性化。
本篇将实现:
- 🎙️ TTS服务深度解析(引擎配置、参数调优)
- 🔊 多场景语音播报(农事提醒、操作指导、数据播报)
- ⚙️ 语音参数配置(语速、音量、音调、音色)
- 📢 智能播报策略(播报队列、优先级管理)
- 🎵 语音效果优化(断句处理、情感表达)
🎯 学习目标
完成本篇教程后,你将掌握:
- HarmonyOS Core Speech Kit TTS API的完整使用
- 如何配置和优化TTS参数
- 如何在不同场景中应用语音播报
- 如何实现智能播报队列管理
- TTS语音播报的最佳实践
一、TTS技术概述
1.1 什么是TTS?
TTS(Text-to-Speech)是将文本转换为语音的技术。HarmonyOS Core Speech Kit提供了端侧TTS能力,具有以下特点:
| 特性 | 说明 |
|---|---|
| 离线可用 | 支持离线模式,无需网络 |
| 低延迟 | 端侧处理,毫秒级响应 |
| 多音色 | 支持男声、女声等多种音色 |
| 可调节 | 语速、音量、音调可自由调节 |
| 高质量 | 自然流畅的语音效果 |
1.2 TTS在智慧农业中的应用场景
智慧农业TTS应用场景
├── 操作反馈
│ ├── 图片识别完成提示
│ ├── 数据保存成功提示
│ ├── 操作错误提示
│ └── 功能引导说明
│
├── 农事提醒
│ ├── 任务到期提醒
│ ├── 浇水施肥提醒
│ ├── 病虫害预警
│ └── 天气变化提醒
│
├── 数据播报
│ ├── 地块统计数据
│ ├── 成本收入分析
│ ├── 产量预测结果
│ └── 天气信息播报
│
└── 知识讲解
├── 病虫害防治方法
├── 农事操作指导
├── 节气农事建议
└── 作物种植技巧
1.3 TTS架构设计
应用层
├── 图像识别页面
├── 地图页面
├── 任务管理页面
└── 数据分析页面
↓
TTSService(单例服务)
├── 初始化管理
├── 播报队列
├── 参数配置
└── 状态监控
↓
Core Speech Kit
├── TextToSpeechEngine
├── SpeakListener
└── 音频输出
↓
系统音频服务
二、TTSService服务详解
2.1 服务类设计
我们已经在entry/src/main/ets/services/TTSService.ets中实现了基础的TTS服务,现在让我们深入理解每个部分:
typescript
/**
* AI语音合成服务 - 使用 TextToSpeech API
* 单例模式,全局共享一个TTS引擎实例
*/
import { textToSpeech } from '@kit.CoreSpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';
export class TTSService {
// 单例实例
private static instance: TTSService | null = null;
// TTS引擎
private ttsEngine: textToSpeech.TextToSpeechEngine | null = null;
// 初始化状态
private isInitialized: boolean = false;
// 播报队列(用于管理多个播报请求)
private speakQueue: string[] = [];
// 是否正在播报
private isSpeaking: boolean = false;
private constructor() {}
/**
* 获取单例实例
*/
static getInstance(): TTSService {
if (TTSService.instance === null) {
TTSService.instance = new TTSService();
}
return TTSService.instance;
}
}
设计要点:
- 单例模式:确保全局只有一个TTS引擎实例,避免资源浪费
- 状态管理:跟踪初始化状态和播报状态
- 队列管理:支持多个播报请求的排队处理
2.2 初始化TTS引擎
typescript
/**
* 初始化TTS引擎
* @param online 是否使用在线模式(默认true)
* @param person 音色选择(0=女声,1=男声)
*/
async initialize(online: boolean = true, person: number = 0): Promise<void> {
if (this.isInitialized) {
console.info('[TTSService] 已经初始化,跳过');
return;
}
try {
console.info('[TTSService] 开始初始化TTS引擎...');
// 1. 配置引擎参数
let extraParam: Record<string, Object> = {
'style': 'interaction-broadcast', // 交互播报风格
'locate': 'CN', // 中国地区
'name': 'LangduTTS' // 引擎名称
};
let initParamsInfo: textToSpeech.CreateEngineParams = {
language: 'zh-CN', // 语言:中文
person: person, // 音色:0=女声,1=男声
online: online ? 1 : 0, // 模式:1=在线,0=离线
extraParams: extraParam
};
// 2. 创建TTS引擎
this.ttsEngine = await textToSpeech.createEngine(initParamsInfo);
if (!this.ttsEngine) {
throw new Error('TTS引擎创建失败');
}
this.isInitialized = true;
console.info('[TTSService] ✅ TTS引擎初始化成功');
console.info(`[TTSService] 配置: 语言=zh-CN, 音色=${person === 0 ? '女声' : '男声'}, 模式=${online ? '在线' : '离线'}`);
} catch (error) {
const err = error as BusinessError;
console.error(`[TTSService] ❌ 初始化失败: ${err.code} - ${err.message}`);
throw Error(`TTS初始化失败: ${err.message}`);
}
}
初始化参数详解:
| 参数 | 类型 | 说明 | 可选值 |
|---|---|---|---|
language |
string | 语言 | 'zh-CN'(中文)、'en-US'(英文) |
person |
number | 音色 | 0(女声-聆小珊)、1(男声) |
online |
number | 模式 | 1(在线)、0(离线) |
style |
string | 风格 | 'interaction-broadcast'(交互播报) |
在线模式 vs 离线模式:
- 在线模式:音质更好,支持更多音色,需要网络
- 离线模式:无需网络,响应更快,音质略逊
2.3 实现语音播报
typescript
/**
* 朗读文本
* @param text 要朗读的文本(最大10000字符)
* @param speed 语速(0.5-2.0),默认1.0
* @param volume 音量(0.0-1.0),默认1.0
* @param pitch 音调(0.5-2.0),默认1.0
*/
async speak(
text: string,
speed: number = 1.0,
volume: number = 1.0,
pitch: number = 1.0
): Promise<void> {
// 1. 检查初始化状态
if (!this.isInitialized || this.ttsEngine === null) {
console.error('[TTSService] 服务未初始化');
throw Error('TTS服务未初始化,请先调用initialize()');
}
// 2. 验证文本
if (!text || text.length === 0) {
console.warn('[TTSService] 文本为空,跳过播报');
return;
}
// 3. 文本长度限制
if (text.length > 10000) {
console.warn('[TTSService] 文本过长,截断到10000字符');
text = text.substring(0, 10000);
}
// 4. 参数范围验证
speed = Math.max(0.5, Math.min(2.0, speed));
volume = Math.max(0.0, Math.min(1.0, volume));
pitch = Math.max(0.5, Math.min(2.0, pitch));
console.info(`[TTSService] 准备播报: "${text.substring(0, 50)}${text.length > 50 ? '...' : ''}"`);
console.info(`[TTSService] 参数: 语速=${speed}, 音量=${volume}, 音调=${pitch}`);
try {
// 5. 配置播报参数
let extraParam: Record<string, Object> = {
'speed': speed, // 语速
'volume': volume, // 音量
'pitch': pitch, // 音调
'languageContext': 'zh-CN', // 语言上下文
'audioType': 'pcm' // 音频类型
};
let speakParams: textToSpeech.SpeakParams = {
requestId: Date.now().toString(), // 请求ID(用于追踪)
extraParams: extraParam
};
// 6. 设置监听器并开始播报
return new Promise<void>((resolve, reject) => {
let speakListener: textToSpeech.SpeakListener = {
// 播报开始
onStart: (requestId: string, response: textToSpeech.StartResponse) => {
console.info(`[TTSService] 🎙️ 开始播报, requestId: ${requestId}`);
this.isSpeaking = true;
},
// 播报完成
onComplete: (requestId: string, response: textToSpeech.CompleteResponse) => {
console.info(`[TTSService] ✅ 播报完成, requestId: ${requestId}`);
this.isSpeaking = false;
resolve();
},
// 播报停止
onStop: (requestId: string, response: textToSpeech.StopResponse) => {
console.info(`[TTSService] ⏹️ 播报停止, requestId: ${requestId}`);
this.isSpeaking = false;
resolve();
},
// 播报错误
onError: (requestId: string, errorCode: number, errorMessage: string) => {
console.error(`[TTSService] ❌ 播报错误: ${errorCode} - ${errorMessage}`);
this.isSpeaking = false;
reject(new Error(`TTS错误: ${errorMessage}`));
},
// 音频数据回调(可选,用于自定义处理)
onData: (requestId: string, audio: ArrayBuffer,
response: textToSpeech.SynthesisResponse) => {
// 可以在这里获取音频数据进行自定义处理
// 例如:保存音频文件、实时分析等
}
};
// 设置监听器
this.ttsEngine!.setListener(speakListener);
// 开始播报
this.ttsEngine!.speak(text, speakParams);
});
} catch (error) {
const err = error as BusinessError;
console.error(`[TTSService] 播报失败: ${err.message}`);
this.isSpeaking = false;
throw Error(`TTS播报失败: ${err.message}`);
}
}
播报参数说明:
| 参数 | 范围 | 默认值 | 效果 |
|---|---|---|---|
speed |
0.5-2.0 | 1.0 | 语速,值越大越快 |
volume |
0.0-1.0 | 1.0 | 音量,1.0为最大 |
pitch |
0.5-2.0 | 1.0 | 音调,值越大音调越高 |
监听器回调说明:
onStart:播报开始时触发onComplete:播报正常完成时触发onStop:播报被停止时触发onError:播报出错时触发onData:音频数据生成时触发(可选)
2.4 停止和状态管理
typescript
/**
* 停止当前播报
*/
stop(): void {
if (this.ttsEngine !== null) {
this.ttsEngine.stop();
this.isSpeaking = false;
console.info('[TTSService] ⏹️ 播报已停止');
}
}
/**
* 判断是否正在播报
*/
isBusy(): boolean {
if (this.ttsEngine !== null) {
return this.ttsEngine.isBusy();
}
return false;
}
/**
* 获取播报状态
*/
getSpeakingStatus(): boolean {
return this.isSpeaking;
}
/**
* 释放资源
*/
async release(): Promise<void> {
if (this.ttsEngine !== null) {
await this.ttsEngine.shutdown();
this.ttsEngine = null;
}
this.isInitialized = false;
this.isSpeaking = false;
console.info('[TTSService] 🔄 资源已释放');
}
三、多场景语音播报实现
3.1 操作反馈播报
在用户操作时提供即时的语音反馈:
typescript
/**
* 操作成功提示
*/
async speakSuccess(operation: string): Promise<void> {
const text = `${operation}成功`;
await ttsService.speak(text, 1.2, 0.8); // 稍快语速,适中音量
}
/**
* 操作失败提示
*/
async speakError(operation: string, reason?: string): Promise<void> {
let text = `${operation}失败`;
if (reason) {
text += `,${reason}`;
}
await ttsService.speak(text, 1.0, 1.0);
}
/**
* 数据保存提示
*/
async speakDataSaved(dataType: string): Promise<void> {
const text = `${dataType}已保存`;
await ttsService.speak(text, 1.2, 0.8);
}
// 使用示例
async saveField(field: FieldInfo): Promise<void> {
try {
await fieldService.addField(field);
await this.speakSuccess('添加地块');
promptAction.showToast({
message: '地块添加成功',
duration: 2000
});
} catch (error) {
await this.speakError('添加地块', '请检查网络连接');
promptAction.showToast({
message: '添加失败',
duration: 2000
});
}
}
3.2 农事提醒播报
实现定时的农事提醒语音播报:
typescript
/**
* 任务提醒播报
*/
async speakTaskReminder(task: TaskInfo): Promise<void> {
const text = `您有一个任务需要处理:${task.title},` +
`计划时间是${task.scheduledDate},` +
`请及时完成`;
await ttsService.speak(text, 1.0, 1.0);
}
/**
* 浇水提醒
*/
async speakWateringReminder(fieldName: string): Promise<void> {
const text = `提醒您,${fieldName}需要浇水了,请及时处理`;
await ttsService.speak(text, 1.0, 1.0);
}
/**
* 施肥提醒
*/
async speakFertilizingReminder(fieldName: string, cropName: string): Promise<void> {
const text = `${fieldName}的${cropName}需要施肥了,建议今天完成`;
await ttsService.speak(text, 1.0, 1.0);
}
/**
* 病虫害预警
*/
async speakPestWarning(fieldName: string, pestName: string): Promise<void> {
const text = `警告!${fieldName}发现${pestName},请立即查看并采取防治措施`;
await ttsService.speak(text, 0.9, 1.0); // 稍慢语速,强调重要性
}
/**
* 天气预警
*/
async speakWeatherWarning(weather: string, suggestion: string): Promise<void> {
const text = `天气预警:${weather},建议${suggestion}`;
await ttsService.speak(text, 1.0, 1.0);
}
3.3 数据播报
播报统计数据和分析结果:
typescript
/**
* 地块统计播报
*/
async speakFieldStatistics(stats: FieldStats): Promise<void> {
const text = `当前共有${stats.totalFields}块地,` +
`总面积${stats.totalArea.toFixed(1)}亩,` +
`其中种植中的有${stats.activeFields}块,` +
`闲置的有${stats.idleFields}块`;
await ttsService.speak(text, 1.0, 0.9);
}
/**
* 成本收入播报
*/
async speakFinancialSummary(totalCost: number, totalIncome: number): Promise<void> {
const profit = totalIncome - totalCost;
const profitRate = totalCost > 0 ? (profit / totalCost * 100).toFixed(1) : '0';
const text = `财务概况:总成本${(totalCost / 10000).toFixed(2)}万元,` +
`总收入${(totalIncome / 10000).toFixed(2)}万元,` +
`净利润${(profit / 10000).toFixed(2)}万元,` +
`利润率${profitRate}%`;
await ttsService.speak(text, 0.9, 0.9); // 稍慢语速,便于理解数字
}
/**
* 产量预测播报
*/
async speakYieldPrediction(cropName: string, predictedYield: number, confidence: number): Promise<void> {
const confidencePercent = (confidence * 100).toFixed(0);
const text = `${cropName}产量预测:预计产量${predictedYield.toFixed(1)}吨,` +
`置信度${confidencePercent}%`;
await ttsService.speak(text, 1.0, 0.9);
}
/**
* 天气信息播报
*/
async speakWeatherInfo(weather: string, temperature: string, suggestion: string): Promise<void> {
const text = `今天天气${weather},温度${temperature}度,${suggestion}`;
await ttsService.speak(text, 1.0, 0.9);
}
3.4 知识讲解播报
播报农业知识和操作指导:
typescript
/**
* 病虫害防治方法播报
*/
async speakPestControl(pestName: string, methods: string[]): Promise<void> {
let text = `${pestName}的防治方法有:`;
methods.forEach((method, index) => {
text += `第${index + 1},${method}。`;
});
await ttsService.speak(text, 0.9, 0.9); // 稍慢语速,便于记忆
}
/**
* 节气农事建议播报
*/
async speakSolarTermAdvice(termName: string, advice: string[]): Promise<void> {
let text = `${termName}节气农事建议:`;
advice.forEach((item, index) => {
text += `${index + 1}、${item}。`;
});
await ttsService.speak(text, 0.9, 0.9);
}
/**
* 作物种植指导播报
*/
async speakPlantingGuide(cropName: string, steps: string[]): Promise<void> {
let text = `${cropName}种植步骤:`;
steps.forEach((step, index) => {
text += `步骤${index + 1},${step}。`;
});
await ttsService.speak(text, 0.9, 0.9);
}
四、智能播报策略
4.1 播报队列管理
实现播报队列,避免多个播报请求冲突:
typescript
/**
* 播报队列管理
*/
export class TTSService {
private speakQueue: Array<{text: string, speed: number, volume: number}> = [];
private isProcessingQueue: boolean = false;
/**
* 添加到播报队列
*/
async addToQueue(text: string, speed: number = 1.0, volume: number = 1.0): Promise<void> {
this.speakQueue.push({ text, speed, volume });
if (!this.isProcessingQueue) {
await this.processQueue();
}
}
/**
* 处理播报队列
*/
private async processQueue(): Promise<void> {
if (this.speakQueue.length === 0) {
this.isProcessingQueue = false;
return;
}
this.isProcessingQueue = true;
while (this.speakQueue.length > 0) {
const item = this.speakQueue.shift();
if (item) {
try {
await this.speak(item.text, item.speed, item.volume);
// 播报间隔(避免连续播报太快)
await this.delay(500);
} catch (error) {
console.error('[TTSService] 队列播报失败:', error);
}
}
}
this.isProcessingQueue = false;
}
/**
* 延时函数
*/
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* 清空队列
*/
clearQueue(): void {
this.speakQueue = [];
console.info('[TTSService] 播报队列已清空');
}
}
4.2 优先级播报
实现不同优先级的播报策略:
typescript
/**
* 播报优先级
*/
enum SpeakPriority {
LOW = 0, // 低优先级(普通提示)
NORMAL = 1, // 正常优先级(操作反馈)
HIGH = 2, // 高优先级(重要提醒)
URGENT = 3 // 紧急优先级(警告)
}
/**
* 优先级播报
*/
async speakWithPriority(
text: string,
priority: SpeakPriority = SpeakPriority.NORMAL
): Promise<void> {
// 紧急播报:立即停止当前播报
if (priority === SpeakPriority.URGENT) {
this.stop();
this.clearQueue();
await this.speak(text, 0.9, 1.0);
return;
}
// 高优先级:插入队列前面
if (priority === SpeakPriority.HIGH) {
this.speakQueue.unshift({ text, speed: 1.0, volume: 1.0 });
if (!this.isProcessingQueue) {
await this.processQueue();
}
return;
}
// 普通和低优先级:添加到队列末尾
await this.addToQueue(text);
}
4.3 智能断句处理
优化长文本的播报效果:
typescript
/**
* 智能断句播报
*/
async speakWithPunctuation(text: string): Promise<void> {
// 在标点符号处添加停顿
const processedText = text
.replace(/,/g, ',') // 逗号后短停顿
.replace(/。/g, '。') // 句号后长停顿
.replace(/!/g, '!') // 感叹号后长停顿
.replace(/?/g, '?'); // 问号后长停顿
await this.speak(processedText, 1.0, 0.9);
}
/**
* 分段播报长文本
*/
async speakLongText(text: string, maxLength: number = 100): Promise<void> {
if (text.length <= maxLength) {
await this.speak(text);
return;
}
// 按句号分段
const sentences = text.split('。').filter(s => s.length > 0);
for (const sentence of sentences) {
await this.speak(sentence + '。');
await this.delay(800); // 句子间停顿
}
}
五、实操练习
练习1:实现欢迎语音
任务:在应用启动时播报欢迎语
代码:
typescript
async aboutToAppear(): Promise<void> {
await ttsService.initialize();
const hour = new Date().getHours();
let greeting = '';
if (hour < 6) {
greeting = '凌晨好';
} else if (hour < 12) {
greeting = '早上好';
} else if (hour < 18) {
greeting = '下午好';
} else {
greeting = '晚上好';
}
await ttsService.speak(`${greeting},欢迎使用高高种地智慧农业管理系统`);
}
练习2:实现数据播报
任务:点击按钮播报地块统计数据
代码:
typescript
Button('播报统计')
.onClick(async () => {
const stats = await fieldService.getFieldStats();
await this.speakFieldStatistics(stats);
})
练习3:实现语速调节
任务:添加语速调节功能
代码:
typescript
@State currentSpeed: number = 1.0;
Slider({
value: this.currentSpeed,
min: 0.5,
max: 2.0,
step: 0.1
})
.onChange((value: number) => {
this.currentSpeed = value;
})
Button('测试语速')
.onClick(async () => {
await ttsService.speak('这是语速测试', this.currentSpeed, 1.0);
})
六、总结
本篇教程完成了TTS语音播报的深入应用,主要包括:
✅ 已实现功能
| 功能 | 说明 |
|---|---|
| TTS引擎管理 | 初始化、配置、释放 |
| 多场景播报 | 操作反馈、农事提醒、数据播报、知识讲解 |
| 参数调优 | 语速、音量、音调调节 |
| 队列管理 | 播报队列、优先级控制 |
| 智能优化 | 断句处理、长文本分段 |
🎯 核心技术点
- TextToSpeechEngine:TTS引擎核心API
- SpeakListener:播报状态监听
- 参数配置:speed、volume、pitch
- 队列管理:避免播报冲突
- 优先级策略:紧急播报优先
🚀 下一步
在下一篇教程中,我们将学习天气服务与气象数据的集成。
恭喜! 🎉 你已经掌握了TTS语音播报的完整应用,让应用更加智能和人性化。