Last active
June 29, 2020 11:57
-
-
Save RobTranquillo/5118f5013d8a10f5ced88836977f3ca2 to your computer and use it in GitHub Desktop.
Unity AudioCapture using a ConcurrentQueue (to a PCM Wav file)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Collections; | |
using System.Collections.Concurrent; | |
using UnityEngine; | |
/// <summary> | |
/// Recording 5 seconds from the first microphone in Microphone.devices | |
/// to a PCM Wav file using a ConcurrentQueue. | |
/// | |
/// The write-to-disk part is mainly taken from https://github.com/Olkor/OutputAudioRecorder | |
/// </summary> | |
public class AudioCapture : MonoBehaviour | |
{ | |
//according to your project settings | |
private int m_ProjectSampleRate; | |
private UInt16 m_Channels = 1; | |
private UInt16 m_BitRate = 16; | |
private AudioSource m_AudioSource; | |
private ConcurrentQueue<float> m_ConcurrentQueue; | |
private int m_Offset; | |
private float[] m_SampleCapture; | |
private int m_RecordDuration = 5; | |
private string m_SelectedMicrophone; | |
private int headerSize = 44; //default for uncompressed wav | |
private void Start() | |
{ | |
m_Offset = 0; | |
m_SelectedMicrophone = Microphone.devices[0]; | |
m_ProjectSampleRate = 16000; //like the project sample rate (UInt16) AudioSettings.outputSampleRate; | |
m_ConcurrentQueue = new ConcurrentQueue<float>(); | |
m_SampleCapture = new float[m_ProjectSampleRate]; | |
m_AudioSource = GetComponent<AudioSource>(); | |
m_AudioSource.clip = Microphone.Start(m_SelectedMicrophone, false, m_RecordDuration, m_ProjectSampleRate); | |
if (Microphone.IsRecording(m_SelectedMicrophone)) | |
StartCoroutine(TriggerBufferRead()); | |
StartCoroutine(StopRecording()); | |
} | |
private IEnumerator TriggerBufferRead() | |
{ | |
yield return new WaitForSeconds(1); | |
CaptureStream(); | |
if (m_Offset * m_ProjectSampleRate < m_AudioSource.clip.samples) | |
StartCoroutine(TriggerBufferRead()); | |
} | |
private void CaptureStream() | |
{ | |
//weiterstreamen an deepspeech | |
//als test steam ich es in eine queue und schreib diese am Ende in eine file | |
m_AudioSource.clip.GetData(m_SampleCapture, m_Offset * m_ProjectSampleRate); | |
m_Offset++; | |
foreach (var sample in m_SampleCapture) | |
m_ConcurrentQueue.Enqueue(sample); | |
} | |
private IEnumerator StopRecording() | |
{ | |
yield return new WaitForSeconds(m_RecordDuration + 1); | |
Microphone.End(m_SelectedMicrophone); | |
WriteToDisk(); | |
} | |
private void WriteToDisk() | |
{ | |
if (m_ConcurrentQueue.IsEmpty) | |
return; | |
FileStream fileStream = new FileStream(Application.dataPath + "/" + "RecordStrom.wav", FileMode.Create); | |
var emptyByte = new byte(); | |
for (int i = 0; i < headerSize; i++) //preparing the header | |
fileStream.WriteByte(emptyByte); | |
Byte[] bytesData = GetBytesFromQueue(); | |
fileStream.Write(bytesData, 0, bytesData.Length); | |
Debug.Log("File written to: " +Application.dataPath + "/" + "RecordStrom.wav"+ | |
"Bytes data: " +bytesData.Length+ | |
"fileStream.Position: " +fileStream.Position); | |
FillHeader(fileStream); | |
fileStream.Close(); | |
} | |
Byte[] GetBytesFromQueue() | |
{ | |
//converting in 2 steps : float[] to Int16[], //then Int16[] to Byte[] | |
var intData = new Int16(); | |
//bytesData array is twice the size of | |
//dataSource array because a float converted in Int16 is 2 bytes. | |
var bytesData = new Byte[m_ConcurrentQueue.Count * 2]; | |
var rescaleFactor = 32767; //to convert float to Int16 | |
int pos = 0; | |
while (m_ConcurrentQueue.TryDequeue(out float sample)) | |
{ | |
intData = (Int16) (sample * rescaleFactor); | |
bytesData[pos + 1] = (byte) (intData >> 8); | |
bytesData[pos] = (byte) (intData & 255); | |
pos += 2; | |
} | |
return bytesData; | |
} | |
private void FillHeader(FileStream fileStream) | |
{ | |
fileStream.Seek(0, SeekOrigin.Begin); | |
var riff = System.Text.Encoding.UTF8.GetBytes("RIFF"); | |
fileStream.Write(riff, 0, 4); | |
var chunkSize = BitConverter.GetBytes(fileStream.Length - 8); | |
fileStream.Write(chunkSize, 0, 4); | |
var wave = System.Text.Encoding.UTF8.GetBytes("WAVE"); | |
fileStream.Write(wave, 0, 4); | |
var fmt = System.Text.Encoding.UTF8.GetBytes("fmt "); | |
fileStream.Write(fmt, 0, 4); | |
var subChunk1 = BitConverter.GetBytes(16); | |
fileStream.Write(subChunk1, 0, 4); | |
UInt16 one = 1; | |
UInt16 two = 2; | |
var audioFormat = BitConverter.GetBytes(one); | |
fileStream.Write(audioFormat, 0, 2); | |
var numChannels = BitConverter.GetBytes(this.m_Channels); | |
fileStream.Write(numChannels, 0, 2); | |
var sampleRate = BitConverter.GetBytes(m_ProjectSampleRate); | |
fileStream.Write(sampleRate, 0, 4); | |
var byteRate = BitConverter.GetBytes(this.m_ProjectSampleRate * this.m_Channels * (this.m_BitRate/8)); | |
fileStream.Write(byteRate, 0, 4); | |
var blockAlign = BitConverter.GetBytes(this.m_Channels * (this.m_BitRate/8)); | |
fileStream.Write(blockAlign, 0, 2); | |
UInt16 sixteen = 16; | |
var bitsPerSample = BitConverter.GetBytes(sixteen); | |
fileStream.Write(bitsPerSample, 0, 2); | |
var dataString = System.Text.Encoding.UTF8.GetBytes("data"); | |
fileStream.Write(dataString, 0, 4); | |
var subChunk2 = BitConverter.GetBytes(fileStream.Length - headerSize); | |
fileStream.Write(subChunk2, 0, 4); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment