From 838a3f83c64e540a6d626ee5016c2d52d9c41a9e Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Tue, 12 Sep 2017 21:16:48 +0100 Subject: [PATCH] Remove OpenAlStreamingSound. Whilst the implementation appears to work most of the time, it sometimes stops playing audio mid-track. This could be an implementation issue, or bugs in the underlying streaming APIs of the OpenAL library. Either way, it is not currently reliable enough to be used. --- OpenRA.Platforms.Default/OpenAlSoundEngine.cs | 193 ------------------ 1 file changed, 193 deletions(-) diff --git a/OpenRA.Platforms.Default/OpenAlSoundEngine.cs b/OpenRA.Platforms.Default/OpenAlSoundEngine.cs index d772579fcf59..0086be0134ff 100644 --- a/OpenRA.Platforms.Default/OpenAlSoundEngine.cs +++ b/OpenRA.Platforms.Default/OpenAlSoundEngine.cs @@ -608,197 +608,4 @@ public override bool Complete get { return playTask.IsCompleted; } } } - - class OpenAlStreamingSound : OpenAlSound - { - const int BufferCount = 3; - const int BufferSizeInSecs = 1; - readonly object bufferDequeueLock = new object(); - readonly CancellationTokenSource cts = new CancellationTokenSource(); - readonly Task streamTask; - readonly Stack freeBuffers = new Stack(BufferCount); - int totalSamplesPlayed; - - public OpenAlStreamingSound(uint source, bool looping, bool relative, WPos pos, float volume, int channels, int sampleBits, int sampleRate, Stream stream) - : base(source, looping, relative, pos, volume, sampleRate) - { - streamTask = Task.Run(async () => - { - var format = OpenAlSoundEngine.MakeALFormat(channels, sampleBits); - var bytesPerSample = sampleBits / 8; - - var buffers = new uint[BufferCount]; - AL10.alGenBuffers(new IntPtr(buffers.Length), buffers); - try - { - foreach (var buffer in buffers) - freeBuffers.Push(buffer); - var data = new byte[sampleRate * bytesPerSample * BufferSizeInSecs]; - var streamEnd = false; - - while (!streamEnd && !cts.IsCancellationRequested) - { - // Fill the data array as fully as possible. - var count = await ReadFillingBuffer(stream, data); - streamEnd = count < data.Length; - - // Fill a buffer and queue it for playback. - var nextBuffer = freeBuffers.Pop(); - AL10.alBufferData(nextBuffer, format, data, new IntPtr(count), new IntPtr(sampleRate)); - AL10.alSourceQueueBuffers(source, new IntPtr(1), ref nextBuffer); - - lock (cts) - { - if (!cts.IsCancellationRequested) - { - // Once we have at least one buffer filled, we can actually start. - // If streaming fell behind, the source will have stopped, - // we also need to play it in this case to resume audio. - int state; - AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE, out state); - if (state == AL10.AL_INITIAL || state == AL10.AL_STOPPED) - { - // If we resume playback from the stopped state, it resets to the beginning! - // To avoid replaying the same audio, we need to dequeue the processed buffers first. - if (state == AL10.AL_STOPPED) - DequeueBuffers(); - - AL10.alSourcePlay(source); - } - } - } - - // Try and dequeue buffers as they become available to be reused. - // When the stream ends, wait for all the buffers to be processed - while (freeBuffers.Count < (streamEnd ? buffers.Length : 1)) - { - await Task.Delay(TimeSpan.FromSeconds(BufferSizeInSecs), cts.Token).ConfigureAwait(false); - DequeueBuffers(); - } - } - } - catch (TaskCanceledException) - { - // Streaming has been cancelled, we'll need to perform some cleanup. - } - finally - { - // If we never actually started the source, we need to start it and then stop it. - // Otherwise it is left in the initial state and never returned to the source pool. - int state; - AL10.alGetSourcei(Source, AL10.AL_SOURCE_STATE, out state); - if (state == AL10.AL_INITIAL) - AL10.alSourcePlay(Source); - - // Ensure the source is stopped, which will mark all buffers as processed. - // Dequeue these, so they can then be freed. - AL10.alSourceStop(Source); - lock (bufferDequeueLock) - { - int buffersProcessed; - AL10.alGetSourcei(source, AL10.AL_BUFFERS_PROCESSED, out buffersProcessed); - for (var i = 0; i < buffersProcessed; i++) - { - var dequeued = 0U; - AL10.alSourceUnqueueBuffers(source, new IntPtr(1), ref dequeued); - } - } - - AL10.alDeleteBuffers(new IntPtr(buffers.Length), buffers); - stream.Dispose(); - } - }).ContinueWith(task => - { - Game.RunAfterTick(() => - { - throw new Exception("Failed to stream a sound.", task.Exception); - }); - }, TaskContinuationOptions.OnlyOnFaulted); - } - - async Task ReadFillingBuffer(Stream stream, byte[] buffer) - { - var offset = 0; - int count; - while (offset < buffer.Length && - (count = await stream.ReadAsync(buffer, offset, buffer.Length - offset, cts.Token).ConfigureAwait(false)) > 0) - offset += count; - return offset; - } - - void DequeueBuffers() - { - lock (bufferDequeueLock) - { - // Check for any processed buffers, and dequeue them for reuse. - int buffersProcessed; - AL10.alGetSourcei(Source, AL10.AL_BUFFERS_PROCESSED, out buffersProcessed); - for (var i = 0; i < buffersProcessed; i++) - { - // Dequeue a processed buffer, so we can reuse it. - var dequeued = 0U; - AL10.alSourceUnqueueBuffers(Source, new IntPtr(1), ref dequeued); - freeBuffers.Push(dequeued); - - // When we remove a buffer, we need to account for this to calculate the overall seek position. - // The final buffer in the track may be shorter then BufferSizeInSecs, so we'll need to check how - // many bytes are actually in each buffer to avoid adding too much at the end. - int byteSize; - AL10.alGetBufferi(dequeued, AL10.AL_SIZE, out byteSize); - - int bitRate; - AL10.alGetBufferi(dequeued, AL10.AL_BITS, out bitRate); - - totalSamplesPlayed += byteSize / (bitRate / 8); - } - } - } - - public override void Stop() - { - lock (cts) - { - StopSource(); - cts.Cancel(); - } - - try - { - streamTask.Wait(); - } - catch (AggregateException) - { - } - } - - public override float SeekPosition - { - get - { - // Stop buffers being dequeued whilst we calculate the seek position. - lock (bufferDequeueLock) - { - int sampleOffset; - AL10.alGetSourcei(Source, AL11.AL_SAMPLE_OFFSET, out sampleOffset); - - int state; - AL10.alGetSourcei(Source, AL10.AL_SOURCE_STATE, out state); - - // If the source is not stopped, add the current offset to the total offset and return that. - if (state != AL10.AL_STOPPED) - return (sampleOffset + totalSamplesPlayed) / SampleRate; - - // If the source stopped, the current offset will have been reset to 0. - // We'll need to dequeue any buffers played first and then return the total. - DequeueBuffers(); - return totalSamplesPlayed / SampleRate; - } - } - } - - public override bool Complete - { - get { return streamTask.IsCompleted; } - } - } }