百度语音智能学习笔记-简单语音识别

本文详细介绍了在Android应用中集成百度语音识别SDK的过程,包括环境准备、权限申请、代码实现及工具类使用。涵盖关键步骤如SDK配置、事件监听、识别结果处理及常见问题排查。

环境准备
1.如下拷贝(由adndroid的那个demo)
在这里插入图片描述
2.bdasr_V3_20190327_58c9395.jar拷贝
拷贝到如下目录:工程目录/app/libs
3.AndroidManifest.xml
3.1权限

<uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

3.2 服务APP_ID/API_KEY/SECRET_KEY



代码

1 语音识别开始

private Button mstart;
mstart = findViewById(R.id.id_button);
        mstart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                start();
            }
        });

语音识别开始

private void start() {
        txtLog.setText("");
        Map<String, Object> params = new LinkedHashMap<String, Object>();
        String event = null;

        event = SpeechConstant.ASR_START; // 替换成测试的event

        if (enableOffline) {
            params.put(SpeechConstant.DECODER, 2);
        }
        // 基于SDK集成2.1 设置识别参数
        params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);
        // params.put(SpeechConstant.NLU, "enable");
        // params.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // 长语音
        // params.put(SpeechConstant.IN_FILE, "res:///com/baidu/android/voicedemo/16k_test.pcm");
        // params.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
        // params.put(SpeechConstant.PID, 1537); // 中文输入法模型,有逗号

        /* 语音自训练平台特有参数 */
        // params.put(SpeechConstant.PID, 8002);
        // 语音自训练平台特殊pid,8002:搜索模型类似开放平台 1537  具体是8001还是8002,看自训练平台页面上的显示
        // params.put(SpeechConstant.LMID,1068); // 语音自训练平台已上线的模型ID,https://ai.baidu.com/smartasr/model
        // 注意模型ID必须在你的appId所在的百度账号下
        /* 语音自训练平台特有参数 */

        // 请先使用如‘在线识别’界面测试和生成识别参数。 params同ActivityRecog类中myRecognizer.start(params);
        // 复制此段可以自动检测错误
        (new AutoCheck(getApplicationContext(), new Handler() {
            public void handleMessage(Message msg) {
                if (msg.what == 100) {
                    AutoCheck autoCheck = (AutoCheck) msg.obj;
                    synchronized (autoCheck) {
                        String message = autoCheck.obtainErrorMessage(); // autoCheck.obtainAllMessage();
                        txtLog.append(message + "\n");
                        ; // 可以用下面一行替代,在logcat中查看代码
                        // Log.w("AutoCheckMessage", message);
                    }
                }
            }
        },enableOffline)).checkAsr(params);
        String json = null; // 可以替换成自己的json
        json = new JSONObject(params).toString(); // 这里可以替换成你需要测试的json
        asr.send(event, json, null, 0, 0);
        //printLog("输入参数:" + json);
    }

响应结果

// 基于sdk集成1.2 自定义输出事件类 EventListener 回调方法
    // 基于SDK集成3.1 开始回调事件
    @Override
    public void onEvent(String name, String params, byte[] data, int offset, int length) {
        String logTxt = "name: " + name;


        if (params != null && !params.isEmpty()) {
            logTxt += " ;params :" + params;
        }
        if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)) {
            if (params != null && params.contains("\"nlu_result\"")) {
                if (length > 0 && data.length > 0) {
                    logTxt += ", 语义解析结果:" + new String(data, offset, length);
                }
            }
        } else if (data != null) {
            logTxt += " ;data length=" + data.length;
        }
        printLog(logTxt);
    }

    private void printLog(String text) {
        if (logTime) {
            text += "  ;time=" + System.currentTimeMillis();
        }
        text += "\n";
        Log.i(getClass().getName(), text);
        txtLog.append(text + "\n");
    }

2.识别后的表示

protected TextView txtLog;
 <TextView
        android:id="@+id/id_log"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

 txtLog = findViewById(R.id.id_log);
 
public void onEvent(String name, String params, byte[] data, int offset, int length) {
       。。。。。
        printLog(logTxt);
    }
 private void printLog(String text) {
        if (logTime) {
            text += "  ;time=" + System.currentTimeMillis();
        }
        text += "\n";
        Log.i(getClass().getName(), text);
        txtLog.append(text + "\n");
    }

3.权限申请

 /**
     * android 6.0 以上需要动态申请权限
     */
    private void initPermission() {
        String permissions[] = {Manifest.permission.RECORD_AUDIO,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.INTERNET,
                Manifest.permission.READ_PHONE_STATE,
                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);
        }

    }
    
 @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        // 此处为android 6.0以上动态授权的回调,用户自行实现。
    }

