app:http://fir.im/gval 这里面包含拨盘UI
开发平台:android studio
模拟一个 原始需求如下:
1) 在界面上,通过声音提示用户讲话;
2) 将语音内容转换为文字并存储。
例如:用户启动应用后,点击按键后,应用问“请问你在哪个国家”,5s后如果用户不回答,再问“请问你在哪个国家”,如果5s后,用户仍然不回答,不再询问。如果5s内用户回答,则将内容记录下来,记录完毕后,再继续问“请问你在那个城市”。
这个真的是,困难很多,虽然现在做好了,但是我有很多要分享的
一开始我是使用百度的接口,百度有提供demo,这个就不说了,下载了语音识别和语音合成,语音合成的没什么声音,虽然声音不太好听,但是语音识别的问题太多了,这两个肯定不是一拨人写的,从代码风格就可以看出来,去他的官网找资料,反正我是没找过什么有用的,语音识别那个,给了一个app,那个跟给的源码都不是一个,简直醉了,把提供的源码搞到项目里,项目可以运行,或许是androidstudio的缘故,要么就是源码有问题,反正问题很多,
对了这里要说一个androidstudio导jar文件和so 文件的,jar文件就拷贝到lib下,右击倒数2-3那边就好了,so文件需要新建一个jniLibs的文件夹,so文件包括文件夹拷贝到这个目录下,使用百度的需要把两个jar文件都加入进来
使用百度的语音识别,我在小米5+的系统上,麦克风一直起不来,该给的权限都给了,可能是系统的问题,这个浪费了我两天时间,不得不说,百度的源码做的不太方便,不方便使用和阅读,
从百度的语音合成上学到一个首页的布局
public class MainActivity extends PreferenceActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.addPreferencesFromResource(R.xml.category); } }然后xml文件:
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:key="root_screen"> <Preference android:title="Version 1.2.6" > </Preference> <Preference android:title="拨盘UI" > <intent android:targetPackage="android.cl.com.tts" android:targetClass="android.cl.com.tts.UIActivity" > </intent> </Preference> <Preference android:title="Only 小Q speaking" > <intent android:targetPackage="android.cl.com.tts" android:targetClass="android.cl.com.tts.BaiDuWordToVoiceActivity" > </intent> </Preference> <Preference android:title="Only you speaking" > <intent android:targetPackage="android.cl.com.tts" android:targetClass="android.cl.com.tts.IFVoiceToWordActivity" > </intent> </Preference> <Preference android:title="小Q 语音交互" > <intent android:targetPackage="android.cl.com.tts" android:targetClass="android.cl.com.tts.TTSActivity" > </intent> </Preference> </PreferenceScreen>不需要写按钮啦,监听啦,个人很喜欢,适合做界面跳转页
然后就找到讯飞的语音,这个怎么说呢,写的很好,怪我不是使用,反正我把这两个功能的源码下载下来,或许是我使用的工具
android studio的问题,反正源码我 出现21001错误码的分析,没时间解决,不知道什么问题
看这位前辈的 看着很有用 http://blog.csdn.net/q610098308/article/details/46981715
后来跟同学要了一个讯飞以前的VoiceToWord源码
这个试了一下,写的简洁,但是功能都有,哈哈哈,一下子找到出路了,用百度的语音合成,用讯飞的语音识别
这边主要就是如何让机器说完了,然后用户说,机器记录显示,机器再提示语音,同时需要判断是否要问同一个问题
我这些算法的代码都在一个文件里,就直接上代码:
package android.cl.com.tts; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.Toast; import com.iflytek.cloud.speech.RecognizerResult; import com.iflytek.cloud.speech.SpeechConstant; import com.iflytek.cloud.speech.SpeechError; import com.iflytek.cloud.speech.SpeechListener; import com.iflytek.cloud.speech.SpeechUser; import com.iflytek.cloud.ui.RecognizerDialog; import com.iflytek.cloud.ui.RecognizerDialogListener; import java.util.ArrayList; import java.util.List; import BaiDuUtil.SpeechUtil; import IFVoiceUtil.JsonParser; public class TTSActivity extends AppCompatActivity { private SpeechUtil speechUtil; private Button btn; //处理结果 private ListView listView; private List<String> ls; private String[] params = {"你好,请问你在哪个国家", "请问你在哪个城市", "请问你的新年愿望是什么", "祝你新的一年心想事成"}; private int i = 0; //记录params 下标 private int count = 2;//一个问题询问两次 //识别窗口 private RecognizerDialog iatDialog; private boolean listen = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.content_tts); init(); btn = (Button) findViewById(R.id.btn_tts); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // //voice.GetWordFromVoice();这个方法必须要所在的函数全部执行完界面才会出现, //所以更新界面的方式放在监听器里去执行 // voice.GetWordFromVoice(); getMessage(); // if (i < params.length && count > 0) { //// SpeechUser.getUser().login(TTSActivity.this, null, null, "appid=5687e671", listener); // btn.setText("下一个问题"); // } else { // // restart(); //// btn.setClickable(false); // } } }); //飞科授权登陆 换成自己的Id SpeechUser.getUser().login(TTSActivity.this, null, null, "appid=000000", listener); } private void init() { speechUtil = new SpeechUtil(this); ls = new ArrayList<String>(); //获取xml文件中listView控件 listView = (ListView) findViewById(R.id.resultListView); //初始化听写Dialog,如果只使用有UI听写功能,无需创建SpeechRecognizer iatDialog = new RecognizerDialog(this); ls.add("Version 2.0"); ls.add("Just a minute..."); listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, ls)); } private void getMessage() { if (check()) { //不管回答与否,先进入下一个问题 这段代码OK // Log.e("msg", "speak"); // Toast.makeText(this, "小Q,已经在准备说话啦", Toast.LENGTH_LONG).show(); speechUtil.speak(params[i++]); Log.e("msg", "wait"); //不是最后一句 if(i < params.length){ //语音没有说完 while (!speechUtil.getStopmessage()) { Log.e("msg", "speak waiting..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } //啊啊啊啊啊啊啊啊啊,看来是这儿的问题,语音说完了,我忘了设置 setStopmessage(false); //提示语音需要联网,肯定没有本地主线程跑的快呀,while语句直接没有执行 //就是这儿的问题, // Toast.makeText(this, "请在屏幕变化时回答小Q的问题", Toast.LENGTH_LONG).show(); speechUtil.setStopmessage(false); Log.e("msg", "next"); //voice.GetWordFromVoice();这个方法必须要所在的函数全部执行完界面才会出现 listen = true; GetWordFromVoiceLocal(); btn.setText("下一个问题"); Log.e("msg", "GetVoice"); } // }else if(count == 0 || i == (params.length - 1) ){ }else { //2 次未回答 ,全部回答完 //提示最后一句 speechUtil.speak(params[i]); listen = false;//不需要监听 Log.e("msg",params[i] ); // Log.e("msg", "wait"); //语音没有说完 while (!speechUtil.getStopmessage()) { Log.e("msg", "speak waiting..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } speechUtil.setStopmessage(false); restart(); } } private void restart(){ btn.setText("重新交互"); i = 0; count = 2; ls.clear(); ls.add("Version 2.0"); ls.add("Just a minute..."); listView.setAdapter(new ArrayAdapter<String>(TTSActivity.this, android.R.layout.simple_list_item_1, ls)); } private Boolean check() { // 2次未回答 问题全部问完 if (count > 0 && i < (params.length-1)) { return true; }else{ return false; } } private void show(String message) { // if(checkMessage(message)){ ls.add(message); //然后为listView控件调用setAdapter方法,让数据显示在界面上。 listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, ls)); // } } //去掉 “ 。 ? !” 这些都是识别的结果,不知为何 private Boolean checkMessage(String message) { // if ("。".equals(message) ||"?".equals(message) || checkList(message) ) { if ("。".equals(message) ||"?".equals(message)||"!".equals(message) ) { return false; } return true; } //去掉 提问的问题 private Boolean checkList(String message) { for(int j = 0;j < params.length;j++){ if( params[j].equals(message)) return true; } return false; } public void GetWordFromVoiceLocal() { iatDialog.setParameter(SpeechConstant.DOMAIN, "iat"); //设置识别语言为中文 iatDialog.setParameter(SpeechConstant.LANGUAGE, "zh_cn"); //设置方言为普通话 iatDialog.setParameter(SpeechConstant.ACCENT, "mandarin"); //设置录音采样率为 iatDialog.setParameter(SpeechConstant.SAMPLE_RATE, "16000"); //清空Grammar_ID,防止识别后进行听写时Grammar_ID的干扰 iatDialog.setParameter(SpeechConstant.CLOUD_GRAMMAR, null); //设置监听对象 iatDialog.setListener(recognizerDialogListener); //开始识别 iatDialog.show(); } /** * 用户登录回调监听器. */ private SpeechListener listener = new SpeechListener() { @Override public void onData(byte[] arg0) { } @Override public void onCompleted(SpeechError error) { if (error != null) { // System.out.println("user login success"); Log.e("msg", "user login success"); } } @Override public void onEvent(int arg0, Bundle arg1) { } }; private RecognizerDialogListener recognizerDialogListener = new RecognizerDialogListener() { //自定义的结果回调函数,成功执行第一个方法,失败执行第二个方法 @Override public void onResult(RecognizerResult results, boolean isLast) { //想想问题是它总是在监听,没有提供类似监听停止的函数,所以我给它加一个判断 //语音没有播报完前不可以监听,试试吧,感觉可以实现 //不过加了 listen 可以成功把 “。 ? !”这些过滤掉,哈哈哈,也是有用的 if(listen) { String text = JsonParser.parseIatResult(results.getResultString()); Log.e("msg", text); if (i != params.length) { count = 2; show(text); } listen = false; } //取消监听对象 ,对话框的 kill,效果就是语音说完,对话框再出现 //下面的都不行,总是第一句问答没有问题,从第二句开始总是监听机器播放的 // iatDialog.setListener(null); // iatDialog.destroy(); // iatDialog.cancel(); // iatDialog.hide(); // iatDialog = null; // Log.e("msg", "iatDialog = null;"); // iatDialog.dismiss()不能用,一使用就强行退出了 // iatDialog.dismiss(); } /** * 识别回调错误. */ @Override public void onError(SpeechError error) { // TODO Auto-generated method stub int errorCoder = error.getErrorCode(); switch (errorCoder) { case 10118: Log.e("msg", "10118"); if (i <( params.length-1 )) { //第一个问题 if(i > 0){ i--; } count --; btn.setText("继续同一个问题"); } iatDialog.hide(); // iatDialog.setListener(null); // SpeechUser.getUser().logout(); break; case 10204: Log.e("msg", "10204"); iatDialog.hide(); // System.out.println("can't connect to internet"); break; default: break; } } }; }一开始忘了设置一个参数 speechUtil.setStopmessage(false);,导致第一问答没有问题,但是第二句提问时,直接就录音了,没有录上用户的语音,反而录上机器的播放的语音,这个问题很奇怪,昨天夜里搞到两点多,把基本的语音顺序播放,等待用户语音输入给弄好,虽然存在这个问题,但是毕竟前进了一大步嘛
昨天的思路是想把那个 RecognizerDialog 给kill 了
地下这几个参数我都试了,可是都达不到想要的效果,或者说直接没有用
//取消监听对象 ,对话框的 kill,效果就是语音说完,对话框再出现 //下面的都不行,总是第一句问答没有问题,从第二句开始总是监听机器播放的 // iatDialog.setListener(null); // iatDialog.destroy(); // iatDialog.cancel(); // iatDialog.hide(); // iatDialog = null; // Log.e("msg", "iatDialog = null;"); // iatDialog.dismiss()不能用,一使用就强行退出了 // iatDialog.dismiss();
发现 RecognizerDialogListener 一直在工作,找不到什么方法让其停止,除非kill Activity,
就想用一个变量判断一下是否接收监听消息
//想想问题是它总是在监听,没有提供类似监听停止的函数,所以我给它加一个判断 //语音没有播报完前不可以监听,试试吧,感觉可以实现 //不过加了 listen 可以成功把 “。 ? !”这些过滤掉,哈哈哈,也是有用的 if(listen) { String text = JsonParser.parseIatResult(results.getResultString()); Log.e("msg", text); if (i != params.length) { count = 2; show(text); } listen = false; }
后来调试了一下,发现在这里
<span style="white-space:pre"> </span>//语音没有说完 while (!speechUtil.getStopmessage()) { Log.e("msg", "speak waiting..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } //啊啊啊啊啊啊啊啊啊,看来是这儿的问题,语音说完了,我忘了设置 setStopmessage(false); //提示语音需要联网,肯定没有本地主线程跑的快呀,while语句直接没有执行 //就是这儿的问题, speechUtil.setStopmessage(false);上面代码最后一句,看一下是什么
//说话语音结束 private Boolean stopmessage = false; public Boolean getStopmessage() { return stopmessage; } public void setStopmessage(Boolean stopmessage) { this.stopmessage = stopmessage; }while 循环结束后,那个值是 true,表示已经讲完,下一次再判断时,while循环里直接被跳过了,因为条件不符合
这个问题解决了,感觉天都快亮了,轮流应答解决了,剩下的就是一下小问题了,算是代码的逻辑问题吧,怎么一步一步代码执行完,上面的代码都有注释
OK,基本算是完成了
有需要的可以自行下载源码扩展
为什么需要 1 分,首先申明我不是缺这一分,是担心有人瞎下载源码,用作其他用途,所以加了限制,但是又不会需要你太多分
如果你需要,但是有没有分,可以私信我,或者:[email protected],不一定及时回复哦!
附件:源码:不是一个项目,由于使用android studio,所以上传的是一个module,这里面包含拨盘UI
http://download.csdn.net/detail/i_do_can/9389208