一.了解语音唤醒原理
语音唤醒的过程,从外部麦克风获取一段音频,然后将该音频进行算法处理,和设置的唤醒关键词进行比较,该关键次进行处理后是一个声学模型文件.jet文件,两者比对,便可确定是否匹配,匹配成功可以唤醒。
即简单理解:输入音频与声学模型比较,两者差距不大则成功匹配。
二.sdk文件获取
在科大讯飞官网注册账号后,根据提示下载,语音唤醒的sdk文件,并且记住appid,在后面的程序中将会使用。
三.操作步骤
操作过程,
步骤1.导入libs包文件(在自己的工程项目中)
步骤2.导入.jet声学模型文件,其中的assets文件夹,下创建ivm文件夹,存放.jet文件,该文件是声学模型文件(简单理解)其余的文件可以根据项目需求进行选择。
步骤3.这里建议libs中的两个文件夹arm64-v8a和armeabi-v7a两个文件夹,在java同级目录创建(原因我在测试的时候发现放在libs下不能正常运行,建议两个位置都放)
步骤4.添加程序权限
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.usb.host" />
<uses-permission android:name="android.permission.USB_PERMISSION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
步骤5.参考科大讯飞源码,编写程序
package com.example.appeal;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechUtility;
import com.iflytek.cloud.VoiceWakeuper;
import com.iflytek.cloud.WakeuperListener;
import com.iflytek.cloud.WakeuperResult;
import com.iflytek.cloud.util.ResourceUtil;
import org.json.JSONException;
import org.json.JSONObject;
public class MainActivity extends AppCompatActivity {
//唤醒的阈值,就相当于门限值,当用户输入的语音的置信度大于这一个值的时候,才被认定为成功唤醒。
private int curThresh = 1450;
//是否持续唤醒
private String keep_alive = "1";
private String ivwNetMode = "0";
// 语音唤醒对象
private VoiceWakeuper mIvw;
//存储唤醒词的ID
private String wordID = "";
// 唤醒结果内容
private String resultString;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeIflytek();
tv=(TextView)findViewById(R.id.tv);
Button button_start=findViewById(R.id.bt_satrt);
// 初始化唤醒对象
mIvw = VoiceWakeuper.createWakeuper(MainActivity.this, null);
button_start.setOnClickListener(view -> {
//非空判断,防止因空指针使程序崩溃
mIvw = VoiceWakeuper.getWakeuper();
if(mIvw != null) {
resultString="";
tv.setText(resultString);
// 清空参数
mIvw.setParameter(SpeechConstant.PARAMS, null);
// 唤醒门限值,根据资源携带的唤醒词个数按照“id:门限;id:门限”的格式传入
mIvw.setParameter(SpeechConstant.IVW_THRESHOLD, "0:"+ curThresh);
// 设置唤醒模式
mIvw.setParameter(SpeechConstant.IVW_SST, "wakeup");
// 设置持续进行唤醒
mIvw.setParameter(SpeechConstant.KEEP_ALIVE, keep_alive);
// 设置闭环优化网络模式
mIvw.setParameter(SpeechConstant.IVW_NET_MODE, ivwNetMode);
// 设置唤醒资源路径
mIvw.setParameter(SpeechConstant.IVW_RES_PATH, getResource());
// 设置唤醒录音保存路径,保存最近一分钟的音频
mIvw.setParameter( SpeechConstant.IVW_AUDIO_PATH, Environment.getExternalStorageDirectory().getPath()+"/msc/ivw.wav" );
System.out.println(Environment.getExternalStorageDirectory().getPath()+"/msc/ivw.wav");
mIvw.setParameter( SpeechConstant.AUDIO_FORMAT, "wav" );
// 如有需要,设置 NOTIFY_RECORD_DATA 以实时通过 onEvent 返回录音音频流字节
//mIvw.setParameter( SpeechConstant.NOTIFY_RECORD_DATA, "1" );
// 启动唤醒
mIvw.startListening(new MyWakeuperListener());
System.out.println("开启唤醒");
}else{
showTip("唤醒未初始化");
}
});
}
private void initializeIflytek()
{
StringBuffer param = new StringBuffer();
//IflytekAPP_id为我们申请的Appid,填写自己的
param.append("appid="+Appid);
param.append(",");
// 设置使用v5+
param.append(SpeechConstant.ENGINE_MODE+"="+ SpeechConstant.MODE_MSC);
SpeechUtility.createUtility(MainActivity.this, param.toString());
System.out.println("授权");
}
public void showTip(String str){
Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();
}
/**
* 获取唤醒词功能
* @return 返回文件位置,下面也是appid
*/
private String getResource() {
final String resPath = ResourceUtil.generateResourcePath(MainActivity.this,ResourceUtil.RESOURCE_TYPE.assets, "ivw/"+"appid"+".jet");
System.out.println("声学模型文件存放路径:"+resPath);
return resPath;
}
/**
* 唤醒词监听类
* @author Administrator
*
*/
private class MyWakeuperListener implements WakeuperListener {
//开始说话
@Override
public void onBeginOfSpeech() {
System.out.println("s");
}
//错误码返回
@Override
public void onError(SpeechError arg0) {
System.out.println("w");
}
@Override
public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {
System.out.println("a");
}
@Override
public void onVolumeChanged(int i) {
System.out.println("q");
}
@Override
public void onResult(WakeuperResult result) {
System.out.println("c");
// if (!"1".equalsIgnoreCase(keep_alive)) {
setRadioEnable(true);
// }
try {
String text = result.getResultString();
JSONObject object;
object = new JSONObject(text);
StringBuffer buffer = new StringBuffer();
buffer.append("【RAW】 " + text);
buffer.append("\n");
buffer.append("【操作类型】" + object.optString("sst"));
buffer.append("\n");
buffer.append("【唤醒词id】" + object.optString("id"));
buffer.append("\n");
buffer.append("【得分】" + object.optString("score"));
buffer.append("\n");
buffer.append("【前端点】" + object.optString("bos"));
buffer.append("\n");
buffer.append("【尾端点】" + object.optString("eos"));
resultString = buffer.toString();
} catch (JSONException e) {
e.printStackTrace();
}
tv.setText(resultString);
}
}
}
步骤6.布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tv"
android:text="hello test"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:textSize="30dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/bt_satrt"
android:text="start"/>
</LinearLayout>