2023-09-14-在Unity中接入语音相关功能(一)
在Unity中接入语音相关功能(一)
若要在Unity中实现一个类似语音助手的功能,那则需使用到语音识别相关技术。
语音相关功能
-
语音识别(Automatic Speech Recognition,ASR):识别语音内容,转化为相应的文字。
-
语音合成(Text to Speech,TTS):将文字信息转变为可以听得懂的、流利的汉语。
-
语音唤醒 (VoiceWake up): 通过识别辨认特定的词汇来返回预置好的结果(唤醒设备)。
以上,是常使用的语音技术。而现在有许多平台(讯飞、百度AI、思必驰...)带有Andorid (Java)SDK供我们选择。
此外,ASRT(Auto Speech Recognition Tool)也提供了Java版SDK。通过这个开源库也可实现ASR。
在Unity中调用后台语音服务
前提条件
安卓设备安装了作者实现的“语音助手”APK。
unity中调用流程
导入依赖
1、将aar拷贝至Unity的Assets/Plugins/Android/目录下即可。
-
eq-aidl-1.0.2.aar :封装了调用“语音助手”的接口
-
unitydev-1.0.6.aar :简化了Unity与Java的互相调用
2、导入当前仓库中的VoiceAssistantService脚本

编写C#脚本
下面这个示例脚本,演示了如何调用“语音助手”的服务。
通过Set...Callback的方式,即可取到不同阶段的回调结果。
注:EqLog会去执行android.util.Log的相关方法。以便于在Logcat中查看日志
public class VoiceService : MonoBehaviour
{
private void Awake()
{
//绑定“语音助手”的后台服务
VoiceAssistantService.Instance.Bind();
EqLog.i("Ikkyu","Bind: ");
}
void Start()
{
//设置相关事件回调
VoiceAssistantService.Instance
.SetAsrResultsCallback(UpdateText)
.SetAsrReadyCallback(OnAsrReady)
.SetAsrEndCallback(OnAsrEnd)
.SetWakeupCallback(WhenWakeup)
.SetTtsSpeechStartCallback(WhenTtsStart)
.SetTtsSpeechFinishCallback(WhenTtsEnd);
//启动服务
VoiceAssistantService.Instance.StartService();
EqLog.i("Ikkyu","Start: ");
}
private void OnDestroy()
{
//取消绑定
VoiceAssistantService.Instance.Unbind();
}
void UpdateText(string text)
{
//语音识别结果
EqLog.i("Ikkyu","UpdateText: " + text);
}
void OnAsrReady()
{
EqLog.i("Ikkyu","OnAsrReady");
}
void OnAsrEnd()
{
EqLog.i("Ikkyu","OnAsrEnd");
}
void WhenWakeup(double confiendce,string wakeupWord)
{
EqLog.i("Ikkyu","word:" + wakeupWord + "_" + confiendce);
}
void WhenTtsStart()
{
EqLog.i("Ikkyu","WhenTtsStart");
}
void WhenTtsEnd()
{
EqLog.i("Ikkyu","WhenTtsEnd");
}
}
创建测试场景
在unity编辑器中创建“语音服务”场景,新建一个名为“语音服务”的游戏对象,并挂载“VoiceService”脚本。

至此,打包运行,结束!
运行结果
查看logcat日志结果如下:

可见,唤醒词唤醒后,监测到“打开微信”的语音指令。
参考流程图如下:

