namespace Oculus.Platform
|
|
{
|
|
using UnityEngine;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
public class VoipAudioSourceHiLevel : MonoBehaviour
|
|
{
|
|
// This is a delegate that exists as a surface for OnAudioFilterRead
|
|
// It will be callled on unity's audio thread
|
|
public class FilterReadDelegate : MonoBehaviour
|
|
{
|
|
public VoipAudioSourceHiLevel parent;
|
|
float[] scratchBuffer;
|
|
|
|
void Awake()
|
|
{
|
|
int bufferSizeElements = (int)CAPI.ovr_Voip_GetOutputBufferMaxSize();
|
|
scratchBuffer = new float[bufferSizeElements];
|
|
}
|
|
|
|
void OnAudioFilterRead(float[] data, int channels)
|
|
{
|
|
int sizeToFetch = data.Length / channels;
|
|
int sourceBufferSize = sizeToFetch;
|
|
if (sourceBufferSize > scratchBuffer.Length)
|
|
{
|
|
Array.Clear(data, 0, data.Length);
|
|
throw new Exception(string.Format("Audio system tried to pull {0} bytes, max voip internal ring buffer size {1}", sizeToFetch, scratchBuffer.Length));
|
|
}
|
|
|
|
int available = parent.pcmSource.PeekSizeElements();
|
|
if (available < sourceBufferSize)
|
|
{
|
|
if (verboseLogging)
|
|
{
|
|
Debug.LogFormat(
|
|
"Voip starved! Want {0}, but only have {1} available",
|
|
sourceBufferSize,
|
|
available);
|
|
}
|
|
return;
|
|
}
|
|
|
|
int copied = parent.pcmSource.GetPCM(scratchBuffer, sourceBufferSize);
|
|
if (copied < sourceBufferSize)
|
|
{
|
|
Debug.LogWarningFormat(
|
|
"GetPCM() returned {0} samples, expected {1}",
|
|
copied,
|
|
sourceBufferSize);
|
|
|
|
return;
|
|
}
|
|
|
|
int dest = 0;
|
|
float tmpPeakAmp = -1;
|
|
for (int i = 0; i < sizeToFetch; i++)
|
|
{
|
|
float val = scratchBuffer[i];
|
|
|
|
for (int j = 0; j < channels; j++)
|
|
{
|
|
data[dest++] = val;
|
|
if (val > tmpPeakAmp)
|
|
{
|
|
tmpPeakAmp = val;
|
|
}
|
|
}
|
|
}
|
|
parent.peakAmplitude = tmpPeakAmp;
|
|
}
|
|
}
|
|
|
|
|
|
int initialPlaybackDelayMS;
|
|
public UInt64 senderID
|
|
{
|
|
set
|
|
{
|
|
pcmSource.SetSenderID(value);
|
|
}
|
|
}
|
|
|
|
public AudioSource audioSource;
|
|
public float peakAmplitude;
|
|
|
|
protected IVoipPCMSource pcmSource;
|
|
|
|
static int audioSystemPlaybackFrequency;
|
|
static bool verboseLogging = false;
|
|
|
|
protected void Stop() {}
|
|
|
|
VoipSampleRate SampleRateToEnum(int rate) {
|
|
switch(rate) {
|
|
case 48000:
|
|
return VoipSampleRate.HZ48000;
|
|
case 44100:
|
|
return VoipSampleRate.HZ44100;
|
|
case 24000:
|
|
return VoipSampleRate.HZ24000;
|
|
default:
|
|
return VoipSampleRate.Unknown;
|
|
}
|
|
}
|
|
|
|
protected void Awake()
|
|
{
|
|
CreatePCMSource();
|
|
if(audioSource == null) {
|
|
audioSource = gameObject.AddComponent<AudioSource>();
|
|
}
|
|
audioSource.gameObject.AddComponent<FilterReadDelegate>();
|
|
var filterDelegate = audioSource.gameObject.GetComponent<FilterReadDelegate>();
|
|
filterDelegate.parent = this;
|
|
|
|
initialPlaybackDelayMS = 40;
|
|
|
|
audioSystemPlaybackFrequency = AudioSettings.outputSampleRate;
|
|
CAPI.ovr_Voip_SetOutputSampleRate(SampleRateToEnum(audioSystemPlaybackFrequency));
|
|
if(verboseLogging) {
|
|
Debug.LogFormat("freq {0}", audioSystemPlaybackFrequency);
|
|
}
|
|
}
|
|
|
|
void Start() {
|
|
audioSource.Stop();
|
|
}
|
|
|
|
protected virtual void CreatePCMSource()
|
|
{
|
|
pcmSource = new VoipPCMSourceNative();
|
|
}
|
|
|
|
protected static int MSToElements(int ms) {
|
|
return ms * audioSystemPlaybackFrequency / 1000;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
pcmSource.Update();
|
|
|
|
if (!audioSource.isPlaying && pcmSource.PeekSizeElements() >= MSToElements(initialPlaybackDelayMS)) {
|
|
if(verboseLogging) {
|
|
Debug.LogFormat("buffered {0} elements, starting playback", pcmSource.PeekSizeElements());
|
|
}
|
|
audioSource.Play();
|
|
}
|
|
}
|
|
}
|
|
}
|