private void BeginOgg(bool isLooping, string volumeGroup) { if (string.IsNullOrEmpty(this.oggFilePath)) { return; } lock (this.oggQueueLock) { } this.oggPlaybackEnded = false; this.oggSource = GenSourceWithVolume(volumeGroup); var currentSource = this.oggSource; int channels, bits_per_sample; VorbisReader vorbis; try { vorbis = new VorbisReader(this.oggFilePath); } catch (ArgumentException) { throw new InvalidOperationException("Invalid .ogg file"); } // get the channels & sample rate channels = vorbis.Channels; bits_per_sample = 16; this.oggSampleRate = vorbis.SampleRate; var soundFormat = GetSoundFormat(channels, bits_per_sample); // OPTIONALLY: get a TimeSpan indicating the total length of the Vorbis stream var totalTime = vorbis.TotalTime; // create a buffer for reading samples var valuesPerBuffer = (int)(channels * oggSampleRate * OggBufferSize); // 1s/5 = 200ms var readBuffer = new float[valuesPerBuffer]; // get the initial position (obviously the start) var position = TimeSpan.Zero; this.oggTotalSamples = vorbis.TotalSamples; var sampleEnd = isLooping ? this.oggLoopEnd : this.oggTotalSamples; if (this.oggProgress < 0 || this.oggProgress >= sampleEnd) { this.OggSeek(0); } this.UpdateOggProgress(this.oggPausePosition); vorbis.SeekTo(this.oggPausePosition); var buffersInitialized = false; var buffers = new List <int>(); var playbackStopDetected = false; this.WaitOnLock(this.oggQueueLock, () => { for (int i = 0; i < OggBufferCount; i++) { var currentBuffer = AL.GenBuffer(); QueueBuffer(currentSource, currentBuffer, vorbis, soundFormat, oggSampleRate, valuesPerBuffer, sampleEnd); buffers.Add(currentBuffer); } var allBuffersProcessed = false; while (!buffersInitialized || (!playbackStopDetected && !this.oggPlaybackEnded)) { buffersInitialized = true; AL.GetSource(currentSource, ALGetSourcei.BuffersQueued, out int buffersQueued); AL.GetSource(currentSource, ALGetSourcei.BuffersProcessed, out int buffersProcessed); var newUnqueuedSize = 0; for (int i = 0; i < buffersProcessed; i++) { var currentBuffer = buffers.First(); buffers.Remove(currentBuffer); AL.GetBuffer(currentBuffer, ALGetBufferi.Size, out int currentBufferSize); newUnqueuedSize += currentBufferSize; AL.SourceUnqueueBuffers(currentSource, 1, new[] { currentBuffer }); QueueBuffer(currentSource, currentBuffer, vorbis, soundFormat, oggSampleRate, valuesPerBuffer, sampleEnd); buffers.Add(currentBuffer); } allBuffersProcessed = vorbis.SamplePosition >= this.oggTotalSamples && (!isLooping); if (allBuffersProcessed && buffersProcessed != 0) { var unqueuedBuffers = new int[buffersProcessed]; AL.SourceUnqueueBuffers(currentSource, buffersProcessed, unqueuedBuffers); foreach (var currentBuffer in unqueuedBuffers) { AL.GetBuffer(currentBuffer, ALGetBufferi.Size, out int currentBufferSize); newUnqueuedSize += currentBufferSize; } } var newProgress = newUnqueuedSize / (channels * (bits_per_sample / 8)); var adjustedProgress = this.oggProgress + newProgress; if (AL.GetSourceState(currentSource) == ALSourceState.Playing) { this.UpdateOggProgress(adjustedProgress); } else { AL.SourcePlay(currentSource); } if (isLooping) { var realLoopEnd = Math.Min(this.oggLoopEnd, this.oggTotalSamples); if (vorbis.SamplePosition >= realLoopEnd) { vorbis.SeekTo(this.oggLoopStart); } if (this.oggProgress >= realLoopEnd) { this.UpdateOggProgress(this.oggLoopStart + (this.oggProgress % realLoopEnd)); } } else { var realLoopEnd = Math.Min(this.oggLoopEnd, this.oggTotalSamples); if (vorbis.SamplePosition >= realLoopEnd) { this.oggPlaybackEnded = true; } } } vorbis.Dispose(); }); while (!buffersInitialized) { Thread.Sleep(10); } this.OggPlayback?.Invoke(this, new OggPlaybackEventArgs { StateChange = ALSourceState.Playing }); this.Play(currentSource, () => { playbackStopDetected = true; this.WaitOnLock(this.oggQueueLock, () => { foreach (var buf in buffers) { AL.DeleteBuffer(buf); } this.OggPlayback?.Invoke(this, new OggPlaybackEventArgs { StateChange = ALSourceState.Stopped }); this.oggSource = -1; }); }, true); }