MoonWorks/src/Audio/StreamingSound.cs

176 lines
3.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.Collections.Generic;
using System.Runtime.InteropServices;
namespace MoonWorks.Audio
{
2022-02-23 05:14:32 +00:00
/// <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; }
2022-02-23 05:14:32 +00:00
public StreamingSound(
AudioDevice device,
ushort formatTag,
ushort bitsPerSample,
ushort blockAlign,
2022-02-23 05:14:32 +00:00
ushort channels,
2022-04-07 21:19:43 +00:00
uint samplesPerSecond
) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond)
{
device.AddDynamicSoundInstance(this);
}
2022-02-23 05:14:32 +00:00
public override void Play()
2022-02-23 05:14:32 +00:00
{
if (State == SoundState.Playing)
{
return;
}
State = SoundState.Playing;
2022-04-05 23:05:42 +00:00
2022-02-23 05:14:32 +00:00
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()
2022-02-23 05:14:32 +00:00
{
if (State != SoundState.Playing)
{
return;
}
FAudio.FAudioSourceVoice_GetState(
Handle,
out var state,
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
);
while (PendingBufferCount > state.BuffersQueued)
{
2022-02-23 05:14:32 +00:00
lock (queuedBuffers)
{
NativeMemory.Free((void*) queuedBuffers[0]);
2022-02-23 05:14:32 +00:00
queuedBuffers.RemoveAt(0);
}
}
2022-02-23 05:14:32 +00:00
QueueBuffers();
}
protected void QueueBuffers()
{
for (
int i = MINIMUM_BUFFER_CHECK - PendingBufferCount;
i > 0;
i -= 1
)
{
AddBuffer();
}
}
protected unsafe void ClearBuffers()
2022-02-23 05:14:32 +00:00
{
lock (queuedBuffers)
{
foreach (IntPtr buf in queuedBuffers)
{
NativeMemory.Free((void*) buf);
2022-02-23 05:14:32 +00:00
}
queuedBuffers.Clear();
}
}
protected unsafe void AddBuffer()
2022-02-23 05:14:32 +00:00
{
void* buffer = NativeMemory.Alloc((nuint) BUFFER_SIZE);
2022-02-23 05:14:32 +00:00
AddBuffer(
buffer,
BUFFER_SIZE,
out int filledLengthInBytes,
out bool reachedEnd
2022-02-23 05:14:32 +00:00
);
lock (queuedBuffers)
{
queuedBuffers.Add((IntPtr) buffer);
2022-02-23 05:14:32 +00:00
if (State != SoundState.Stopped)
{
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer
{
AudioBytes = (uint) filledLengthInBytes,
pAudioData = (IntPtr) buffer,
2022-02-23 05:14:32 +00:00
PlayLength = (
(uint) (filledLengthInBytes /
2022-02-23 05:14:32 +00:00
Format.nChannels /
(uint) (Format.wBitsPerSample / 8))
2022-02-23 05:14:32 +00:00
)
};
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
Handle,
ref buf,
IntPtr.Zero
);
}
}
2022-02-23 05:14:32 +00:00
/* We have reached the end of the file, what do we do? */
if (reachedEnd)
{
OnReachedEnd();
2022-02-23 05:14:32 +00:00
}
}
protected virtual void OnReachedEnd()
{
Stop(false);
}
protected unsafe abstract void AddBuffer(
void* buffer,
int bufferLength, /* in bytes */
out int filledLength, /* in bytes */
2022-02-23 05:14:32 +00:00
out bool reachedEnd
);
protected override void Destroy()
{
Stop(true);
}
}
2021-01-20 03:26:30 +00:00
}