protected override void UpdateBackend() { try { // Check if the context is initialized. if (!_layerContext.Initialized) { // Fill buffer before starting to prevent noise. FillBuffer(_layerContext.RenderClient, _bufferLengthInFrames); _layerContext.Start(); } // Start if not started. if (!_layerContext.Started) { _layerContext.Start(); } // Get more frames. int error = _layerContext.AudioClient.GetCurrentPadding(out int padding); if (error != 0) { Engine.Log.Warning($"Couldn't get device padding, error {error}.", MessageSource.WasApi); } FillBuffer(_layerContext.RenderClient, _bufferLengthInFrames - padding); } catch (COMException ex) { // Audio device is the same, but the configuration has changed. // Tracking these changes in the adapter is a huge drag, so we just catch the error instead. // https://www.hresult.info/FACILITY_AUDCLNT/0x88890004 if ((uint)ex.ErrorCode == 0x88890004) { SetDevice(_context.DefaultDevice); Engine.Log.Trace("Default audio device changed.", MessageSource.WasApi); } else { Engine.Log.Error(ex.ToString(), MessageSource.WasApi); } } }
private void LayerThread() { if (Thread.CurrentThread.Name == null) { Thread.CurrentThread.Name = $"Audio Layer - {Name}"; } Engine.Log.Trace($"Layer {Name} started.", MessageSource.Audio); while (_alive && Engine.Status != EngineStatus.Stopped) { // Check if the device has changed. if (_updateDevice) { SetDevice(_parent.DefaultDevice); _updateDevice = false; } // If not playing, wait for it to start playing. if (Status != PlaybackStatus.Playing) { _playWait.WaitOne(); continue; } if (_playlist.Count == 0 || _currentTrack == -1 || _currentTrack > _playlist.Count - 1) { Debug.Assert(false); } try { // Get the number of frames the buffer can hold total. var frameCount = (int)_layerContext.BufferSize; // Check if the context is initialized. if (!_layerContext.Initialized) { FillBuffer(_layerContext.RenderClient, (int)_layerContext.BufferSize); _layerContext.Start(); } // Start if not started. if (!_layerContext.Started) { _layerContext.Start(); } // Wait until more of the buffer is requested. bool success = _layerContext.WaitHandle.WaitOne(_layerContext.TimeoutPeriod); if (!success) { Engine.Log.Warning($"Layer {Name} audio context timeout.", MessageSource.WasApi); continue; } // Get more frames. int error = _layerContext.AudioClient.GetCurrentPadding(out int padding); if (error != 0) { Engine.Log.Warning($"Couldn't get device padding, error {error}.", MessageSource.WasApi); } if (!FillBuffer(_layerContext.RenderClient, frameCount - padding)) { continue; } } catch (COMException ex) { // Audio device has disappeared or whatever. if ((uint)ex.ErrorCode == 0x88890004) { _updateDevice = true; continue; } Engine.Log.Error(ex.ToString(), MessageSource.WasApi); } // If done, reset the audio client. Task.Delay(_layerContext.TimeoutPeriod).Wait(); _layerContext.Stop(); _layerContext.Reset(); } Engine.Log.Trace($"Layer {Name} exited.", MessageSource.Audio); _layerContext.Stop(); _layerContext = null; }