151 lines
5.6 KiB
C#
151 lines
5.6 KiB
C#
//#define VERBOSE_LOGGING
|
|
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
using System;
|
|
using Oculus.Platform;
|
|
|
|
/// BufferedAudioStream provides a way to stream audio data from an AudioSource object.
|
|
/// It maintains an internal buffer and offers methods for updating and adding new data.
|
|
/// Update() updates playback based on remaining buffer time, while AddData() adds new data to the buffer,
|
|
/// wrapping around if necessary. This class is useful for real-time audio streaming, such as playing back
|
|
/// audio from a network connection or generating audio dynamically.
|
|
/// This class is used in VoipAudioSource, which has been deprecated. See details [here](https://developers.meta.com/horizon/blog/deprecating-oculus-rooms-api-in-march-2023/).
|
|
public class BufferedAudioStream
|
|
{
|
|
const bool VerboseLogging = false;
|
|
AudioSource audio;
|
|
|
|
float[] audioBuffer;
|
|
int writePos;
|
|
|
|
const float bufferLengthSeconds = 0.25f;
|
|
const int sampleRate = 48000;
|
|
const int bufferSize = (int)(sampleRate * bufferLengthSeconds);
|
|
const float playbackDelayTimeSeconds = 0.05f;
|
|
|
|
float playbackDelayRemaining;
|
|
float remainingBufferTime;
|
|
|
|
/// The constructor of BufferedAudioStream takes an AudioSource object as a parameter and initializes the audio buffer with a specified size.
|
|
/// It also sets the loop property of the AudioSource object to true and creates an AudioClip object with the specified buffer size,
|
|
/// sample rate, and number of channels. This class is used in VoipAudioSource, which has been deprecated. See details [here](https://developers.meta.com/horizon/blog/deprecating-oculus-rooms-api-in-march-2023/).
|
|
public BufferedAudioStream(AudioSource audio)
|
|
{
|
|
audioBuffer = new float[bufferSize];
|
|
this.audio = audio;
|
|
|
|
audio.loop = true;
|
|
audio.clip = AudioClip.Create("", bufferSize, 1, sampleRate, false);
|
|
|
|
Stop();
|
|
}
|
|
|
|
/// This method updates the audio playback based on the remaining buffer time. If there is still buffer time remaining,
|
|
/// it checks if the audio should start playing and updates the playback delay timer. If the audio is currently playing,
|
|
/// it decrements the remaining buffer time. If the buffer is empty, it stops the audio playback and logs a message.
|
|
/// This method is used in VoipAudioSource which got drecated, which has been deprecated. See details [here](https://developers.meta.com/horizon/blog/deprecating-oculus-rooms-api-in-march-2023/).
|
|
public void Update()
|
|
{
|
|
if (remainingBufferTime > 0)
|
|
{
|
|
#if VERBOSE_LOGGING
|
|
Debug.Log(string.Format("current time: {0}, remainingBufferTime: {1}", Time.time, remainingBufferTime));
|
|
#endif
|
|
|
|
if (!audio.isPlaying && remainingBufferTime > playbackDelayTimeSeconds)
|
|
{
|
|
playbackDelayRemaining -= Time.deltaTime;
|
|
if (playbackDelayRemaining <= 0)
|
|
{
|
|
#if VERBOSE_LOGGING
|
|
Debug.Log("Starting playback");
|
|
#endif
|
|
audio.Play();
|
|
}
|
|
}
|
|
|
|
if (audio.isPlaying)
|
|
{
|
|
remainingBufferTime -= Time.deltaTime;
|
|
if (remainingBufferTime < 0)
|
|
{
|
|
remainingBufferTime = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (remainingBufferTime <= 0)
|
|
{
|
|
if (audio.isPlaying)
|
|
{
|
|
Debug.Log("Buffer empty, stopping " + DateTime.Now);
|
|
Stop();
|
|
}
|
|
else
|
|
{
|
|
if (writePos != 0)
|
|
{
|
|
Debug.LogError("writePos non zero while not playing, how did this happen?");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Stop()
|
|
{
|
|
audio.Stop();
|
|
audio.time = 0;
|
|
writePos = 0;
|
|
playbackDelayRemaining = playbackDelayTimeSeconds;
|
|
}
|
|
|
|
/// This method adds new audio data to the buffer. It takes an array of float samples as input and writes them to the internal audio buffer.
|
|
/// If there is not enough space in the buffer, it will wrap around to the beginning of the buffer. If the write position exceeds the buffer length,
|
|
/// an exception is thrown. The method returns the number of samples written to the buffer.
|
|
/// This method is used in VoipAudioSource which got drecated, which has been deprecated. See details [here](https://developers.meta.com/horizon/blog/deprecating-oculus-rooms-api-in-march-2023/).
|
|
public void AddData(float[] samples)
|
|
{
|
|
int remainingWriteLength = samples.Length;
|
|
|
|
if (writePos > audioBuffer.Length)
|
|
{
|
|
throw new Exception();
|
|
}
|
|
|
|
do
|
|
{
|
|
int writeLength = remainingWriteLength;
|
|
int remainingSpace = audioBuffer.Length - writePos;
|
|
|
|
if (writeLength > remainingSpace)
|
|
{
|
|
writeLength = remainingSpace;
|
|
}
|
|
|
|
Array.Copy(samples, 0, audioBuffer, writePos, writeLength);
|
|
|
|
remainingWriteLength -= writeLength;
|
|
writePos += writeLength;
|
|
if (writePos > audioBuffer.Length)
|
|
{
|
|
throw new Exception();
|
|
}
|
|
|
|
if (writePos == audioBuffer.Length)
|
|
{
|
|
writePos = 0;
|
|
}
|
|
} while (remainingWriteLength > 0);
|
|
|
|
#if VERBOSE_LOGGING
|
|
float prev = remainingBufferTime;
|
|
#endif
|
|
remainingBufferTime += (float)samples.Length / sampleRate;
|
|
#if VERBOSE_LOGGING
|
|
Debug.Log(string.Format("previous remaining: {0}, new remaining: {1}, added {2} samples", prev, remainingBufferTime, samples.Length));
|
|
#endif
|
|
audio.clip.SetData(audioBuffer, 0);
|
|
}
|
|
}
|