Skip to content

Instantly share code, notes, and snippets.

@WikkidEdd
Created December 23, 2021 00:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save WikkidEdd/35fea92260b95135d6837188006919dc to your computer and use it in GitHub Desktop.
Save WikkidEdd/35fea92260b95135d6837188006919dc to your computer and use it in GitHub Desktop.
AVPro audio capture from multiple sources
#if UNITY_STANDALONE_WIN
using RenderHeads.Media.AVProMovieCapture;
#endif
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
public class CaptureAudioFromMultipleSources :
#if UNITY_STANDALONE_WIN
UnityAudioCapture
#else
MonoBehaviour
#endif
{
[SerializeField] bool _debugLogging = false;
[SerializeField] bool _muteAudio = false;
public List<OnAudioFilterReadForwarder> _OnAudioFilterReadForwarders;
#if UNITY_STANDALONE_WIN
private const int BufferSize = 16;
private float[] _buffer = new float[0];
private float[] _readBuffer = new float[0];
private int _bufferIndex;
private GCHandle _bufferHandle;
private int _numChannels;
private int _overflowCount;
private object _lockObject = new object();
public float[] Buffer { get { return _readBuffer; } }
public int BufferLength { get { return _bufferIndex; } }
public System.IntPtr BufferPtr { get { return _bufferHandle.AddrOfPinnedObject(); } }
public override int OverflowCount { get { return _overflowCount; } }
public override int ChannelCount { get { return _numChannels; } }
private void Start()
{
foreach(var onAudioFilterReadForwarder in _OnAudioFilterReadForwarders)
{
onAudioFilterReadForwarder.Callback += OnAudioFilterReadCombiner;
}
}
public override void PrepareCapture()
{
int bufferLength = 0;
int numBuffers = 0;
AudioSettings.GetDSPBufferSize(out bufferLength, out numBuffers);
_numChannels = GetUnityAudioChannelCount();
if (_debugLogging)
{
Debug.Log(string.Format("[UnityAudioCapture] SampleRate: {0}hz SpeakerMode: {1} BestDriverMode: {2} (DSP using {3} buffers of {4} bytes using {5} channels)", AudioSettings.outputSampleRate, AudioSettings.speakerMode.ToString(), AudioSettings.driverCapabilities.ToString(), numBuffers, bufferLength, _numChannels));
}
_buffer = new float[bufferLength * _numChannels * numBuffers * BufferSize];
_readBuffer = new float[bufferLength * _numChannels * numBuffers * BufferSize];
_bufferIndex = 0;
_bufferHandle = GCHandle.Alloc(_readBuffer, GCHandleType.Pinned);
_overflowCount = 0;
}
public override void StartCapture()
{
FlushBuffer();
}
public override void StopCapture()
{
lock (_lockObject)
{
_bufferIndex = 0;
if (_bufferHandle.IsAllocated)
_bufferHandle.Free();
_readBuffer = _buffer = null;
}
_numChannels = 0;
}
public override System.IntPtr ReadData(out int length)
{
lock (_lockObject)
{
System.Array.Copy(_buffer, 0, _readBuffer, 0, _bufferIndex);
length = _bufferIndex;
_bufferIndex = 0;
}
return _bufferHandle.AddrOfPinnedObject();
}
public override void FlushBuffer()
{
lock (_lockObject)
{
_bufferIndex = 0;
_overflowCount = 0;
}
}
int _count = 0;
int _lastLength;
private void OnAudioFilterReadCombiner(float[] data, int channels)
{
if (_buffer != null && _buffer.Length > 0)
{
_count++;
// TODO: use double buffering
lock (_lockObject)
{
int length = Mathf.Min(data.Length, _buffer.Length - _bufferIndex);
if (!_muteAudio)
{
if (_count == 1)
{
for (int i = 0; i < length; i++)
{
_buffer[i + _bufferIndex] = data[i];
}
}
else
{
for (int i = 0; i < length; i++)
{
_buffer[i + _bufferIndex] += data[i];
}
_count = 0;
}
}
else
{
for (int i = 0; i < length; i++)
{
_buffer[i + _bufferIndex] = data[i];
data[i] = 0f;
}
}
if (_count == 0)
{
_bufferIndex += length;
_lastLength = length;
}
if (length < data.Length)
{
_overflowCount++;
Debug.LogWarning("[AVProMovieCapture] Audio buffer overflow, may cause sync issues. Disable this component if not recording Unity audio.");
}
}
}
}
#endif
}
Remove this line in CaptureBase.cs
removeComponent = !(_unityAudioCapture is CaptureAudioFromAudioListener);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OnAudioFilterReadForwarder : MonoBehaviour
{
public enum MuteBehaviour
{
None,
Before,
After
}
public MuteBehaviour _MuteBehaviour;
public System.Action<float[], int> Callback { get; internal set; }
private void OnAudioFilterRead(float[] data, int channels)
{
if (_MuteBehaviour == MuteBehaviour.Before)
{
for (int i = 0; i < data.Length; i++)
{
data[i] = 0;
}
}
Callback?.Invoke(data, channels);
if(_MuteBehaviour == MuteBehaviour.After)
{
for(int i = 0; i< data.Length; i++)
{
data[i] = 0;
}
}
}
}
@BillO57
Copy link

BillO57 commented Aug 24, 2023

Will the script work on the Mac? It has a few conditional statements -- #if UNITY_STANDALONE_WIN. Can that be updated to #if UNITY_STANDALONE without affecting anything. Are there any other platform specific behaviors?

@WikkidEdd
Copy link
Author

Assuming the AVProMovieCapture works on Mac then I think so. Our app doesn’t run on Mac so I’m not 100%.

We just used that define because our app needs to compile for other non-standalone devices which AVPro doesn’t support. You might be able to remove the #ifdefs completely if you don’t need it to compile on other platforms

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment