华为HarmonyOS实现实时文本转语音

场景介绍

  • 将一段不超过10000字符的文本合成为语音并进行播报。
  • 场景举例:手机在无网状态下,系统应用无障碍(屏幕朗读)接入文本转语音能力,为视障人士提供播报能力。

开发步骤

  1. 在使用文本转语音时,将实现文本转语音相关的类添加至工程。

    TypeScript 复制代码
    import { textToSpeech } from '@kit.CoreSpeechKit';
    import { BusinessError } from '@kit.BasicServicesKit';
  2. 调用createEngine接口,创建textToSpeechEngine实例。

    createEngine接口提供了两种调用形式,当前以其中一种作为示例,其他方式可参考API参考

    TypeScript 复制代码
    let ttsEngine: textToSpeech.TextToSpeechEngine;
    
    // 设置创建引擎参数
    let extraParam: Record<string, Object> = {"style": 'interaction-broadcast', "locate": 'CN', "name": 'EngineName'};
    let initParamsInfo: textToSpeech.CreateEngineParams = {
    language: 'zh-CN',
    person: 0,
    online: 1,
    extraParams: extraParam
    };
    
    // 调用createEngine方法
    textToSpeech.createEngine(initParamsInfo, (err: BusinessError, textToSpeechEngine: textToSpeech.TextToSpeechEngine) => {
    if (!err) {
    console.info('Succeeded in creating engine');
    // 接收创建引擎的实例
    ttsEngine = textToSpeechEngine;
    } else {
    // 创建引擎失败时返回错误码1003400005,可能原因:引擎不存在、资源不存在、创建引擎超时
    console.error(`Failed to create engine. Code: ${err.code}, message: ${err.message}.`);
    }
    });
  3. 得到TextToSpeechEngine实例对象后,实例化SpeakParams对象、SpeakListener对象,并传入待合成及播报的文本originalText,调用speak接口进行播报。

    TypeScript 复制代码
    // 设置speak的回调信息
    let speakListener: textToSpeech.SpeakListener = {
    // 开始播报回调
    onStart(requestId: string, response: textToSpeech.StartResponse) {
    console.info(`onStart, requestId: ${requestId} response: ${JSON.stringify(response)}`);
    },
    // 合成完成及播报完成回调
    onComplete(requestId: string, response: textToSpeech.CompleteResponse) {
    console.info(`onComplete, requestId: ${requestId} response: ${JSON.stringify(response)}`);
    },
    // 停止播报回调
    onStop(requestId: string, response: textToSpeech.StopResponse) {
    console.info(`onStop, requestId: ${requestId} response: ${JSON.stringify(response)}`);
    },
    // 返回音频流
    onData(requestId: string, audio: ArrayBuffer, response: textToSpeech.SynthesisResponse) {
    console.info(`onData, requestId: ${requestId} sequence: ${JSON.stringify(response)} audio: ${JSON.stringify(audio)}`);
    },
    // 错误回调
    onError(requestId: string, errorCode: number, errorMessage: string) {
    console.error(`onError, requestId: ${requestId} errorCode: ${errorCode} errorMessage: ${errorMessage}`);
    }
    };
    // 设置回调
    ttsEngine.setListener(speakListener);
    let originalText: string = '你好,鸿蒙';
    // 设置播报相关参数
    let extraParam: Record<string, Object> = {"queueMode": 0, "speed": 1, "volume": 2, "pitch": 1, "languageContext": 'zh-CN',
    "audioType": "pcm", "soundChannel": 3, "playType": 1 };
    let speakParams: textToSpeech.SpeakParams = {
    requestId: '123456', // requestId在同一实例内仅能用一次,请勿重复设置
    extraParams: extraParam
    };
    // 调用播报方法
    ttsEngine.speak(originalText, speakParams);
  4. (可选)当需要停止合成及播报时,可调用stop接口。

    TypeScript 复制代码
    ttsEngine.stop();
  5. (可选)当需要查询文本转语音服务是否处于忙碌状态时,可调用isBusy接口。

    TypeScript 复制代码
    ttsEngine.isBusy();
  6. (可选)当需要查询支持的语种音色信息时,可调用listVoices接口。

    listVoices接口提供了两种调用形式,当前以其中一种作为示例,其他方式可参考API参考

    TypeScript 复制代码
    // 在组件中声明并初始化字符串voiceInfo
    @State voiceInfo: string = "";
    
    // 设置查询相关参数
    let voicesQuery: textToSpeech.VoiceQuery = {
    requestId: '12345678', // requestId在同一实例内仅能用一次,请勿重复设置
    online: 1
    };
    // 调用listVoices方法,以callback返回
    ttsEngine.listVoices(voicesQuery, (err: BusinessError, voiceInfo: textToSpeech.VoiceInfo[]) => {
    if (!err) {
    // 接收目前支持的语种音色等信息
    this.voiceInfo = JSON.stringify(voiceInfo);
    console.info(`Succeeded in listing voices, voiceInfo is ${this.voiceInfo}`);
    } else {
    console.error(`Failed to list voices. Code: ${err.code}, message: ${err.message}`);
    }
    });

