Created
October 29, 2019 23:44
-
-
Save maxweisel/0d7e009f1046fa4e2506fc2a165afdcc to your computer and use it in GitHub Desktop.
Oculus Platform Microphone Test
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
#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]; | |
} | |
} | |
} |
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 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