zoukankan      html  css  js  c++  java
  • BaiduSpeechDemo【百度语音SDK集成】(基于v3.0.7.3)

    版权声明:本文为HaiyuKing原创文章,转载请注明出处!

    前言

    本Demo将百度语音SDK(其中一部分功能)和自定义的UI对话框封装到一个module中,便于后续的SDK版本更新以及调用。

    本Demo使用的百度语音SDK版本是audiobd_speech_sdk_asr_v3.0.7.3_bdasr_20180313_726f26e

    本Demo中使用的appkey已失效,请自行创建应用,使用新的appkey。

    效果图

    前提

    (1)新建项目(获取包名)

    (2)在百度AI开发平台上创建应用,获取API Key及Secret Key

    官网地址:http://ai.baidu.com/tech/speech

    1、成为开发者

    参考《接入指南

    2、创建应用

    2.1、点击百度AI开放平台导航右侧的控制台,选择需要使用的AI服务项【这里选择语音技术】。

     2.2、创建应用

     2.3、填写应用信息

     2.4、创建成功

    2.5、应用列表

    (3)下载SDK

    3.1、管理应用

    3.2、下载SDK

    下载地址:https://ai.baidu.com/sdk#asr

     

    代码分析

    普通话 search搜索模型:参考SpeechBottomSheetDialog.java类

    普通话 input输入法模型,适用于长句及长语音,有逗号分割,无语义:参考SpeechLongBottomSheetDialog.java类

    注意:关于语音识别状态维护,API调用的代码,是自己根据官网demo的理解进行整理的,可能有所偏颇,仅供参考。【希望官网demo可以添加百度APP的语音对话框效果就好了】

    使用步骤

    一、项目组织结构图

    注意事项:

    1、  导入类文件后需要change包名以及重新import R文件路径

    2、  Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖

    二、导入步骤

    (1)新建module,命名为BaiduSpeech

    (2)在baiduspeech的AndroidManifest.xml中添加以下代码

    从官方demo的AndroidManifest.xml中找到如下信息,然后复制到您自己的同名文件中。此处需要您复制1、权限2、官网申请的应用信息3、SDK的Service。

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="com.why.project.baiduspeech">
    
        <!-- ======================百度语音====================== -->
        <!-- begin: baidu speech sdk  权限 -->
        <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" />
        <!-- end: baidu speech sdk  权限 -->
    
        <application>
    
            <!-- ======================百度语音========================== -->
            <!-- 正式发布时,请替换成您自己的appId 本demo的appId会不定时下线 -->
            <meta-data
                android:name="com.baidu.speech.APP_ID"
                android:value="11588936" />
            <meta-data
                android:name="com.baidu.speech.API_KEY"
                android:value="XRF3IOf2tNGePzlv47cBnvF3" />
            <meta-data
                android:name="com.baidu.speech.SECRET_KEY"
                android:value="diC8lQ7XDcGBKQ6FzCpvnan54F5CnMZI" />
            <service
                android:name="com.baidu.speech.VoiceRecognitionService"
                android:exported="false" />
        </application>
    </manifest>

    注意:此时<service>标签那里是红色错误的标记,暂时不用管,导入jar、so文件后编译下就正常了。

    (3)复制jar 文件

     将官方demo中的applibsdasr_V3_20180320_9066860.jar复制进您项目的同名目录。

    在build.gradle中确认是否含有以下红色标记的代码

    (4)复制jni库的so文件

    复制官方demo中 appsrcmainjniLibs 至项目的同名目录。 

    这个时候编译下,就会发现AndroidManifest.xml文件的<service>标签那里正常了。

    (5)在官方demo中找到下面的文件复制到项目中(按照下面的包名进行查找)【注意,复制过来后,需要重新import 相关类

    (6)修改MessageStatusRecogListener.java文件【根据实际情况进行修改

    package com.why.project.baiduspeech.recognization;
    
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    
    /**
     * Created by fujiayi on 2017/6/16.
     */
    
    public class MessageStatusRecogListener extends StatusRecogListener {
        private Handler handler;
    
        private long speechEndTime;
    
        private boolean needTime = true;
    
        private static final String TAG = "MesStatusRecogListener";
    
        public MessageStatusRecogListener(Handler handler) {
            this.handler = handler;
        }
    
    
        @Override
        public void onAsrReady() {
            super.onAsrReady();
            sendStatusMessage("引擎就绪,可以开始说话。");
        }
    
        @Override
        public void onAsrBegin() {
            super.onAsrBegin();
            sendStatusMessage("检测到用户说话");
        }
    
        @Override
        public void onAsrEnd() {
            super.onAsrEnd();
            speechEndTime = System.currentTimeMillis();
            sendMessage("检测到用户说话结束");
        }
    
        @Override
        public void onAsrPartialResult(String[] results, RecogResult recogResult) {
            sendStatusMessage("临时识别结果,结果是“" + results[0] + "”;原始json:" + recogResult.getOrigalJson());
            super.onAsrPartialResult(results, recogResult);
        }
    
        @Override
        public void onAsrFinalResult(String[] results, RecogResult recogResult) {
            super.onAsrFinalResult(results, recogResult);
            //String message = "识别结束,结果是”" + results[0] + "”";//why 实际中可以去掉,不需要
            String message = recogResult.getOrigalJson();//{"results_recognition":["什么什么"],"origin_result":{"corpus_no":6522034498058113957,"err_no":0,"result":{"word":["什么什么"]},"sn":"bfa8b286-ab0e-4f86-9209-1d36d38b1224","voice_energy":16191.7705078125},"error":0,"best_result":"什么什么","result_type":"final_result"}
            sendStatusMessage(message + "“;原始json:" + recogResult.getOrigalJson());
            if (speechEndTime > 0) {
                long diffTime = System.currentTimeMillis() - speechEndTime;
                //message += ";说话结束到识别结束耗时【" + diffTime + "ms】";// why 实际中可以去掉,不需要
    
            }
            speechEndTime = 0;
            sendMessage(message, status, true);
        }
    
        @Override
        public void onAsrFinishError(int errorCode, int subErrorCode, String errorMessage, String descMessage,
                                     RecogResult recogResult) {
            super.onAsrFinishError(errorCode, subErrorCode, errorMessage, descMessage, recogResult);
            //String message = "识别错误, 错误码:" + errorCode + " ," + subErrorCode + " ; " + descMessage;// why 实际中可以去掉,不需要
            String message = recogResult.getOrigalJson();//{"origin_result":{"sn":"","error":7,"desc":"No recognition result match","sub_error":7001},"error":7,"desc":"No recognition result match","sub_error":7001}
            sendStatusMessage(message + ";错误消息:" + errorMessage + ";描述信息:" + descMessage);
            if (speechEndTime > 0) {
                long diffTime = System.currentTimeMillis() - speechEndTime;
                //message += "。说话结束到识别结束耗时【" + diffTime + "ms】";// why实际中可以去掉,不需要
            }
            speechEndTime = 0;
            sendMessage(message, status, true);
            speechEndTime = 0;
        }
    
        @Override
        public void onAsrOnlineNluResult(String nluResult) {
            super.onAsrOnlineNluResult(nluResult);
            if (!nluResult.isEmpty()) {
                sendStatusMessage("原始语义识别结果json:" + nluResult);
            }
        }
    
        @Override
        public void onAsrFinish(RecogResult recogResult) {
            super.onAsrFinish(recogResult);
            sendStatusMessage("识别一段话结束。如果是长语音的情况会继续识别下段话。");
    
        }
    
        /**
         * 长语音识别结束
         */
        @Override
        public void onAsrLongFinish() {
            super.onAsrLongFinish();
            sendStatusMessage("长语音识别结束。");
        }
    
    
        /**
         * 使用离线命令词时,有该回调说明离线语法资源加载成功
         */
        @Override
        public void onOfflineLoaded() {
            sendStatusMessage("【重要】asr.loaded:离线资源加载成功。没有此回调可能离线语法功能不能使用。");
        }
    
        /**
         * 使用离线命令词时,有该回调说明离线语法资源加载成功
         */
        @Override
        public void onOfflineUnLoaded() {
            sendStatusMessage(" 离线资源卸载成功。");
        }
    
        @Override
        public void onAsrExit() {
            super.onAsrExit();
            sendStatusMessage("识别引擎结束并空闲中");
        }
    
        private void sendStatusMessage(String message) {
            sendMessage(message, status);
        }
    
        private void sendMessage(String message) {
            sendMessage(message, WHAT_MESSAGE_STATUS);
        }
    
        private void sendMessage(String message, int what) {
            sendMessage(message, what, false);
        }
    
    
        private void sendMessage(String message, int what, boolean highlight) {
    
    
            if (needTime && what != STATUS_FINISHED) {
                message += "  ;time=" + System.currentTimeMillis();
            }
            if (handler == null){
                Log.i(TAG, message );
                return;
            }
            Message msg = Message.obtain();
            msg.what = what;
            msg.arg1 = status;
            if (highlight) {
                msg.arg2 = 1;
            }
            msg.obj = message + "
    ";
            handler.sendMessage(msg);
        }
    }

    至此,百度语音SDK集成到baiduspeech中了,下一步就是在baiduspeech中创建UI对话框。

    (7)创建底部对话框SpeechBottomSheetDialog【根据实际情况自行修改UI布局

    1、在baiduspeech的build.gradle中引用recyclerview【版本号和项目的appcompat保持一致】【因为demo中用到了】

    apply plugin: 'com.android.library'
    
    android {
        compileSdkVersion 27
    
    
    
        defaultConfig {
            minSdkVersion 16
            targetSdkVersion 27
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
    
        implementation 'com.android.support:appcompat-v7:27.1.1'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'com.android.support.test:runner:1.0.2'
        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    
        //RecyclerView
        compile "com.android.support:recyclerview-v7:27.1.1"
    }

    2、对话框类、列表适配器类、布局文件xml文件、图片资源、动画style样式等复制到baiduspeech中

    3、这里主要标注下SpeechBottomSheetDialog.java中百度语音的相关代码

    package com.why.project.baiduspeech.dialog;
    
    import android.content.Context;
    import android.content.DialogInterface;
    import android.graphics.drawable.ColorDrawable;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.support.v4.app.DialogFragment;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.Window;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.baidu.speech.asr.SpeechConstant;
    import com.baidu.speech.utils.LogUtil;
    import com.why.project.baiduspeech.R;
    import com.why.project.baiduspeech.control.MyRecognizer;
    import com.why.project.baiduspeech.recognization.IStatus;
    import com.why.project.baiduspeech.recognization.MessageStatusRecogListener;
    import com.why.project.baiduspeech.recognization.StatusRecogListener;
    import com.why.project.baiduspeech.util.Logger;
    
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.util.ArrayList;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * Created by HaiyuKing
     * Used 语音识别底部对话框
     */
    
    public class SpeechBottomSheetDialog extends DialogFragment {
        private static final String TAG = SpeechBottomSheetDialog.class.getSimpleName();
    
        private Context mContext;
        /**View实例*/
        private View myView;
    
        private ImageView img_close;
        private ProgressBar loadProgressBar;
        private TextView tv_tishi;
        private RecyclerView result_list;
        private Button btn_start;
    
        private ArrayList<String> resultWordList;
        private SpeechResultAdapter speechResultAdapter;
    
        private String BtnStartText = "按一下开始听音";
        private String BtnStopText = "按一下结束听音";
        private String BtnSearchingText = "正在识别";
    
        private String TishiNoText = "没听清,请重说一遍";
    
        /**识别控制器,使用MyRecognizer控制识别的流程*/
        protected MyRecognizer myRecognizer;
        /**控制UI按钮的状态*/
        protected int status;
    
        protected Handler handler;
    
    
        public static SpeechBottomSheetDialog getInstance(Context mContext)
        {
            SpeechBottomSheetDialog speechBottomSheetDialog = new SpeechBottomSheetDialog();
            speechBottomSheetDialog.mContext = mContext;
    
            return speechBottomSheetDialog;
        }
    
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));//设置背景为透明,并且没有标题
            myView = inflater.inflate(R.layout.dialog_bottomsheet_speech, container, false);
            return myView;
    
        }
    
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onActivityCreated(savedInstanceState);
    
            initHandler();//初始化handler
            initRecog();//初始化语音
    
            initViews();
            initDatas();
            initEvents();
        }
    
        /**
         * 设置宽度和高度值,以及打开的动画效果
         */
        @Override
        public void onStart() {
            super.onStart();
            //设置对话框的宽高,必须在onStart中
            DisplayMetrics metrics = new DisplayMetrics();
            this.getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
            Window window = this.getDialog().getWindow();
            window.setLayout(metrics.widthPixels, this.getDialog().getWindow().getAttributes().height);
            window.setGravity(Gravity.BOTTOM);//设置在底部
            //打开的动画效果
            //设置dialog的 进出 动画
            getDialog().getWindow().setWindowAnimations(R.style.speechbottomsheetdialog_animation);
        }
    
        @Override
        public void onDismiss(DialogInterface dialog) {
            super.onDismiss(dialog);
            LogUtil.w(TAG,"{onDismiss}");
            //当对话框消失的时候统一执行销毁语音功能
            destroyRecog();//销毁语音
        }
    
    
        private void initViews() {
            img_close = (ImageView) myView.findViewById(R.id.img_close);
            loadProgressBar = (ProgressBar) myView.findViewById(R.id.loadProgressBar);
            tv_tishi = (TextView) myView.findViewById(R.id.tv_tishi);
            result_list = (RecyclerView) myView.findViewById(R.id.result_list);
            btn_start = (Button) myView.findViewById(R.id.btn_start);
        }
    
        /**初始化数据*/
        private void initDatas() {
            resultWordList = new ArrayList<String>();
            speechResultAdapter = null;
            //设置布局管理器
            LinearLayoutManager linerLayoutManager = new LinearLayoutManager(getActivity());
            result_list.setLayoutManager(linerLayoutManager);
    
            //可以设置为打开后自动识别语音
            startRecog();
            showProgress();
        }
    
        private void initEvents() {
            //关闭图标的点击事件
            img_close.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dismiss();
                }
            });
    
            //按钮的点击事件
            btn_start.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    switch (status) {
                        case IStatus.STATUS_NONE: // 初始状态
                            startRecog();
                            status = IStatus.STATUS_WAITING_READY;
                            updateBtnTextByStatus();//更改按钮的文本
                            //显示加载区域
                            showProgress();
                            break;
                        case IStatus.STATUS_WAITING_READY: // 调用本类的start方法后,即输入START事件后,等待引擎准备完毕。
                        case IStatus.STATUS_READY: // 引擎准备完毕。
                        case IStatus.STATUS_SPEAKING:
                        case IStatus.STATUS_FINISHED: // 长语音情况
                        case IStatus.STATUS_RECOGNITION:
                            stopRecog();
                            status = IStatus.STATUS_STOPPED; // 引擎识别中
                            updateBtnTextByStatus();//更改按钮的文本
                            break;
                        case IStatus.STATUS_STOPPED: // 引擎识别中
                            cancelRecog();
                            status = IStatus.STATUS_NONE; // 识别结束,回到初始状态
                            updateBtnTextByStatus();//更改按钮的文本
                            break;
                        default:
                            break;
                    }
                }
            });
        }
    
    
        /**
         * 显示加载进度区域,隐藏其他区域*/
        private void showProgress(){
            loadProgressBar.setVisibility(View.VISIBLE);
            tv_tishi.setVisibility(View.GONE);
            result_list.setVisibility(View.GONE);
        }
    
        /**
         * 显示文本提示区域,隐藏其他区域*/
        private void showTishi(){
            tv_tishi.setVisibility(View.VISIBLE);
            loadProgressBar.setVisibility(View.GONE);
            result_list.setVisibility(View.GONE);
        }
    
        /**
         * 显示语音结果区域,隐藏其他区域*/
        private void showListView(){
            result_list.setVisibility(View.VISIBLE);
            loadProgressBar.setVisibility(View.GONE);
            tv_tishi.setVisibility(View.GONE);
        }
    
        //======================================语音相关代码==========================================
        /**
         * 初始化handler*/
        private void initHandler(){
            handler = new Handler() {
                /*@param msg*/
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    handleMsg(msg);
                }
            };
            Logger.setHandler(handler);
        }
    
    
        /**
         * 在onCreate中调用。初始化识别控制类MyRecognizer
         */
        protected void initRecog() {
    
            StatusRecogListener listener = new MessageStatusRecogListener(handler);
            myRecognizer = new MyRecognizer(mContext,listener);
    
            status = IStatus.STATUS_NONE;//默认什么也没有做
        }
    
        /**
         * 销毁时需要释放识别资源。
         */
        protected void destroyRecog() {
            myRecognizer.release();
            Log.i(TAG, "destroyRecog");
        }
    
        /**
         * 开始录音,点击“开始”按钮后调用。
         */
        protected void startRecog() {
            Map<String, Object> params = new LinkedHashMap<String, Object>();
            params.put(SpeechConstant.ACCEPT_AUDIO_DATA, false);//是否保存音频
            params.put(SpeechConstant.DISABLE_PUNCTUATION, false);//是否禁用标点符号,在选择输入法模型的前提下生效【不禁用的话,说完一段话,就自带标点符号】
            params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);//暂时不知道什么意思
            params.put(SpeechConstant.PID, 1536); // 普通话 search搜索模型,默认,适用于短句,无逗号,可以有语义
            //params.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // 长语音,建议搭配input输入法模型
            myRecognizer.start(params);
        }
    
        /**
         * 开始录音后,手动停止录音。SDK会识别在此过程中的录音。点击“停止”按钮后调用。
         */
        private void stopRecog() {
            myRecognizer.stop();
        }
    
        /**
         * 开始录音后,取消这次录音。SDK会取消本次识别,回到原始状态。点击“取消”按钮后调用。
         */
        private void cancelRecog() {
            myRecognizer.cancel();
        }
    
    
        protected void handleMsg(Message msg) {
            Log.e(TAG,"msg.what="+msg.what);
            Log.e(TAG,"msg.obj.toString()="+msg.obj.toString());
            Log.e(TAG,"msg.arg2="+msg.arg2);
            switch (msg.what) { // 处理MessageStatusRecogListener中的状态回调
                case IStatus.STATUS_FINISHED:
                    //识别结束时候的调用【判断显示结果列表区域还是提示区域】
                    if (msg.arg2 == 1) {
                        //解析json字符串
                        try {
                            JSONObject msgObj = new JSONObject(msg.obj.toString());
                            String error = msgObj.getString("error");
                            if(error.equals("0")){
                                //解析结果集合,展现列表
                                JSONObject origin_resultObj = msgObj.getJSONObject("origin_result");
                                JSONObject resultObj = origin_resultObj.getJSONObject("result");
                                JSONArray wordList = resultObj.getJSONArray("word");
    
                                initList(wordList);//初始化集合数据
    
                                showListView();
                            }else if(error.equals("7")){
                                tv_tishi.setText(TishiNoText);
                                showTishi();
                            }else{//应该根据不同的状态值,显示不同的提示
                                tv_tishi.setText(TishiNoText);
                                showTishi();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                            tv_tishi.setText(TishiNoText);
                            showTishi();
                        }
                    }else if(msg.arg2 == 0){//无网络的情况
                        //解析json字符串{"origin_result":{"sn":"","error":2,"desc":"Network is not available","sub_error":2100},"error":2,"desc":"Network is not available","sub_error":2100}
                        try {
                            JSONObject msgObj = new JSONObject(msg.obj.toString());
                            JSONObject origin_resultObj = msgObj.getJSONObject("origin_result");
                            String error = origin_resultObj.getString("error");
                            if(error.equals("2")){
                                //解析结果集合,展现列表
                                String desc = origin_resultObj.getString("desc");
                                Toast.makeText(mContext,desc,Toast.LENGTH_SHORT).show();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                    status = msg.what;
                    updateBtnTextByStatus();
                    break;
                case IStatus.STATUS_NONE:
                case IStatus.STATUS_READY:
                case IStatus.STATUS_SPEAKING:
                case IStatus.STATUS_RECOGNITION:
                    status = msg.what;
                    updateBtnTextByStatus();
                    break;
                default:
                    break;
            }
        }
    
        /**更改按钮的文本*/
        private void updateBtnTextByStatus() {
            switch (status) {
                case IStatus.STATUS_NONE:
                    btn_start.setText(BtnStartText);
                    btn_start.setEnabled(true);
                    break;
                case IStatus.STATUS_WAITING_READY:
                case IStatus.STATUS_READY:
                case IStatus.STATUS_SPEAKING:
                case IStatus.STATUS_RECOGNITION:
                    btn_start.setText(BtnStopText);
                    btn_start.setEnabled(true);
                    break;
                case IStatus.STATUS_STOPPED:
                    btn_start.setText(BtnSearchingText);
                    btn_start.setEnabled(true);
                    break;
                default:
                    break;
            }
        }
    
        //========================================更改列表==========================
        /**获取集合数据,并显示*/
        private void initList(JSONArray wordList){
            //先清空
            if(resultWordList.size() > 0){
                resultWordList.clear();
            }
            //再赋值
            for(int i=0;i<wordList.length();i++){
                String wordItem = "";
                try {
                    wordItem = wordList.getString(i);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                resultWordList.add(wordItem);
            }
    
            if(speechResultAdapter == null){
                //设置适配器
                speechResultAdapter = new SpeechResultAdapter(getActivity(), resultWordList);
                result_list.setAdapter(speechResultAdapter);
                //添加分割线
                //设置添加删除动画
    
                //调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
                result_list.setSelected(true);
            }else{
                speechResultAdapter.notifyDataSetChanged();
            }
    
            speechResultAdapter.setOnItemClickLitener(new SpeechResultAdapter.OnItemClickLitener() {
                @Override
                public void onItemClick(int position) {
                    dismiss();
                    if(mOnResultListItemClickListener != null){
                        mOnResultListItemClickListener.onItemClick(resultWordList.get(position));
                    }
                }
            });
        }
    
        //=========================语音列表项的点击事件监听==============================
        public static abstract interface OnResultListItemClickListener
        {
            //语音结果列表项的点击事件接口
            public abstract void onItemClick(String title);
        }
    
        private OnResultListItemClickListener mOnResultListItemClickListener;
    
        public void seOnResultListItemClickListener(OnResultListItemClickListener mOnResultListItemClickListener)
        {
            this.mOnResultListItemClickListener = mOnResultListItemClickListener;
        }
    
    }

    4、如果想要使用长语音功能,请参考SpeechLongBottomSheetDialog.java文件

    package com.why.project.baiduspeech.dialog;
    
    import android.content.Context;
    import android.content.DialogInterface;
    import android.graphics.drawable.ColorDrawable;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.support.v4.app.DialogFragment;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.Window;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.baidu.speech.asr.SpeechConstant;
    import com.baidu.speech.utils.LogUtil;
    import com.why.project.baiduspeech.R;
    import com.why.project.baiduspeech.control.MyRecognizer;
    import com.why.project.baiduspeech.recognization.IStatus;
    import com.why.project.baiduspeech.recognization.MessageStatusRecogListener;
    import com.why.project.baiduspeech.recognization.StatusRecogListener;
    import com.why.project.baiduspeech.util.Logger;
    
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.util.ArrayList;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * Created by HaiyuKing
     * Used 普通话 input输入法模型,适用于长句及长语音,有逗号分割,无语义【基本上和SpeechBottomSheetDialog代码相同】
     */
    
    public class SpeechLongBottomSheetDialog extends DialogFragment {
        private static final String TAG = SpeechBottomSheetDialog.class.getSimpleName();
    
        private Context mContext;
        /**View实例*/
        private View myView;
    
        private ImageView img_close;
        private ProgressBar loadProgressBar;
        private TextView tv_tishi;
        private RecyclerView result_list;
        private Button btn_start;
    
        private ArrayList<String> resultWordList;
        private SpeechResultAdapter speechResultAdapter;
    
        private String BtnStartText = "按一下开始听音";
        private String BtnStopText = "按一下结束听音";
        private String BtnSearchingText = "正在识别";
    
        private String TishiNoText = "没听清,请重说一遍";
    
        /**识别控制器,使用MyRecognizer控制识别的流程*/
        protected MyRecognizer myRecognizer;
        /**控制UI按钮的状态*/
        protected int status;
    
        protected Handler handler;
    
    
        public static SpeechLongBottomSheetDialog getInstance(Context mContext)
        {
            SpeechLongBottomSheetDialog speechLongBottomSheetDialog = new SpeechLongBottomSheetDialog();
            speechLongBottomSheetDialog.mContext = mContext;
    
            return speechLongBottomSheetDialog;
        }
    
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));//设置背景为透明,并且没有标题
            myView = inflater.inflate(R.layout.dialog_bottomsheet_speech, container, false);
            return myView;
    
        }
    
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onActivityCreated(savedInstanceState);
    
            initHandler();//初始化handler
            initRecog();//初始化语音
    
            initViews();
            initDatas();
            initEvents();
        }
    
        /**
         * 设置宽度和高度值,以及打开的动画效果
         */
        @Override
        public void onStart() {
            super.onStart();
            //设置对话框的宽高,必须在onStart中
            DisplayMetrics metrics = new DisplayMetrics();
            this.getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
            Window window = this.getDialog().getWindow();
            window.setLayout(metrics.widthPixels, this.getDialog().getWindow().getAttributes().height);
            window.setGravity(Gravity.BOTTOM);//设置在底部
            //打开的动画效果
            //设置dialog的 进出 动画
            getDialog().getWindow().setWindowAnimations(R.style.speechbottomsheetdialog_animation);
        }
    
        @Override
        public void onDismiss(DialogInterface dialog) {
            super.onDismiss(dialog);
            LogUtil.w(TAG,"{onDismiss}");
            //当对话框消失的时候统一执行销毁语音功能
            destroyRecog();//销毁语音
        }
    
    
        private void initViews() {
            img_close = (ImageView) myView.findViewById(R.id.img_close);
            loadProgressBar = (ProgressBar) myView.findViewById(R.id.loadProgressBar);
            tv_tishi = (TextView) myView.findViewById(R.id.tv_tishi);
            result_list = (RecyclerView) myView.findViewById(R.id.result_list);
            btn_start = (Button) myView.findViewById(R.id.btn_start);
        }
    
        /**初始化数据*/
        private void initDatas() {
            resultWordList = new ArrayList<String>();
            speechResultAdapter = null;
            //设置布局管理器
            LinearLayoutManager linerLayoutManager = new LinearLayoutManager(getActivity());
            result_list.setLayoutManager(linerLayoutManager);
    
            btn_start.setText(BtnStartText);//显示文字,和下面的二选一即可 why
            //可以设置为打开后自动识别语音
            /*startRecog();
            showProgress();*/
        }
    
        private void initEvents() {
            //关闭图标的点击事件
            img_close.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dismiss();
                }
            });
    
            //按钮的点击事件
            btn_start.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    switch (status) {
                        case IStatus.STATUS_NONE: // 初始状态
                            startRecog();
                            status = IStatus.STATUS_WAITING_READY;
                            updateBtnTextByStatus();//更改按钮的文本
                            //显示加载区域
                            showProgress();
                            break;
                        case IStatus.STATUS_WAITING_READY: // 调用本类的start方法后,即输入START事件后,等待引擎准备完毕。
                        case IStatus.STATUS_READY: // 引擎准备完毕。
                        case IStatus.STATUS_SPEAKING:
                        case IStatus.STATUS_FINISHED: // 长语音情况
                        case IStatus.STATUS_RECOGNITION:
                            stopRecog();
                            status = IStatus.STATUS_STOPPED; // 引擎识别中
                            updateBtnTextByStatus();//更改按钮的文本
                            //对于长语音来讲,需要手动执行代码,否则还得点击一次才能取消why
                            btn_start.callOnClick();
                            break;
                        case IStatus.STATUS_STOPPED: // 引擎识别中
                            cancelRecog();
                            hiddenAll();//隐藏加载区域why
                            status = IStatus.STATUS_NONE; // 识别结束,回到初始状态
                            updateBtnTextByStatus();//更改按钮的文本
                            break;
                        default:
                            break;
                    }
                }
            });
        }
    
    
        /**
         * 显示加载进度区域,隐藏其他区域*/
        private void showProgress(){
            loadProgressBar.setVisibility(View.VISIBLE);
            tv_tishi.setVisibility(View.GONE);
            result_list.setVisibility(View.GONE);
        }
    
        /**
         * 显示文本提示区域,隐藏其他区域*/
        private void showTishi(){
            tv_tishi.setVisibility(View.VISIBLE);
            loadProgressBar.setVisibility(View.GONE);
            result_list.setVisibility(View.GONE);
        }
    
        /**
         * 显示语音结果区域,隐藏其他区域*/
        private void showListView(){
            result_list.setVisibility(View.VISIBLE);
            loadProgressBar.setVisibility(View.GONE);
            tv_tishi.setVisibility(View.GONE);
        }
    
        /**隐藏所有的区域【主要用于长语音】why*/
        private void hiddenAll(){
            result_list.setVisibility(View.GONE);
            loadProgressBar.setVisibility(View.GONE);
            tv_tishi.setVisibility(View.GONE);
        }
    
        //======================================语音相关代码==========================================
        /**
         * 初始化handler*/
        private void initHandler(){
            handler = new Handler() {
                /*@param msg*/
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    handleMsg(msg);
                }
            };
            Logger.setHandler(handler);
        }
    
    
        /**
         * 在onCreate中调用。初始化识别控制类MyRecognizer
         */
        protected void initRecog() {
    
            StatusRecogListener listener = new MessageStatusRecogListener(handler);
            myRecognizer = new MyRecognizer(mContext,listener);
    
            status = IStatus.STATUS_NONE;//默认什么也没有做
        }
    
        /**
         * 销毁时需要释放识别资源。
         */
        protected void destroyRecog() {
            myRecognizer.release();
            Log.i(TAG, "destroyRecog");
        }
    
        /**
         * 开始录音,点击“开始”按钮后调用。
         */
        protected void startRecog() {
            Map<String, Object> params = new LinkedHashMap<String, Object>();
            params.put(SpeechConstant.ACCEPT_AUDIO_DATA, false);//是否保存音频
            params.put(SpeechConstant.DISABLE_PUNCTUATION, false);//是否禁用标点符号,在选择输入法模型的前提下生效【不禁用的话,说完一段话,就自带标点符号】
            params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);//暂时不知道什么意思
            //下面的1936和1537选择其中一个 why
            //params.put(SpeechConstant.PID, 1936); // 普通话 far,远场模型,高级,适用于音源离麦克风较远(>1m)的录音,有逗号分隔,可以有语义
            params.put(SpeechConstant.PID, 1537); // 普通话 input输入法模型,适用于长句及长语音,有逗号分割,无语义
            params.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // 长语音,建议搭配input输入法模型
            myRecognizer.start(params);
        }
    
        /**
         * 开始录音后,手动停止录音。SDK会识别在此过程中的录音。点击“停止”按钮后调用。
         */
        private void stopRecog() {
            myRecognizer.stop();
        }
    
        /**
         * 开始录音后,取消这次录音。SDK会取消本次识别,回到原始状态。点击“取消”按钮后调用。
         */
        private void cancelRecog() {
            myRecognizer.cancel();
        }
    
    
        protected void handleMsg(Message msg) {
            switch (msg.what) { // 处理MessageStatusRecogListener中的状态回调
                case IStatus.STATUS_FINISHED:
                    //识别结束时候的调用【判断显示结果列表区域还是提示区域】
                    if (msg.arg2 == 1) {
                        //解析json字符串
                        try {
                            JSONObject msgObj = new JSONObject(msg.obj.toString());
                            String error = msgObj.getString("error");
                            if(error.equals("0")){
                                //直接输入到文本框中 why
                                JSONArray recognitionObj = msgObj.getJSONArray("results_recognition");
                                String result = recognitionObj.getString(0);
                                if(mOnResultListItemClickListener != null){
                                    mOnResultListItemClickListener.onItemClick(result);
                                }
                            }else if(error.equals("7")){
                                tv_tishi.setText(TishiNoText);
                                showTishi();
                            }else{//应该根据不同的状态值,显示不同的提示
                                tv_tishi.setText(TishiNoText);
                                showTishi();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                            tv_tishi.setText(TishiNoText);
                            showTishi();
                        }
                    }else if(msg.arg2 == 0){//无网络的情况
                        //解析json字符串{"origin_result":{"sn":"","error":2,"desc":"Network is not available","sub_error":2100},"error":2,"desc":"Network is not available","sub_error":2100}
                        try {
                            JSONObject msgObj = new JSONObject(msg.obj.toString());
                            JSONObject origin_resultObj = msgObj.getJSONObject("origin_result");
                            String error = origin_resultObj.getString("error");
                            if(error.equals("2")){
                                //解析结果集合,展现列表
                                String desc = origin_resultObj.getString("desc");
                                Toast.makeText(mContext,desc,Toast.LENGTH_SHORT).show();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                    status = msg.what;
                    updateBtnTextByStatus();
                    break;
                case IStatus.STATUS_NONE:
                case IStatus.STATUS_READY:
                case IStatus.STATUS_SPEAKING:
                case IStatus.STATUS_RECOGNITION:
                    status = msg.what;
                    updateBtnTextByStatus();
                    break;
                default:
                    break;
            }
        }
    
        /**更改按钮的文本*/
        private void updateBtnTextByStatus() {
            switch (status) {
                case IStatus.STATUS_NONE:
                    btn_start.setText(BtnStartText);
                    btn_start.setEnabled(true);
                    break;
                case IStatus.STATUS_WAITING_READY:
                case IStatus.STATUS_READY:
                case IStatus.STATUS_SPEAKING:
                case IStatus.STATUS_RECOGNITION:
                    btn_start.setText(BtnStopText);
                    btn_start.setEnabled(true);
                    break;
                case IStatus.STATUS_STOPPED:
                    btn_start.setText(BtnSearchingText);
                    btn_start.setEnabled(true);
                    break;
                default:
                    break;
            }
        }
    
        //========================================更改列表==========================
        /**获取集合数据,并显示*/
        private void initList(JSONArray wordList){
            //先清空
            if(resultWordList.size() > 0){
                resultWordList.clear();
            }
            //再赋值
            for(int i=0;i<wordList.length();i++){
                String wordItem = "";
                try {
                    wordItem = wordList.getString(i);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                resultWordList.add(wordItem);
            }
    
            if(speechResultAdapter == null){
                //设置适配器
                speechResultAdapter = new SpeechResultAdapter(getActivity(), resultWordList);
                result_list.setAdapter(speechResultAdapter);
                //添加分割线
                //设置添加删除动画
    
                //调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
                result_list.setSelected(true);
            }else{
                speechResultAdapter.notifyDataSetChanged();
            }
    
            speechResultAdapter.setOnItemClickLitener(new SpeechResultAdapter.OnItemClickLitener() {
                @Override
                public void onItemClick(int position) {
                    dismiss();
                    if(mOnResultListItemClickListener != null){
                        mOnResultListItemClickListener.onItemClick(resultWordList.get(position));
                    }
                }
            });
        }
    
        //=========================语音列表项的点击事件监听==============================
        public static abstract interface OnResultListItemClickListener
        {
            //语音结果列表项的点击事件接口
            public abstract void onItemClick(String title);
        }
    
        private OnResultListItemClickListener mOnResultListItemClickListener;
    
        public void seOnResultListItemClickListener(OnResultListItemClickListener mOnResultListItemClickListener)
        {
            this.mOnResultListItemClickListener = mOnResultListItemClickListener;
        }
    }

    三、使用方法

    (1)因为需要使用到运行时权限,所以参考《Android6.0运行时权限(基于RxPermission开源库)》在APP的build.gradle中引入第三方库

    (2)在APP的build.gradle中引入baiduspeech

    (3)在Activity中调用

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        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="com.why.project.baiduspeechdemo.MainActivity">
    
        <TextView
            android:id="@+id/tv_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0.448"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.325"/>
    
        <Button
            android:id="@+id/btn_openSpeechDialog"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="32dp"
            android:text="打开搜索模型语音识别对话框"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.419"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
    
        <Button
            android:id="@+id/btn_openSpeechLongDialog"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="16dp"
            android:text="打开input输入模型语音识别对话框"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.4"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/btn_openSpeechDialog"/>
    
    </android.support.constraint.ConstraintLayout>
    activity_main.xml
    package com.why.project.baiduspeechdemo;
    
    import android.Manifest;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.tbruyelle.rxpermissions2.RxPermissions;
    import com.why.project.baiduspeech.dialog.SpeechBottomSheetDialog;
    import com.why.project.baiduspeech.dialog.SpeechLongBottomSheetDialog;
    
    import io.reactivex.functions.Action;
    import io.reactivex.functions.Consumer;
    
    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = MainActivity.class.getSimpleName();
    
        private Button mOpenSpeechDialogBtn;
        private Button mOpenSpeechLongDialogBtn;
        private TextView mResultTv;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            onePermission();
    
            initViews();
            initEvents();
        }
    
        private void initViews() {
            mOpenSpeechDialogBtn = findViewById(R.id.btn_openSpeechDialog);
            mOpenSpeechLongDialogBtn = findViewById(R.id.btn_openSpeechLongDialog);
            mResultTv = findViewById(R.id.tv_result);
        }
    
        private void initEvents() {
            mOpenSpeechDialogBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //打开百度语音对话框
                    SpeechBottomSheetDialog speechBottomSheetDialog = SpeechBottomSheetDialog.getInstance(MainActivity.this);
                    speechBottomSheetDialog.seOnResultListItemClickListener(new SpeechBottomSheetDialog.OnResultListItemClickListener() {
                        @Override
                        public void onItemClick(String title) {
                            //填充到输入框中
                            mResultTv.setText(title);
                        }
                    });
                    speechBottomSheetDialog.show(getSupportFragmentManager(), TAG);
                }
            });
            mOpenSpeechLongDialogBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //打开百度语音对话框
                    SpeechLongBottomSheetDialog speechLongBottomSheetDialog = SpeechLongBottomSheetDialog.getInstance(MainActivity.this);
                    speechLongBottomSheetDialog.seOnResultListItemClickListener(new SpeechLongBottomSheetDialog.OnResultListItemClickListener() {
                        @Override
                        public void onItemClick(String title) {
                            //填充到输入框中
                            mResultTv.setText(mResultTv.getText()+title);
                        }
                    });
                    speechLongBottomSheetDialog.show(getSupportFragmentManager(), TAG);
                }
            });
    
        }
    
        /**只有一个运行时权限申请的情况*/
        private void onePermission(){
            RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance
            rxPermissions.request(Manifest.permission.RECORD_AUDIO,
                    Manifest.permission.READ_PHONE_STATE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE) //权限名称,多个权限之间逗号分隔开
                    .subscribe(new Consumer<Boolean>() {
                        @Override
                        public void accept(Boolean granted) throws Exception {
                            Log.e(TAG, "{accept}granted=" + granted);//执行顺序——1【多个权限的情况,只有所有的权限均允许的情况下granted==true】
                            if (granted) { // 在android 6.0之前会默认返回true
                                // 已经获取权限
                            } else {
                                // 未获取权限
                                Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show();
                            }
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
                        }
                    }, new Action() {
                        @Override
                        public void run() throws Exception {
                            Log.e(TAG,"{run}");//执行顺序——2
                        }
                    });
        }
    }

    混淆配置

    #=====================百度语音混淆=====================
    -keep class com.baidu.speech.**{*;}

    参考资料

    http://ai.baidu.com/tech/speech

    集成指南

    项目demo下载地址

    https://github.com/haiyuKing/BaiduSpeechDemo

  • 相关阅读:
    Redis单机操作
    Scala
    RDD算子
    Python学习之旅(七)
    python学习之旅(六)
    python学习之旅(五)
    python学习之旅(四)
    python学习之旅(二)
    python学习之旅(三)
    Python学习之旅(一)
  • 原文地址:https://www.cnblogs.com/whycxb/p/9367382.html
Copyright © 2011-2022 走看看