开发实例

点击按钮,播报一段文本。

TypeScript 复制代码
import { textToSpeech } from '@kit.CoreSpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';

let ttsEngine: textToSpeech.TextToSpeechEngine;
@Entry
@Component
struct Index {
@State createCount: number = 0;
@State result: boolean = false;
@State voiceInfo: string = "";
@State text: string = "";
@State textContent: string = "";
@State utteranceId: string = "123456";
@State originalText: string = "\n\t\t古人学问无遗力,少壮工夫老始成;\n\t\t" +
"纸上得来终觉浅,绝知此事要躬行。\n\t\t";
@State illegalText: string = "";

build() {
Column() {
Scroll() {
Column() {
TextArea({ placeholder: 'Please enter tts original text', text: `${this.originalText}` })
.margin(20)
.focusable(false)
.border({ width: 5, color: 0x317AE7, radius: 10, style: BorderStyle.Dotted })
.onChange((value: string) => {
this.originalText = value;
console.info(`original text: ${this.originalText}`);
})

Button() {
Text("CreateEngineByCallback")
.fontColor(Color.White)
.fontSize(20)
}
.type(ButtonType.Capsule)
.backgroundColor("#0x317AE7")
.width("80%")
.height(50)
.margin(10)
.onClick(() => {
this.createCount++;
console.info(`CreateTtsEngine:createCount:${this.createCount}`);
this.createByCallback();
})

Button() {
Text("speak")
.fontColor(Color.White)
.fontSize(20)
}
.type(ButtonType.Capsule)
.backgroundColor("#0x317AE7")
.width("80%")
.height(50)
.margin(10)
.onClick(() => {
this.createCount++;
this.speak();
})

Button() {
Text("listVoicesCallback")
.fontColor(Color.White)
.fontSize(20)
}
.type(ButtonType.Capsule)
.backgroundColor("#0x317AE7")
.width("80%")
.height(50)
.margin(10)
.onClick(() => {
this.listVoicesCallback();
})

Button() {
Text("stop")
.fontColor(Color.White)
.fontSize(20)
}
.type(ButtonType.Capsule)
.backgroundColor("#0x317AE7")
.width("80%")
.height(50)
.margin(10)
.onClick(() => {
// 停止播报
console.info("Stop button clicked.");
ttsEngine.stop();
})

Button() {
Text("isBusy")
.fontColor(Color.White)
.fontSize(20)
}
.type(ButtonType.Capsule)
.backgroundColor("#0x317AE7")
.width("80%")
.height(50)
.margin(10)
.onClick(() => {
// 查询播报状态
let isBusy = ttsEngine.isBusy();
console.info(`isBusy: ${isBusy}`);
})

Button() {
Text("shutdown")
.fontColor(Color.White)
.fontSize(20)
}
.type(ButtonType.Capsule)
.backgroundColor("#0x317AA7")
.width("80%")
.height(50)
.margin(10)
.onClick(() => {
// 释放引擎
ttsEngine.shutdown();
})
}
.layoutWeight(1)
}
.width('100%')
.height('100%')
}
}

// 创建引擎,通过callback形式返回
// 当引擎不存在、引擎资源不存在、初始化超时,返回错误码1003400005,引擎创建失败
private createByCallback() {
// 设置创建引擎参数
let extraParam: Record<string, Object> = {"style": 'interaction-broadcast', "locate": 'CN', "name": 'EngineName'};
let initParamsInfo: textToSpeech.CreateEngineParams = {
language: 'zh-CN',
person: 0,
online: 1,
extraParams: extraParam
};

// 调用createEngine方法
textToSpeech.createEngine(initParamsInfo, (err: BusinessError, textToSpeechEngine: textToSpeech.TextToSpeechEngine) => {
if (!err) {
console.info('Succeeded in creating engine.');
// 接收创建引擎的实例
ttsEngine = textToSpeechEngine;
} else {
// 创建引擎失败时返回错误码1003400005,可能原因:引擎不存在、资源不存在、创建引擎超时
console.error(`Failed to create engine. Code: ${err.code}, message: ${err.message}.`);
}
});
};

// 调用speak播报方法
// 未初始化引擎时调用speak方法,返回错误码1003400007,合成及播报失败
private speak() {
let speakListener: textToSpeech.SpeakListener = {
// 开始播报回调
onStart(requestId: string, response: textToSpeech.StartResponse) {
console.info(`onStart, requestId: ${requestId} response: ${JSON.stringify(response)}`);
},
// 完成播报回调
onComplete(requestId: string, response: textToSpeech.CompleteResponse) {
console.info(`onComplete, requestId: ${requestId} response: ${JSON.stringify(response)}`);
},
// 停止播报完成回调,调用stop方法并完成时会触发此回调
onStop(requestId: string, response: textToSpeech.StopResponse) {
console.info(`onStop, requestId: ${requestId} response: ${JSON.stringify(response)}`);
},
// 返回音频流
onData(requestId: string, audio: ArrayBuffer, response: textToSpeech.SynthesisResponse) {
console.info(`onData, requestId: ${requestId} sequence: ${JSON.stringify(response)} audio: ${JSON.stringify(audio)}`);
},
// 错误回调,播报过程发生错误时触发此回调
// 未创建引擎时调用speak方法时返回错误码1003400007,合成及播报失败
// 连续调用两次speak,第二次speak会返回错误码1003400006,服务正忙碌
onError(requestId: string, errorCode: number, errorMessage: string) {
console.error(`onError, requestId: ${requestId} errorCode: ${errorCode} errorMessage: ${errorMessage}`);
}
};
// 设置回调
ttsEngine.setListener(speakListener);
// 设置播报相关参数
let extraParam: Record<string, Object> = {"queueMode": 0, "speed": 1, "volume": 2, "pitch": 1, "languageContext": 'zh-CN', "audioType": "pcm", "soundChannel": 3, "playType":1}
let speakParams: textToSpeech.SpeakParams = {
requestId: '123456-a', // requestId在同一实例内仅能用一次,请勿重复设置
extraParams: extraParam
};
// 调用speak播报方法
ttsEngine.speak(this.originalText, speakParams);
};

// 查询语种音色信息,以callback形式返回
private listVoicesCallback() {
// 设置查询相关参数
let voicesQuery: textToSpeech.VoiceQuery = {
requestId: '123456-b', // requestId在同一实例内仅能用一次,请勿重复设置
online: 1
};

// 调用listVoices方法,以callback返回语种音色查询结果
ttsEngine.listVoices(voicesQuery, (err: BusinessError, voiceInfo: textToSpeech.VoiceInfo[]) => {
if (!err) {
// 接收目前支持的语种音色等信息
this.voiceInfo = JSON.stringify(voiceInfo);
console.info(`Succeeded in listing voices, voiceInfo is ${voiceInfo}`);
} else {
console.error(`Failed to list voices. Code: ${err.code}, message: ${err.message}`);
}
});
};
}
相关推荐
UMS攸信技术42 分钟前
汽车电子行业数字化转型的实践与探索——以盈趣汽车电子为例
人工智能·汽车
ws2019071 小时前
聚焦汽车智能化与电动化︱AUTO TECH 2025 华南展,以展带会,已全面启动,与您相约11月广州!
大数据·人工智能·汽车
堇舟2 小时前
斯皮尔曼相关(Spearman correlation)系数
人工智能·算法·机器学习
爱写代码的小朋友2 小时前
使用 OpenCV 进行人脸检测
人工智能·opencv·计算机视觉
Cici_ovo3 小时前
摄像头点击器常见问题——摄像头视窗打开慢
人工智能·单片机·嵌入式硬件·物联网·计算机视觉·硬件工程
QQ39575332373 小时前
中阳智能交易系统:创新金融科技赋能投资新时代
人工智能·金融
这个男人是小帅3 小时前
【图神经网络】 AM-GCN论文精讲(全网最细致篇)
人工智能·pytorch·深度学习·神经网络·分类
放松吃羊肉4 小时前
【约束优化】一次搞定拉格朗日,对偶问题,弱对偶定理,Slater条件和KKT条件
人工智能·机器学习·支持向量机·对偶问题·约束优化·拉格朗日·kkt
MJ绘画中文版4 小时前
灵动AI:艺术与科技的融合
人工智能·ai·ai视频
zyhomepage4 小时前
科技的成就(六十四)
开发语言·人工智能·科技·算法·内容运营