305 lines
10 KiB
C#
Raw Permalink Normal View History

2025-06-07 17:43:34 +08:00
/* 版权所有 (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;
}
}
}