这几天两个舍友都买了iPhone 6S,玩起了“Hey, Siri”,我依旧对我的Nexus 5喊着“OK,Google”。但种种原因,国内的“OK,Google”并不能展示出他的全部威力,于是上网搜索国内Android平台的语音助手,个人觉得评价最好的是讯飞的——灵犀语音助手。其实讯飞语音云平台早就注册过了,并下载了相应的SDK,只是没仔细研究。今天突然想好好学习一下,以方便以后集成到自己开发的APP中,也可以方便大家参考。开发工具:Android Studio。好了,废话就说这些。先来看效果图:

注册讯飞开放平台

首先在讯飞开放平台注册开发者账号,注册流程很简单,就不详细介绍。网址:http://www.xfyun.cn/。然后就是像各大平台一样的步骤:新建Android应用获取APPID—〉下载语音听写SDK。此处需要注意的是一定要新建自己的应用,别人的工程虽然能用,但不方便后续开发。

下载SDK

解压下载的SDK目录,里面有详细的教程,可自行参考。然后新建工程,将MSC.jar(放入到libs文件夹下)和libmsc.so(放入到新建的jniLibs文件夹下)导入到自己的工程中(只有在线的所有功能,没有提供离线服务。)另外,讯飞提供了两种语音识别接口,一种是后台进行语音检测,没有界面UI提示;另一种是带UI的接口,在录音、播放音频的时候,会有dialog弹出,并伴有相应的动画,相对来说用户交互体验很不错。如果使用带UI接口时,请将assets下文件拷贝到项目中。这里注意的是每个SDK下载的assets可能不一样(没有亲自测试过,因为recognize.xml打开乱码),一定要将自己下载的SDK里的assets目录拷贝到XXX/src/main文件夹下。工程目录如下:

在AndroidManifest.xml文件中添加权限:

<!-- 在工程 AndroidManifest.xml 文件中添加如下权限 -->
<!-- 连接网络权限,用于执行云端语音能力 -->
<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.CHANGE_NETWORK_STATE" />
<!-- 读取手机信息权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 读取联系人权限,上传联系人需要用到此权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- 外存储写入权限, 构建语法需要用到此权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

布局文件activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/content_rec"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="识别内容" />

    <Button
        android:id="@+id/bt_start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:onClick="startRec"
        android:text="开始" />

    <Button
        android:id="@+id/btn_read"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/bt_start"
        android:layout_alignLeft="@+id/bt_start"
        android:layout_alignParentRight="true"
        android:onClick="read"
        android:text="朗读" />

</RelativeLayout>

JsonParser.java 和 MainActivity.java

JsonParser.java

package com.xiaobailong24.xunfeiyun;

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) {
      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 parseLocalGrammarResult(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("\n");
            }
         }
         ret.append("【置信度】" + joResult.optInt("sc"));

      } catch (Exception e) {
         e.printStackTrace();
         ret.append("没有匹配结果.");
      }
      return ret.toString();
   }
}

MainActivity.java

package com.xiaobailong24.xunfeiyun;

import android.app.Activity;
import android.os.Environment;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.RecognizerListener;
import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechRecognizer;
import com.iflytek.cloud.SpeechSynthesizer;
import com.iflytek.cloud.SpeechUtility;
import com.iflytek.cloud.SynthesizerListener;
import com.iflytek.cloud.ui.RecognizerDialog;
import com.iflytek.cloud.ui.RecognizerDialogListener;


public class MainActivity extends Activity {

    private static String TAG = "MainActivity";
    // 函数调用返回值
    int ret = 0;
    // 语音听写对象
    private SpeechRecognizer mIat;
    // 语音听写UI
    private RecognizerDialog iatDialog;
    // 听写结果内容
    private EditText mResultText;
    // 语音合成对象
    private SpeechSynthesizer mTts;

    // 默认发音人
    private String voicer = "xiaoyan";

    // 缓冲进度
    private int mPercentForBuffering = 0;
    // 播放进度
    private int mPercentForPlaying = 0;

