- A+
前几篇都是介绍一些辅助功能,如新闻、H5游戏、段子趣图、原生小游戏,手电筒应用等,现在再来聊聊机器人陪聊主体功能--语音功能及其实现。

【小萌伴】中的语音功能使用的是百度语音sdk,包含语音输入、语音播放、语音转文字、文字转语音、声音变换、离线语音语义识别、语音唤醒等。
其中功能的主要可分为三部分:语音识别、语音合成、语音唤醒。(我用的sdk比较老了,下面代码也许已经不兼容新sdk,具体请参考 百度语音 官网)
ChatActivity实现了RecognitionListener及SpeechSynthesizerListener接口,这两个接口是语音识别与合成的监听。
初始化
语音识别和语音合成需要在进入Activity后执行初始化,在销毁时进行销毁。初始化如下,语音识别初始化没有封装,语音合成则用TtsUtils封装了一下。
1 2 3 4 5 |
mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(ChatActivity.<span class="hljs-keyword">this</span>, <span class="hljs-keyword">new</span> ComponentName(ChatActivity.<span class="hljs-keyword">this</span>, VoiceRecognitionService.class)); <span class="hljs-comment">// 注册监听器</span> mSpeechRecognizer.setRecognitionListener(ChatActivity.<span class="hljs-keyword">this</span>); mSpeechSynthesizer = TtsUtils.getSpeechSynthesizer(ChatActivity.<span class="hljs-keyword">this</span>, ChatActivity.<span class="hljs-keyword">this</span>, getVoice(mRobot)); |
语音输入及识别
对这一块,通过BdVoiceUtil类进行了封装,通过调用如下方法即可开启语音识别:
1 2 |
BdVoiceUtil.startASR(mSpeechRecognizer, mSpeechSynthesizer, <span class="hljs-literal">true</span>); |
在RecognitionListener的回调中获取语音识别的结果,包括实时(部分)转换及全量(整句话)转换为文字,在onResults或者onPartialResults中将转换的文字发送到机器人api,之后逻辑与正常机器人陪聊一致。
语音合成
将机器人返回的语音转换为文字,这一块也在BdVoiceUtil进行了一下封装:
1 2 |
<span class="hljs-selector-tag">BdVoiceUtil</span><span class="hljs-selector-class">.startTTS</span>(<span class="hljs-selector-tag">mSpeechSynthesizer</span>, <span class="hljs-selector-tag">msg</span><span class="hljs-selector-class">.getContent</span>()); |
通过以上代码即可以开始转换,将msg.getContent()转换为语音,可以通过SpeechSynthesizerListener监听转换是否成功,语音播放的进度等。
语音合成后,也可以通过setParam控制声音(0 (普通女声), 1 (普通男声), 2 (特别男声), 3 (情感男声), 4 (童声))等
1 2 |
<span class="hljs-selector-tag">mSpeechSynthesizer</span><span class="hljs-selector-class">.setParam</span>(<span class="hljs-selector-tag">SpeechSynthesizer</span><span class="hljs-selector-class">.PARAM_SPEAKER</span>, <span class="hljs-selector-tag">String</span><span class="hljs-selector-class">.valueOf</span>(<span class="hljs-selector-tag">getVoice</span>(<span class="hljs-selector-tag">robot</span>))); |
语音唤醒
语音唤醒功能在这里没有用到,主要是在后续介绍的“找手机”功能用到;包括语音唤醒、通过语音识别达到唤醒的目的、与微信等的语音输入冲突问题等,这些留到后续介绍“找手机”时再说。
BdVoiceUtil
对语音相关的简单封装,其实还有TtsUtils等,总的代码太多,这里就不贴了...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BdVoiceUtil</span> </span>{ <span class="hljs-comment">/** * 开始识别(会先停止SpeechSynthesizer) * <span class="hljs-doctag">@param</span> speechRecognizer * <span class="hljs-doctag">@param</span> mSpeechSynthesizer * <span class="hljs-doctag">@param</span> bindParams 是否需要提示音 */</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startASR</span><span class="hljs-params">(SpeechRecognizer speechRecognizer, SpeechSynthesizer mSpeechSynthesizer, <span class="hljs-keyword">boolean</span> bindParams)</span> </span>{ stopTTS(mSpeechSynthesizer); Intent intent = <span class="hljs-keyword">new</span> Intent(); bindParams(intent, bindParams); <span class="hljs-keyword">if</span>(speechRecognizer != <span class="hljs-keyword">null</span>) { speechRecognizer.startListening(intent); } } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">stopASR</span><span class="hljs-params">(SpeechRecognizer speechRecognizer)</span> </span>{ <span class="hljs-comment">// 说完了</span> <span class="hljs-keyword">if</span>(speechRecognizer != <span class="hljs-keyword">null</span>) { speechRecognizer.stopListening(); } } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">cancelASR</span><span class="hljs-params">(SpeechRecognizer speechRecognizer)</span> </span>{ <span class="hljs-comment">// 取消</span> <span class="hljs-keyword">if</span>(speechRecognizer != <span class="hljs-keyword">null</span>) { speechRecognizer.cancel(); } } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">destroyASR</span><span class="hljs-params">(SpeechRecognizer speechRecognizer)</span> </span>{ cancelASR(speechRecognizer); <span class="hljs-keyword">if</span>(speechRecognizer != <span class="hljs-keyword">null</span>) { speechRecognizer.destroy(); } } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">bindParams</span><span class="hljs-params">(Intent intent, <span class="hljs-keyword">boolean</span> hintSound)</span> </span>{ <span class="hljs-keyword">if</span>(hintSound) { <span class="hljs-comment">// 设置识别参数</span> intent.putExtra(TtsUtils.EXTRA_SOUND_START, R.raw.bdspeech_recognition_start); intent.putExtra(TtsUtils.EXTRA_SOUND_END, R.raw.bdspeech_speech_end); intent.putExtra(TtsUtils.EXTRA_SOUND_SUCCESS, R.raw.bdspeech_recognition_success); intent.putExtra(TtsUtils.EXTRA_SOUND_ERROR, R.raw.bdspeech_recognition_error); intent.putExtra(TtsUtils.EXTRA_SOUND_CANCEL, R.raw.bdspeech_recognition_cancel); } intent.putExtra(<span class="hljs-string">"sample"</span>, <span class="hljs-number">16000</span>); <span class="hljs-comment">// 离线仅支持16000采样率</span> intent.putExtra(<span class="hljs-string">"language"</span>, <span class="hljs-string">"cmn-Hans-CN"</span>); <span class="hljs-comment">// 离线仅支持中文普通话</span> intent.putExtra(<span class="hljs-string">"prop"</span>, <span class="hljs-number">20000</span>); <span class="hljs-comment">// 输入</span> <span class="hljs-comment">// 语音输入附加资源,value替换为资源文件实际路径</span> <span class="hljs-comment">// 离线包过大,暂不考虑支持 intent.putExtra("lm-res-file-path", "/path/to/s_2_InputMethod");</span> } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> EventManager <span class="hljs-title">initEventWakeUp</span><span class="hljs-params">(<span class="hljs-keyword">final</span> Activity context)</span> </span>{ <span class="hljs-comment">// 唤醒功能打开步骤</span> <span class="hljs-comment">// 1) 创建唤醒事件管理器</span> EventManager mWpEventManager = EventManagerFactory.create(context, <span class="hljs-string">"wp"</span>); <span class="hljs-comment">// 2) 注册唤醒事件监听器</span> mWpEventManager.registerListener(<span class="hljs-keyword">new</span> EventListener() { <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onEvent</span><span class="hljs-params">(String name, String params, <span class="hljs-keyword">byte</span>[] data, <span class="hljs-keyword">int</span> offset, <span class="hljs-keyword">int</span> length)</span> </span>{ <span class="hljs-keyword">try</span> { <span class="hljs-keyword">if</span>(params == <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">return</span>; } JSONObject json = <span class="hljs-keyword">new</span> JSONObject(params); <span class="hljs-keyword">if</span> (<span class="hljs-string">"wp.data"</span>.equals(name)) { <span class="hljs-comment">// 每次唤醒成功, 将会回调name=wp.data的时间, 被激活的唤醒词在params的word字段</span> String word = json.getString(<span class="hljs-string">"word"</span>); <span class="hljs-comment">// 唤醒词</span> WpEventManagerUtil.doEvent(context, word); <span class="hljs-keyword">if</span>(Logs.isDebug()) { Logs.logI(BdVoiceUtil.class.getSimpleName(), <span class="hljs-string">"百度语音唤醒"</span> + word); } } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-string">"wp.exit"</span>.equals(name)) { <span class="hljs-comment">// 唤醒已经停止</span> } } <span class="hljs-keyword">catch</span> (JSONException e) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> AndroidRuntimeException(e); } } }); <span class="hljs-keyword">return</span> mWpEventManager; } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> EventManager <span class="hljs-title">eventWakeUp</span><span class="hljs-params">(<span class="hljs-keyword">final</span> Activity context, EventManager mWpEventManager)</span> </span>{ <span class="hljs-keyword">if</span>(mWpEventManager == <span class="hljs-keyword">null</span>) { mWpEventManager = initEventWakeUp(context); } <span class="hljs-comment">// 3) 通知唤醒管理器, 启动唤醒功能</span> HashMap params = <span class="hljs-keyword">new</span> HashMap(); params.put(<span class="hljs-string">"kws-file"</span>, <span class="hljs-string">"assets:///WakeUp.bin"</span>); <span class="hljs-comment">// 设置唤醒资源, 唤醒资源请到 http://yuyin.baidu.com/wake#m4 来评估和导出</span> mWpEventManager.send(<span class="hljs-string">"wp.start"</span>, <span class="hljs-keyword">new</span> JSONObject(params).toString(), <span class="hljs-keyword">null</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>); <span class="hljs-keyword">return</span> mWpEventManager; } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">eventWekeUpStop</span><span class="hljs-params">(EventManager mWpEventManager)</span> </span>{ <span class="hljs-keyword">if</span>(mWpEventManager != <span class="hljs-keyword">null</span>) { <span class="hljs-comment">// 停止唤醒监听</span> mWpEventManager.send(<span class="hljs-string">"wp.stop"</span>, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>); } } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">stopTTS</span><span class="hljs-params">(SpeechSynthesizer mSpeechSynthesizer)</span> </span>{ <span class="hljs-keyword">if</span>(mSpeechSynthesizer != <span class="hljs-keyword">null</span>) { mSpeechSynthesizer.stop(); } } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">releaseTTS</span><span class="hljs-params">(SpeechSynthesizer mSpeechSynthesizer)</span> </span>{ stopTTS(mSpeechSynthesizer); <span class="hljs-keyword">if</span>(mSpeechSynthesizer != <span class="hljs-keyword">null</span>) { mSpeechSynthesizer.release(); } } <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startTTS</span><span class="hljs-params">(SpeechSynthesizer mSpeechSynthesizer, String text)</span> </span>{ stopTTS(mSpeechSynthesizer); <span class="hljs-keyword">if</span>(mSpeechSynthesizer != <span class="hljs-keyword">null</span>) { mSpeechSynthesizer.speak(text); } } } |
页面布局与一般输入框没大差别,这里就不多说了~~~
简书:ThinkinLiu 博客: IT老五

相关内容:
【小萌伴Android】相关文章目录
1.【小萌伴Android】思量再三,终于鼓起勇气开源~
2.【小萌伴Android】机器人陪聊模块分享
3.【小萌伴Android】新闻/H5游戏模块及广告过滤
4.【小萌伴Android】段子趣图模块及其实现 及 段子趣图数据爬取
5.【小萌伴Android】原生小游戏及其实现(一)2048
6.【小萌伴Android】原生小游戏及其实现(二)小鸟
7.【小萌伴Android】原生小游戏及其实现(三)飞机
8.【小萌伴Android】手电筒功能及其实现
9.【小萌伴Android】机器人陪聊--语音功能及其实现

来自外部的引用: 1