本文节选自洪流学堂公众号技术专栏《大话Unity2019》,未经允许不可转载。
洪流学堂公众号回复语音识别
获取源码工程。
洪流学堂,让你快人几步。你好,我是郑洪智。
大智:“今天给你来点刺激的。”
小新满面红光:“啥刺激的?人家可还是个小孩子。”
大智:“带你实战!”
小新:“啊?智哥你变了!”
大智:“变是不可能的,带你实战百度语音识别。”
小新:“嗨,您这说话能不大喘气不,我还以为是那个啥呢”
大智:“你说的那个啥我可不懂啊,走了上战场。”
大智:“百度语音识别用来学习HTTP通信是个特别好的例子。”
首先要做一些准备工作:
大智:“看完文档了没?”
小新:“看完了”
大智:“那我们就开始了。”
语音识别主要有两个过程:
鉴权认证的流程在百度语音识别的文档中有详细介绍,我们在这把流程再梳理一下:
使用Client Credentials获取Access Token需要应用在其服务端发送请求(推荐用POST方法)到百度OAuth2.0授权服务的“ https://openapi.baidu.com/oauth/2.0/token ”地址上,并带上以下参数:
- grant_type:必须参数,固定为“client_credentials”;
- client_id:必须参数,应用的 API Key;
- client_secret:必须参数,应用的 Secret Key;
例如:
https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=Va5yQRHl********LT0vuXV4&client_secret= 0rDSjzQ20XUj5i********PQSzr5pVw2
参数是什么呢?
URL中的参数(paramter)部分:
参数部分:从“?”开始到“#”为止之间(如果没有#那就是到url结束)的部分为参数部分,又称搜索部分、查询部分。本例中的参数部分为grant_type=client_credentials&client_id=Va5yQRHl********LT0vuXV4&client_secret= 0rDSjzQ20XUj5i********PQSzr5pVw2
。参数可以允许有多个参数,参数与参数之间用“&”作为分隔符。
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class BaiduAsr : MonoBehaviour
{
public string APIKey;
public string SecretKey;
private string Token;
// 用于解析返回的json
[Serializable]
class TokenResponse
{
public string access_token = null;
}
IEnumerator Start()
{
// 拼接请求的URL,使用到了C#中新的字符串拼接的方法
var uri = $"https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id={APIKey}&client_secret={SecretKey}";
var www = UnityWebRequest.Get(uri);
yield return www.SendWebRequest();
if (www.isHttpError || www.isNetworkError)
{
Debug.Log("[BaiduAip]" + www.downloadHandler.text);
var result = JsonUtility.FromJson(www.downloadHandler.text);
Token = result.access_token;
Debug.Log("[WitBaiduAip]Token has been fetched successfully");
}
else
{
Debug.LogError("[BaiduAip]" + www.error);
Debug.LogError("[BaiduAip]Token was fetched failed. Please check your APIKey and SecretKey");
}
}
}
获取请求结果后,使用JsonUtility将json字符串解析出来,将Toekn保存下来留作后用,我们的鉴权认证就完成了。
录制音频的核心代码如下。
其中方法的参数可以参考文档:https://docs.unity3d.com/ScriptReference/Microphone.html
_clipRecord = Microphone.Start(null, false, 30, 16000);
Microphone.End(null);
百度语音识别支持两种方式请求:
为了更好的学习HTTP请求,在这我们选用Raw方式进行请求。
使用Raw方式需要处理几点:
Content-Type: audio/pcm;rate=16000
,具体说明见百度语音文档具体代码如下:
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class BaiduAsr : MonoBehaviour
{
public string APIKey;
public string SecretKey;
private string Token;
private AudioClip _clipRecord;
// 用于解析返回的json
[Serializable]
class TokenResponse
{
public string access_token = null;
}
[Serializable]
public class AsrResponse
{
public int err_no;
public string err_msg;
public string sn;
public string[] result;
}
IEnumerator Start()
{
// 拼接请求的URL
var uri = $"https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id={APIKey}&client_secret={SecretKey}";
var www = UnityWebRequest.Get(uri);
yield return www.SendWebRequest();
if (www.isHttpError || www.isNetworkError)
{
Debug.Log("[BaiduAip]" + www.downloadHandler.text);
var result = JsonUtility.FromJson(www.downloadHandler.text);
Token = result.access_token;
Debug.Log("[WitBaiduAip]Token has been fetched successfully");
}
else
{
Debug.LogError("[BaiduAip]" + www.error);
Debug.LogError("[BaiduAip]Token was fetched failed. Please check your APIKey and SecretKey");
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
_clipRecord = Microphone.Start(null, false, 30, 16000);
}
if (Input.GetKeyUp(KeyCode.A))
{
Microphone.End(null);
Debug.Log("[WitBaiduAip demo]end record");
var data = ConvertAudioClipToPCM16(_clipRecord);
StartCoroutine(Recognize(data, s =>
{
var text = s.result != null && s.result.Length > 0 ? s.result[0] : "未识别到声音";
Debug.Log(text);
}));
}
}
public IEnumerator Recognize(byte[] data, Action callback)
{
var uri = $"https://vop.baidu.com/server_api?lan=zh&cuid={SystemInfo.deviceUniqueIdentifier}&token={Token}";
var form = new WWWForm();
form.AddBinaryData("audio", data);
var www = UnityWebRequest.Post(uri, form);
www.SetRequestHeader("Content-Type", "audio/pcm;rate=16000");
yield return www.SendWebRequest();
if (string.IsNullOrEmpty(www.error))
{
Debug.Log("[WitBaiduAip]" + www.downloadHandler.text);
callback(JsonUtility.FromJson(www.downloadHandler.text));
}
else
Debug.LogError(www.error);
}
///
/// 将Unity的AudioClip数据转化为PCM格式16bit数据
///
///
///
public static byte[] ConvertAudioClipToPCM16(AudioClip clip)
{
var samples = new float[clip.samples * clip.channels];
clip.GetData(samples, 0);
var samples_int16 = new short[samples.Length];
for (var index = 0; index < samples.Length; index++)
{
var f = samples[index];
samples_int16[index] = (short)(f * short.MaxValue);
}
var byteArray = new byte[samples_int16.Length * 2];
Buffer.BlockCopy(samples_int16, 0, byteArray, 0, byteArray.Length);
return byteArray;
}
}
上述代码中有一个AudioClip的数据转化为PCM16数据的方法。
具体操作时,按住键盘A键开始录制语音,松开开始识别并Log结果。
大智:“学会HTTP请求以后,你就掌握了向外拓展的能力,现在很多云服务都是以HTTP请求的方式提供的,要好好掌握哦。”
洪流学堂公众号回复语音识别
获取源码工程。
大智:“最后我们使用了Raw方式来请求,另外还有一种json的请求方式,你来试试吧。”
小新:“好嘞!”
大智:“收获别忘了分享出来!也别忘了分享给你学Unity的朋友,也许能够帮到他。”
洪流学堂公众号回复专栏
,查看更多专栏文章。
《大话Unity2019》,大智带小新学Unity2019的有趣经历,让你学Unity更简单。