前言:此博客内容有,集成讯飞语音sdk,实现了语音唤醒,在线语音识别,离线语音识别,更新本地离线语音词典
集成讯飞语音SDK
注册账号下载SDK
百度搜索科大讯飞,打开网站,没有账号的根据操作步骤注册账号,注册完成之后,根据个人需求下载sdk,因为咱们要实现的内容有离线语音识别,语音唤醒,以及更新本地离线语音字典,所以在下载SDK之前需要添加唤醒词,添加完之后再去下载SDK
导入demo
导入demo,有可能会报错,怕大家不知道怎么解决,这里我就给大家详细介绍一下,会遇到的一些问题都是怎么解决的;
先导入demo
导入demo后我的会报着个错误
ERROR: Plugin with id 'com.android.application' not found.Open File
我们仔细观察会发现此项目结构中只有 Project 的build.grade 文件,没有Module 的 build.grade,所以我们在 Project 的build.gradle中添加如下代码,然后try again
buildscript {repositories {google()jcenter()}dependencies {//版本号请根据自己的gradle插件版本号自行更改classpath 'com.android.tools.build:gradle:3.2.0'}}
在这里需要注意一下,buildscript 、 android 和 dependencies 他们三个是同级的关系,如下图
同步完之后我的又报如下错误
ERROR: The minSdk version should not be declared in the android manifest file. You can move the version from the manifest to the defaultConfig in the build.gradle file.Remove minSdkVersion and sync projectAffected Modules: mscV5PlusDemoWARNING: The specified Android SDK Build Tools version (26.0.2) is ignored, as it is below the minimum supported version (28.0.2) for Android Gradle Plugin 3.2.0.Android SDK Build Tools 28.0.2 will be used.To suppress this warning, remove "buildToolsVersion '26.0.2'" from your build.gradle file, as each version of the Android Gradle Plugin now has a default version of the build tools.Remove Build Tools version and sync projectAffected Modules: mscV5PlusDemoWARNING: Configuration 'compile' is obsolete and has been replaced with 'implementation' and 'api'.It will be removed at the end of . For more information see: /r/tools/update-dependency-configurations.htmlAffected Modules: mscV5PlusDemo
第一个错误的意思是:minSdk版本不应该在android manifest文件中声明。您可以将版本从清单移动到构建中的defaultConfig。gradle文件。
那么我们就打开android manifest,找到minSdk,把他删除
第二个错误的意思是从构建版本中删除"buildToolsVersion ‘26.0.2’",那么就删除他
第三个错误的意思是配置’compile’已过时,已被’implementation’和’api’取代。
然后同步一下,运行程序,发现不能跑起来,在build下回找到,如下图一段代码,
在build.gradle里面添加google(),如下图
同步一下,在运行,就可以装到手机上了,但是这里还有一点需要修改,就是把demo里面的APPID替换成自己注册的APPID,别忘了替换呀!!!
配置项目
讲demo中libs拷贝到自己的lib里
然后再将assets里面的文件全部导入到项目中
在线语音识别
首先添加权限
<!-- 连接网络权限,用于执行云端语音能力 --><uses-permission android:name="android.permission.INTERNET" /> <!-- 获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 --><uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 读取网络信息状态 --><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 获取当前wifi状态 --><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /><uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.READ_CONTACTS" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_SETTINGS" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_CONTACTS" />
然后创建一个application,代码如下
public class SpeechApplication extends Application {@Overridepublic void onCreate() {// 5ef048e1 为在开放平台注册的APPID 注意没有空格,直接替换即可SpeechUtility.createUtility(SpeechApplication.this, "appid=5ef048e1");super.onCreate();}}
下面是MainActivity里面的代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {private static final String TAG = "yaya";private SpeechRecognizer mIat;// 语音听写对象private RecognizerDialog mIatDialog;// 语音听写UI// 用HashMap存储听写结果private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();private SharedPreferences mSharedPreferences;//缓存private String mEngineType = SpeechConstant.TYPE_CLOUD;// 引擎类型private String language = "zh_cn";//识别语言private TextView tvResult;//识别结果private Button btnStart;//开始识别private String resultType = "json";//结果内容数据格式@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tvResult = findViewById(R.id.tv_result);btnStart = findViewById(R.id.btn_start);btnStart.setOnClickListener(this);//实现点击监听initPermission();// 使用SpeechRecognizer对象,可根据回调消息自定义界面;mIat = SpeechRecognizer.createRecognizer(MainActivity.this, mInitListener);// 使用UI听写功能,请根据sdk文件目录下的notice.txt,放置布局文件和图片资源mIatDialog = new RecognizerDialog(MainActivity.this, mInitListener);mSharedPreferences = getSharedPreferences("ASR",Activity.MODE_PRIVATE);}@Overridepublic void onClick(View v) {if (null == mIat) {// 创建单例失败,与 21001 错误为同样原因,参考 /forum.php?mod=viewthread&tid=9688showMsg("创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化");return;}mIatResults.clear();//清除数据setParam(); // 设置参数mIatDialog.setListener(mRecognizerDialogListener);//设置监听mIatDialog.show();// 显示对话框}/*** android 6.0 以上需要动态申请权限*/private void initPermission() {String permissions[] = {Manifest.permission.RECORD_AUDIO,Manifest.permission.ACCESS_NETWORK_STATE,Manifest.permission.INTERNET,Manifest.permission.WRITE_EXTERNAL_STORAGE};ArrayList<String> toApplyList = new ArrayList<String>();for (String perm : permissions) {if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {toApplyList.add(perm);}}String tmpList[] = new String[toApplyList.size()];if (!toApplyList.isEmpty()) {ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);}}/*** 权限申请回调,可以作进一步处理** @param requestCode* @param permissions* @param grantResults*/@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {// 此处为android 6.0以上动态授权的回调,用户自行实现。}/*** 初始化监听器。*/private InitListener mInitListener = new InitListener() {@Overridepublic void onInit(int code) {Log.d(TAG, "SpeechRecognizer init() code = " + code);if (code != ErrorCode.SUCCESS) {showMsg("初始化失败,错误码:" + code + ",请点击网址/document/error-code查询解决方案");}}};/*** 听写UI监听器*/private RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() {public void onResult(RecognizerResult results, boolean isLast) {printResult(results);//结果数据解析}/*** 识别回调错误.*/public void onError(SpeechError error) {showMsg(error.getPlainDescription(true));}};/*** 提示消息** @param msg*/private void showMsg(String msg) {Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();}/*** 数据解析** @param results*/private void printResult(RecognizerResult results) {String text = JsonParser.parseIatResult(results.getResultString());String sn = null;// 读取json结果中的sn字段try {JSONObject resultJson = new JSONObject(results.getResultString());sn = resultJson.optString("sn");} catch (JSONException e) {e.printStackTrace();}mIatResults.put(sn, text);StringBuffer resultBuffer = new StringBuffer();for (String key : mIatResults.keySet()) {resultBuffer.append(mIatResults.get(key));}tvResult.setText(resultBuffer.toString());//听写结果显示if (resultBuffer.toString().equals("语音唤醒。")) {startActivity(new Intent(this, WeakUpActivity.class));} else if (resultBuffer.toString().equals("离线")) {startActivity(new Intent(this, OfflineActivity.class));}}/*** 参数设置** @return*/public void setParam() {// 清空参数mIat.setParameter(SpeechConstant.PARAMS, null);// 设置听写引擎mIat.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);// 设置返回结果格式mIat.setParameter(SpeechConstant.RESULT_TYPE, resultType);if (language.equals("zh_cn")) {String lag = mSharedPreferences.getString("iat_language_preference","mandarin");Log.e(TAG, "language:" + language);// 设置语言mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");// 设置语言区域mIat.setParameter(SpeechConstant.ACCENT, lag);} else {mIat.setParameter(SpeechConstant.LANGUAGE, language);}Log.e(TAG, "last language:" + mIat.getParameter(SpeechConstant.LANGUAGE));//此处用于设置dialog中不显示错误码信息//mIat.setParameter("view_tips_plain","false");// 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理mIat.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString("iat_vadbos_preference", "4000"));// 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音mIat.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString("iat_vadeos_preference", "1000"));// 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点mIat.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString("iat_punc_preference", "1"));// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限mIat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/iat.wav");}@Overrideprotected void onDestroy() {super.onDestroy();if (null != mIat) {// 退出时释放连接mIat.cancel();mIat.destroy();}}}
接下来是MainActivity的布局文件
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"tools:context=".MainActivity"><TextViewandroid:id="@+id/tv_result"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="识别到的内容"android:textColor="#000" /><Buttonandroid:id="@+id/btn_start"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:text="开始识别" /></LinearLayout>
好了运行起来就可以实现语音识别了
语音唤醒
代码如下
public class WeakUpActivity extends AppCompatActivity {//唤醒的阈值,就相当于门限值,当用户输入的语音的置信度大于这一个值的时候,才被认定为成功唤醒。private int curThresh = 1450;//是否持续唤醒private String keep_alive = "1";private String ivwNetMode = "0";// 语音唤醒对象private VoiceWakeuper mIvw;//存储唤醒词的IDprivate String wordID = "";// 唤醒结果内容private String resultString;private TextView tv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_weak_up);tv=(TextView)findViewById(R.id.tv);// 初始化唤醒对象mIvw = VoiceWakeuper.createWakeuper(this, null);//非空判断,防止因空指针使程序崩溃mIvw = VoiceWakeuper.getWakeuper();if(mIvw != null) {resultString="";tv.setText(resultString);// 清空参数mIvw.setParameter(SpeechConstant.PARAMS, null);// 唤醒门限值,根据资源携带的唤醒词个数按照“id:门限;id:门限”的格式传入mIvw.setParameter(SpeechConstant.IVW_THRESHOLD, "0:"+ curThresh);// 设置唤醒模式mIvw.setParameter(SpeechConstant.IVW_SST, "wakeup");// 设置持续进行唤醒mIvw.setParameter(SpeechConstant.KEEP_ALIVE, keep_alive);// 设置闭环优化网络模式mIvw.setParameter(SpeechConstant.IVW_NET_MODE, ivwNetMode);// 设置唤醒资源路径mIvw.setParameter(SpeechConstant.IVW_RES_PATH, getResource());// 设置唤醒录音保存路径,保存最近一分钟的音频mIvw.setParameter( SpeechConstant.IVW_AUDIO_PATH, Environment.getExternalStorageDirectory().getPath()+"/msc/ivw.wav" );mIvw.setParameter( SpeechConstant.AUDIO_FORMAT, "wav" );// 如有需要,设置 NOTIFY_RECORD_DATA 以实时通过 onEvent 返回录音音频流字节//mIvw.setParameter( SpeechConstant.NOTIFY_RECORD_DATA, "1" );// 启动唤醒mIvw.startListening(new MyWakeuperListener());}else{showTip("唤醒未初始化");}}public void showTip(String str){Toast.makeText(this,str, Toast.LENGTH_SHORT).show();}/*** 获取唤醒词功能* @return 返回文件位置*/private String getResource() {final String resPath = ResourceUtil.generateResourcePath(this,ResourceUtil.RESOURCE_TYPE.assets, "ivw/"+"a381fd1d"+".jet");return resPath;}/*** 唤醒词监听类* @author Administrator**/private class MyWakeuperListener implements WakeuperListener {//开始说话@Overridepublic void onBeginOfSpeech() {}//错误码返回@Overridepublic void onError(SpeechError arg0) {}@Overridepublic void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {}@Overridepublic void onVolumeChanged(int i) {}@Overridepublic void onResult(WakeuperResult result) {if (!"1".equalsIgnoreCase(keep_alive)) {//setRadioEnable(true);}try {String text = result.getResultString();JSONObject object;object = new JSONObject(text);StringBuffer buffer = new StringBuffer();buffer.append("【RAW】 " + text);buffer.append("\n");buffer.append("【操作类型】" + object.optString("sst"));buffer.append("\n");buffer.append("【唤醒词id】" + object.optString("id"));buffer.append("\n");buffer.append("【得分】" + object.optString("score"));buffer.append("\n");buffer.append("【前端点】" + object.optString("bos"));buffer.append("\n");buffer.append("【尾端点】" + object.optString("eos"));resultString = buffer.toString();} catch (JSONException e) {e.printStackTrace();}tv.setText(resultString);}}}
对应的布局文件
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".WeakUpActivity"><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="识别到的内容"android:textColor="#000" /></LinearLayout>
离线语音识别并更新本地词典命令词
在这里需要注意一下啊,命令词的更新词典是更新的槽,主要是为了方便想通讯录的录入,不支持大批量更新词典的,建议在设置命令词的时候就包含完整命令词即可。
代码如下
public class OfflineActivity extends AppCompatActivity {private final static String TAG = "yaya";private Toast mToast;// 语音识别对象private SpeechRecognizer mAsr;// 缓存// private SharedPreferences mSharedPreferences;// 本地语法文件private String mLocalGrammar = null;// 本地词典private String mLocalLexicon = null;// 本地语法构建路径private String grmPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/msc/test";// 返回结果格式,支持:xml,jsonprivate String mResultType = "json";private String mEngineType = "cloud";private final String GRAMMAR_TYPE_BNF = "bnf";String mContent;// 语法、词典临时变量String action;// 语法、词典临时变量int ret = 0;// 函数调用返回值private TextView tvResult;private WebView webview;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_offline);initPermission();// 初始化识别对象mAsr = SpeechRecognizer.createRecognizer(this, mInitListener);if (mAsr == null) {Log.e(TAG, "masr is null");}// 初始化语法、命令词mLocalLexicon = "张海洋\n张三\n李四\n王五\n";mLocalGrammar = FucUtil.readFile(this, "call.bnf", "utf-8");// 获取联系人,本地更新词典时使用// ContactManager mgr = ContactManager.createManager(OfflineActivity.this, mContactListener);// mgr.asyncQueryAllContactsName();// mSharedPreferences = getSharedPreferences(getPackageName(), MODE_PRIVATE);mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);initView();}/*** android 6.0 以上需要动态申请权限*/private void initPermission() {String permissions[] = {Manifest.permission.RECORD_AUDIO,Manifest.permission.ACCESS_NETWORK_STATE,Manifest.permission.INTERNET,Manifest.permission.WRITE_EXTERNAL_STORAGE};ArrayList<String> toApplyList = new ArrayList<String>();for (String perm : permissions) {if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {toApplyList.add(perm);}}String tmpList[] = new String[toApplyList.size()];if (!toApplyList.isEmpty()) {ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);}}private void initView() {tvResult = findViewById(R.id.tv_result);tvResult.setText(mLocalLexicon);webview = findViewById(R.id.webview);//本地库语音mEngineType = SpeechConstant.TYPE_LOCAL;showTip("上传预设关键词/语法文件");// 本地-构建语法文件,生成语法id// if (mEngineType.equals(SpeechConstant.TYPE_LOCAL)) {// tvResult.setText(mLocalGrammar);mContent = new String(mLocalGrammar);action = new String(mLocalGrammar);mAsr.setParameter(SpeechConstant.PARAMS, null);// 设置文本编码格式mAsr.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");// 设置引擎类型mAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);// 设置语法构建路径mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);//使用8k音频的时候请解开注释//mAsr.setParameter(SpeechConstant.SAMPLE_RATE, "8000");// 设置资源路径mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());ret = mAsr.buildGrammar(GRAMMAR_TYPE_BNF, mContent, grammarListener);ret = mAsr.buildGrammar(GRAMMAR_TYPE_BNF, action, grammarListener);if (ret != ErrorCode.SUCCESS) {showTip("语法构建失败,错误码:" + ret + ",请点击网址/document/error-code查询解决方案");}}// }/*** 语音识别** @param view*/public void start(View view) {if (null == mAsr) {// 创建单例失败,与 21001 错误为同样原因,参考 /forum.php?mod=viewthread&tid=9688this.showTip("创建对象失败,请确认 libmsc.so 放置正确,\n 且有调用 createUtility 进行初始化");return;}if (!setParam()) {showTip("请先构建语法。");return;}ret = mAsr.startListening(mRecognizerListener);if (ret != ErrorCode.SUCCESS) {showTip("识别失败,错误码: " + ret + ",请点击网址/document/error-code查询解决方案");}}/*** 更新本地词库** @param view*/public void updata(View view) {tvResult.setText(mLocalLexicon);mContent = new String(mLocalLexicon);mAsr.setParameter(SpeechConstant.PARAMS, null);// 设置引擎类型mAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);// 设置资源路径mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());//使用8k音频的时候请解开注释//mAsr.setParameter(SpeechConstant.SAMPLE_RATE, "8000");// 设置语法构建路径mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);// 设置语法名称mAsr.setParameter(SpeechConstant.GRAMMAR_LIST, "call");// 设置文本编码格式mAsr.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");//更新词典ret = mAsr.updateLexicon("contact", mContent, lexiconListener);if (ret != ErrorCode.SUCCESS) {showTip("更新词典失败,错误码:" + ret + ",请点击网址/document/error-code查询解决方案");}}/*** 参数设置** @return*/public boolean setParam() {boolean result = false;// 清空参数mAsr.setParameter(SpeechConstant.PARAMS, null);// 设置识别引擎mAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);// 设置本地识别资源mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());// 设置语法构建路径mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);// 设置返回结果格式mAsr.setParameter(SpeechConstant.RESULT_TYPE, mResultType);// 设置本地识别使用语法idmAsr.setParameter(SpeechConstant.LOCAL_GRAMMAR, "call");// 设置识别的门限值mAsr.setParameter(SpeechConstant.MIXED_THRESHOLD, "30");// 使用8k音频的时候请解开注释//mAsr.setParameter(SpeechConstant.SAMPLE_RATE, "8000");result = true;// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限mAsr.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");mAsr.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/asr.wav");return result;}//获取识别资源路径private String getResourcePath() {StringBuffer tempBuffer = new StringBuffer();//识别通用资源tempBuffer.append(ResourceUtil.generateResourcePath(this, ResourceUtil.RESOURCE_TYPE.assets, "asr/common.jet"));return tempBuffer.toString();}/*** 初始化监听器。*/private InitListener mInitListener = new InitListener() {@Overridepublic void onInit(int code) {Log.d(TAG, "SpeechRecognizer init() code = " + code);if (code != ErrorCode.SUCCESS) {showTip("初始化失败,错误码:" + code + ",请点击网址/document/error-code查询解决方案");}}};/*** 更新词典监听器。*/private LexiconListener lexiconListener = new LexiconListener() {@Overridepublic void onLexiconUpdated(String lexiconId, SpeechError error) {if (error == null) {showTip("词典更新成功" + lexiconId);Log.d("yaya", "=mLocalGrammar=" + mLocalGrammar);} else {showTip("词典更新失败,错误码:" + error.getErrorCode());}}};/*** 构建语法监听器。*/private GrammarListener grammarListener = new GrammarListener() {@Overridepublic void onBuildFinish(String grammarId, SpeechError error) {if(error == null){showTip("语法构建成功:" + grammarId);}else{showTip("语法构建失败,错误码:" + error.getErrorCode());}}};/*** 获取联系人监听器。*/private ContactManager.ContactListener mContactListener = new ContactManager.ContactListener() {@Overridepublic void onContactQueryFinish(String contactInfos, boolean changeFlag) {//获取联系人mLocalLexicon = contactInfos;}};/*** 识别监听器。*/private RecognizerListener mRecognizerListener = new RecognizerListener() {@Overridepublic void onVolumeChanged(int volume, byte[] data) {showTip("当前正在说话,音量大小:" + volume);Log.d(TAG, "返回音频数据:" + data.length);}@Overridepublic void onResult(final RecognizerResult result, boolean isLast) {if (null != result && !TextUtils.isEmpty(result.getResultString())) {Log.d(TAG, "recognizer result:" + result.getResultString());String text = "";if (mResultType.equals("json")) {text = JsonParser.parseGrammarResult(result.getResultString(), mEngineType);} else if (mResultType.equals("xml")) {text = XmlParser.parseNluResult(result.getResultString());} else {text = result.getResultString();}// 显示tvResult.setText(text);loadWeb();} else {Log.d(TAG, "recognizer result : null");}}@Overridepublic void onEndOfSpeech() {// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入showTip("结束说话");}@Overridepublic void onBeginOfSpeech() {// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入showTip("开始说话");}@Overridepublic void onError(SpeechError error) {showTip("onError Code:" + error.getErrorCode());}@Overridepublic void onEvent(int eventType, int arg1, int arg2, Bundle obj) {}};private void showTip(final String str) {runOnUiThread(new Runnable() {@Overridepublic void run() {mToast.setText(str);mToast.show();}});}private void loadWeb() {String url = "/service/commandWord";// 允许从任何来源加载内容,即使起源是不安全的;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {webview.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);}webview.getSettings().setJavaScriptEnabled(true);webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);webview.getSettings().setSupportMultipleWindows(true);webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);//不加缓存webview.getSettings().setJavaScriptEnabled(true);webview.getSettings().setDomStorageEnabled(true);//设置适应Html5 //重点是这个设置webview.setWebViewClient(new WebViewClient());webview.setWebChromeClient(new WebChromeClient());webview.loadUrl(url);}}
xml文件
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".OfflineActivity"><TextViewandroid:id="@+id/tv_result"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="5dp"android:textSize="15sp" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="start"android:padding="20dp"android:text="开始听写" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="updata"android:padding="20dp"android:text="更新本地词库" /><WebViewandroid:id="@+id/webview"android:layout_width="match_parent"android:layout_height="match_parent"></WebView></LinearLayout>
其实到这里已经结束了,不过里面会涉及到其他几个实体类,讯飞给的sdk包里面就有,这里怕大家懒的去找,我就也贴出来吧
import android.os.Bundle;import android.preference.EditTextPreference;import android.preference.Preference;import android.preference.Preference.OnPreferenceChangeListener;import android.preference.PreferenceActivity;import android.view.Window;import com.gogowan.xfvoice.R;import com.gogowan.xfvoice.SettingTextWatcher;/*** 听写设置界面*/public class IatSettings extends PreferenceActivity implements OnPreferenceChangeListener {public static final String PREFER_NAME = "com.iflytek.setting";private EditTextPreference mVadbosPreference;private EditTextPreference mVadeosPreference;@SuppressWarnings("deprecation")public void onCreate(Bundle savedInstanceState) {requestWindowFeature(Window.FEATURE_NO_TITLE);super.onCreate(savedInstanceState);getPreferenceManager().setSharedPreferencesName(PREFER_NAME);addPreferencesFromResource(R.xml.iat_setting);mVadbosPreference = (EditTextPreference)findPreference("iat_vadbos_preference");mVadbosPreference.getEditText().addTextChangedListener(new SettingTextWatcher(IatSettings.this,mVadbosPreference,0,10000));mVadeosPreference = (EditTextPreference)findPreference("iat_vadeos_preference");mVadeosPreference.getEditText().addTextChangedListener(new SettingTextWatcher(IatSettings.this,mVadeosPreference,0,10000));}@Overridepublic boolean onPreferenceChange(Preference preference, Object newValue) {return true;}}
import android.content.Context;import java.io.IOException;import java.io.InputStream;/*** 功能性函数扩展类*/public class FucUtil {/*** 读取asset目录下文件。* @return content*/public static String readFile(Context mContext, String file, String code){int len = 0;byte []buf = null;String result = "";try {InputStream in = mContext.getAssets().open(file);len = in.available();buf = new byte[len];in.read(buf, 0, len);result = new String(buf,code);} catch (Exception e) {e.printStackTrace();}return result;}/*** 读取asset目录下音频文件。* * @return 二进制文件数据*/public static byte[] readAudioFile(Context context, String filename) {try {InputStream ins = context.getAssets().open(filename);byte[] data = new byte[ins.available()];ins.read(data);ins.close();return data;} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}}
import org.json.JSONArray;import org.json.JSONObject;import org.json.JSONTokener;/*** Json结果解析类*/public class JsonParser {public static String parseIatResult(String json) {StringBuffer ret = new StringBuffer();try {JSONTokener tokener = new JSONTokener(json);JSONObject joResult = new JSONObject(tokener);JSONArray words = joResult.getJSONArray("ws");for (int i = 0; i < words.length(); i++) {// 转写结果词,默认使用第一个结果JSONArray items = words.getJSONObject(i).getJSONArray("cw");JSONObject obj = items.getJSONObject(0);ret.append(obj.getString("w"));//如果需要多候选结果,解析数组其他字段//for(int j = 0; j < items.length(); j++)//{//JSONObject obj = items.getJSONObject(j);//ret.append(obj.getString("w"));//}}} catch (Exception e) {e.printStackTrace();} return ret.toString();}public static String parseGrammarResult(String json, String engType) {StringBuffer ret = new StringBuffer();try {JSONTokener tokener = new JSONTokener(json);JSONObject joResult = new JSONObject(tokener);JSONArray words = joResult.getJSONArray("ws");// 云端和本地结果分情况解析if ("cloud".equals(engType)) {for (int i = 0; i < words.length(); i++) {JSONArray items = words.getJSONObject(i).getJSONArray("cw");for(int j = 0; j < items.length(); j++){JSONObject obj = items.getJSONObject(j);if(obj.getString("w").contains("nomatch")){ret.append("没有匹配结果.");return ret.toString();}ret.append("【结果】" + obj.getString("w"));ret.append("【置信度】" + obj.getInt("sc"));ret.append("\n");}}} else if ("local".equals(engType)) {ret.append("【结果】");for (int i = 0; i < words.length(); i++) {JSONObject wsItem = words.getJSONObject(i);JSONArray items = wsItem.getJSONArray("cw");if ("<contact>".equals(wsItem.getString("slot"))) {// 可能会有多个联系人供选择,用中括号括起来,这些候选项具有相同的置信度ret.append("【");for(int j = 0; j < items.length(); j++){JSONObject obj = items.getJSONObject(j);if(obj.getString("w").contains("nomatch")){ret.append("没有匹配结果.");return ret.toString();}ret.append(obj.getString("w")).append("|");}ret.setCharAt(ret.length() - 1, '】');} else {//本地多候选按照置信度高低排序,一般选取第一个结果即可JSONObject obj = items.getJSONObject(0);if(obj.getString("w").contains("nomatch")){ret.append("没有匹配结果.");return ret.toString();}ret.append(obj.getString("w"));}}ret.append("【置信度】" + joResult.getInt("sc"));ret.append("\n");}} catch (Exception e) {e.printStackTrace();ret.append("没有匹配结果.");} return ret.toString();}public static String parseGrammarResult(String json) {StringBuffer ret = new StringBuffer();try {JSONTokener tokener = new JSONTokener(json);JSONObject joResult = new JSONObject(tokener);JSONArray words = joResult.getJSONArray("ws");for (int i = 0; i < words.length(); i++) {JSONArray items = words.getJSONObject(i).getJSONArray("cw");for(int j = 0; j < items.length(); j++){JSONObject obj = items.getJSONObject(j);if(obj.getString("w").contains("nomatch")){ret.append("没有匹配结果.");return ret.toString();}ret.append("【结果】" + obj.getString("w"));ret.append("【置信度】" + obj.getInt("sc"));ret.append("\n");}}} catch (Exception e) {e.printStackTrace();ret.append("没有匹配结果.");} return ret.toString();}public static String parseTransResult(String json, String key) {StringBuffer ret = new StringBuffer();try {JSONTokener tokener = new JSONTokener(json);JSONObject joResult = new JSONObject(tokener);String errorCode = joResult.optString("ret");if(!errorCode.equals("0")) {return joResult.optString("errmsg");}JSONObject transResult = joResult.optJSONObject("trans_result");ret.append(transResult.optString(key));} catch (Exception e) {e.printStackTrace();}return ret.toString();}}
mport android.content.Context;import android.preference.EditTextPreference;import android.text.Editable;import android.text.TextUtils;import android.text.TextWatcher;import android.widget.Toast;import java.util.regex.Pattern;/*** 输入框输入范围控制*/public class SettingTextWatcher implements TextWatcher {private int editStart ;private int editCount ;private EditTextPreference mEditTextPreference;int minValue;//最小值int maxValue;//最大值private Context mContext;public SettingTextWatcher(Context context, EditTextPreference e, int min, int max) {mContext = context;mEditTextPreference = e;minValue = min;maxValue = max;}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {//Log.e("demo", "onTextChanged start:"+start+" count:"+count+" before:"+before);editStart = start;editCount = count;}@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {//Log.e("demo", "beforeTextChanged start:"+start+" count:"+count+" after:"+after);}@Overridepublic void afterTextChanged(Editable s) {if (TextUtils.isEmpty(s)) {return;}String content = s.toString();//Log.e("demo", "content:"+content);if (isNumeric(content)) {int num = Integer.parseInt(content);if (num > maxValue || num < minValue) {s.delete(editStart, editStart+editCount);mEditTextPreference.getEditText().setText(s);Toast.makeText(mContext, "超出有效值范围", Toast.LENGTH_SHORT).show();}}else {s.delete(editStart, editStart+editCount);mEditTextPreference.getEditText().setText(s);Toast.makeText(mContext, "只能输入数字哦", Toast.LENGTH_SHORT).show();}}/*** 正则表达式-判断是否为数字*/public static boolean isNumeric(String str){Pattern pattern = pile("[0-9]*");return pattern.matcher(str).matches(); } }
import org.w3c.dom.Document;import org.w3c.dom.Element;import java.io.ByteArrayInputStream;import java.io.InputStream;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;/*** Xml结果解析类*/public class XmlParser {public static String parseNluResult(String xml){StringBuffer buffer = new StringBuffer();try{// DOM builderDocumentBuilder domBuilder = null;// DOM docDocument domDoc = null;// init DOMDocumentBuilderFactory domFact = DocumentBuilderFactory.newInstance();domBuilder = domFact.newDocumentBuilder();InputStream is = new ByteArrayInputStream(xml.getBytes());domDoc = domBuilder.parse(is);// 获取根节点Element root = (Element) domDoc.getDocumentElement();Element raw = (Element)root.getElementsByTagName("rawtext").item(0);buffer.append("【识别结果】" + raw.getFirstChild().getNodeValue());buffer.append("\n");Element e = (Element)root.getElementsByTagName("result").item(0);Element focus = (Element)e.getElementsByTagName("focus").item(0);buffer.append("【FOCUS】" + focus.getFirstChild().getNodeValue());buffer.append("\n");Element action = (Element)e.getElementsByTagName("action").item(0);Element operation = (Element)action.getElementsByTagName("operation").item(0);buffer.append("【ACTION】" + operation.getFirstChild().getNodeValue());buffer.append("\n");}catch(Exception e){e.printStackTrace();};buffer.append("【ALL】" + xml);return buffer.toString();}}
最后大家需要注意一下啊,如果你只是单单实现某一个功能,一定要记得添加动态权限,我这个有的添加了,有的activity没写上
好了,到这里,功能都已经实现了,代码也全部贴上了