在AI席卷全球时,围绕语音交互的产品之争正愈演愈烈,苹果siri、亚马逊echo这些产品风靡全球的同时,国内外科技巨头、创业团队也在暗流涌动,各种智能音箱以及语音解决方案层出不穷。这种顺势发展的方式,造就了语音交互已经成为人工智能领域最成熟也是落地最快的技术。尤其是深度学习的起势,让语音识别、语音合成以及自然语言处理的发展速度提升到了一个新的高度。
发展到现在各个公司平台语音识别技术都相当成熟,Android 平台现在主要是基于谷歌、科大讯飞、百度等较为主流的SDK进行开发。谷歌的话,在国内的环境是需要修改,且访问速度慢;科大讯飞的话,虽然也是国内的,但只有一段时间的试用期;最终选择百度,既方便使用,又是白嫖产品。
1)百度搜索 “语音识别技术” 点击第一个超链接进入主页面
2)主页面菜单栏下 开发与教学=>SDK下载
3)找到 “语音识别Android SDK” 进行下载,后续也可进入 “使用说明”,查看源码的使用方式。
源代码提供的是一个在SDK的基础上封装了输入和输出格式的DEMO,主要由app、core、uiasr、uidialog这四个Module组成。其中core为核心且带有SDK的库,其下的 “ActivityMiniRecog类” (精简版识别,带有SDK唤醒运行的最少代码,仅仅展示如何调用)中的在线识别部分也是此次研究的重点。
参见DEMO中的ActivityMiniRecog类
SDK调用过程(在线识别)如下:
EventManager asr = EventManagerFactory.create(this, "asr");
// this是Activity或其它Context类
EventListener yourListener = new EventListener() {
@Override
public void onEvent(String name, String params, byte [] data, int offset, int length) {
if(name.equals(SpeechConstant.CALLBACK_EVENT_ASR_READY)){
// 引擎就绪,可以说话,一般在收到此事件后通过UI通知用户可以说话了
}
if(name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)){
// 一句话的临时结果,最终结果及语义结果
}
// ... 支持的输出事件和事件支持的事件参数见“输入和输出参数”一节
}
};
asr.registerListener(yourListener);
asr.send(SpeechConstant.ASR_START, json, null, 0, 0);
public void onEvent(String name, String params, byte [] data, int offset, int length);
asr.send(SpeechConstant.ASR_STOP, null, null, 0, 0);
//发送停止录音事件,提前结束录音等待识别结果
asr.send(SpeechConstant.ASR_CANCEL null, null, 0, 0);
//发送停止录音事件,提前结束录音等待识别结果
asr.unregisterListener(this);
demo 根目录下有doc_integration_DOCUMENT目录,里面有demo的测试图文教程和集成图文教程。其中 “ASR-INTEGRATION-helloworld-V3.01.docx” 中描述了如何集成Android识别SDK到新项目。
修改app/java/com.baidu.speech.recognizerdemo/MainActivity.java,集成完毕,直接启动app即可测试。
Compatible side by side NDK version was not found. Default is 20.0.5594570.
网上还有一种分别在新项目的 core/libs和新项目下新建一个core/src/main/jniLibs文件夹中导入jar和.so库的集成操作。但这种操作比起上述详细说明的操作,在我看来相对更为复杂,且容易出错。
主要是对 core模块中的 “ActivityMiniRecog类” 进行仿写。
<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.WRITE_EXTERNAL_STORAGE" />
<meta-data android:name="com.baidu.speech.APP_ID"
android:value="15590577" />
<meta-data
android:name="com.baidu.speech.API_KEY"
android:value="q2uPyBe6LmWTZlvb0g1dzcHV" />
<meta-data
android:name="com.baidu.speech.SECRET_KEY"
android:value="y7S7hAI894BB3LF1yHYmvQEus1B6wPvj" />
package com.example.speechtest;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.baidu.speech.EventListener;
import com.baidu.speech.EventManager;
import com.baidu.speech.EventManagerFactory;
import com.baidu.speech.asr.SpeechConstant;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
// 继承百度内部的监听
public class Main extends AppCompatActivity implements EventListener {
protected EditText Result;
protected Button btn;
// 设置事件管理器
private EventManager asr;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 动态申请权限
initPermission();
// 基于sdk集成1.1 初始化EventManager对象 设置自定义监听
asr = EventManagerFactory.create(this, "asr");
// 基于sdk集成1.3 注册自己的输出事件类
asr.registerListener(this);
Result = findViewById(R.id.EditView);
btn = findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast toast = Toast.makeText(getApplicationContext(),"开始识别",Toast.LENGTH_LONG);
toast.show();
start();
}
});
}
/**
* 基于SDK集成2.2 发送开始事件
* 点击开始按钮
*/
private void start(){
Map<String, Object> params = new LinkedHashMap<String, Object>();
// 基于SDK集成2.1 设置识别参数
params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);
String json = null; // 可以替换成自己的json
json = new JSONObject(params).toString(); // 这里可以替换成你需要测试的json
// 基于SDK集成2.2 发送start开始事件
asr.send(SpeechConstant.ASR_START, json, null, 0, 0);
}
/*
*基于sdk集成1.2 自定义输出事件类
*EventListener 回调方法
*/
@Override
public void onEvent(String name, String params, byte[] data, int offset, int length) {
if (SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL.equals(name)){
// 识别相关的结果都在这里CALLBACK_EVENT_ASR_PARTIAL
try {
// 结果类型result_type(临时结果partial_result,最终结果final_result)
// best_result最佳结果参数
//在2.2熟悉源码的2.2.1设置识别/唤醒输入参数中可查看原始Json
JSONObject object = new JSONObject(params);
String result = object.getString("best_result");
String resultType = object.getString("result_type");
// 判断识别是否结束
if ("final_result".equals(resultType)){
Result.setText(result);
Toast toast = Toast.makeText(getApplicationContext(),"识别已结束",Toast.LENGTH_SHORT);
toast.show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
/**
* android 6.0 以上需要动态申请权限
* (以下可直接从ActivityMiniRecog类中复制)
*/
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);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
// 此处为android 6.0以上动态授权的回调,用户自行实现。
}
}
简单熟悉了对在线语音识别的操作后,发现其应用是十分广泛的。最简单的例子就是可以应用于期末大作业NotePad的扩展应用语音搜索上,然后复杂些的,基于SDK进行封装输入和输出格式等,应用在手机语音助手,智能家具,机器人等等上。语音交互的发展越来越好,人们也在人机合一的道路上前进了一大步。
参考资料:
(官网)短语语音识别-Android-SDK使用说明
demo/doc_integration_DOCUMENT/ASR-INTEGRATION-helloworld-V3.01.docx