注:应用VR(HTC)获取圆盘键,进行语音的回放、转化文字、保存语音等(确认百度智能云有可用资源)
using UnityEngine;
using UnityEngine.UI;
using System;
using System.IO;
using UnityEditor;
using System.Collections;
using UnityEngine.Networking;
using System.Text.RegularExpressions;
public class TestMicro : MonoBehaviour
{
public static TestMicro _Instance;
public AudioClip RecordedClip;//录音
public AudioSource audioSource;//播放的音频
public Text Infotxt;//提示信息
public Text Adress;//音频保存地址
private string fileName;//保存的文件名
private byte[] data;
/// <summary>
/// VR手柄控制器
/// </summary>
public SteamVR_TrackedObject trackdeObjec;
//是否开启
public bool IsMicro = false;
[Header("倒计时")]
public Text time_add;
private float number;
private float time_offect;
private bool stop;
//---------------------------------------------------
//百度语音识别相关key
string appId = "18518840";
string apiKey = ""; //填写自己的apiKey
string secretKey = ""; //填写自己的secretKey
//记录accesstoken令牌
string accessToken = string.Empty;
//录音频率,控制录音质量(8000,16000)
int recordFrequency = 8000;
//标记是否有麦克风
private bool isHaveMic = false;
//当前录音设备名称
string currentDeviceName = string.Empty;
[Header("显示结果的文本")]
public Text textResult;
//语音识别的结果
string asrResult = string.Empty;
//实际录音长度(由于unity的录音需先指定长度,导致识别上传时候会上传多余的无效字节)
//通过该字段,获取有效录音长度,上传时候剪切到无效的字节数据即可
int trueLength = 0;
//表示录音的最大时长
int recordMaxLength = 100;
//上次按下时间戳
double lastPressTimestamp = 0;
private void Start()
{
_Instance = this;
number = recordMaxLength;
//获取麦克风设备,判断是否有麦克风设备
if (Microphone.devices.Length > 0)
{
isHaveMic = true;
currentDeviceName = Microphone.devices[0];
Infotxt.text = "设备名称为:" + Microphone.devices[0].ToString() + "请点击触摸板上面开始录音!";
}
else
{
Infotxt.text = "缺少麦克风设备!";
}
}
void Update()
{
if (IsMicro)
{
SteamVR_Controller.Device device = SteamVR_Controller.Input((int)trackdeObjec.index);
//圆盘键分区
if (device.GetPressDown(SteamVR_Controller.ButtonMask.Axis0))
{
Vector2 pad = device.GetAxis();
Vector3 cross = Vector3.Cross(new Vector2(1, 0), pad);
float angle = Vector2.Angle(new Vector2(1, 0), pad);
float ang = cross.z > 0 ? -angle : angle;
//下
if (ang > 45 && ang < 135)
{
stop = false;
//Stop();
EndRecording();
Debug.Log("下边");
}
//上
else if (ang < -45 && ang > -135)
{
stop = true;
StartRecording(true);
//Begin();
Debug.Log("上边");
}
//左
else if ((ang < 180 && ang > 135) || (ang < -135 && ang > -180))
{
Player();
Debug.Log("左边");
}
//右
else if ((ang > 0 && ang < 45) || (ang > -45 && ang < 0))
{
Save();
Debug.Log("右边");
}
}
}
if (stop == true)
{
time_offect += Time.deltaTime;
if (time_offect > 1)
{
number--;
if (number <= 0)
{
stop = false;
EndRecording();
number = 0;
}
time_add.text = "倒计时:\n" + number.ToString();
time_offect -= 1;
}
}
else
{
number = 60;
time_add.text = "倒计时:\n" + number.ToString();
}
}
#region 语音识别与录入
/// <summary>
/// 开始录音
/// </summary>
/// <param name="isLoop"></param>
/// <param name="lengthSec"></param>
/// <param name="frequency"></param>
/// <returns></returns>
public bool StartRecording(bool isLoop = false) //8000,16000
{
if (isHaveMic == false || Microphone.IsRecording(currentDeviceName))
{
Infotxt.text = "请确认麦克风设备是否已连接!";
return false;
}
Infotxt.text = "开始录音!";
//开始录音
/*
* public static AudioClip Start(string deviceName, bool loop, int lengthSec, int frequency);
* deviceName 录音设备名称.
* loop 如果达到长度,是否继续记录
* lengthSec 指定录音的长度.
* frequency 音频采样率
*/
lastPressTimestamp = GetTimestampOfNowWithMillisecond();
RecordedClip = Microphone.Start(currentDeviceName, isLoop, recordMaxLength, recordFrequency);
return true;
}
/// <summary>
/// 录音结束,返回实际的录音时长
/// </summary>
/// <returns></returns>
public int EndRecording()
{
if (isHaveMic == false || !Microphone.IsRecording(currentDeviceName))
{
Infotxt.text = "请确认麦克风设备是否已连接!";
return 0;
}
//结束录音
Microphone.End(currentDeviceName);
data = GetRealAudio(ref RecordedClip);
Infotxt.text = "录音结束!";
trueLength = Mathf.CeilToInt((float)(GetTimestampOfNowWithMillisecond() - lastPressTimestamp) / 1000f);
//向上取整,避免遗漏录音末尾
return Mathf.CeilToInt((float)(GetTimestampOfNowWithMillisecond() - lastPressTimestamp) / 1000f);
}
/// <summary>
/// 放开录音按钮
/// </summary>
/// <param name="eventData"></param>
public void Player()
{
if (trueLength > 1)
{
Infotxt.text = "正在播放录音!";
audioSource.PlayOneShot(RecordedClip);
StartCoroutine(_StartBaiduYuYin());
}
else
{
textResult.text = "录音时长过短";
Infotxt.text = "录音时长过短!";
}
}
/// <summary>
/// 获取毫秒级别的时间戳,用于计算按下录音时长
/// </summary>
/// <returns></returns>
public double GetTimestampOfNowWithMillisecond()
{
return (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;
}
/// <summary>
/// 获取真正大小的录音
/// </summary>
/// <param name="recordedClip"></param>
/// <returns></returns>
public static byte[] GetRealAudio(ref AudioClip recordedClip)
{
int position = Microphone.GetPosition(null);
if (position <= 0 || position > recordedClip.samples)
{
position = recordedClip.samples;
}
float[] soundata = new float[position * recordedClip.channels];
recordedClip.GetData(soundata, 0);
recordedClip = AudioClip.Create(recordedClip.name, position,
recordedClip.channels, recordedClip.frequency, false);
recordedClip.SetData(soundata, 0);
int rescaleFactor = 32767;
byte[] outData = new byte[soundata.Length * 2];
for (int i = 0; i < soundata.Length; i++)
{
short temshort = (short)(soundata[i] * rescaleFactor);
byte[] temdata = BitConverter.GetBytes(temshort);
outData[i * 2] = temdata[0];
outData[i * 2 + 1] = temdata[1];
}
Debug.Log("position=" + position + " outData.leng=" + outData.Length);
return outData;
}
//#endregion
/// <summary>
/// 保存录音
/// </summary>
public void Save()
{
if (!Microphone.IsRecording(currentDeviceName) && RecordedClip != null)
{
fileName = DateTime.Now.ToString("yyyyMMddHHmmssffff");
if (!fileName.ToLower().EndsWith(".wav"))
{//如果不是“.wav”格式的,加上后缀
fileName += ".wav";
}
string path = Path.Combine(Application.streamingAssetsPath, fileName);//录音保存路径
Debug.Log(path);
print(path);//输出路径
Adress.text = path;
using (FileStream fs = CreateEmpty(path))
{
fs.Write(data, 0, data.Length);
WriteHeader(fs, RecordedClip); //wav文件头
}
Infotxt.text = "已经保存";
}
else
{
Infotxt.text = "正在录音中,请先停止录音!";
}
}
/// <summary>
/// 写文件头
/// </summary>
/// <param name="stream"></param>
/// <param name="clip"></param>
public static void WriteHeader(FileStream stream, AudioClip clip)
{
int hz = clip.frequency;
int channels = clip.channels;
int samples = clip.samples;
stream.Seek(0, SeekOrigin.Begin);
Byte[] riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
stream.Write(riff, 0, 4);
Byte[] chunkSize = BitConverter.GetBytes(stream.Length - 8);
stream.Write(chunkSize, 0, 4);
Byte[] wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
stream.Write(wave, 0, 4);
Byte[] fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
stream.Write(fmt, 0, 4);
Byte[] subChunk1 = BitConverter.GetBytes(16);
stream.Write(subChunk1, 0, 4);
UInt16 one = 1;
Byte[] audioFormat = BitConverter.GetBytes(one);
stream.Write(audioFormat, 0, 2);
Byte[] numChannels = BitConverter.GetBytes(channels);
stream.Write(numChannels, 0, 2);
Byte[] sampleRate = BitConverter.GetBytes(hz);
stream.Write(sampleRate, 0, 4);
Byte[] byteRate = BitConverter.GetBytes(hz * channels * 2);
stream.Write(byteRate, 0, 4);
UInt16 blockAlign = (ushort)(channels * 2);
stream.Write(BitConverter.GetBytes(blockAlign), 0, 2);
UInt16 bps = 16;
Byte[] bitsPerSample = BitConverter.GetBytes(bps);
stream.Write(bitsPerSample, 0, 2);
Byte[] datastring = System.Text.Encoding.UTF8.GetBytes("data");
stream.Write(datastring, 0, 4);
Byte[] subChunk2 = BitConverter.GetBytes(samples * channels * 2);
stream.Write(subChunk2, 0, 4);
}
/// <summary>
/// 创建wav格式文件头
/// </summary>
/// <param name="filepath"></param>
/// <returns></returns>
private FileStream CreateEmpty(string filepath)
{
FileStream fileStream = new FileStream(filepath, FileMode.Create);
byte emptyByte = new byte();
for (int i = 0; i < 44; i++) //为wav文件头留出空间
{
fileStream.WriteByte(emptyByte);
}
return fileStream;
}
/// <summary>
/// 获取accessToken请求令牌
/// </summary>
/// <returns></returns>
IEnumerator _GetAccessToken()
{
var uri =
string.Format(
"https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id={0}&client_secret={1}",
apiKey, secretKey);
UnityWebRequest unityWebRequest = UnityWebRequest.Get(uri);
yield return unityWebRequest.SendWebRequest();
if (unityWebRequest.isDone)
{
print(unityWebRequest.downloadHandler.text);
//这里可以考虑用Json,本人比较懒所以用正则匹配出accessToken
Match match = Regex.Match(unityWebRequest.downloadHandler.text, @"access_token.:.(.*?).,");
if (match.Success)
{
//表示正则匹配到了accessToken
accessToken = match.Groups[1].ToString();
}
else
{
textResult.text = "验证错误,获取AccessToken失败!!!";
}
}
}
/// <summary>
/// 发起语音识别请求
/// </summary>
/// <returns></returns>
IEnumerator _StartBaiduYuYin()
{
if (string.IsNullOrEmpty(accessToken))
{
yield return _GetAccessToken();
}
asrResult = string.Empty;
//处理当前录音数据为PCM16
float[] samples = new float[recordFrequency * trueLength * RecordedClip.channels];
RecordedClip.GetData(samples, 0);
var samplesShort = new short[samples.Length];
for (var index = 0; index < samples.Length; index++)
{
samplesShort[index] = (short)(samples[index] * short.MaxValue);
}
byte[] datas = new byte[samplesShort.Length * 2];
Buffer.BlockCopy(samplesShort, 0, datas, 0, datas.Length);
string url = string.Format("{0}?cuid={1}&token={2}", "https://vop.baidu.com/server_api", SystemInfo.deviceUniqueIdentifier, accessToken);
WWWForm wwwForm = new WWWForm();
wwwForm.AddBinaryData("audio", datas);
UnityWebRequest unityWebRequest = UnityWebRequest.Post(url, wwwForm);
unityWebRequest.SetRequestHeader("Content-Type", "audio/pcm;rate=" + recordFrequency);
yield return unityWebRequest.SendWebRequest();
if (string.IsNullOrEmpty(unityWebRequest.error))
{
asrResult = unityWebRequest.downloadHandler.text;
if (Regex.IsMatch(asrResult, @"err_msg.:.success"))
{
Match match = Regex.Match(asrResult, "result.:..(.*?)..]");
if (match.Success)
{
asrResult = match.Groups[1].ToString();
}
}
else
{
asrResult = "识别结果为空";
}
textResult.text = asrResult;
}
}
#endregion
}