305 lines
10 KiB
C#
305 lines
10 KiB
C#
|
/* 版权所有 (C) 2014 DaikonForge */
|
|||
|
|
|||
|
namespace DaikonForge.VoIP
|
|||
|
{
|
|||
|
using UnityEngine;
|
|||
|
using System.Collections;
|
|||
|
|
|||
|
// 将该脚本添加到Unity的组件菜单中
|
|||
|
[AddComponentMenu("DFVoice/Microphone Input Device")]
|
|||
|
public class MicrophoneInputDevice : AudioInputDeviceBase
|
|||
|
{
|
|||
|
// 默认麦克风设备名称
|
|||
|
public static string DefaultMicrophone = null;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取当前正在用于录制音频的设备
|
|||
|
/// </summary>
|
|||
|
public string ActiveDevice
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return device;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 每次读取的音频数据块大小
|
|||
|
public int ChunkSize = 320;
|
|||
|
|
|||
|
// 音频频率模式
|
|||
|
public FrequencyMode Mode = FrequencyMode.Wide;
|
|||
|
|
|||
|
// 按下通话的按键
|
|||
|
//public KeyCode PushToTalk = KeyCode.None;
|
|||
|
|
|||
|
// 音量阈值,用于判断是否有声音输入
|
|||
|
public float AmplitudeThreshold = 0;
|
|||
|
|
|||
|
// 录制的音频剪辑
|
|||
|
public AudioClip recordedAudio;
|
|||
|
|
|||
|
// 最大录制时间(秒)
|
|||
|
public int maxRecordTime = 15;
|
|||
|
|
|||
|
// 上一次读取音频数据的位置
|
|||
|
private int prevReadPosition = 0;
|
|||
|
|
|||
|
// 重采样缓冲区
|
|||
|
private BigArray<float> resampleBuffer;
|
|||
|
|
|||
|
// 当前使用的麦克风设备名称
|
|||
|
private string device = null;
|
|||
|
|
|||
|
// 录制音频的频率
|
|||
|
private int recordingFrequency;
|
|||
|
|
|||
|
// 按下通话的计时器(已注释,未使用)
|
|||
|
//private float pushToTalkTimer = 0f;
|
|||
|
|
|||
|
// 录音状态标志
|
|||
|
bool recording = false;
|
|||
|
#if UNITY_4_7 || UNITY_4_6 || UNITY_4_5 || UNITY_4_4 || UNITY_4_3 || UNITY_4_2
|
|||
|
// 麦克风授权状态标志
|
|||
|
bool hasAuthorization = false;
|
|||
|
#endif
|
|||
|
|
|||
|
// 启动时的协程,用于请求麦克风授权
|
|||
|
IEnumerator Start()
|
|||
|
{
|
|||
|
#if UNITY_4_7 || UNITY_4_6 || UNITY_4_5 || UNITY_4_4 || UNITY_4_3 || UNITY_4_2
|
|||
|
// 请求用户授权使用麦克风
|
|||
|
yield return Application.RequestUserAuthorization(UserAuthorization.Microphone);
|
|||
|
//Debug.LogWarning("[StartRecording]" + (Application.HasUserAuthorization(UserAuthorization.Microphone)));
|
|||
|
// 检查是否有可用的麦克风设备且已获得授权
|
|||
|
if (Microphone.devices.Length > 0 && Application.HasUserAuthorization(UserAuthorization.Microphone))
|
|||
|
{
|
|||
|
hasAuthorization = true;
|
|||
|
Debug.LogWarning("StartRecording(): 没有可用的设备!");
|
|||
|
}
|
|||
|
#else
|
|||
|
yield return null;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 开始录音
|
|||
|
/// </summary>
|
|||
|
public override void StartRecording()
|
|||
|
{
|
|||
|
//Debug.LogWarning("[StartRecording]" + (hasAuthorization));
|
|||
|
// 检查是否有可用的麦克风设备
|
|||
|
if (Microphone.devices.Length <= 0)
|
|||
|
{
|
|||
|
Debug.LogWarning("StartRecording(): 没有可用的设备!");
|
|||
|
// 没有录音权限的处理
|
|||
|
NoRecordingPerimissions();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 使用默认麦克风设备
|
|||
|
device = DefaultMicrophone;
|
|||
|
|
|||
|
// 重置上一次读取位置
|
|||
|
prevReadPosition = 0;
|
|||
|
|
|||
|
// 获取指定模式下的音频频率
|
|||
|
this.recordingFrequency = AudioUtils.GetFrequency(Mode);
|
|||
|
//this.recordingFrequency = 8000;
|
|||
|
int min, max;
|
|||
|
// 获取麦克风设备的频率范围
|
|||
|
Microphone.GetDeviceCaps(device, out min, out max);
|
|||
|
min = 0;
|
|||
|
if (max == 0) max = 48000;
|
|||
|
|
|||
|
// 确保录制频率在设备支持的范围内
|
|||
|
int frequency = Mathf.Clamp(this.recordingFrequency, min, max);
|
|||
|
|
|||
|
// 初始化重采样缓冲区
|
|||
|
resampleBuffer = new BigArray<float>(ChunkSize, 0);
|
|||
|
|
|||
|
// 停止所有正在进行的录音
|
|||
|
Microphone.End(null);
|
|||
|
// 开始录制音频
|
|||
|
recordedAudio = Microphone.Start(null, false, maxRecordTime, frequency);
|
|||
|
|
|||
|
// 触发录音开始事件
|
|||
|
RecordingStart();
|
|||
|
|
|||
|
// 设置录音状态为正在录音
|
|||
|
recording = true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 停止录音
|
|||
|
/// </summary>
|
|||
|
public override void StopRecording()
|
|||
|
{
|
|||
|
// 检查是否正在录音
|
|||
|
if (recording)
|
|||
|
{
|
|||
|
int audioLength; // 录音的长度,单位为秒,UI上可能需要显示
|
|||
|
// 获取当前录音的位置
|
|||
|
int lastPos = Microphone.GetPosition(null);
|
|||
|
// 计算录音时长
|
|||
|
audioLength = (int)(System.Math.Round((float)lastPos / this.recordingFrequency));
|
|||
|
Debug.Log("StopRecording 录音时长= " + audioLength);
|
|||
|
// 触发录音结束事件
|
|||
|
RecordingFinish(audioLength);
|
|||
|
// 停止录音
|
|||
|
Microphone.End(null);
|
|||
|
// 清空录制的音频剪辑
|
|||
|
recordedAudio = null;
|
|||
|
// 设置录音状态为停止录音
|
|||
|
recording = false;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.Log("[StopRecording] 没有正在进行的录音!");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 切换到新的麦克风设备
|
|||
|
/// </summary>
|
|||
|
public void ChangeMicrophoneDevice(string newDevice)
|
|||
|
{
|
|||
|
// 停止当前录音
|
|||
|
StopRecording();
|
|||
|
|
|||
|
// 设置新的默认麦克风设备
|
|||
|
DefaultMicrophone = newDevice;
|
|||
|
|
|||
|
// 开始新的录音
|
|||
|
StartRecording();
|
|||
|
}
|
|||
|
|
|||
|
// 每帧更新方法
|
|||
|
void Update()
|
|||
|
{
|
|||
|
// 检查是否正在录音,以及录音剪辑是否存在且正在录制
|
|||
|
if (!recording && (recordedAudio == null || !Microphone.IsRecording(device)))
|
|||
|
{
|
|||
|
//Debug.LogWarning(string.Format("IsRecording = {0} , recordedAudion = {1}", !Microphone.IsRecording(device), (recordedAudio == null)));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 从对象池中获取临时数组
|
|||
|
float[] tempArray = TempArray<float>.Obtain(ChunkSize);
|
|||
|
|
|||
|
// 处理脚本重新编译的情况
|
|||
|
if (resampleBuffer == null)
|
|||
|
{
|
|||
|
resampleBuffer = new BigArray<float>(ChunkSize, 0);
|
|||
|
}
|
|||
|
|
|||
|
// 获取当前录音的位置
|
|||
|
int readPosition = Microphone.GetPosition(device);
|
|||
|
// 检查是否有足够的数据可供读取
|
|||
|
if (readPosition >= (prevReadPosition + ChunkSize))
|
|||
|
{
|
|||
|
while (readPosition >= (prevReadPosition + ChunkSize))
|
|||
|
{
|
|||
|
//if (canTalk())
|
|||
|
//{
|
|||
|
// 从录音剪辑中读取数据
|
|||
|
recordedAudio.GetData(tempArray, prevReadPosition);
|
|||
|
// 检查音量是否超过阈值
|
|||
|
if (exceedsVolumeThreshold(tempArray))
|
|||
|
{
|
|||
|
// 对数据进行重采样
|
|||
|
resample(tempArray);
|
|||
|
// 触发缓冲区准备好事件
|
|||
|
bufferReady(resampleBuffer, this.recordingFrequency);
|
|||
|
}
|
|||
|
//}
|
|||
|
|
|||
|
// 更新上一次读取位置
|
|||
|
prevReadPosition += ChunkSize;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (prevReadPosition > readPosition)
|
|||
|
{
|
|||
|
var endReadPos = readPosition + recordedAudio.samples;
|
|||
|
var diff = endReadPos - prevReadPosition;
|
|||
|
while (diff >= ChunkSize)
|
|||
|
{
|
|||
|
//if (canTalk())
|
|||
|
//{
|
|||
|
// 从录音剪辑中读取数据
|
|||
|
recordedAudio.GetData(tempArray, prevReadPosition);
|
|||
|
// 检查音量是否超过阈值
|
|||
|
if (exceedsVolumeThreshold(tempArray))
|
|||
|
{
|
|||
|
// 对数据进行重采样
|
|||
|
resample(tempArray);
|
|||
|
// 触发缓冲区准备好事件
|
|||
|
bufferReady(resampleBuffer, this.recordingFrequency);
|
|||
|
}
|
|||
|
//}
|
|||
|
|
|||
|
// 更新上一次读取位置
|
|||
|
prevReadPosition += ChunkSize;
|
|||
|
if (prevReadPosition >= recordedAudio.samples)
|
|||
|
{
|
|||
|
prevReadPosition -= recordedAudio.samples;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
endReadPos = readPosition + recordedAudio.samples;
|
|||
|
diff = endReadPos - prevReadPosition;
|
|||
|
}
|
|||
|
}
|
|||
|
// 将临时数组释放回对象池
|
|||
|
TempArray<float>.Release(tempArray);
|
|||
|
}
|
|||
|
|
|||
|
// 检查音频数据的音量是否超过阈值
|
|||
|
bool exceedsVolumeThreshold(float[] data)
|
|||
|
{
|
|||
|
// 如果阈值为0,则认为始终超过阈值
|
|||
|
if (AmplitudeThreshold == 0f)
|
|||
|
return true;
|
|||
|
|
|||
|
// 获取音频数据中的最大值
|
|||
|
var max = Mathf.Max(data);
|
|||
|
// 判断最大值是否超过阈值
|
|||
|
return max >= AmplitudeThreshold;
|
|||
|
}
|
|||
|
|
|||
|
// 对音频数据进行重采样
|
|||
|
void resample(float[] tempArray)
|
|||
|
{
|
|||
|
// 初始化重采样缓冲区
|
|||
|
resampleBuffer = new BigArray<float>(tempArray.Length, tempArray.Length);
|
|||
|
// 调整重采样缓冲区的大小
|
|||
|
resampleBuffer.Resize(tempArray.Length);
|
|||
|
// 将临时数组的数据复制到重采样缓冲区
|
|||
|
resampleBuffer.CopyFrom(tempArray, 0, 0, tempArray.Length * 4);
|
|||
|
|
|||
|
//Debug.Log( "Resampling from: " + recordedAudio.frequency + ", to: " + this.recordingFrequency );
|
|||
|
// 调用音频工具类进行重采样
|
|||
|
AudioUtils.Resample(resampleBuffer, recordedAudio.frequency, this.recordingFrequency);
|
|||
|
}
|
|||
|
|
|||
|
// 能否通话的标志
|
|||
|
bool _cantTalk = false;
|
|||
|
|
|||
|
// 设置能否通话的状态
|
|||
|
void SetCanTalk()
|
|||
|
{
|
|||
|
_cantTalk = !_cantTalk;
|
|||
|
}
|
|||
|
|
|||
|
// 判断是否可以通话
|
|||
|
bool canTalk()
|
|||
|
{
|
|||
|
return true;
|
|||
|
|
|||
|
//if( PushToTalk == KeyCode.None ) return true;
|
|||
|
//if( Input.GetKey( PushToTalk ) ) pushToTalkTimer = 0.2f;
|
|||
|
|
|||
|
//pushToTalkTimer -= Time.deltaTime;
|
|||
|
//return pushToTalkTimer > 0f;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|