MoonWorks/src/Audio/StreamingSound.cs

179 lines
4.8 KiB
C#
Raw Normal View History

2021-01-20 03:26:30 +00:00
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace MoonWorks.Audio
{
/// <summary>
2021-01-29 02:01:42 +00:00
/// For streaming long playback.
/// Can be extended to support custom decoders.
/// </summary>
public abstract class StreamingSound : SoundInstance
2021-01-20 03:26:30 +00:00
{
private readonly List<IntPtr> queuedBuffers = new List<IntPtr>();
private readonly List<uint> queuedSizes = new List<uint>();
2021-01-20 03:26:30 +00:00
private const int MINIMUM_BUFFER_CHECK = 3;
public int PendingBufferCount => queuedBuffers.Count;
public StreamingSound(
2021-01-20 03:26:30 +00:00
AudioDevice device,
ushort channels,
uint samplesPerSecond,
2021-01-20 18:55:15 +00:00
bool is3D,
bool loop
) : base(device, channels, samplesPerSecond, is3D, loop) { }
2021-01-20 03:26:30 +00:00
2021-01-29 02:01:42 +00:00
public override void Play()
2021-01-20 03:26:30 +00:00
{
if (State == SoundState.Playing)
{
return;
}
State = SoundState.Playing;
2021-01-20 18:55:15 +00:00
Update();
FAudio.FAudioSourceVoice_Start(Handle, 0, 0);
2021-01-20 03:26:30 +00:00
}
2021-01-29 02:01:42 +00:00
public override void Pause()
2021-01-20 03:26:30 +00:00
{
if (State == SoundState.Playing)
{
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
State = SoundState.Paused;
}
}
2021-01-29 02:01:42 +00:00
public override void Stop(bool immediate = true)
2021-01-20 03:26:30 +00:00
{
2021-01-20 18:55:15 +00:00
if (immediate)
{
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle);
ClearBuffers();
}
2021-01-20 03:26:30 +00:00
State = SoundState.Stopped;
}
internal void Update()
2021-01-20 03:26:30 +00:00
{
if (State != SoundState.Playing)
{
return;
}
FAudio.FAudioSourceVoice_GetState(
Handle,
out var state,
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
);
while (PendingBufferCount > state.BuffersQueued)
lock (queuedBuffers)
{
Marshal.FreeHGlobal(queuedBuffers[0]);
queuedBuffers.RemoveAt(0);
}
2021-01-20 18:55:15 +00:00
QueueBuffers();
2021-01-20 03:26:30 +00:00
}
protected void QueueBuffers()
2021-01-20 03:26:30 +00:00
{
for (
int i = MINIMUM_BUFFER_CHECK - PendingBufferCount;
i > 0;
i -= 1
)
{
2021-01-20 03:26:30 +00:00
AddBuffer();
}
}
protected void ClearBuffers()
2021-01-20 03:26:30 +00:00
{
lock (queuedBuffers)
{
foreach (IntPtr buf in queuedBuffers)
{
Marshal.FreeHGlobal(buf);
}
queuedBuffers.Clear();
2021-01-20 18:55:15 +00:00
queuedSizes.Clear();
2021-01-20 03:26:30 +00:00
}
}
protected void AddBuffer()
2021-01-20 03:26:30 +00:00
{
AddBuffer(
2021-01-29 02:01:42 +00:00
out var buffer,
out var bufferOffset,
out var bufferLength,
out var reachedEnd
2021-01-20 03:26:30 +00:00
);
var lengthInBytes = bufferLength * sizeof(float);
2021-01-20 18:55:15 +00:00
IntPtr next = Marshal.AllocHGlobal((int) lengthInBytes);
Marshal.Copy(buffer, (int) bufferOffset, next, (int) bufferLength);
2021-01-20 03:26:30 +00:00
lock (queuedBuffers)
{
queuedBuffers.Add(next);
if (State != SoundState.Stopped)
{
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer
{
AudioBytes = lengthInBytes,
pAudioData = next,
PlayLength = (
lengthInBytes /
Format.nChannels /
(uint)(Format.wBitsPerSample / 8)
2021-01-20 03:26:30 +00:00
)
};
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
Handle,
ref buf,
IntPtr.Zero
);
}
else
{
queuedSizes.Add(lengthInBytes);
}
}
2021-01-20 18:55:15 +00:00
/* We have reached the end of the file, what do we do? */
if (reachedEnd)
2021-01-20 18:55:15 +00:00
{
if (Loop)
{
SeekStart();
2021-01-20 18:55:15 +00:00
}
else
{
Stop(false);
}
}
2021-01-20 03:26:30 +00:00
}
protected abstract void AddBuffer(
out float[] buffer,
out uint bufferOffset, /* in floats */
out uint bufferLength, /* in floats */
out bool reachedEnd
);
protected abstract void SeekStart();
2021-02-27 00:17:26 +00:00
protected override void Destroy()
{
2021-02-27 00:17:26 +00:00
Stop(true);
}
2021-01-20 03:26:30 +00:00
}
}