3.取消语音识别

<Button
            android:id="@+id/id_clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="结束" />
 Button mclaer = findViewById(R.id.id_clear);
        mclaer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onStop();
            }
        });
        
protected void onStop() {
        super.onStop();
        this.getDelegate().onStop();
    }

4.工具类(直接由百度的deomo拷贝过来即可)

package com.qiming.yuyinshibie;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.util.Log;

import com.baidu.speech.asr.SpeechConstant;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeSet;

public class AutoCheck {
    public static final boolean isOnlineLited = false; // 是否只需要是纯在线识别功能
    private LinkedHashMap<String, Check> checks;

    private Context context;
    private Handler handler;

    private boolean hasError;
    private boolean enableOffline;
    private boolean isFinished = false;

    private String name;

    private static final String TAG = "AutoCheck";

    public AutoCheck(Context context, final Handler handler, boolean enableOffline) {
        this.context = context;
        checks = new LinkedHashMap<>();
        this.handler = handler;
        this.enableOffline = enableOffline;
    }

    public void checkAsr(final Map<String, Object> params) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                AutoCheck obj = checkAsrInternal(params);
                name = "识别";
                synchronized (obj) { // 偶发,同步线程信息
                    isFinished = true;
                    Message msg = handler.obtainMessage(100, obj);
                    handler.sendMessage(msg);
                }
            }
        });
        t.start();
    }

    public String obtainErrorMessage() {
        PrintConfig config = new PrintConfig();
        return formatString(config);
    }

    public String obtainDebugMessage() {
        PrintConfig config = new PrintConfig();
        config.withInfo = true;
        return formatString(config);
    }

    public String obtainAllMessage() {
        PrintConfig config = new PrintConfig();
        config.withLog = true;
        config.withInfo = true;
        config.withLogOnSuccess = true;
        return formatString(config);
    }

    private String formatString(PrintConfig config) {
        StringBuilder sb = new StringBuilder();
        hasError = false;

        for (HashMap.Entry<String, Check> entry : checks.entrySet()) {
            Check check = entry.getValue();
            String testName = entry.getKey();
            if (check.hasError()) {
                if (!hasError) {
                    hasError = true;
                }

                sb.append("【错误】【").append(testName).append(" 】  ").append(check.getErrorMessage()).append("\n");
                Log.e("AutoCheck", sb.toString());
                if (check.hasFix()) {
                    sb.append("【修复方法】【").append(testName).append(" 】  ").append(check.getFixMessage()).append("\n");
                }
            } else if (config.withEachCheckInfo) {
                sb.append("【无报错】【").append(testName).append(" 】  ").append("\n");
            }
            if (config.withInfo && check.hasInfo()) {
                sb.append("【请手动检查】【").append(testName).append("】 ").append(check.getInfoMessage()).append("\n");
            }
            if (config.withLog && (config.withLogOnSuccess || hasError) && check.hasLog()) {
                sb.append("【log】:" + check.getLogMessage()).append("\n");
            }
        }
        if (!hasError) {
            sb.append("【" + name + "】集成自动排查工具: 恭喜没有检测到任何问题\n");
        }
        return sb.toString();
    }

    private AutoCheck checkAsrInternal(Map<String, Object> params) {
        commonSetting(params);
        checks.put("外部音频文件存在校验", new FileCheck(context, params, SpeechConstant.IN_FILE));
        checks.put("离线命令词及本地语义bsg文件存在校验",
                new FileCheck(context, params, SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH));
        for (Map.Entry<String, Check> e : checks.entrySet()) {
            Check check = e.getValue();
            check.check();
            if (check.hasError()) {
                break;
            }
        }
        return this;
    }

    private void commonSetting(Map<String, Object> params) {
        checks.put("检查申请的Android权限", new PermissionCheck(context));
        checks.put("检查so文件是否存在", new JniCheck(context));
        AppInfoCheck infoCheck = null;
        try {
            infoCheck = new AppInfoCheck(context, params);
            checks.put("检查AppId AppKey SecretKey", infoCheck);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            Log.e(TAG, "检查AppId AppKey SecretKey 错误", e);
            return;
        }
        if (enableOffline) {
            checks.put("检查包名", new ApplicationIdCheck(context, infoCheck.appId));
        }

    }

    private static class PrintConfig {
        public boolean withEachCheckInfo = false;
        public boolean withInfo = false;
        public boolean withLog = false;
        public boolean withLogOnSuccess = false;
    }


    private static class PermissionCheck extends Check {
        private Context context;

        public PermissionCheck(Context context) {
            this.context = context;
        }

        @Override
        public void check() {
            String[] permissions = {
                    Manifest.permission.RECORD_AUDIO,
                    Manifest.permission.ACCESS_NETWORK_STATE,
                    Manifest.permission.INTERNET,
                    // Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_PHONE_STATE
            };

            ArrayList<String> toApplyList = new ArrayList<String>();
            for (String perm : permissions) {
                if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(context, perm)) {
                    toApplyList.add(perm);
                    // 进入到这里代表没有权限.
                }
            }
            if (!toApplyList.isEmpty()) {
                errorMessage = "缺少权限:" + toApplyList;
                fixMessage = "请从AndroidManifest.xml复制相关权限";
            }
        }
    }

    private static class JniCheck extends Check {
        private Context context;

        private String[] soNames;

        public JniCheck(Context context) {
            this.context = context;
            if (isOnlineLited) {
                soNames = new String[]{"libBaiduSpeechSDK.so", "libvad.dnn.so"};
            } else {
                soNames = new String[]{"libBaiduSpeechSDK.so", "libvad.dnn.so",
                        "libbd_easr_s1_merge_normal_20151216.dat.so", "libbdEASRAndroid.so",
                        "libbdSpilWakeup.so"};
            }
        }

        @Override
        public void check() {
            String path = context.getApplicationInfo().nativeLibraryDir;
            appendLogMessage("Jni so文件目录 " + path);
            File[] files = new File(path).listFiles();
            TreeSet<String> set = new TreeSet<>();
            if (files != null) {
                for (File file : files) {
                    set.add(file.getName());
                }
            }
            // String debugMessage = "Jni目录内文件: " + set.toString();
            // boolean isSuccess = true;
            for (String name : soNames) {
                if (!set.contains(name)) {
                    errorMessage = "Jni目录" + path + " 缺少so文件:" + name + ", 该目录文件列表: " + set.toString();
                    fixMessage = "如果您的app内没有其它so文件,请复制demo里的src/main/jniLibs至同名目录。"
                            + " 如果app内有so文件,请合并目录放一起(注意目录取交集,多余的目录删除)。";
                    break;
                }
            }
        }
    }

    private static class AppInfoCheck extends Check {
        private String appId;
        private String appKey;
        private String secretKey;

        public AppInfoCheck(Context context, Map<String, Object> params) throws PackageManager.NameNotFoundException {
            Bundle metaData = context.getPackageManager().getApplicationInfo(context.getPackageName(),
                    PackageManager.GET_META_DATA).metaData;
            if (params.get(SpeechConstant.APP_ID) != null) {
                appId = params.get(SpeechConstant.APP_ID).toString();
            } else {
                int id = metaData.getInt("com.baidu.speech.APP_ID", 0);
                if (id > 0) {
                    appId = "" + id;
                }
            }
            if (params.get(SpeechConstant.APP_KEY) != null) {
                appKey = params.get(SpeechConstant.APP_KEY).toString();
            } else {
                appKey = metaData.getString("com.baidu.speech.API_KEY", "");
            }

            if (params.get(SpeechConstant.SECRET) != null) {
                secretKey = params.get(SpeechConstant.SECRET).toString();
            } else {
                secretKey = metaData.getString("com.baidu.speech.SECRET_KEY", "");
            }
        }


        public void check() {
            do {
                appendLogMessage("try to check appId " + appId + " ,appKey=" + appKey + " ,secretKey" + secretKey);
                if (appId == null || appId.isEmpty()) {
                    errorMessage = "appId 为空";
                    fixMessage = "填写appID";
                    break;
                }
                if (appKey == null || appKey.isEmpty()) {
                    errorMessage = "appKey 为空";
                    fixMessage = "填写appID";
                    break;
                }
                if (secretKey == null || secretKey.isEmpty()) {
                    errorMessage = "secretKey 为空";
                    fixMessage = "secretKey";
                    break;
                }


                try {
                    checkOnline();
                } catch (UnknownHostException e) {
                    infoMessage = "无网络或者网络不连通,忽略检测 : " + e.getMessage();
                } catch (Exception e) {
                    errorMessage = e.getClass().getCanonicalName() + ":" + e.getMessage();
                    fixMessage = " 重新检测appId, appKey, appSecret是否正确";
                }
            } while (false);
        }

        public void checkOnline() throws Exception {
            String urlpath = "http://openapi.baidu.com/oauth/2.0/token?client_id="
                    + appKey + "&client_secret=" + secretKey + "&grant_type=client_credentials";
            Log.i("AutoCheck", "Url is " + urlpath);
            URL url = new URL(urlpath);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(1000);
            InputStream is = conn.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder result = new StringBuilder();
            String line = "";
            do {
                line = reader.readLine();
                if (line != null) {
                    result.append(line);
                }
            } while (line != null);
            String res = result.toString();
            if (!res.contains("audio_voice_assistant_get")) {
                errorMessage = "appid:" + appId + ",没有audio_voice_assistant_get 权限,请在网页上开通\"语音识别\"能力";
                fixMessage = "secretKey";
                return;
            }
            appendLogMessage("openapi return " + res);
            JSONObject jsonObject = new JSONObject(res);
            String error = jsonObject.optString("error");
            if (error != null && !error.isEmpty()) {
                errorMessage = "appkey secretKey 错误" + ", error:" + error + ", json is" + result;
                fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
                return;
            }
            String token = jsonObject.getString("access_token");
            if (token == null || !token.endsWith("-" + appId)) {
                errorMessage = "appId 与 appkey及 appSecret 不一致。appId = " + appId + " ,token = " + token;
                fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
            }
        }
    }

    private static class ApplicationIdCheck extends Check {

        private String appId;
        private Context context;

        public ApplicationIdCheck(Context context, String appId) {
            this.appId = appId;
            this.context = context;
        }

        @Override
        public void check() {
            infoMessage = "如果您集成过程中遇见离线命令词或者唤醒初始化问题,请检查网页上appId:" + appId
                    + "  应用填写了Android包名:"
                    + getApplicationId();
        }

        private String getApplicationId() {
            return context.getPackageName();
        }
    }

    private static class FileCheck extends Check {
        private Map<String, Object> params;
        private String key;
        private Context context;
        private boolean allowRes = false;
        private boolean allowAssets = true;

        public FileCheck(Context context, Map<String, Object> params, String key) {
            this.context = context;
            this.params = params;
            this.key = key;
            if (key.equals(SpeechConstant.IN_FILE)) {
                allowRes = true;
                allowAssets = false;
            }
        }

        @Override
        public void check() {
            if (!params.containsKey(key)) {
                return;
            }
            String value = params.get(key).toString();
            if (allowAssets) {
                int len = "assets".length();
                int totalLen = len + ":///".length();
                if (value.startsWith("assets")) {
                    String filename = value.substring(totalLen);
                    if (!":///".equals(value.substring(len, totalLen)) || filename.isEmpty()) {
                        errorMessage = "参数:" + key + "格式错误:" + value;
                        fixMessage = "修改成" + "assets:///sdcard/xxxx.yyy";
                    }
                    try {
                        context.getAssets().open(filename);
                    } catch (IOException e) {
                        errorMessage = "assets 目录下,文件不存在:" + filename;
                        fixMessage = "demo的assets目录是:src/main/assets";
                        e.printStackTrace();
                    }
                    appendLogMessage("assets 检验完毕:" + filename);
                }
            }
            if (allowRes) {
                int len = "res".length();
                int totalLen = len + ":///".length();
                if (value.startsWith("res")) {
                    String filename = value.substring(totalLen);
                    if (!":///".equals(value.substring(len, totalLen)) || filename.isEmpty()) {
                        errorMessage = "参数:" + key + "格式错误:" + value;
                        fixMessage = "修改成" + "res:///com/baidu/android/voicedemo/16k_test.pcm";
                    }
                    InputStream is = getClass().getClassLoader().getResourceAsStream(filename);
                    if (is == null) {
                        errorMessage = "res,文件不存在:" + filename;
                        fixMessage = "demo的res目录是:app/src/main/resources";
                    } else {
                        try {
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    appendLogMessage("res 检验完毕:" + filename);
                }
            }
            if (value.startsWith("/")) {
                if (!new File(value).canRead()) {
                    errorMessage = "文件不存在:" + value;
                    fixMessage = "请查看文件是否存在";
                }
                appendLogMessage("文件路径 检验完毕:" + value);
            }
        }
    }

    private abstract static class Check {
        protected String errorMessage = null;

        protected String fixMessage = null;

        protected String infoMessage = null;

        protected StringBuilder logMessage;

        public Check() {
            logMessage = new StringBuilder();
        }

        public abstract void check();

        public boolean hasError() {
            return errorMessage != null;
        }

        public boolean hasFix() {
            return fixMessage != null;
        }

        public boolean hasInfo() {
            return infoMessage != null;
        }

        public boolean hasLog() {
            return !logMessage.toString().isEmpty();
        }

        public void appendLogMessage(String message) {
            logMessage.append(message + "\n");
        }

        public String getErrorMessage() {
            return errorMessage;
        }

        public String getFixMessage() {
            return fixMessage;
        }

        public String getInfoMessage() {
            return infoMessage;
        }

        public String getLogMessage() {
            return logMessage.toString();
        }
    }
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值