Android integrates iFlytek Voice and implements speech recognition

Foreword: The contents of this blog include integrating iFlytek Voice SDK to realize voice wake-up, online voice recognition, offline voice recognition, and updating local offline voice dictionary.

Integrated iFlytek Voice SDK

Register an account to download SDK

Search iFlytek on Baidu, open the website, and register an account according to the steps if you don’t have an account. After the registration is completed, download the SDK according to your personal needs, because what we want to achieve includes offline speech recognition, voice wake-up, and updating the local offline speech dictionary, so You need to add the wake word before downloading the SDK, and then download the SDK after adding it.

Import demo

When you import the demo, you may get an error. I’m afraid you don’t know how to solve it. Here I will give you a detailed introduction and how to solve some of the problems you may encounter.
Import the demo first
!Import demo
and then I will get an error.

ERROR: Plugin with id 'com.android.application' not found.
Open File

If we look closely, we will find that there is only the build.grade file of the Project in this project structure, and there is no build.grade of the Module, so we add the following code to the build.gradle of the Project, and then try again

buildscript {
    
    
    repositories {
    
    
        google()
        jcenter()

    }
    dependencies {
    
    
      //版本号请根据自己的gradle插件版本号自行更改
        classpath 'com.android.tools.build:gradle:3.2.0'

    }
}

It should be noted here that buildscript, android and dependencies are in the same level relationship, as shown below.
Insert image description here
After synchronization, I reported the following error.

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

The first error means: minSdk version should not be declared in the android manifest file. You can move the version from manifest to defaultConfig in the build. gradle file.
Then we open the android manifest, find minSdk, and delete it.
Insert image description here
The second error means deleting "buildToolsVersion '26.0.2'" from the build version, then delete it. The
Insert image description here
third error means that the configuration 'compile' has been Obsolete, superseded by 'implementation' and 'api'.
Insert image description here
Then synchronize it, run the program, and find that it cannot run. Find it under build, as shown below in the code section, Insert image description here
add google() in build.gradle,
Insert image description here
synchronize it as shown below, and after running, it can be installed on the phone, but There is one more thing that needs to be modified here, which is to replace the APPID in the demo with your own registered APPID. Don’t forget to replace it! ! !

Configuration items

Let’s talk about copying the libs in the demo to your own lib
Insert image description here
, and then importing all the files in the assets into the project.
Insert image description here

Online speech recognition

First add permissions

 <!-- 连接网络权限,用于执行云端语音能力 -->
    <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" />

Then create an application, the code is as follows

public class SpeechApplication extends Application {

    @Override
    public void onCreate() {

        //   5ef048e1  为在开放平台注册的APPID  注意没有空格,直接替换即可
        SpeechUtility.createUtility(SpeechApplication.this, "appid=5ef048e1");

        super.onCreate();
    }
}

The following is the code in 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();
        }
    }

}

Next is the layout file of 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>

Once you run it, you can implement speech recognition.

Voice wake-up

code show as below

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);
        }
    }

}

Corresponding layout file

<?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>

Offline speech recognition and updating local dictionary command words

It should be noted here that the update dictionary of command words is an update slot, mainly to facilitate the entry of the address book. It does not support updating dictionaries in large batches. It is recommended to include the complete command words when setting the command words.
code show as below


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 file

<?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>

In fact, it’s over here, but it will involve several other entity classes, which are included in the sdk package provided by iFlytek. I am afraid that everyone will be too lazy to look for them, so I will post them here.

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();
	}
}

Finally, everyone needs to pay attention. If you only implement a certain function, you must remember to add dynamic permissions. I have added some, and some activities have not
been All posted

Guess you like

Origin blog.csdn.net/qq_42221857/article/details/118391085