176 lines
3.2 KiB
C#
176 lines
3.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace MoonWorks.Audio
|
|
{
|
|
/// <summary>
|
|
/// For streaming long playback.
|
|
/// Can be extended to support custom decoders.
|
|
/// </summary>
|
|
public abstract class StreamingSound : SoundInstance
|
|
{
|
|
private readonly List<IntPtr> queuedBuffers = new List<IntPtr>();
|
|
private const int MINIMUM_BUFFER_CHECK = 3;
|
|
|
|
private int PendingBufferCount => queuedBuffers.Count;
|
|
|
|
public abstract int BUFFER_SIZE { get; }
|
|
|
|
public StreamingSound(
|
|
AudioDevice device,
|
|
ushort formatTag,
|
|
ushort bitsPerSample,
|
|
ushort blockAlign,
|
|
ushort channels,
|
|
uint samplesPerSecond
|
|
) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond)
|
|
{
|
|
device.AddDynamicSoundInstance(this);
|
|
}
|
|
|
|
public override void Play()
|
|
{
|
|
if (State == SoundState.Playing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
State = SoundState.Playing;
|
|
|
|
Update();
|
|
FAudio.FAudioSourceVoice_Start(Handle, 0, 0);
|
|
}
|
|
|
|
public override void Pause()
|
|
{
|
|
if (State == SoundState.Playing)
|
|
{
|
|
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
|
State = SoundState.Paused;
|
|
}
|
|
}
|
|
|
|
public override void Stop(bool immediate = true)
|
|
{
|
|
if (immediate)
|
|
{
|
|
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
|
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle);
|
|
ClearBuffers();
|
|
}
|
|
|
|
State = SoundState.Stopped;
|
|
}
|
|
|
|
internal unsafe void Update()
|
|
{
|
|
if (State != SoundState.Playing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FAudio.FAudioSourceVoice_GetState(
|
|
Handle,
|
|
out var state,
|
|
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
|
|
);
|
|
|
|
while (PendingBufferCount > state.BuffersQueued)
|
|
{
|
|
lock (queuedBuffers)
|
|
{
|
|
NativeMemory.Free((void*) queuedBuffers[0]);
|
|
queuedBuffers.RemoveAt(0);
|
|
}
|
|
}
|
|
|
|
QueueBuffers();
|
|
}
|
|
|
|
protected void QueueBuffers()
|
|
{
|
|
for (
|
|
int i = MINIMUM_BUFFER_CHECK - PendingBufferCount;
|
|
i > 0;
|
|
i -= 1
|
|
)
|
|
{
|
|
AddBuffer();
|
|
}
|
|
}
|
|
|
|
protected unsafe void ClearBuffers()
|
|
{
|
|
lock (queuedBuffers)
|
|
{
|
|
foreach (IntPtr buf in queuedBuffers)
|
|
{
|
|
NativeMemory.Free((void*) buf);
|
|
}
|
|
queuedBuffers.Clear();
|
|
}
|
|
}
|
|
|
|
protected unsafe void AddBuffer()
|
|
{
|
|
void* buffer = NativeMemory.Alloc((nuint) BUFFER_SIZE);
|
|
|
|
AddBuffer(
|
|
buffer,
|
|
BUFFER_SIZE,
|
|
out int filledLengthInBytes,
|
|
out bool reachedEnd
|
|
);
|
|
|
|
lock (queuedBuffers)
|
|
{
|
|
queuedBuffers.Add((IntPtr) buffer);
|
|
if (State != SoundState.Stopped)
|
|
{
|
|
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer
|
|
{
|
|
AudioBytes = (uint) filledLengthInBytes,
|
|
pAudioData = (IntPtr) buffer,
|
|
PlayLength = (
|
|
(uint) (filledLengthInBytes /
|
|
Format.nChannels /
|
|
(uint) (Format.wBitsPerSample / 8))
|
|
)
|
|
};
|
|
|
|
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
|
|
Handle,
|
|
ref buf,
|
|
IntPtr.Zero
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/* We have reached the end of the file, what do we do? */
|
|
if (reachedEnd)
|
|
{
|
|
OnReachedEnd();
|
|
}
|
|
}
|
|
|
|
protected virtual void OnReachedEnd()
|
|
{
|
|
Stop(false);
|
|
}
|
|
|
|
protected unsafe abstract void AddBuffer(
|
|
void* buffer,
|
|
int bufferLength, /* in bytes */
|
|
out int filledLength, /* in bytes */
|
|
out bool reachedEnd
|
|
);
|
|
|
|
protected override void Destroy()
|
|
{
|
|
Stop(true);
|
|
}
|
|
}
|
|
}
|