/**************************************************************************** * Copyright 2019 Nreal Techonology Limited. All rights reserved. * * This file is part of NRSDK. * * https://www.nreal.ai/ * *****************************************************************************/ namespace NRKernal.Record { using System; using UnityEngine; using UnityEngine.Assertions; public class AudioRecordTool : MonoBehaviour { private byte[] m_CacheBuffer; private const int MaxBufferSize = 2048 * 20; private int m_ReadIndex, m_WriteIndex; public int RIndex { get { return m_ReadIndex; } } public int WIndex { get { return m_WriteIndex; } } private bool m_RecOutput = false; public bool IsRecording { get { return m_RecOutput; } } private int m_OutputSampleRate; public int SampleRate { get { return m_OutputSampleRate; } } void Awake() { m_ReadIndex = 0; m_WriteIndex = 0; m_OutputSampleRate = AudioSettings.GetConfiguration().sampleRate; //AudioSettings.GetDSPBufferSize(out bufferSize, out numBuffers); } protected void InitBuffer(int len) { m_CacheBuffer = new byte[len <= MaxBufferSize ? len : MaxBufferSize]; } public void StartRecord() { m_RecOutput = true; } public void StopRecord() { m_RecOutput = false; } private void OnAudioFilterRead(float[] data, int channels) { if (m_RecOutput) { if (m_CacheBuffer == null) { InitBuffer(data.Length * 10); } ConvertToSinChaAndWrite(data, channels); //audio data is interlaced } } /// /// Only take the data of the first channel. /// /// /// private void ConvertToSinChaAndWrite(float[] dataSource, int channels) { var intData = new Int16[dataSource.Length / channels]; //converting in 2 steps : float[] to Int16[], //then Int16[] to Byte[] var bytesData = new Byte[intData.Length * 2]; //bytesData array is twice the size of //dataSource array because a float converted in Int16 is 2 bytes. var rescaleFactor = Int16.MaxValue; //to convert float to Int16 var byteArr = new Byte[2]; for (var i = 0; i < intData.Length; i++) { intData[i] = (Int16)(dataSource[i * 2] * rescaleFactor); byteArr = BitConverter.GetBytes(intData[i]); byteArr.CopyTo(bytesData, i * 2); } Write(bytesData); } /// /// Take all data of the channel. /// /// private void ConvertAndWrite(float[] dataSource) { var intData = new Int16[dataSource.Length]; //converting in 2 steps : float[] to Int16[], //then Int16[] to Byte[] var bytesData = new Byte[dataSource.Length * 2]; //bytesData array is twice the size of //dataSource array because a float converted in Int16 is 2 bytes. var rescaleFactor = 32767; //to convert float to Int16 for (var i = 0; i < dataSource.Length; i++) { intData[i] = (Int16)(dataSource[i] * rescaleFactor); var byteArr = new Byte[2]; byteArr = BitConverter.GetBytes(intData[i]); byteArr.CopyTo(bytesData, i * 2); } Write(bytesData); } protected void Write(byte[] bytesData) { lock (m_CacheBuffer) { if (m_WriteIndex + bytesData.Length <= m_CacheBuffer.Length) { Array.Copy(bytesData, 0, m_CacheBuffer, m_WriteIndex, bytesData.Length); m_WriteIndex += bytesData.Length; } else { int left = m_CacheBuffer.Length - m_WriteIndex; Assert.IsTrue(left >= 0); if (left > 0) { Array.Copy(bytesData, 0, m_CacheBuffer, m_WriteIndex, left); m_WriteIndex = 0; Array.Copy(bytesData, left, m_CacheBuffer, m_WriteIndex, bytesData.Length - left); m_WriteIndex += bytesData.Length - left; } else { m_WriteIndex = 0; Array.Copy(bytesData, 0, m_CacheBuffer, m_WriteIndex, bytesData.Length); m_WriteIndex += bytesData.Length; } } } } public bool Flush(ref byte[] outBytesData) { if (m_CacheBuffer == null || m_ReadIndex == m_WriteIndex) { return false; } lock (m_CacheBuffer) { int count = 0; if (m_ReadIndex < m_WriteIndex) { count = m_WriteIndex - m_ReadIndex; if (outBytesData == null || outBytesData.Length != count) { outBytesData = new byte[count]; } Array.Copy(m_CacheBuffer, m_ReadIndex, outBytesData, 0, count); } else { int left = m_CacheBuffer.Length - m_ReadIndex; count = left + m_WriteIndex; Assert.IsTrue(left >= 0); if (outBytesData == null || outBytesData.Length != count) { outBytesData = new byte[count]; } if (left == 0) { m_ReadIndex = 0; Assert.IsTrue(m_WriteIndex != 0); Array.Copy(m_CacheBuffer, m_ReadIndex, outBytesData, 0, count); } else { Array.Copy(m_CacheBuffer, m_ReadIndex, outBytesData, 0, m_CacheBuffer.Length - m_ReadIndex); Array.Copy(m_CacheBuffer, 0, outBytesData, m_CacheBuffer.Length - m_ReadIndex, m_WriteIndex); } } m_ReadIndex = m_WriteIndex; } return true; } } }