    private Toast mToast;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.e(TAG, "onCreate");
        super.onCreate(savedInstanceState);
//        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        // 用于验证应用的key,将XXXXXXXX改为你申请的APPID
        SpeechUtility.createUtility(MainActivity.this, SpeechConstant.APPID + "=XXXXXXXX");
        // 创建语音听写对象
        mIat = SpeechRecognizer.createRecognizer(this, mInitListener);
        // 初始化听写Dialog,如果只使用有UI听写功能,无需创建SpeechRecognizer
        // 创建语音听写UI
        iatDialog = new RecognizerDialog(MainActivity.this, mInitListener);
        // 创建语音合成对象
        mTts = SpeechSynthesizer.createSynthesizer(this, mInitListener);
        mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
        mResultText = ((EditText) findViewById(R.id.content_rec));
    }

    public void startRec(View view) {
        mResultText.setText(null);
        setParam();
        boolean isShowDialog = true;
        if (isShowDialog) {
            // 显示听写对话框
            iatDialog.setListener(recognizerDialogListener);
            iatDialog.show();
            // showTip("begin");
        } else {
            // 不显示听写对话框
            ret = mIat.startListening(recognizerListener);
            if (ret != ErrorCode.SUCCESS) {
                // showTip("听写失败,错误码:" + ret);
            } else {
                // showTip("begin");
            }
        }
    }

    public void read(View view) {
        String text = mResultText.getText().toString();
        // 设置参数
        setParam2();
        //朗读
        int code = mTts.startSpeaking(text, mTtsListener);
        if (code != ErrorCode.SUCCESS) {
            if (code == ErrorCode.ERROR_COMPONENT_NOT_INSTALLED) {
                // 未安装则跳转到提示安装页面
            } else {
                showTip("语音合成失败,错误码: " + code);
            }
        }
    }

    /**
     * 参数设置
     *
     * @param
     * @return
     */
    private void setParam2() {
        Log.e(TAG, "setParam2");
        // 设置合成
        mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
        // 设置发音人
        mTts.setParameter(SpeechConstant.VOICE_NAME, voicer);

        // 设置语速
        mTts.setParameter(SpeechConstant.SPEED, "50");

        // 设置音调
        mTts.setParameter(SpeechConstant.PITCH, "50");

        // 设置音量
        mTts.setParameter(SpeechConstant.VOLUME, "50");

        // 设置播放器音频流类型
        mTts.setParameter(SpeechConstant.STREAM_TYPE, "3");
    }

    public void setParam() {
        Log.e(TAG, "setParam");
        // 清空参数
        mIat.setParameter(SpeechConstant.PARAMS, null);
        // 设置听写引擎
        mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);

        // 设置语言
        mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
        // 设置语言区域
        mIat.setParameter(SpeechConstant.ACCENT, "mandarin");

        // 设置语音前端点
        mIat.setParameter(SpeechConstant.VAD_BOS, "4000");
        // 设置语音后端点
        mIat.setParameter(SpeechConstant.VAD_EOS, "1000");
        // 设置标点符号 1为有标点 0为没标点
        mIat.setParameter(SpeechConstant.ASR_PTT, "0");
        // 设置音频保存路径
        mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH,
                Environment.getExternalStorageDirectory()
                        + "/xiaobailong24/xunfeiyun");
    }

    /**
     * 合成回调监听。
     */
    private SynthesizerListener mTtsListener = new SynthesizerListener() {
        @Override
        public void onSpeakBegin() {
            Log.e(TAG, "mTtsListener-->onSpeakBegin");
            showTip("开始播放");
        }

        @Override
        public void onSpeakPaused() {
            Log.e(TAG, "mTtsListener-->onSpeakPaused");
            showTip("暂停播放");
        }

        @Override
        public void onSpeakResumed() {
            Log.e(TAG, "mTtsListener-->onSpeakResumed");
            showTip("继续播放");
        }

        @Override
        public void onBufferProgress(int percent, int beginPos, int endPos,
                                     String info) {
            Log.e(TAG, "mTtsListener-->onBufferProgress");
            mPercentForBuffering = percent;
            showTip(String.format(getString(R.string.tts_toast_format),
                    mPercentForBuffering, mPercentForPlaying));
        }

        @Override
        public void onSpeakProgress(int percent, int beginPos, int endPos) {
            Log.e(TAG, "mTtsListener-->onSpeakProgress");
            mPercentForPlaying = percent;
            showTip(String.format(getString(R.string.tts_toast_format),
                    mPercentForBuffering, mPercentForPlaying));
        }

        @Override
        public void onCompleted(SpeechError error) {
            Log.e(TAG, "mTtsListener-->onCompleted");
            if (error == null) {
                showTip("播放完成");
            } else if (error != null) {
                showTip(error.getPlainDescription(true));
            }
        }

        @Override
        public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
            Log.e(TAG, "mTtsListener-->onEvent");
        }
    };
    /**
     * 听写监听器。
     */
    private RecognizerListener recognizerListener = new RecognizerListener() {

        @Override
        public void onVolumeChanged(int i, byte[] bytes) {
            Log.e(TAG, "recognizerListener-->onVolumeChanged");
            showTip("当前正在说话,音量大小:" + i);
        }

        @Override
        public void onBeginOfSpeech() {
            Log.e(TAG, "recognizerListener-->onBeginOfSpeech");
            showTip("开始说话");
        }

        @Override
        public void onEndOfSpeech() {
            Log.e(TAG, "recognizerListener-->onEndOfSpeech");
            showTip("结束说话");
        }

        @Override
        public void onResult(RecognizerResult results, boolean isLast) {
            Log.e(TAG, "recognizerListener-->onResult");
            String text = JsonParser.parseIatResult(results.getResultString());
            mResultText.append(text);
            mResultText.setSelection(mResultText.length());
            if (isLast) {
                // TODO 最后的结果
            }
        }

        @Override
        public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
            Log.e(TAG, "recognizerListener-->onEvent");
        }

        @Override
        public void onError(SpeechError arg0) {
            Log.e(TAG, "recognizerListener-->onError");
            // TODO Auto-generated method stub
        }
    };
    /**
     * 听写UI监听器
     */
    private RecognizerDialogListener recognizerDialogListener = new RecognizerDialogListener() {

        public void onResult(RecognizerResult results, boolean isLast) {
            Log.e(TAG, "recognizerDialogListener-->onResult");
            String text = JsonParser.parseIatResult(results.getResultString());
            mResultText.append(text);
            mResultText.setSelection(mResultText.length());
        }

        /**
         * 识别回调错误.
         */
        public void onError(SpeechError error) {
            Log.e(TAG, "recognizerDialogListener-->onError");
            showTip(error.getPlainDescription(true));
        }
    };
    private InitListener mInitListener = new InitListener() {
        @Override
        public void onInit(int code) {
            Log.e(TAG, "mInitListener-->onInit");
            Log.d(TAG, "SpeechRecognizer init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showTip("初始化失败,错误码:" + code);
            }
        }
    };

    private void showTip(final String str) {
        Log.e(TAG, "showTip-->" + str);
        mToast.setText(str);
        mToast.show();
    }


    @Override
    protected void onDestroy() {
        Log.e(TAG, "onDestroy");
        super.onDestroy();
        // 退出时释放连接
        mIat.cancel();
        mIat.destroy();
    }

    @Override
    protected void onResume() {
        Log.e(TAG, "onResume");
        super.onResume();
    }

    @Override
    protected void onPause() {
        Log.e(TAG, "onPause");
        super.onPause();
    }

}

工程下载地址:

http://download.csdn.net/detail/xiaobailong24/9296953

​ Github:https://github.com/xiaobailong24-library/Xunfeiyun

常见错误码查询:

http://www.xfyun.cn/index.php/default/doccenter/doccenterInner?itemTitle=ZmFx&anchor=Y29udGl0bGU2Mw