简介

语音识别是一项将语音转换为文本的技术,想象一下它如何在游戏中发挥作用?发出命令操纵控制面板或者游戏角色、直接与 NPC 对话、提升交互性等等,都有可能。本文将介绍如何使用 Hugging Face Unity API 在 Unity 游戏中集成 SOTA 语音识别功能。

您可以访问 itch.io 网站 下载 Unity 游戏样例,亲自尝试一下语音识别功能。

先决条件

阅读文本可能需要了解一些 Unity 的基本概念。除此之外,您还需安装 Hugging Face Unity API,可以点击 之前的博文 阅读 API 安装说明。

步骤

1. 设置场景

在本教程中,我们将设置一个非常简单的场景。玩家可以点击按钮来开始或停止录制语音,识别音频并转换为文本。

首先我们新建一个 Unity 项目,然后创建一个包含三个 UI 组件的画布 (Canvas):

  1. 开始按钮: 按下以开始录制语音。
  2. 停止按钮: 按下以停止录制语音。
  3. 文本组件 (TextMeshPro): 显示语音识别结果文本的地方。

2. 创建脚本

创建一个名为 SpeechRecognitionTest 的脚本,并将其附加到一个空的游戏对象 (GameObject) 上。

在脚本中,首先定义对 UI 组件的引用:

[SerializeField] private Button startButton;
[SerializeField] private Button stopButton;
[SerializeField] private TextMeshProUGUI text;

在 inspector 窗口中分配对应组件。

然后,使用 Start() 方法为开始和停止按钮设置监听器:

private void Start() {
startButton.onClick.AddListener(StartRecording);
stopButton.onClick.AddListener(StopRecording);
}

此时,脚本中的代码应该如下所示:

using TMPro;
using UnityEngine;
using UnityEngine.UI; public class SpeechRecognitionTest : MonoBehaviour {
[SerializeField] private Button startButton;
[SerializeField] private Button stopButton;
[SerializeField] private TextMeshProUGUI text; private void Start() {
startButton.onClick.AddListener(StartRecording);
stopButton.onClick.AddListener(StopRecording);
} private void StartRecording() { } private void StopRecording() { }
}

3. 录制麦克风语音输入

现在,我们来录制麦克风语音输入,并将其编码为 WAV 格式。这里需要先定义成员变量:

private AudioClip clip;
private byte[] bytes;
private bool recording;

然后,在 StartRecording() 中,使用 Microphone.Start() 方法实现开始录制语音的功能:

private void StartRecording() {
clip = Microphone.Start(null, false, 10, 44100);
recording = true;
}

上面代码实现以 44100 Hz 录制最长为 10 秒的音频。

当录音时长达到 10 秒的最大限制,我们希望录音行为自动停止。为此,需要在 Update() 方法中写上以下内容:

private void Update() {
if (recording && Microphone.GetPosition(null) >= clip.samples) {
StopRecording();
}
}

接着,在 StopRecording() 中,截取录音片段并将其编码为 WAV 格式:

private void StopRecording() {
var position = Microphone.GetPosition(null);
Microphone.End(null);
var samples = new float[position * clip.channels];
clip.GetData(samples, 0);
bytes = EncodeAsWAV(samples, clip.frequency, clip.channels);
recording = false;
}

最后,我们需要实现音频编码的 EncodeAsWAV() 方法,这里直接使用 Hugging Face API,只需要将音频数据准备好即可:

private byte[] EncodeAsWAV(float[] samples, int frequency, int channels) {
using (var memoryStream = new MemoryStream(44 + samples.Length * 2)) {
using (var writer = new BinaryWriter(memoryStream)) {
writer.Write("RIFF".ToCharArray());
writer.Write(36 + samples.Length * 2);
writer.Write("WAVE".ToCharArray());
writer.Write("fmt ".ToCharArray());
writer.Write(16);
writer.Write((ushort)1);
writer.Write((ushort)channels);
writer.Write(frequency);
writer.Write(frequency * channels * 2);
writer.Write((ushort)(channels * 2));
writer.Write((ushort)16);
writer.Write("data".ToCharArray());
writer.Write(samples.Length * 2); foreach (var sample in samples) {
writer.Write((short)(sample * short.MaxValue));
}
}
return memoryStream.ToArray();
}
}

完整的脚本如下所示:

