2025-06-07 17:43:34 +08:00

305 lines
10 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 版权所有 (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;
}
}
}