using System; using System.Runtime.InteropServices; namespace MoonWorks.Audio { public class StreamingVoice : SourceVoice, IPoolable { private const int BUFFER_COUNT = 3; private readonly IntPtr[] buffers; private int nextBufferIndex = 0; private uint BufferSize; public bool Loop { get; set; } public AudioData AudioData { get; protected set; } public static StreamingVoice Create(AudioDevice device, Format format) { return new StreamingVoice(device, format); } public unsafe StreamingVoice(AudioDevice device, Format format) : base(device, format) { buffers = new IntPtr[BUFFER_COUNT]; } public void Load(AudioData data) { lock (StateLock) { if (AudioData != null) { AudioData.Unload(); } data.Load(); AudioData = data; InitializeBuffers(); QueueBuffers(); } } public void Unload() { lock (StateLock) { if (AudioData != null) { Stop(); AudioData.Unload(); AudioData = null; } } } public override void Update() { lock (StateLock) { if (!IsDisposed) { if (AudioData == null || State != SoundState.Playing) { return; } QueueBuffers(); } } } protected void QueueBuffers() { int buffersNeeded = BUFFER_COUNT - (int) BuffersQueued; // don't get got by uint underflow! for (int i = 0; i < buffersNeeded; i += 1) { AddBuffer(); } } protected unsafe void AddBuffer() { var buffer = buffers[nextBufferIndex]; nextBufferIndex = (nextBufferIndex + 1) % BUFFER_COUNT; AudioData.Decode( (void*) buffer, (int) BufferSize, out int filledLengthInBytes, out bool reachedEnd ); if (filledLengthInBytes > 0) { var buf = new FAudio.FAudioBuffer { AudioBytes = (uint) filledLengthInBytes, pAudioData = buffer, PlayLength = ( (uint) (filledLengthInBytes / Format.Channels / (uint) (Format.BitsPerSample / 8)) ) }; Submit(buf); } if (reachedEnd) { /* We have reached the end of the data, what do we do? */ if (Loop) { AudioData.Seek(0); AddBuffer(); } } } private unsafe void InitializeBuffers() { BufferSize = AudioData.DecodeBufferSize; for (int i = 0; i < BUFFER_COUNT; i += 1) { if (buffers[i] != IntPtr.Zero) { NativeMemory.Free((void*) buffers[i]); } buffers[i] = (IntPtr) NativeMemory.Alloc(BufferSize); } } } }