Skip to content

Instantly share code, notes, and snippets.

@maxweisel
Created October 29, 2019 23:44
Show Gist options
  • Save maxweisel/0d7e009f1046fa4e2506fc2a165afdcc to your computer and use it in GitHub Desktop.
Save maxweisel/0d7e009f1046fa4e2506fc2a165afdcc to your computer and use it in GitHub Desktop.
Oculus Platform Microphone Test
#region Copyright & License
/*************************************************************************
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Roman Atachiants ([email protected])
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*************************************************************************/
#endregion
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace System.Collections {
/// <summary>
/// Defines a class that represents a resizable circular float queue.
/// </summary>
public sealed class FloatQueue {
// Private fields
private int fHead;
private int fTail;
private int fSize;
private int fSizeUntilCut;
private float[] fInternalBuffer;
/// <summary>
/// Gets the length of the float queue
/// </summary>
public int Length {
get { return fSize; }
}
/// <summary>
/// Constructs a new instance of a float queue.
/// </summary>
public FloatQueue() {
fInternalBuffer = new float[2048];
}
/// <summary>
/// Clears the float queue
/// </summary>
internal void Clear() {
fHead = 0;
fTail = 0;
fSize = 0;
fSizeUntilCut = fInternalBuffer.Length;
}
/// <summary>
/// Clears the float queue
/// </summary>
internal void Clear(int size) {
lock (this) {
if (size > fSize)
size = fSize;
if (size == 0)
return;
fHead = (fHead + size) % fInternalBuffer.Length;
fSize -= size;
if (fSize == 0) {
fHead = 0;
fTail = 0;
}
fSizeUntilCut = fInternalBuffer.Length - fHead;
return;
}
}
/// <summary>
/// Extends the capacity of the float queue
/// </summary>
private void SetCapacity(int capacity) {
float[] newBuffer = new float[capacity];
if (fSize > 0) {
if (fHead < fTail) {
Array.Copy(fInternalBuffer, fHead, newBuffer, 0, fSize);
} else {
Array.Copy(fInternalBuffer, fHead, newBuffer, 0, fInternalBuffer.Length - fHead);
Array.Copy(fInternalBuffer, 0, newBuffer, fInternalBuffer.Length - fHead, fTail);
}
}
fHead = 0;
fTail = fSize;
fInternalBuffer = newBuffer;
}
/// <summary>
/// Enqueues a buffer to the queue and inserts it to a correct position
/// </summary>
/// <param name="buffer">Buffer to enqueue</param>
/// <param name="offset">The zero-based float offset in the buffer</param>
/// <param name="size">The number of floats to enqueue</param>
internal void Enqueue(float[] buffer, int offset, int size) {
if (size == 0)
return;
lock (this) {
if ((fSize + size) > fInternalBuffer.Length)
SetCapacity((fSize + size + 2047) & ~2047);
if (fHead < fTail) {
int rightLength = (fInternalBuffer.Length - fTail);
if (rightLength >= size) {
Array.Copy(buffer, offset, fInternalBuffer, fTail, size);
} else {
Array.Copy(buffer, offset, fInternalBuffer, fTail, rightLength);
Array.Copy(buffer, offset + rightLength, fInternalBuffer, 0, size - rightLength);
}
} else {
Array.Copy(buffer, offset, fInternalBuffer, fTail, size);
}
fTail = (fTail + size) % fInternalBuffer.Length;
fSize += size;
fSizeUntilCut = fInternalBuffer.Length - fHead;
}
}
/// <summary>
/// Dequeues a buffer from the queue
/// </summary>
/// <param name="buffer">Buffer to enqueue</param>
/// <param name="offset">The zero-based float offset in the buffer</param>
/// <param name="size">The number of floats to dequeue</param>
/// <returns>Number of floats dequeued</returns>
internal int Dequeue(float[] buffer, int offset, int size) {
lock (this) {
if (size > fSize)
size = fSize;
if (size == 0)
return 0;
if (fHead < fTail) {
Array.Copy(fInternalBuffer, fHead, buffer, offset, size);
} else {
int rightLength = (fInternalBuffer.Length - fHead);
if (rightLength >= size) {
Array.Copy(fInternalBuffer, fHead, buffer, offset, size);
} else {
Array.Copy(fInternalBuffer, fHead, buffer, offset, rightLength);
Array.Copy(fInternalBuffer, 0, buffer, offset + rightLength, size - rightLength);
}
}
fHead = (fHead + size) % fInternalBuffer.Length;
fSize -= size;
if (fSize == 0) {
fHead = 0;
fTail = 0;
}
fSizeUntilCut = fInternalBuffer.Length - fHead;
return size;
}
}
/// <summary>
/// Peeks a float with a relative index to the fHead
/// Note: should be used for special cases only, as it is rather slow
/// </summary>
/// <param name="index">A relative index</param>
/// <returns>The float peeked</returns>
private float PeekOne(int index) {
return index >= fSizeUntilCut
? fInternalBuffer[index - fSizeUntilCut]
: fInternalBuffer[fHead + index];
}
}
}
using Oculus.Platform;
using System;
using System.Collections;
using UnityEngine;
public class NativeMicrophoneTest : MonoBehaviour {
private IntPtr _microphone;
private float[] _microphoneBuffer = new float[480]; // 10ms @ 48khz mono
private FloatQueue _microphoneSamples = new FloatQueue();
private AudioSource _audioSource;
private float[] _audioSourceBuffer;
private void Start() {
// Initialize core (This requires a valid Oculus app ID and also that your Oculus account be entitled to the application)
Core.Initialize();
// Create a native microphone instance
_microphone = CAPI.ovr_Microphone_Create();
// Start it
CAPI.ovr_Microphone_Start(_microphone);
// Create an audio source to echo the microphone data through.
_audioSource = gameObject.AddComponent<AudioSource>();
// Give it some garbage data to play (we'll replace it with the microphone data in OnAudioFilterRead)
_audioSource.loop = true;
_audioSource.clip = AudioClip.Create("Normcore Audio Stream", 48000, 1, 48000, true, (float[] data) => { for (int i = 0; i < data.Length; i++) data[i] = 1.0f; });
_audioSource.Play();
}
void OnAudioFilterRead(float[] data, int channels) {
// Read as much microphone data as possible
while (_microphone != IntPtr.Zero) {
UIntPtr samplesRead = CAPI.ovr_Microphone_GetPCMFloat(_microphone, _microphoneBuffer, (UIntPtr)_microphoneBuffer.Length);
if (samplesRead == (UIntPtr)0)
break;
_microphoneSamples.Enqueue(_microphoneBuffer, 0, (int)samplesRead);
}
// Fill the audio output buffer with microphone data, or silence if we haven't buffered enough yet.
int numberOfSamplesNeeded = data.Length / channels;
if (numberOfSamplesNeeded <= _microphoneSamples.Length) {
// Resize the buffer if necessary
if (_audioSourceBuffer == null || _audioSourceBuffer.Length < numberOfSamplesNeeded)
_audioSourceBuffer = new float[numberOfSamplesNeeded];
// Fill the audio buffer with microphone samples
int samplesDequeued = _microphoneSamples.Dequeue(_audioSourceBuffer, 0, numberOfSamplesNeeded);
// Copy samples to audio output
for (int i = 0; i < numberOfSamplesNeeded; i++) {
// Copy the microphone sample to all channels (usually mono -> stereo)
for (int c = 0; c < channels; c++) {
int outIndex = i*channels + c;
data[outIndex] = _audioSourceBuffer[i];
}
}
} else {
// Clear the buffer out because we do not have enough microphone samples to fill it.
for (int i = 0; i < data.Length; i++)
data[i] = 0.0f;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment