public void Start() { Stop(); Running = true; _cancellationTokenSource = new CancellationTokenSource(); ReadFinished = false; _readTask = Task.Run(async() => { while (Running && _cancellationTokenSource?.IsCancellationRequested == false) { try { if (InStream.CanRead) { AudioChunk chunk = new AudioChunk(BufferReadSize); int bytesRead = await InStream.ReadAsync(chunk.Memory, _cancellationTokenSource.Token).ConfigureAwait(false); if (bytesRead > 0) { chunk.Length = bytesRead; // Wait for the dequeue, comparing in megabytes. while (Running && (Queue.Count * (BufferReadSize * 0.000001)) > BufferLimit) { await Task.Delay(100, _cancellationTokenSource?.Token ?? CancellationToken.None).ConfigureAwait(false); } Queue.Enqueue(chunk); } else { Log.Debug("AudioBuffer: Read EOF"); break; } } else { Log.Error("AudioBuffer: Could not read InStream."); await Task.Delay(25).ConfigureAwait(false); } } catch (OperationCanceledException) { return; } catch (Exception ex) { Log.Error($"AudioBuffer | {ex}"); } } ReadFinished = true; }, _cancellationTokenSource.Token); _writeTask = Task.Run(async() => { while (Running && _cancellationTokenSource?.IsCancellationRequested == false) { if (ReadFinished && Queue.IsEmpty) { break; } try { if (Queue.TryDequeue(out var audioChunk)) { using (audioChunk) { if (OutStream.CanWrite) { // We're required to process the chunks in the write task, it's far slower which allows us to change the volume while it's still playing. if (ProcessBuffer != null) { var bytes = audioChunk.Memory; if (audioChunk.Length != audioChunk.Memory.Length) { bytes = audioChunk.Memory.Part(0, audioChunk.Length, false).ToArray(); } var processedBytes = ProcessBuffer?.Invoke(bytes); processedBytes.CopyTo(audioChunk.Memory, 0); audioChunk.Length = processedBytes.Length; } await OutStream.WriteAsync(audioChunk.Memory, 0, audioChunk.Length, _cancellationTokenSource.Token); } else { Log.Error("AudioBuffer: Could not write to OutStream."); await Task.Delay(25).ConfigureAwait(false); } } } } catch (OperationCanceledException) { return; } catch (Exception ex) { Log.Error($"AudioBuffer | {ex.Message}"); Log.Debug($"AudioBuffer | {ex}"); await Task.Delay(25).ConfigureAwait(false); } } }, _cancellationTokenSource.Token); }