MoonWorks/src/Audio/StreamingSound.cs

221 lines
4.2 KiB
C#
Raw Normal View History

2022-02-23 05:14:32 +00:00
using System;
2021-01-20 03:26:30 +00:00
using System.Runtime.InteropServices;
namespace MoonWorks.Audio
{
2022-02-23 05:14:32 +00:00
/// <summary>
/// For streaming long playback.
/// Must be extended with a decoder routine called by FillBuffer.
/// See StreamingSoundOgg for an example.
2022-02-23 05:14:32 +00:00
/// </summary>
public abstract class StreamingSound : SoundInstance
{
// Should the AudioDevice thread automatically update this class?
public abstract bool AutoUpdate { get; }
// Are we actively consuming buffers?
protected bool ConsumingBuffers = false;
private const int BUFFER_COUNT = 3;
private nuint BufferSize;
private readonly IntPtr[] buffers;
private int nextBufferIndex = 0;
private uint queuedBufferCount = 0;
private readonly object StateLock = new object();
2022-02-23 05:14:32 +00:00
public unsafe StreamingSound(
2022-02-23 05:14:32 +00:00
AudioDevice device,
ushort formatTag,
ushort bitsPerSample,
ushort blockAlign,
2022-02-23 05:14:32 +00:00
ushort channels,
uint samplesPerSecond,
uint bufferSize
) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond)
{
BufferSize = bufferSize;
buffers = new IntPtr[BUFFER_COUNT];
for (int i = 0; i < BUFFER_COUNT; i += 1)
{
buffers[i] = (IntPtr) NativeMemory.Alloc(bufferSize);
}
}
public override void Play()
{
PlayUsingOperationSet(0);
}
public override void QueueSyncPlay()
{
PlayUsingOperationSet(1);
}
private void PlayUsingOperationSet(uint operationSet)
2022-02-23 05:14:32 +00:00
{
lock (StateLock)
2022-02-23 05:14:32 +00:00
{
if (State == SoundState.Playing)
{
return;
}
2022-02-23 05:14:32 +00:00
State = SoundState.Playing;
2022-04-05 23:05:42 +00:00
ConsumingBuffers = true;
QueueBuffers();
FAudio.FAudioSourceVoice_Start(Voice, 0, operationSet);
}
2022-02-23 05:14:32 +00:00
}
public override void Pause()
{
lock (StateLock)
2022-02-23 05:14:32 +00:00
{
if (State == SoundState.Playing)
{
ConsumingBuffers = false;
FAudio.FAudioSourceVoice_Stop(Voice, 0, 0);
State = SoundState.Paused;
}
2022-02-23 05:14:32 +00:00
}
}
public override void Stop()
2022-02-23 05:14:32 +00:00
{
lock (StateLock)
{
ConsumingBuffers = false;
State = SoundState.Stopped;
}
}
public override void StopImmediate()
{
lock (StateLock)
{
ConsumingBuffers = false;
FAudio.FAudioSourceVoice_Stop(Voice, 0, 0);
FAudio.FAudioSourceVoice_FlushSourceBuffers(Voice);
ClearBuffers();
2022-02-23 05:14:32 +00:00
State = SoundState.Stopped;
}
2022-02-23 05:14:32 +00:00
}
internal unsafe void Update()
2022-02-23 05:14:32 +00:00
{
lock (StateLock)
2022-02-23 05:14:32 +00:00
{
if (!IsDisposed)
{
if (State != SoundState.Playing)
{
return;
}
QueueBuffers();
}
2022-02-23 05:14:32 +00:00
}
}
2022-02-23 05:14:32 +00:00
protected void QueueBuffers()
{
2022-02-23 05:14:32 +00:00
FAudio.FAudioSourceVoice_GetState(
2023-03-02 01:47:09 +00:00
Voice,
2022-02-23 05:14:32 +00:00
out var state,
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
);
queuedBufferCount = state.BuffersQueued;
2022-02-23 05:14:32 +00:00
if (ConsumingBuffers)
2022-02-23 05:14:32 +00:00
{
for (int i = 0; i < BUFFER_COUNT - queuedBufferCount; i += 1)
{
AddBuffer();
}
}
else if (queuedBufferCount == 0)
{
Stop();
2022-02-23 05:14:32 +00:00
}
}
protected unsafe void ClearBuffers()
2022-02-23 05:14:32 +00:00
{
nextBufferIndex = 0;
queuedBufferCount = 0;
2022-02-23 05:14:32 +00:00
}
protected unsafe void AddBuffer()
2022-02-23 05:14:32 +00:00
{
var buffer = buffers[nextBufferIndex];
nextBufferIndex = (nextBufferIndex + 1) % BUFFER_COUNT;
FillBuffer(
(void*) buffer,
(int) BufferSize,
out int filledLengthInBytes,
out bool reachedEnd
2022-02-23 05:14:32 +00:00
);
if (filledLengthInBytes > 0)
2022-02-23 05:14:32 +00:00
{
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer
{
AudioBytes = (uint) filledLengthInBytes,
pAudioData = (IntPtr) buffer,
PlayLength = (
(uint) (filledLengthInBytes /
Format.nChannels /
(uint) (Format.wBitsPerSample / 8))
)
};
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
Voice,
ref buf,
IntPtr.Zero
);
queuedBufferCount += 1;
}
2022-02-23 05:14:32 +00:00
if (reachedEnd)
{
/* We have reached the end of the data, what do we do? */
ConsumingBuffers = false;
OnReachedEnd();
2022-02-23 05:14:32 +00:00
}
}
protected unsafe abstract void FillBuffer(
void* buffer,
int bufferLengthInBytes, /* in bytes */
out int filledLengthInBytes, /* in bytes */
2022-02-23 05:14:32 +00:00
out bool reachedEnd
);
protected abstract void OnReachedEnd();
protected unsafe override void Destroy()
2022-02-23 05:14:32 +00:00
{
lock (StateLock)
{
if (!IsDisposed)
{
StopImmediate();
for (int i = 0; i < BUFFER_COUNT; i += 1)
{
NativeMemory.Free((void*) buffers[i]);
}
}
}
2022-02-23 05:14:32 +00:00
}
}
2021-01-20 03:26:30 +00:00
}