Android 集成科大讯飞语音并实现语音识别

前言:此博客内容有,集成讯飞语音sdk,实现了语音唤醒,在线语音识别,离线语音识别,更新本地离线语音词典

集成讯飞语音SDK

注册账号下载SDK

百度搜索科大讯飞,打开网站,没有账号的根据操作步骤注册账号,注册完成之后,根据个人需求下载sdk,因为咱们要实现的内容有离线语音识别,语音唤醒,以及更新本地离线语音字典,所以在下载SDK之前需要添加唤醒词,添加完之后再去下载SDK

导入demo

导入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 project
Affected Modules: mscV5PlusDemo


WARNING: 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 project
Affected Modules: mscV5PlusDemo


WARNING: Configuration 'compile' is obsolete and has been replaced with 'implementation' and 'api'.
It will be removed at the end of 2018. For more information see: http://d.android.com/r/tools/update-dependency-configurations.html
Affected 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 {

    @Override
    public 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";//结果内容数据格式
    
    @Override
    protected 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);

    }

    @Override
    public void onClick(View v) {
    
    
        if (null == mIat) {
    
    
            // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
            showMsg("创建对象失败,请确认 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
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    
    
        // 此处为android 6.0以上动态授权的回调,用户自行实现。
    }
    
    /**
     * 初始化监听器。
     */
    private InitListener mInitListener = new InitListener() {
    
    

        @Override
        public void onInit(int code) {
    
    
            Log.d(TAG, "SpeechRecognizer init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
    
    
                showMsg("初始化失败,错误码:" + code + ",请点击网址https://www.xfyun.cn/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");
    }
    
    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();

        if (null != mIat) {
    
    
            // 退出时释放连接
            mIat.cancel();
            mIat.destroy();
        }
    }

}

接下来是MainActivity的布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="识别到的内容"
        android:textColor="#000" />

    <Button
        android: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;
    //存储唤醒词的ID
    private String wordID = "";
    // 唤醒结果内容
    private String resultString;

    private TextView tv;

    @Override
    protected 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 {
    
    
        //开始说话
        @Override
        public void onBeginOfSpeech() {
    
    

        }

        //错误码返回
        @Override
        public void onError(SpeechError arg0) {
    
    

        }

        @Override
        public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {
    
    

        }

        @Override
        public void onVolumeChanged(int i) {
    
    

        }

        @Override
        public 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="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".WeakUpActivity">
    <TextView
        android: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,json
    private 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;


    @Override
    protected 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 + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案");
        }
    }
//    }


    /**
     * 语音识别
     *
     * @param view
     */
    public void start(View view) {
    
    
        if (null == mAsr) {
    
    
            // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
            this.showTip("创建对象失败,请确认 libmsc.so 放置正确,\n 且有调用 createUtility 进行初始化");
            return;
        }

        if (!setParam()) {
    
    
            showTip("请先构建语法。");
            return;
        }

        ret = mAsr.startListening(mRecognizerListener);

        if (ret != ErrorCode.SUCCESS) {
    
    
            showTip("识别失败,错误码: " + ret + ",请点击网址https://www.xfyun.cn/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 + ",请点击网址https://www.xfyun.cn/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);
        // 设置本地识别使用语法id
        mAsr.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() {
    
    

        @Override
        public void onInit(int code) {
    
    
            Log.d(TAG, "SpeechRecognizer init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
    
    
                showTip("初始化失败,错误码:" + code + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案");
            }
        }
    };

    /**
     * 更新词典监听器。
     */
    private LexiconListener lexiconListener = new LexiconListener() {
    
    
        @Override
        public 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() {
    
    
        @Override
        public void onBuildFinish(String grammarId, SpeechError error) {
    
    

            if(error == null){
    
    

                showTip("语法构建成功:" + grammarId);
            }else{
    
    
                showTip("语法构建失败,错误码:" + error.getErrorCode());
            }


        }
    };

    /**
     * 获取联系人监听器。
     */
    private ContactManager.ContactListener mContactListener = new ContactManager.ContactListener() {
    
    
        @Override
        public void onContactQueryFinish(String contactInfos, boolean changeFlag) {
    
    
            //获取联系人
            mLocalLexicon = contactInfos;
        }
    };

    /**
     * 识别监听器。
     */
    private RecognizerListener mRecognizerListener = new RecognizerListener() {
    
    

        @Override
        public void onVolumeChanged(int volume, byte[] data) {
    
    
            showTip("当前正在说话,音量大小:" + volume);
            Log.d(TAG, "返回音频数据:" + data.length);
        }

        @Override
        public 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");
            }
        }

        @Override
        public void onEndOfSpeech() {
    
    
            // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
            showTip("结束说话");
        }

        @Override
        public void onBeginOfSpeech() {
    
    
            // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
            showTip("开始说话");
        }

        @Override
        public void onError(SpeechError error) {
    
    
            showTip("onError Code:" + error.getErrorCode());
        }

        @Override
        public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
    
    

        }

    };

    private void showTip(final String str) {
    
    
        runOnUiThread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                mToast.setText(str);
                mToast.show();
            }
        });
    }

    private void loadWeb() {
    
    

        String url = "https://www.xfyun.cn/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="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".OfflineActivity">

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:textSize="15sp" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="start"
        android:padding="20dp"
        android:text="开始听写" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="updata"
        android:padding="20dp"
        android:text="更新本地词库" />

   
    <WebView
        android: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));
	}
	@Override
	public 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 block
			e.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;
	 }
	
	@Override
	public void onTextChanged(CharSequence s, int start, int before, int count) {
    
    
//		Log.e("demo", "onTextChanged start:"+start+" count:"+count+" before:"+before);
		editStart = start;
		editCount = count;
	}
	
	@Override
	public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
    
//		Log.e("demo", "beforeTextChanged start:"+start+" count:"+count+" after:"+after);
	}
	
	@Override
	public 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 = Pattern.compile("[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 builder
			DocumentBuilder domBuilder = null;
			// DOM doc
			Document domDoc = null;

			// init DOM
			DocumentBuilderFactory 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没写上
好了,到这里,功能都已经实现了,代码也全部贴上了

猜你喜欢

转载自blog.csdn.net/qq_42221857/article/details/118391085