using System.IO;
using TMPro;
using UnityEngine;
using UnityEngine.UI; public class SpeechRecognitionTest : MonoBehaviour {
[SerializeField] private Button startButton;
[SerializeField] private Button stopButton;
[SerializeField] private TextMeshProUGUI text; private AudioClip clip;
private byte[] bytes;
private bool recording; private void Start() {
startButton.onClick.AddListener(StartRecording);
stopButton.onClick.AddListener(StopRecording);
} private void Update() {
if (recording && Microphone.GetPosition(null) >= clip.samples) {
StopRecording();
}
} private void StartRecording() {
clip = Microphone.Start(null, false, 10, 44100);
recording = true;
} private void StopRecording() {
var position = Microphone.GetPosition(null);
Microphone.End(null);
var samples = new float[position * clip.channels];
clip.GetData(samples, 0);
bytes = EncodeAsWAV(samples, clip.frequency, clip.channels);
recording = false;
} private byte[] EncodeAsWAV(float[] samples, int frequency, int channels) {
using (var memoryStream = new MemoryStream(44 + samples.Length * 2)) {
using (var writer = new BinaryWriter(memoryStream)) {
writer.Write("RIFF".ToCharArray());
writer.Write(36 + samples.Length * 2);
writer.Write("WAVE".ToCharArray());
writer.Write("fmt ".ToCharArray());
writer.Write(16);
writer.Write((ushort)1);
writer.Write((ushort)channels);
writer.Write(frequency);
writer.Write(frequency * channels * 2);
writer.Write((ushort)(channels * 2));
writer.Write((ushort)16);
writer.Write("data".ToCharArray());
writer.Write(samples.Length * 2); foreach (var sample in samples) {
writer.Write((short)(sample * short.MaxValue));
}
}
return memoryStream.ToArray();
}
}
}

如要测试该脚本代码是否正常运行,您可以在 StopRecording() 方法末尾添加以下代码:

File.WriteAllBytes(Application.dataPath + "/test.wav", bytes);

好了,现在您点击 Start 按钮,然后对着麦克风说话,接着点击 Stop 按钮,您录制的音频将会保存为 test.wav 文件,位于工程目录的 Unity 资产文件夹中。

4. 语音识别

接下来,我们将使用 Hugging Face Unity API 对编码音频实现语音识别。为此,我们创建一个 SendRecording() 方法:

using HuggingFace.API;

private void SendRecording() {
HuggingFaceAPI.AutomaticSpeechRecognition(bytes, response => {
text.color = Color.white;
text.text = response;
}, error => {
text.color = Color.red;
text.text = error;
});
}

该方法实现将编码音频发送到语音识别 API,如果发送成功则以白色显示响应,否则以红色显示错误消息。

别忘了在 StopRecording() 方法的末尾调用 SendRecording():

private void StopRecording() {
/* other code */
SendRecording();
}

5. 最后润色

最后来提升一下用户体验,这里我们使用交互性按钮和状态消息。

开始和停止按钮应该仅在适当的时候才产生交互效果,比如: 准备录制、正在录制、停止录制。

在录制语音或等待 API 返回识别结果时,我们可以设置一个简单的响应文本来显示对应的状态信息。

完整的脚本如下所示:

using System.IO;
using HuggingFace.API;
using TMPro;
using UnityEngine;
using UnityEngine.UI; public class SpeechRecognitionTest : MonoBehaviour {
[SerializeField] private Button startButton;
[SerializeField] private Button stopButton;
[SerializeField] private TextMeshProUGUI text; private AudioClip clip;
private byte[] bytes;
private bool recording; private void Start() {
startButton.onClick.AddListener(StartRecording);
stopButton.onClick.AddListener(StopRecording);
stopButton.interactable = false;
} private void Update() {
if (recording && Microphone.GetPosition(null) >= clip.samples) {
StopRecording();
}
} private void StartRecording() {
text.color = Color.white;
text.text = "Recording...";
startButton.interactable = false;
stopButton.interactable = true;
clip = Microphone.Start(null, false, 10, 44100);
recording = true;
} private void StopRecording() {
var position = Microphone.GetPosition(null);
Microphone.End(null);
var samples = new float[position * clip.channels];
clip.GetData(samples, 0);
bytes = EncodeAsWAV(samples, clip.frequency, clip.channels);
recording = false;
SendRecording();
} private void SendRecording() {
text.color = Color.yellow;
text.text = "Sending...";
stopButton.interactable = false;
HuggingFaceAPI.AutomaticSpeechRecognition(bytes, response => {
text.color = Color.white;
text.text = response;
startButton.interactable = true;
}, error => {
text.color = Color.red;
text.text = error;
startButton.interactable = true;
});
} private byte[] EncodeAsWAV(float[] samples, int frequency, int channels) {
using (var memoryStream = new MemoryStream(44 + samples.Length * 2)) {
using (var writer = new BinaryWriter(memoryStream)) {
writer.Write("RIFF".ToCharArray());
writer.Write(36 + samples.Length * 2);
writer.Write("WAVE".ToCharArray());
writer.Write("fmt ".ToCharArray());
writer.Write(16);
writer.Write((ushort)1);
writer.Write((ushort)channels);
writer.Write(frequency);
writer.Write(frequency * channels * 2);
writer.Write((ushort)(channels * 2));
writer.Write((ushort)16);
writer.Write("data".ToCharArray());
writer.Write(samples.Length * 2); foreach (var sample in samples) {
writer.Write((short)(sample * short.MaxValue));
}
}
return memoryStream.ToArray();
}
}
}

祝贺!现在您可以在 Unity 游戏中集成 SOTA 语音识别功能了!

如果您有任何疑问,或想更多地参与 Hugging Face for Games 系列,可以加入 Hugging Face Discord 频道!


英文原文: https://hf.co/blog/unity-asr

作者: Dylan Ebert

