完整项目地址:gitee.com/Tziq/withou…
一、前言
上篇文章我们大概讲了一下,使用Springboot搭配阿里云小蜜实现文本机器人的方案。上一篇地址:juejin.cn/post/687488…。
本章会提供智能语音机器人的解决方案及集成方案。
二、解决方案
问题:
想实现集成智能语音机器人的话,主要有3个痛点。
- 如何把麦克风的字节流转换为文本
- 拿到用户文本之后,如何分析用户的意图并给出对应答案
- 拿到答案之后,如何把对应答案转为语音
解决:
- 获取麦克风的字节流可以使用现在主流的语音转写功能(ASR)
- 拿到文本之后使用自然语言理解能力(NLU),进行解析用户意图。
- 拿到答案之后,使用文本转语音功能(TTS)。
所以一直知道如何解决的话,就选择使用哪家厂商的能力,我这边主要是选择阿里云的AI能力来实现咱们的智能语音机器人
三、技术选型及时序图
后台
- springboot
- WebSocket
- 阿里asr(实时)
- 阿里tts
- 阿里云小蜜
前台
- HZRecorder2.js(h5录音功能,解码js)
- Socket.io(集成websocket的js)
时序图:
四、集成阿里asr与tts
asr地址:help.aliyun.com/document_de…
tts地址:help.aliyun.com/document_de…
-
在maven项目引入所需SDK
<dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.5.2</version> </dependency> <dependency> <groupId>com.alibaba.nls</groupId> <artifactId>nls-sdk-tts</artifactId> <version>2.1.6</version> </dependency> <dependency> <groupId>com.alibaba.nls</groupId> <artifactId>nls-sdk-transcriber</artifactId> <version>2.1.6</version> </dependency>
-
集成asr代码到项目中
public void process() { SpeechSynthesizer synthesizer = null; try { //创建实例,建立连接。 synthesizer = new SpeechSynthesizer(client, getSynthesizerListener()); synthesizer.setAppKey(appKey); //设置返回音频的编码格式 synthesizer.setFormat(OutputFormatEnum.WAV); //设置返回音频的采样率 synthesizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K); //发音人 synthesizer.setVoice("siyue"); //语调,范围是-500~500,可选,默认是0。 synthesizer.setPitchRate(100); //语速,范围是-500~500,默认是0。 synthesizer.setSpeechRate(100); //设置用于语音合成的文本 synthesizer.setText("欢迎使用阿里巴巴智能语音合成服务,您可以说北京明天天气怎么样啊"); // 是否开启字幕功能(返回相应文本的时间戳),默认不开启,需要注意并非所有发音人都支持该参数。 synthesizer.addCustomedParam("enable_subtitle", false); //此方法将以上参数设置序列化为JSON格式发送给服务端,并等待服务端确认。 long start = System.currentTimeMillis(); synthesizer.start(); logger.info("tts start latency " + (System.currentTimeMillis() - start) + " ms"); SpeechSynthesizerDemo.startTime = System.currentTimeMillis(); //等待语音合成结束 synthesizer.waitForComplete(); logger.info("tts stop latency " + (System.currentTimeMillis() - start) + " ms"); } catch (Exception e) { e.printStackTrace(); } finally { //关闭连接 if (null != synthesizer) { synthesizer.close(); } } }
-
集成TTS方法到项目中
public void process() { SpeechSynthesizer synthesizer = null; try { //创建实例,建立连接。 synthesizer = new SpeechSynthesizer(client, getSynthesizerListener()); synthesizer.setAppKey(appKey); //设置返回音频的编码格式 synthesizer.setFormat(OutputFormatEnum.valueOf(ttsParam.getFormat())); //设置返回音频的采样率 synthesizer.setSampleRate(ttsParam.getSampleRate() == 8000 ? SampleRateEnum.SAMPLE_RATE_8K : SampleRateEnum.SAMPLE_RATE_16K); //发音人 // synthesizer.setVoice("Aixia"); synthesizer.setVoice(ttsParam.getVoice()); //语调,范围是-500~500,可选,默认是0。 synthesizer.setPitchRate(0); //语速,范围是-500~500,默认是0。 synthesizer.setSpeechRate(ttsParam.getSpeechRate()); //设置用于语音合成的文本 synthesizer.setText(ttsParam.getText()); // 是否开启字幕功能(返回相应文本的时间戳),默认不开启,需要注意并非所有发音人都支持该参数。 synthesizer.addCustomedParam("enable_subtitle", false); //此方法将以上参数设置序列化为JSON格式发送给服务端,并等待服务端确认。 long start = System.currentTimeMillis(); synthesizer.start(); log.info("tts start latency " + (System.currentTimeMillis() - start) + " ms"); SpeechSynthesizerUtil.startTime = System.currentTimeMillis(); //等待语音合成结束 synthesizer.waitForComplete(); log.info("tts stop latency " + (System.currentTimeMillis() - start) + " ms"); } catch (Exception e) { e.printStackTrace(); } finally { //关闭连接 if (null != synthesizer) { synthesizer.close(); } } }
五、集成websocket
因为是智能语音机器人考虑到需要频繁传输字节,所以使用长连接websocket来交互字节流。
websocket简介:
简单的说: 传统 http 通讯一次交互数据后就断开连接了,服务端没法主动向客户端推送信息。 而长连接的 websocket 解决了这一问题
-
在maven项目引入所需SDK
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
-
集成获取语音流方法
/** * 1.获取到前台传输来的语音流,并且储存到asr需要识别的文件中 * 2.WebScoketUtils.intentSwitchMap判断机器人是否可以说话 * 3.判断公共变量中使用存有TTS语音文件,如果存在的话,就转换为byte数组传输给前台 * @param message * @param session * @param uid * @param roleType * @throws IOException */ @OnMessage(maxMessageSize = 1024000) public void onMessage(byte[] message, Session session, @PathParam("uid") String uid, @PathParam("roleType") int roleType) throws IOException { String keyId = WebScoketUtils.getScoketKey(uid, roleType); writeFile(message, uid, roleType); try { if (null != WebScoketUtils.userMap.get(keyId) && !WebScoketUtils.userMap.get(keyId).equals("")) { if ("on".equals(WebScoketUtils.intentSwitchMap.get(keyId))) { log.info("机器人说话"); String filePath = WebScoketUtils.userMap.get(keyId); WebScoketUtils.userMap.remove(keyId); onMessageByte(keyId, filePath); WebScoketUtils.intentSwitchMap.put(keyId, "off"); } } } catch (Exception e) { e.printStackTrace(); } }
-
集成监听用户是否离开事件
@OnClose public void onClose(Session session, @PathParam("uid") String uid, @PathParam("roleType") int roleType) { remove(session, uid, roleType); }
六、前台集成HZRecorder2.js(录音插件)
-
使用recorder.js可以采集麦克风语音并且进行录音。
-
部分代码
//开始录音 this.start = function () { audioInput.connect(recorder); recorder.connect(context.destination); }
七、前台集成websocket进行实时传输语音字节
-
使用recorder.js采集麦克风录音,并且通过websocket协议传输到后台进行解析
-
部分代码
var wssurl = WS_SOCKET; var url = wssurl + 'audioMessage/' + obj.channelId + "/" + obj.roleType; console.log("url=" + url); socket = new WebSocket(url); socket.onopen = onChannelOpened; socket.onmessage = onChannelMessage; socket.onclose = onChannelClosed; socket.onerror = onChannelError;
八、使用h5自带的audioContext进行播放语音
-
直接通过audio标签播放音乐已经在主流浏览器中不被允许,所以考虑到一些兼容性的问题,故而使用audioContext进行播放,可以很好的兼容各大主流浏览器
-
部分代码
reader.onload = function(evt) { if (evt.target.readyState == FileReader.DONE) { var data = new Uint8Array(evt.target.result); audioContext.decodeAudioData(evt.target.result, function(buffer) { //解码成pcm流 var audioBufferSouceNode = audioContext.createBufferSource(); audioBufferSouceNode.buffer = buffer; audioBufferSouceNode.connect(audioContext.destination); audioBufferSouceNode.start(0); }, function(e) { // alert("Fail to decode the file."); }); } } reader.readAsArrayBuffer(data);
九、演示效果
十、结语
好了做完上述操作基本上语音识别就实现了,自己可以先打开html的录音录好后,运行java程序,就可以体验实时语音交互流程了。
好的,就这些了,希望能帮助到需要语音识别的你们。
如果有不懂的地方可以留言,谢谢。