Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
RoosterDragon authored and reaperrr committed Sep 17, 2017
1 parent 8512cdb commit 838a3f8
Showing 1 changed file with 0 additions and 193 deletions.
193 changes: 0 additions & 193 deletions OpenRA.Platforms.Default/OpenAlSoundEngine.cs
Expand Up @@ -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<uint> freeBuffers = new Stack<uint>(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<int> 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; }
}
}
}

0 comments on commit 838a3f8

Please sign in to comment.