译者: SuSung-boy

审校/排版: zhongdongy (阿东)

如何在 Unity 游戏中集成 AI 语音识别?的更多相关文章

  1. Unity优化方向——优化Unity游戏中的脚本(译)

    原文地址:https://unity3d.com/cn/learn/tutorials/topics/performance-optimization/optimizing-scripts-unity ...

  2. Unity3d 游戏中集成Firebase 统计和Admob广告最新中文教程

    之前写过俩相关的教程,最近发现插件官方更新了不少内容,所以也更新一篇Firebase Admob Unity3d插件的教程,希望能帮到大家. Firebase Admob Unity3d插件是一个Un ...

  3. [译]如何在Unity编辑器中添加你自己的工具

    在这篇教程中你会学习如何扩展你的Unity3D编辑器,以便在你的项目中更好的使用它.你将会学习如何绘制你自己的gizmo,用代码来实现创建和删除物体,创建编辑器窗口,使用组件,并且允许用户撤销他们所作 ...

  4. Unity优化方向——优化Unity游戏中的垃圾回收(译)

    介绍 当我们的游戏运行时,它使用内存来存储数据.当不再需要该数据时,存储该数据的内存将被释放,以便可以重用.垃圾是用来存储数据但不再使用的内存的术语.垃圾回收是该内存再次可用以进行重用的进程的名称. ...

  5. Unity优化方向——优化Unity游戏中的图形渲染(译)

    CPU bound:CPU性能边界,是指CPU计算时一直处于占用率很高的情况. GPU bound:GPU性能边界,同样的是指GPU计算时一直处于占用率很高的情况. 原文:https://unity3 ...

  6. Unity游戏中使用贝塞尔曲线

    孙广东   2015.8.15 比方在3D rpg游戏中.我们想设置弹道,不同的轨迹类型! 目的:这篇文章的主要目的是要给你关于在游戏怎样使用贝塞尔曲线的基本想法. 贝塞尔曲线是最主要的曲线,一般用在 ...

  7. 如何在Cocos2D游戏中实现A*寻路算法(八)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  8. 如何在Cocos2D游戏中实现A*寻路算法(六)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  9. 如何在Cocos2D游戏中实现A*寻路算法(四)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  10. 如何在Cocos2D游戏中实现A*寻路算法(一)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

随机推荐

  1. 解决报错Invalid bound statement (not found)

    解决报错Invalid bound statement (not found) 问题描述: 在玩mybatis-plus的时候,在测试类写了一个测试批量插入的方法,结果就报错: 它的意思是 无效的跳转 ...

  2. git中的ole mode 和 new mode提示问题

    git status 显示如下(文件内容其实并没有改变): old mode 100644 new mode 100755 原因是: 使用chmod修改过文件权限后,filemode会有变化. 解决办 ...

  3. TechEmpower 22轮Web框架 性能评测:.NET 8 战绩斐然

    自从2022年7月第21轮公布的测试以后,一年后 的2023年10月17日 发布了 TechEmpower 22轮测试报告 刚刚发布:Round 22 results - TechEmpower Fr ...

  4. top命令和ps命令

    top 命令和 ps 命令 ps 命令 ps 命令查看系统的瞬时信息.通常使用ps -ef | grep 进程名, -e 代表显示所有进程,-f 表示做一个更为完整的输出.经常使用这个命令获得进程的 ...

  5. Java八股面试整理(4)

    34.遇到过异常吗,如何处理? 在Java中,可以按照如下三个步骤处理异常: 捕获异常 将业务代码包裹在try块内部,当业务代码中发生任何异常时,系统都会为此异常创建一个异常对象.创建异常对象之后,J ...

  6. Aiganize组局小程序开发手册

    1. 开发阶段概述: 第一阶段: 针对组局和参局的产品功能落地完善小程序, 修改前端界面,去除冗余, 完善数据库设计 完善组局参局的功能 让每个用户能参与组局大厅的组局 让用户能申请为组局者发起组局, ...

  7. Detecting Unknown Encrypted Malicious Traffic in Real Time via Flow Interaction Graph Analysis

    1 前言 1.1 标题 Detecting Unknown Encrypted Malicious Traffic in Real Time via Flow Interaction Graph An ...

  8. [ABC280G] Do Use Hexagon Grid 2

    Problem Statement A hexagonal cell is represented as $(i,j)$ with two integers $i$ and $j$. Cell $(i ...

  9. 华企盾DSC防泄密系统:半透明问题调试方法

    1.先添加下图中的注册表​ 2.用debugview工具监控操作过程,然后找到后面是DSE_SANDBOX,把它前面的值一个一个加到控制台的半透明沙盒对象里面调,直到找到可以正常的为止 3.用supe ...

  10. 零基础电气专业毕业生,花费9.9元自学前端,成都月薪6.5K

    介绍 毕业于成都理工电气专业,大学毕业后进入了一家电气公司,月薪2000元.一直对互联网行业感兴趣,但由于没有相关专业背景,所以一直没有勇气转行. 转行契机 公司的书记想搞一个内部生产管理系统,看我们 ...