语音技术
语音技术目前支持 ASR 与 TTS 两种技术。
ASR,自动语音识别技术(Automatic Speech Recognition)是一种将人的语音转换为文本的技术, 支持实时短语音识别与长语音听写。 TTS,文字转语音技术(Text to Speech)是一种将文字转成语音的技术,可将上传的单句文本转成播报音频,包含了短音频生成和长音频生成能力。
使用前提条件
Section titled “使用前提条件”该接口底层依赖以下接口实现,开发者使用前需要在 manifest.json 中声明以下接口:
{ "name": "blueos.network.webSocket" }{ "name": "blueos.media.audio.mediaManager" }import speech from "@blueos.ai.speech"speech 接口总览
Section titled “speech 接口总览”| 接口名称 | 接口说明 |
|---|---|
| createAsr | 创建 ASR 语音识别服务实例 AsrInstance |
| createTts | 创建 TTS 文字转语音服务实例 TtsInstance |
AsrInstance 实例接口总览
Section titled “AsrInstance 实例接口总览”| 接口名称 | 接口说明 |
|---|---|
| start() | 开启语音识别服务 |
| send(data:ArrayBuffer | TypedArray) |
| finish() | 结束语音识别服务 |
| onMessage = (result:ShortModeResult | LongModeResult) =>{} |
| onStarted = () =>{} | 语音识别服务开启成功的回调。在服务开启成功后,语音识别服务将开始处理通过 send() 接口发送的数据 |
| onFinished = () =>{} | 语音识别服务结束回调。该回调可通过调用 finish()接口触发或者 endVadTime 超时自动触发 |
| onError = (data:string, code: number) =>{} | 语音识别服务异常回调 |
TtsInstance 实例接口总览
Section titled “TtsInstance 实例接口总览”| 接口名称 | 接口说明 |
|---|---|
| connect() | 连接文本转语音服务 |
| disconnect() | 断开文本转语音服务,并且释放网络资源与相关播放器资源,建议在应用销毁时调用。 |
| send(data:string) | 发送文本内容 |
| pauseAudio() | tts 实例服务暂停播放音频 |
| resumeAudio() | tts 实例服务恢复播放音频 |
| onMessage = (result:TtsResult) =>{} | 文本识别信息回调。通过 send()接口发送文本内容后,文本识别结果将通过该回调事件返回 |
| onConnected = () =>{} | 文字转语音服务连接成功的回调。在调用 connect() 连接服务成功后触发。服务连接成功后,文字转语音服务将开始处理通过 send() 接口发送的文本内容。 |
| onSpeakStarted() | 播放开始回调 |
| onSpeakPaused() | 播放暂停回调,调用了TtsInstance.pauseAudio()后会触发此回调 |
| onSpeakProgress(progress:string) | 播放进度信息回调。开始播放后,播放进度信息会通过该回调多次触发,直到播放完毕 |
| onSpeakFinished() | 播放完毕回调 |
| onSpeakResumed() | 恢复播放回调,调用了TtsInstance.resumeAudio()后会触发此回调 |
| onError = (data:string, code: number) =>{} | 文字转语音服务异常回调 |
createAsr(asrParams: AsrParams):AsrInstance
Section titled “createAsr(asrParams: AsrParams):AsrInstance”创建 ASR 实例
AsrParams 参数
Section titled “AsrParams 参数”| 属性 | 必填 | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
| auth | 是 | Auth | 无 | 请求的身份验证信息,确保请求来源合法 |
| model | 是 | AsrModel | AsrModel.ShortInput | 使用的 ASR 模型,支持短语音与长语句模型。 |
| config | 否 | AsrConfig | 无 | 初始化 ASR 客户端相关配置 |
| 属性 | 必填 | 类型 | 说明 |
|---|---|---|---|
| appId | 是 | string | 应用 appId |
| appKey | 是 | string | 应用 appKey |
注: appId & appKey,需要在 vivo 开发者平台 申请
AsrModel
Section titled “AsrModel”| 属性 | 说明 |
|---|---|
| ShortInputt | 短语音,输入法模型。 |
| ShortJovi | 短语音,语音助手模型 |
| LongListen | 长语句,听说模型。 |
| LongSubtitle | 长语句,字幕模型 |
- 短语音指单轮识别时长在 60s 之内。
- 长语句指单轮识别不限制时长。
AsrConfig
Section titled “AsrConfig”| 参数 | 类型 | 说明 | 必填 | 默认值 | 备注 |
|---|---|---|---|---|---|
| audioType | AudioType | 音频类型 | 否 | AudioType.Pcm | 使用的音频类型 |
| isWithPunctuation | boolean | 是否打开标点符号 | 否 | true | |
| endVadTime | number | 后端检测时间 | 否 | 1000 | 单位:毫秒, 仅实时短语音支持该参数 |
| isChinese2digital | boolean | 是否打开汉字转数字 | 否 | true | 仅实时短语音支持该参数 |
| lang | AsrLang | 语言 | 否 | AsrLang.Cn | 仅长语音听写支持该参数 |
AudioType
Section titled “AudioType”| 属性 | 说明 |
|---|---|
| Pcm | pcm 音频类型 |
| Opus | opus 音频类型 |
AsrLang
Section titled “AsrLang”| 属性 | 说明 |
|---|---|
| Cn | 中文 |
| En | 英文 |
AsrInstance.start()
Section titled “AsrInstance.start()”开启语音识别服务
AsrInstance.send(data:ArrayBuffer|TypedArray)
Section titled “AsrInstance.send(data:ArrayBuffer|TypedArray)”发送语音数据,语音数据建议分帧发送,每帧包含的语音时长是 40 毫秒,单句不超过 60s
AsrInstance.onMessage = (result:ShortModeResult|LongModeResult) =>{}
Section titled “AsrInstance.onMessage = (result:ShortModeResult|LongModeResult) =>{}”语音识别信息回调。通过 send()接口发送语音数据后,语音识别结果将通过该回调事件返回
ShortModeResult
Section titled “ShortModeResult”| 参数 | 类型 | 说明 |
|---|---|---|
| text | string | asr 识别结果 |
| resultId | number | 结果序列号 |
| reformation | number | asr 识别返回, 1 代表修正 0 代表追加 |
| isLast | boolean | 是否为本次会话最后一条结果 |
LongModeResult
Section titled “LongModeResult”| 参数 | 类型 | 说明 |
|---|---|---|
| code | ResultCodeEnum | 结果代码描述 |
| midPoint | string | 识别中间 midPoint 结果即一句话中间结果 |
| endPoint | string | 识别中间 rec 结果即完整一句话或者最后一句结果 |
| beginTime | number | 开始时间,单位毫秒 |
| endTime | number | 结束时间,单位毫秒 |
| speaker | number | 当有角色分离时返回 0 表示当前说话人, 非 0 表示角色 id,有角色变化 |
ResultCodeEnum
Section titled “ResultCodeEnum”| 属性 | 说明 |
|---|---|
| 0 | 表示本次返回为识别中间结果,即一句话的完整结果,整个过程就是一句话中间结果,一句话完整结果…结束 |
| 8 | 表示本次返回为识别中间 midPoint 结果,即一句话的中间结果。 |
| 9 | 表示为客户端发完语音数据后的最后一句,客户端可以断开链接。 |
举例说明 9、8 和 0 的区别:
Section titled “举例说明 9、8 和 0 的区别:”比如有两句话,一句是“今天天气怎么样”,下一句是“明天天气怎么样”, 第一次返回“今天”,就是 8 第二次返回“今天天气怎么样”,就是 0,表示一句话的结束 第三次返回“明天”,就是 8 第四次返回“明天天气怎么样”,就是 9 然后就结束了
AsrInstance.onStarted = () =>{}
Section titled “AsrInstance.onStarted = () =>{}”语音识别服务开启成功的回调。在服务开启成功后,语音识别服务将开始处理通过 send() 接口发送的数据
AsrInstance.onFinished = () =>{}
Section titled “AsrInstance.onFinished = () =>{}”语音识别服务结束回调。该回调可通过调用 finish()接口触发或者 endVadTime 超时自动触发
AsrInstance.onError = (data:string, code: number) =>{}
Section titled “AsrInstance.onError = (data:string, code: number) =>{}”语音识别服务异常回调
onError 回调参数定义
Section titled “onError 回调参数定义”| 属性 | 类型 | 说明 |
|---|---|---|
| data | string | 失败信息描述 |
| code | FailCodeEnum | 失败业务码 |
FailCodeEnum
Section titled “FailCodeEnum”| 属性 | 说明 |
|---|---|
| 10000 | 参数校验失败 |
| 10002 | 引擎服务异常 |
| 10003 | 获取中间识别结果失败 |
| 10004 | 获取最终识别结果失败 |
| 10005 | 解析引擎数据异常 |
| 10006 | 引擎内部错误 |
| 10007 | 请求 nlu 出错 |
| 10008 | 音频超长 |
| 50001 | 使用超量 |
createAsr 示例代码
Section titled “createAsr 示例代码”import media from "@blueos.media.audio.mediaManager";import speech from "@blueos.ai.speech";
/** * 录音状态 * @enum {number} */const AudioRecordState = { /** 录音中 */ record: 1, /** 录音结束 */ stop: 2,
ready: 3,};
export default { data: { result: "结果", auth: { appId: "xxx", appKey: "xxxx", }, asr: null, model: "", pagraph: "", audioRecorder: null, audioRecordState: AudioRecordState.stop, }, createAsr() { this.model = speech.AsrModel.ShortInput; const asr = speech.createAsr({ auth: this.auth, model: this.model, config: { audioType: speech.AudioType.Pcm, isWithPunctuation: true, endVadTime: 1000, isChinese2digital: false, }, }); const arrs = []; asr.onMessage = (data) => { console.log(`onmessage data :${JSON.stringify(data)}`); if (this.model === speech.AsrModel.LongListen || this.model === speech.AsrModel.LongSubtitle ) { if (data.midPoint) { this.result = data.midPoint; } else if (data.endPoint) { arrs.push(data.endPoint); this.pagraph = arrs.join("\n"); this.result = ""; } } else { this.result = data.text; } }; asr.onStarted = () => { console.log("onstarted"); this.audioRecordState = AudioRecordState.ready; }; this.asr = asr; }, async recordAudio() { console.log("recordAudio start");
if (this.audioRecordState == AudioRecordState.record) { console.log(`正在录音中,不允许重复录音`); return; } if (this.audioRecordState != AudioRecordState.ready) { console.log(`asr 接口还没ready ,请稍后`); return; }
const audioRecorder = media.createAudioRecord({ channelConfig: 1, sampleRateInHz: 16000, });
this.audioRecordState = AudioRecordState.record; audioRecorder.read({ callback: (buffer) => { this.asr.send(buffer); }, });
this.asr.onError = (data, code) => { console.log(`ux ws.onError data:${data};code:${code}`); this.audioRecordState = AudioRecordState.stop; audioRecorder.stop(); audioRecorder.release(); }; this.asr.onFinished = (reason) => { console.log(`ux onFinished ${reason}`); this.audioRecordState = AudioRecordState.stop; audioRecorder.stop(); audioRecorder.release(); };
this.audioRecorder = audioRecorder; }, startWebsocket() { this.asr.start(); }, releaseRecord() { if (this.audioRecorder) { this.audioRecorder.stop(); this.audioRecorder.release(); this.asr && this.asr.finish(); } this.audioRecordState = AudioRecordState.stop; },};createTts(auth:Auth,engineId:string,clientInfo:ClientInfo):TtsInstance
Section titled “createTts(auth:Auth,engineId:string,clientInfo:ClientInfo):TtsInstance”创建 TTS 文字转语音服务实例
| 属性 | 必填 | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
| auth | 是 | Auth | 无 | 请求的身份验证信息,确保请求来源合法 |
| model | 是 | TtsModel | TtsModel.LongDefault | TTS 音频生成模型,支持短音频模型与长音频生成模型 |
| config | 否 | TtsConfig | 无 | 初始化 TTS 客户端相关配置 |
| isNeedPlay | 否 | boolean | false | 设置是否让TTS实例来播放声音,如果不需要TTS实例播放声音,则可以通过回调方法onMessage中 TtsResult.audio 获得合成语音的PCM类型数据 |
| 属性 | 必填 | 类型 | 说明 |
|---|---|---|---|
| appId | 是 | string | 应用 appId |
| appKey | 是 | string | 应用 appKey |
注: appId & appKey,需要在 vivo 开发者平台 申请
TtsModel
Section titled “TtsModel”| 属性 | 说明 |
|---|---|
| ShortJovi | 短音频生成,适用于对话合成场景,比如语音助手的应用场景 |
| LongDefault | 长音频生成,适用于长文本合成场景,比如小说阅读,屏幕朗读 |
TtsConfig
Section titled “TtsConfig”| 参数 | 类型 | 说明 | 必填 | 默认值 |
|---|---|---|---|---|
| engineType | EngineType | 引擎类型, 支持中英文与优化效果 | 否 | EngineType.normal |
| audioType | AudioType | 音频类型 | 否 | AudioType.Pcm |
| sampleRate | number | 采样率。采样率越高,语音合成播放的效果越好,但是占用流量更多。可选值:[8000,16000,24000] | 否 | 24000 |
| speaker | ShortSpeaker | LongSpeaker | 语音合成的发音人 | 否 |
| speed | number | 语速,可选值:[0-100] | 否 | 50 |
| volume | number | 音量,可选值:[1-100] | 否 | 50 |
| isStream | boolean | 是否按照流式方式返回音频,假如设置为 false 则按标点分段返回 | 否 | true |
EngineType
Section titled “EngineType”| 属性 | 说明 |
|---|---|
| normal | 普通效果 |
| enZh | 中英文 |
| en | 英文 |
| optimized | 优化效果 |
AudioType
Section titled “AudioType”| 属性 | 说明 |
|---|---|
| Pcm | pcm 音频类型 |
| Opus | opus 音频类型 |
ShortSpeaker
Section titled “ShortSpeaker”| 属性 | 说明 |
|---|---|
| yiwen | 奕雯 |
| yunye | 云野-温柔 |
| wanqing | 婉清 |
| xiaofu | 晓芙-少女 |
| yigeChild | 小萌-女童 |
| yige | 依格 |
| yiyi | 依依 |
| xiaoming | 小茗 |
LongSpeaker
Section titled “LongSpeaker”| 属性 | 说明 |
|---|---|
| yiwen | 奕雯 |
| yunye | 云野-温柔 |
| yunyeNews | 云野-稳重 |
| yige | 依格-甜美 |
| yigeNews | 依格-稳重 |
| huaibin | 怀斌-浑厚 |
| zhaokun | 兆坤-成熟 |
| yaheng | 亚恒-磁性 |
| haiwei | 海蔚-大气 |
| qianqian | 倩倩-清甜 |
| enFemale | 英文女声 |
| xiaoyun | 晓云-稳重 |
TtsInstance.connect(data:string)
Section titled “TtsInstance.connect(data:string)”连接文本转语音服务
TtsInstance.disconnect()
Section titled “TtsInstance.disconnect()”断开文本转语音服务,并且释放网络资源与相关播放器资源。建议在应用销毁时调用。
TtsInstance.send(data:string)
Section titled “TtsInstance.send(data:string)”发送文本内容
TtsInstance.pauseAudio()
Section titled “TtsInstance.pauseAudio()”tts 实例服务暂停播放音频
TtsInstance.resumeAudio()
Section titled “TtsInstance.resumeAudio()”tts 实例服务恢复播放音频
TtsInstance.onConnected = () =>{}
Section titled “TtsInstance.onConnected = () =>{}”文字转语音服务连接成功的回调。在调用 connect() 连接服务成功后触发。服务连接成功后,文字转语音服务将开始处理通过 send() 接口发送的文本内容。
TtsInstance.onMessage = (result:TtsResult) =>{}
Section titled “TtsInstance.onMessage = (result:TtsResult) =>{}”文本识别信息回调。通过 send()接口发送文本内容后,文本识别结果将通过该回调事件返回
TtsResult
Section titled “TtsResult”| 参数 | 类型 | 说明 |
|---|---|---|
| audio | Unit8Array | 合成后的音频片段 |
| status | number | 当前音频流状态,0 表示开始合成(返回的第一帧数据),1 表示合成中,2 表示合成结束(返回的最后一帧数据) |
| progress | string | 合成进度,指当前合成文本的字符数-总的字符数。注:请注意合成是以句为单位切割的,若文本只有一句话,则每次返回结果是相同的。 |
| slice | int | 返回的第几帧数据 |
TtsInstance.onSpeakStarted = () =>{}
Section titled “TtsInstance.onSpeakStarted = () =>{}”播放开始回调
TtsInstance.onSpeakPaused = () =>{}
Section titled “TtsInstance.onSpeakPaused = () =>{}”播放暂停回调,调用了TtsInstance.pauseAudio()后会触发此回调
TtsInstance.onSpeakProgress = (progress:string) =>{}
Section titled “TtsInstance.onSpeakProgress = (progress:string) =>{}”播放进度信息回调。开始播放后,播放进度信息会通过该回调多次触发,直到播放完毕。
| 参数 | 类型 | 说明 |
|---|---|---|
| progress | string | 播放进度,指当前播放文本的字符数-总的字符数。注:请注意合成是以句为单位切割的,若文本只有一句话,则每次返回结果是相同的。 |
TtsInstance.onSpeakFinished = () =>{}
Section titled “TtsInstance.onSpeakFinished = () =>{}”播放完毕回调
TtsInstance.onSpeakResumed = () =>{}
Section titled “TtsInstance.onSpeakResumed = () =>{}”恢复播放回调,调用了TtsInstance.resumeAudio()后会触发此回调
TtsInstance.onError = (data:string, code: number) =>{}
Section titled “TtsInstance.onError = (data:string, code: number) =>{}”文字转语音服务异常回调
onError 回调参数定义
Section titled “onError 回调参数定义”| 属性 | 类型 | 说明 |
|---|---|---|
| data | string | 失败信息描述 |
| code | FailCodeEnum | 失败业务码 |
FailCodeEnum
Section titled “FailCodeEnum”| 属性 | 说明 |
|---|---|
| 10000 | 缺少请求参数或者签名错误 (http 协议返回,status=400) |
| 10001 | 升级到 websocket 协议失败(http 协议返回,status=400) |
| 10010 | 发送数据不是 json 格式 |
| 10011 | 发送文本时,缺少必要的参数 |
| 10012 | 发送文本时,签名错误 |
| 以下是逻辑层服务器错误 | |
| 10030 | 发送文本到引擎时错误 |
| 10031 | 获取 audio 数据发生错误 |
| 10032 | 无可用的引擎服务器 |
| 以下是引擎层服务器错误 | |
| 11001 | 负载过大,拒绝新的请求 |
| 11002 | 请求头协议错误 |
| 11003 | 设置合成文本的请求参数错误 |
| 11004 | 获取 andio 数据的请求参数错误 |
| 11005 | session 重复了 |
| 11006 | 获取数据时,找到不到 session |
| 11007 | 创建引擎错误 |
| 11008 | 向算法引擎获取数据时出错 |
| 11009 | opus 压缩出现问题 |
| 11010 | 输入的合成文本不合法 |
| 以下是语音服务层错误 | |
| 20000 | 语音正在合成中,不允许发送新的文字 |
| 20010 | 语音缓存已满 |
| 20030 | 语音服务已经断连 |
createTts 示例代码
Section titled “createTts 示例代码”import media from "@blueos.media.audio.mediaManager";import speech from "@blueos.ai.speech";
/** * 录音状态 * @enum {number} */const AudioRecordState = { /** 录音结束 */ init: 0, inited: 1, play: 1, stop: 2, release: 3,};
export default { data: { title: "欢迎体验 AI 服务", inputtext: "你好", auth: { appId: "您的appId", appKey: "您的appKey", }, tts: null, model: "", audioRecorder: null, audioRecordState: AudioRecordState.stop, },
createShort() { this.model = speech.TtsModel.ShortJovi; this.createTts(); }, createLong() { this.model = speech.TtsModel.LongDefault; this.createTts(); }, createTts() { const tts = speech.createTts({ auth: this.auth, model: this.model, config: { speaker: speech.ShortSpeaker.yige, sampleRate: 24000, isStream: false, }, }); tts.onMessage = (data) => { if (this.audioRecorder) { this.audioRecorder.write({ buffer: data.audio.buffer, }); } }; tts.onConnected = () => { logtool("onConnected"); this.modetext = "短语音实例已连接" };
this.tts = tts; }, async createAudio() { const audioRecorder = media.createAudioTrack({ sampleRateInHz: 24000, contentType: "speech", channelConfig: 1, audioFormat: 16, }); audioRecorder.onError = function () { logtool(`createAudioRecord error`); this.audioRecordState = AudioRecordState.stop; }; this.audioRecordState = AudioRecordState.inited; logtool(`createAudioRecord success`); this.audioRecorder = audioRecorder; }, connect() { this.tts && this.tts.connect() }, send() { this.tts && this.tts.send(this.inputtext); }, stop() { this.audioRecordState = AudioRecordState.stop this.audioRecorder && this.audioRecorder.stop(); }, release() { this.audioRecordState = AudioRecordState.release this.audioRecorder && this.audioRecorder.release(); }, play() { this.audioRecordState = AudioRecordState.play this.audioRecorder && this.audioRecorder.play(); }, releaseRecord() { if (this.audioRecorder) { this.audioRecorder.stop(); this.audioRecorder.release(); } },};