实现语音指令任务
在上面的运行过程中,监听到”打开微信“这个指令。那么如何我们怎样实现这些指令任务呢?
实现思路
- 提取指令中的关键词,匹配任务类型
- 根据对应任务类型的指令,调用对应的方法
此外,为了语音交互更加友好,还得添加任务执行后的语音反馈。
关键类
抽象任务
注:以下在Android Studio中编写
创建一个任务的抽象类,列出所必需的公用方法
/**
* 语音任务
* <pre>
* 子类必需要有一个init()方法,参数按业务需要添加。init方法内必须给taskTxt赋值
* 此外,子类需要实现{@link #getKey()}、{@link #getTips(int)} ()}、{@link #exec(int)} ()}
* </pre>
* @version 1.0
**/
public abstract class BaseTask {
/**
* 执行任务
* @param taskId 任务指令
* @return 任务返回状态
*/
@Keep
protected abstract void exec(int taskId);
/**
* 获取提示语
* <p>如:(正在为您)打开蓝牙</p>
* @return
*/
protected abstract String getTips(int taskId);
/**
* 设置任务指令
* @param commandTxt
*/
public void setCommandTxt(CommandTxt[] commandTxt){
this.taskTxt = commandTxt;
}
//...
}
任务示例
以实现“打开微信”为例
新增继承BaseTask的子类,名为“ProgramTask”。
/**
* 程序打开任务
**/
public class ProgramTask extends BaseTask{
/**
* 程序包名
*/
private String packageName;
private final int openCmd = 0;
private String programName;
//...
/**
* 初始化
* @param context 上下文
* @param programName 程序名称
* @param packageName 程序包名
*/
public ProgramTask init(Context context,String programName,String packageName) {
//...
return this;
}
@Override
protected void exec(int taskId) {
//注意:若语音指令匹配,会自动调用本方法
if (packageName == null) {
Log.e(getClass().getSimpleName(), "exec: packageName was null.");
}
switch (taskId){
case openCmd:
openApp();
}
}
//打开程序
private void openApp() {
Intent launchIntent = getContext().getPackageManager().getLaunchIntentForPackage(packageName);
if (launchIntent != null) {
getContext().startActivity(launchIntent);
} else {
// 应用程序未安装或不存在
Log.e(getClass().getSimpleName(), "exec: packageName was not exist.");
}
}
//...
}
语音任务管理器
采用单例,实现任务管理器,实现指令任务匹配。
/**
* 语音任务管理器
**/
public class VoiceTaskManager {
private static volatile VoiceTaskManager instance = null;
private HashMap<String,BaseTask> taskHashMap;
//...
private VoiceTaskManager() {
this.taskHashMap = new HashMap<String,BaseTask>();
//...
}
/**
* 获取实例
*/
public static VoiceTaskManager getInstance() {
if (instance == null){
synchronized (VoiceTaskManager.class){
if (instance == null){
instance = new VoiceTaskManager();
}
}
}
return instance;
}
/**
* 任务注册
* @param task 任务项
*/
public void register(BaseTask task){
//...
taskHashMap.put(task.getKey(), task);
}
/**
* 移除已注册的任务
* @param task 任务
*/
public void unregister(BaseTask task){
//...
taskHashMap.remove(task);
}
/**
* 执行语音任务
* @param voiceTest 语音文本内容
* @return 关键词,若返回null,则表示没有匹配到任务
*/
public String execTask(String voiceTest){
//...
}
//...
}
调用方式
参考下列流程调用接口
- 程序初始化时,执行下面的方法注册语音任务。
private void initVoiceTask() {
//todo 后续实现通过配置文件自动载入
VoiceTaskManager instance = VoiceTaskManager.getInstance();
//添加蓝牙开关任务
instance.register(new BluetoothTask().init(this));
//添加音量调节任务
instance.register(new VolumeTask().init(this));
//添加亮度调节任务
instance.register(new BrightnessTask().init(this));
//自定义程序
instance.register(new ProgramTask().init(this,"QQ","com.tencent.mobileqq"));
instance.register(new ProgramTask().init(this,"微信","com.tencent.mm"));
}
- 在匹配到语音指令后(如:打开微信),执行任务
示例如下:
VoiceTaskManager.getInstance().execTask("打开微信");
其它
起初,“后台语音服务”采用内置录音机采集音频,但是一些设备不具备内置麦克风。
若是设备具有蓝牙硬件,那么也可以实现语音交互。

浙公网安备 33010602011771号