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.
|
2022-08-02 21:04:12 +00:00
|
|
|
|
/// 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
|
|
|
|
|
{
|
2022-08-02 21:04:12 +00:00
|
|
|
|
private const int BUFFER_COUNT = 3;
|
|
|
|
|
private readonly IntPtr[] buffers;
|
|
|
|
|
private int nextBufferIndex = 0;
|
|
|
|
|
private uint queuedBufferCount = 0;
|
|
|
|
|
protected abstract int BUFFER_SIZE { get; }
|
2022-02-23 05:14:32 +00:00
|
|
|
|
|
2022-08-02 21:04:12 +00:00
|
|
|
|
public unsafe StreamingSound(
|
2022-02-23 05:14:32 +00:00
|
|
|
|
AudioDevice device,
|
2022-04-05 06:33:36 +00:00
|
|
|
|
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
|
2022-08-02 21:04:12 +00:00
|
|
|
|
) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond)
|
|
|
|
|
{
|
|
|
|
|
device.AddDynamicSoundInstance(this);
|
2022-02-23 05:14:32 +00:00
|
|
|
|
|
2022-08-02 21:04:12 +00:00
|
|
|
|
buffers = new IntPtr[BUFFER_COUNT];
|
|
|
|
|
for (int i = 0; i < BUFFER_COUNT; i += 1)
|
|
|
|
|
{
|
|
|
|
|
buffers[i] = (IntPtr) NativeMemory.Alloc((nuint) BUFFER_SIZE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-02 21:04:12 +00:00
|
|
|
|
public override void Stop()
|
2022-02-23 05:14:32 +00:00
|
|
|
|
{
|
2022-08-02 21:04:12 +00:00
|
|
|
|
State = SoundState.Stopped;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void StopImmediate()
|
|
|
|
|
{
|
|
|
|
|
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
|
|
|
|
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle);
|
|
|
|
|
ClearBuffers();
|
2022-02-23 05:14:32 +00:00
|
|
|
|
|
|
|
|
|
State = SoundState.Stopped;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-02 21:04:12 +00:00
|
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2022-08-02 21:04:12 +00:00
|
|
|
|
queuedBufferCount = state.BuffersQueued;
|
2022-02-23 05:14:32 +00:00
|
|
|
|
|
|
|
|
|
QueueBuffers();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void QueueBuffers()
|
|
|
|
|
{
|
2022-08-02 21:04:12 +00:00
|
|
|
|
for (int i = 0; i < BUFFER_COUNT - queuedBufferCount; i += 1)
|
2022-02-23 05:14:32 +00:00
|
|
|
|
{
|
|
|
|
|
AddBuffer();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-02 21:04:12 +00:00
|
|
|
|
protected unsafe void ClearBuffers()
|
2022-02-23 05:14:32 +00:00
|
|
|
|
{
|
2022-08-02 21:04:12 +00:00
|
|
|
|
nextBufferIndex = 0;
|
|
|
|
|
queuedBufferCount = 0;
|
2022-02-23 05:14:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-02 21:04:12 +00:00
|
|
|
|
protected unsafe void AddBuffer()
|
2022-02-23 05:14:32 +00:00
|
|
|
|
{
|
2022-08-02 21:04:12 +00:00
|
|
|
|
var buffer = buffers[nextBufferIndex];
|
|
|
|
|
nextBufferIndex = (nextBufferIndex + 1) % BUFFER_COUNT;
|
|
|
|
|
|
|
|
|
|
FillBuffer(
|
|
|
|
|
(void*) buffer,
|
|
|
|
|
BUFFER_SIZE,
|
|
|
|
|
out int filledLengthInBytes,
|
|
|
|
|
out bool reachedEnd
|
2022-02-23 05:14:32 +00:00
|
|
|
|
);
|
|
|
|
|
|
2022-08-02 21:04:12 +00:00
|
|
|
|
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer
|
2022-02-23 05:14:32 +00:00
|
|
|
|
{
|
2022-08-02 21:04:12 +00:00
|
|
|
|
AudioBytes = (uint) filledLengthInBytes,
|
|
|
|
|
pAudioData = (IntPtr) buffer,
|
|
|
|
|
PlayLength = (
|
|
|
|
|
(uint) (filledLengthInBytes /
|
|
|
|
|
Format.nChannels /
|
|
|
|
|
(uint) (Format.wBitsPerSample / 8))
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
|
|
|
|
|
Handle,
|
|
|
|
|
ref buf,
|
|
|
|
|
IntPtr.Zero
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
queuedBufferCount += 1;
|
2022-02-23 05:14:32 +00:00
|
|
|
|
|
|
|
|
|
/* We have reached the end of the file, what do we do? */
|
|
|
|
|
if (reachedEnd)
|
|
|
|
|
{
|
2022-08-02 21:04:12 +00:00
|
|
|
|
OnReachedEnd();
|
2022-02-23 05:14:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-02 21:04:12 +00:00
|
|
|
|
protected virtual void OnReachedEnd()
|
|
|
|
|
{
|
|
|
|
|
Stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2022-08-02 21:04:12 +00:00
|
|
|
|
protected unsafe override void Destroy()
|
2022-02-23 05:14:32 +00:00
|
|
|
|
{
|
2022-08-02 21:04:12 +00:00
|
|
|
|
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
|
|
|
|
}
|