admin管理员组

文章数量:1562472

以下代码主要参考博客:
Android 科大讯飞语音识别

Android蓝牙串口开发部分请参照博客:
Android蓝牙串口开发

讯飞语音官方开发文档:
语音听写 Android SDK 文档

文章目录

  • 前言
  • 一、SDK的下载和导入
    • 1.SDK的下载
    • 2.新建工程
    • 3.配置项目
      • a.导包
      • b.权限
      • c.修改buid.gradle
  • 二、工具类
    • 1.Json解析类
    • 2.语音识别相关方法
      • a.变量声明
      • b.权限请求
      • c.语音监听
      • d.数据解析
      • c.参数配置
  • 三、调用代码编写
    • 1.点击事件的监听
    • 2.识别结果的显示
    • 3.语音命令识别和发送
  • 总结


前言

语音识别目前使用的比较频繁,项目需要使用语音识别用户指令,然后通过蓝牙通信传递指定,命令下位机做出反应。手机端主要负责命令的发送,选用讯飞语音SDK,学习官方文档和参考博客进行开发。写这篇博客记录一下。


一、SDK的下载和导入

这一部分可以可以参照讯飞语音官方文档来进行
官方文档:语音听写 Android SDK 文档

1.SDK的下载

首先在讯飞开放平台登录,没有账号进行注册

在控制台中,按流程创建新应用。


在下方的SDK下载中,下载相应的SDK。

解压完成后,先建立工程。

2.新建工程

打开Android Studio 开发平台,新建工程画好前端设计界面,建立工程这一部分就不在赘述。

3.配置项目

a.导包

将解压后的libs文件夹中的文件放入项目的libs文件夹中。


对Msc包右键,选择Add As Library导入,此时Msc包可展开。
然后再添加assets,将assets文件夹添加到工程main文件夹下面。

b.权限

导包完成后,我们需要给App一些必要权限,来使用手机的硬件功能。

<!--连接网络权限,用于执行云端语音能力 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!--获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <!--读取网络信息状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!--获取当前wifi状态 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <!--允许程序改变网络连接状态 -->
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
    <!--读取手机信息权限 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <!--读取联系人权限,上传联系人需要用到此权限 -->
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <!--外存储写权限,构建语法需要用到此权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!--外存储读权限,构建语法需要用到此权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <!--配置权限,用来记录应用配置信息 -->
    <uses-permission android:name="android.permission.WRITE_SETTINGS"
        tools:ignore="ProtectedPermissions" />
    <!--手机定位信息,用来为语义等功能提供定位,提供更精准的服务-->
    <!--定位信息是敏感信息,可通过Setting.setLocationEnable(false)关闭定位请求 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <!--如需使用人脸识别,还要添加:摄相头权限,拍照需要用到 -->
    <uses-permission android:name="android.permission.CAMERA" />

c.修改buid.gradle

打开buid.gradle后,在文件中做以下修改。

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation files('libs/Msc.jar')
}

然后点击Sync Now。

二、工具类

1.Json解析类

识别语音时,需要用到Json格式,所以需要有一个解析Json格式的类。
在工程中,新建一个JsonParser 的类。

package com.example.speechrecognitionforbluetooth;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;

/**
 * Json结果解析类
 */
public class JsonParser {

