Skip to content

Instantly share code, notes, and snippets.

@RobTranquillo
Last active June 29, 2020 11:57
Show Gist options
  • Save RobTranquillo/5118f5013d8a10f5ced88836977f3ca2 to your computer and use it in GitHub Desktop.
Save RobTranquillo/5118f5013d8a10f5ced88836977f3ca2 to your computer and use it in GitHub Desktop.
Unity AudioCapture using a ConcurrentQueue (to a PCM Wav file)
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