- 获取录音设备, 返回值是设备名
string[] mDevice = Microphone.devices;
- 开始录音
// 如果 _deviceName = null; 会选默认的录音输入口
// _micSecond 为录音时常,最大不超过3600秒
//_frequency 采样率一般为44100,一定要大于0
// 返回一个 audioClip
CurAudioSource.clip = Microphone.Start(_deviceName, false, _micSecond, _frequency);
- 结束录音
// 如果 _deviceName = null; 会选默认的录音输入口
Microphone.End(_deviceName);
- 是否正在录音
// 如果 _deviceName = null; 会选默认的录音输入口
Microphone.IsRecording(_deviceName
- 录音的当前位置
// 如果 _deviceName = null; 会选默认的录音输入口
int position = Microphone.GetPosition(_deviceName);
注意事项:
-
使用时需要请求权限,特别是iOS,不然没有提示,就是不能使用,而且需要在player setting里填写权限申请的说明
-
如果 iOS上 unity 勾选了 perpara iOS for recording 会导致 录音功能失效
完成代码:
public class MicroPhoneManager : MonoBehaviour
{
private const int VOLUME_DATA_LENGTH = 64;
private const int MIN_RECORD_TIME = 5;
[SerializeField] private int _frequency = 44100;
[SerializeField] private int _micSecond = 90;
public int MaxRecordTime => _micSecond;
public float[] Volume { get; private set; }
private AudioSource _curAudioSource;
public AudioSource CurAudioSource
{
get
{
if (_curAudioSource == null)
{
_curAudioSource = gameObject.AddComponent<AudioSource>();
}
return _curAudioSource;
}
}
public float RecordAudioTime => AudioLength();
public bool HasLocalVoice => CurAudioSource.clip != null;
private int _deviceLength;
private string _deviceName = null;
private bool _isStartRecording = false;
#region [public Way]
public void RequestPermission()
{
MicrophonePermissionHelper.CheckPermission(GetMicrophoneDevice, OnPermissionRequestDenied);
}
private void OnPermissionRequestDenied()
{
Debug.LogError(DPPermissionType.Microphone, "OnPermissionRequestDenied ");
}
public void GetMicrophoneDevice()
{
string[] mDevice = Microphone.devices;
_deviceLength = mDevice.Length;
if (_deviceLength == 0)
{
ShowInfoLog("cant find Microphone");
}
else
{
for (int i = 0; i < _deviceLength; i++)
{
ShowInfoLog($"find Microphone == {i} " + mDevice[i]);
}
}
}
public void StartRecordAudio()
{
StartRecord();
}
public void StopRecordAudio()
{
ShowInfoLog("end record.....");
if (!Microphone.IsRecording(_deviceName))
{
return;
}
AudioLength();
_isStartRecording = false;
Microphone.End(_deviceName);
CurAudioSource.Stop();
}
public void PlayRecordAudio()
{
ShowInfoLog("ready Play Record.....");
if (Microphone.IsRecording(_deviceName))
{
return;
}
if (CurAudioSource.clip == null)
{
return;
}
CurAudioSource.mute = false;
CurAudioSource.loop = false;
CurAudioSource.Play();
string infoLog = "total length: time:" + CurAudioSource.clip.length;
ShowInfoLog("Play Record.....");
}
public void StopAudio()
{
if (Microphone.IsRecording(_deviceName))
{
return;
}
if (CurAudioSource.clip == null)
{
return;
}
CurAudioSource.Stop();
ShowInfoLog("Stop Record.....");
}
public void SaveRecordData(string audioName)
{
if (Microphone.IsRecording(_deviceName))
{
Microphone.End(_deviceName);
}
if (CurAudioSource.clip.length < MIN_RECORD_TIME)
{
return;
}
using (FileStream fs = CreateEmpty(Application.persistentDataPath + $"/{audioName}.wav"))
{
ConvertAndWrite(fs, CurAudioSource.clip);
WriteHeader(fs, CurAudioSource.clip);
}
}
public void LoadLocalAudio(string audioName, Action onComplete)
{
StartCoroutine(LoadAudio(Application.persistentDataPath + $"/{audioName}.wav", onComplete));
}
private IEnumerator LoadAudio(string filepath, Action onComplete)
{
filepath = "file://" + filepath;
using (var uwr = UnityWebRequestMultimedia.GetAudioClip(filepath, AudioType.WAV))
{
yield return uwr.SendWebRequest();
if (uwr.isDone && !uwr.isHttpError)
{
AudioClip clip = DownloadHandlerAudioClip.GetContent(uwr);
CurAudioSource.clip = clip;
string infoLog = $"LoadAudio responseCode, {uwr.responseCode}";
ShowInfoLog(infoLog);
}
else
{
string infoLog = $"Fail LoadAudio {filepath}, {uwr.error}";
ShowInfoLog(infoLog);
}
onComplete?.Invoke();
}
}
private 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 two = 2;
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); // sampleRate * bytesPerSample*number of channels, here 44100*2*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);
Debug.LogError("stream.Length == " + stream.Length);
}
private FileStream CreateEmpty(string filepath)
{
FileStream fileStream = new FileStream(filepath, FileMode.Create);
byte emptyByte = new byte();
for (int i = 0; i < 44; i++) //preparing the header
{
fileStream.WriteByte(emptyByte);
}
return fileStream;
}
private void ConvertAndWrite(FileStream fileStream, AudioClip clip)
{
float[] samples = new float[clip.samples];
clip.GetData(samples, 0);
Int16[] intData = new Int16[samples.Length];
Byte[] bytesData = new Byte[samples.Length * 2];
int rescaleFactor = Int16.MaxValue;
for (int i = 0; i < samples.Length; i++)
{
intData[i] = (short) (samples[i] * rescaleFactor);
Byte[] byteArr = new Byte[2];
byteArr = BitConverter.GetBytes(intData[i]);
byteArr.CopyTo(bytesData, i * 2);
}
fileStream.Write(bytesData, 0, bytesData.Length);
}
public byte[] GetClipData()
{
if (CurAudioSource.clip == null)
{
return null;
}
float[] samples = new float[CurAudioSource.clip.samples];
CurAudioSource.clip.GetData(samples, 0);
byte[] outData = new byte[samples.Length * 2];
int reScaleFactor = Int16.MaxValue;
for (int i = 0; i < samples.Length; i++)
{
short tempShort = (short) (samples[i] * reScaleFactor);
byte[] tempData = BitConverter.GetBytes(tempShort);
outData[i * 2] = tempData[0];
outData[i * 2 + 1] = tempData[1];
}
if (outData.Length <= 0)
{
ShowInfoLog("get data fail");
return null;
}
return outData;
}
#endregion
private void StartRecord()
{
ShowInfoLog("ready start record.....");
CurAudioSource.Stop();
CurAudioSource.loop = false;
CurAudioSource.mute = true;
CurAudioSource.clip = Microphone.Start(_deviceName, false, _micSecond, _frequency);
while (!(Microphone.GetPosition(_deviceName) > 0))
{
}
_isStartRecording = true;
CurAudioSource.Play();
ShowInfoLog("start record.....");
}
private void Update()
{
if (_isStartRecording)
{
Volume = GetMaxVolume();
}
else
{
Volume = null;
}
}
private float[] GetMaxVolume()
{
if (CurAudioSource.clip == null)
{
return null;
}
float maxVolume = 0f;
float[] volumeData = new float[VOLUME_DATA_LENGTH];
int offset;
offset = Microphone.GetPosition(_deviceName) - VOLUME_DATA_LENGTH + 1;
if(offset < 0)
{
return volumeData;
}
CurAudioSource.clip.GetData(volumeData, offset);
return volumeData;
}
private float AudioLength()
{
int position = Microphone.GetPosition(_deviceName);
if (position > 0)
{
var soundata = new float[CurAudioSource.clip.samples * CurAudioSource.clip.channels];
CurAudioSource.clip.GetData(soundata, 0);
var newdata = new float[position * CurAudioSource.clip.channels];
for (int i = 0; i < newdata.Length; i++)
{
newdata[i] = soundata[i];
}
CurAudioSource.clip = AudioClip.Create(CurAudioSource.clip.name, position, CurAudioSource.clip.channels,
CurAudioSource.clip.frequency, false);
CurAudioSource.clip.SetData(newdata, 0);
}
return CurAudioSource.clip.length;
}
private void ShowInfoLog(string info)
{
Debug.Log(info);
}
}