	public static String parseIatResult(String json) {
		StringBuffer ret = new StringBuffer();
		try {
			JSONTokener tokener = new JSONTokener(json);
			JSONObject joResult = new JSONObject(tokener);

			JSONArray words = joResult.getJSONArray("ws");
			for (int i = 0; i < words.length(); i++) {
				// 转写结果词,默认使用第一个结果
				JSONArray items = words.getJSONObject(i).getJSONArray("cw");
				JSONObject obj = items.getJSONObject(0);
				ret.append(obj.getString("w"));
//				如果需要多候选结果,解析数组其他字段
//				for(int j = 0; j < items.length(); j++)
//				{
//					JSONObject obj = items.getJSONObject(j);
//					ret.append(obj.getString("w"));
//				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return ret.toString();
	}
	
	public static String parseGrammarResult(String json) {
		StringBuffer ret = new StringBuffer();
		try {
			JSONTokener tokener = new JSONTokener(json);
			JSONObject joResult = new JSONObject(tokener);

			JSONArray words = joResult.getJSONArray("ws");
			for (int i = 0; i < words.length(); i++) {
				JSONArray items = words.getJSONObject(i).getJSONArray("cw");
				for(int j = 0; j < items.length(); j++)
				{
					JSONObject obj = items.getJSONObject(j);
					if(obj.getString("w").contains("nomatch"))
					{
						ret.append("没有匹配结果.");
						return ret.toString();
					}
					ret.append("【结果】" + obj.getString("w"));
					ret.append("【置信度】" + obj.getInt("sc"));
					ret.append("\n");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			ret.append("没有匹配结果.");
		} 
		return ret.toString();
	}
	
	public static String parseLocalGrammarResult(String json) {
		StringBuffer ret = new StringBuffer();
		try {
			JSONTokener tokener = new JSONTokener(json);
			JSONObject joResult = new JSONObject(tokener);

			JSONArray words = joResult.getJSONArray("ws");
			for (int i = 0; i < words.length(); i++) {
				JSONArray items = words.getJSONObject(i).getJSONArray("cw");
				for(int j = 0; j < items.length(); j++)
				{
					JSONObject obj = items.getJSONObject(j);
					if(obj.getString("w").contains("nomatch"))
					{
						ret.append("没有匹配结果.");
						return ret.toString();
					}
					ret.append("【结果】" + obj.getString("w"));
					ret.append("\n");
				}
			}
			ret.append("【置信度】" + joResult.optInt("sc"));

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

	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));
			/*JSONArray words = joResult.getJSONArray("results");
			for (int i = 0; i < words.length(); i++) {
				JSONObject obj = words.getJSONObject(i);
				ret.append(obj.getString(key));
			}*/
		} catch (Exception e) {
			e.printStackTrace();
		}
		return ret.toString();
	}
}

用来给语音识别的方法调用。

2.语音识别相关方法

现在需要实现语音识别的核心代码了,具体代码请参考官方文档Demo或访问参考博客学习,这里做一个展示。

a.变量声明

首先对比要的包进行导入。

import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechRecognizer;
import com.iflytek.cloud.ui.RecognizerDialog;
import com.iflytek.cloud.ui.RecognizerDialogListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import static android.widget.Toast.LENGTH_SHORT;

对于语音识别部分必要的变量进行声明。

//语音识别部分
    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 mBTN_SPEECH;
    private String resultType = "json";//结果内容数据格式

b.权限请求

调用过程中,需要动态的获取权限。

 /**
     * 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以上动态授权的回调,用户自行实现。
    }

c.语音监听

/**
     * 初始化监听器。
     */
    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/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();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

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

d.数据解析

分析得到的数据,利用Text将结果显示出来,然后分析结果,利用蓝牙将指令发送出去。

/**
     * 数据解析
     *
     * @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));
        }

        mET_DATE.setText(resultBuffer.toString());//听写结果显示
        sendCMDByBluetooth(resultBuffer.toString());//发送命令
    }

    

c.参数配置

/**
     * 参数设置
     *
     * @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");
    }

三、调用代码编写

在准备的工作基本完成之后,现在就需要在主程序的按逻辑去调用方法实现功能。

1.点击事件的监听

首先对于屏幕实现一个点击事件的监听。

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
	//这里省略了其他必要的代码,仅展示改动部分
    private static final String TAG ="MainActivity" ;
    
    /**
     * 屏幕点击响应事件
     * @param v
     */
    @Override
    public void onClick(View v) {
        if( null == mIat ){
            // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun/forum.php?mod=viewthread&tid=9688
            showMsg( "创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化" );
            return;
        }

        mIatResults.clear();//清除数据
        setParam(); // 设置参数
        mIatDialog.setListener(mRecognizerDialogListener);//设置监听
        mIatDialog.show();// 显示对话框
    }
}

2.识别结果的显示

结果的显示包含在数据分析的部分代码中

//该部分代码位于数据解析代码段中,具体情况自己按情况更改
mET_DATE.setText(resultBuffer.toString());//听写结果显示
sendCMDByBluetooth(resultBuffer.toString());//发送命令

3.语音命令识别和发送

检测到语音输入后,产生一个识别结果,显示出来后,根据自己需要的判断逻辑对识别结果进行分析,发出指令。

/**
     * 识别语音由蓝牙发送指令
     * @param Speech
     * 为语音指令
     */
    public void sendCMDByBluetooth(String Speech){
        //去掉命令中的句号
        String CMD = Speech.substring(0,Speech.indexOf("。"));

        //判断是否打开蓝牙
        if (CONNECT_STATUS) {
            switch (CMD){
                case "打开灯光":{
                    write("A");
                    Toast.makeText(MainActivity.this, "指令:打开灯光 CMD:A", LENGTH_SHORT).show();
                    break;
                }
                case "关闭灯光":{
                    write("B");
                    Toast.makeText(MainActivity.this, "指令:关闭灯光 CMD:B", LENGTH_SHORT).show();
                    break;
                }
                case "打开风扇":{
                    write("C");
                    Toast.makeText(MainActivity.this, "指令:打开风扇 CMD:C", LENGTH_SHORT).show();
                    break;
                }
                case "关闭风扇":{
                    write("D");
                    Toast.makeText(MainActivity.this, "指令:关闭风扇 CMD:D", LENGTH_SHORT).show();
                    break;
                }
            }
        } else {
            Toast.makeText(getApplicationContext(), "请先连接蓝牙", Toast.LENGTH_SHORT).show();
        }

    }

总结

对我来说,该项目主要的难点在于对SDK的使用,以及类的调用。更灵活的使用还需要加深理解。

关于Android经典蓝牙串口开发在我的另一篇博客有记录。
Android蓝牙串口开发

本文标签: 飞语功能android