/// <summary> /// Runs the timer cycle. /// </summary> /// <param name="state">The state.</param> private void RunTimerCycle(object state) { // Skip running this cycle if we are already in the middle of one if (IsCycleDone.IsInProgress) { return; } try { // Start a cycle by signaling it IsCycleDone.Begin(); // Call the configured timer callback TimerCallback(); } finally { if (HasRequestedStop == false) { // Finalize the cycle IsCycleDone.Complete(); } else { // Prevent a new cycle from being queued ThreadingTimer?.Change(Timeout.Infinite, Timeout.Infinite); FormsTimer?.Stop(); DispatcherTimer?.Stop(); // Handle the dispose process. ThreadingTimer?.Dispose(); FormsTimer?.Dispose(); // Remove references ThreadingTimer = null; FormsTimer = null; DispatcherTimer = null; // Complete the cycle and dispose of it IsCycleDone.Complete(); IsCycleDone.Dispose(); } } }
/// <summary> /// Implementation using the ThreadPool with a wait event. /// </summary> private void DelayThreadPool() { lock (SyncRoot) { if (DelayEvent == null) { DelayEvent = WaitEventFactory.Create(isCompleted: true, useSlim: true); } } DelayEvent.Begin(); ThreadPool.QueueUserWorkItem(s => { DelaySleep(); DelayEvent.Complete(); }); DelayEvent.Wait(); }
static void Callback(IWaitEvent waitEvent) { try { waitEvent.Complete(); waitEvent.Begin(); } catch { // ignore } }
/// <summary> /// Runs the timer cycle. /// </summary> /// <param name="state">The state.</param> private void RunTimerCycle(object state) { // Handle the dispose process. if (IsDisposing) { if (ThreadingTimer != null) { ThreadingTimer.Dispose(); ThreadingTimer = null; } if (FormsTimer != null) { FormsTimer.Dispose(); FormsTimer = null; } if (DispatcherTimer != null) { DispatcherTimer.Stop(); DispatcherTimer = null; } DisposeCallback?.Invoke(); return; } // Skip running this cycle if we are already in the middle of one if (IsCycleDone.IsInProgress) { return; } // Start a cycle by signaling it IsCycleDone.Begin(); try { // Call the configured timer callback TimerCallback(); } catch { throw; } finally { // Finalize the cycle IsCycleDone.Complete(); } }
private void DelayThreadPool() { if (_delayEvent == null) { _delayEvent = WaitEventFactory.Create(isCompleted: true, useSlim: true); } _delayEvent.Begin(); ThreadPool.QueueUserWorkItem((s) => { DelaySleep(); _delayEvent.Complete(); }); _delayEvent.Wait(); }
/// <inheritdoc /> public int Read(byte[] targetBuffer, int targetBufferOffset, int requestedBytes) { // We sync-lock the reads to avoid null reference exceptions as destroy might have been called var lockTaken = false; Monitor.TryEnter(SyncLock, SyncLockTimeout, ref lockTaken); if (lockTaken == false || HasFiredAudioDeviceStopped) { Array.Clear(targetBuffer, targetBufferOffset, requestedBytes); return(requestedBytes); } try { WaitForReadyEvent.Complete(); var speedRatio = MediaCore.State.SpeedRatio; // Render silence if we don't need to output samples if (MediaCore.State.IsPlaying == false || speedRatio <= 0d || MediaCore.State.HasAudio == false || AudioBuffer.ReadableCount <= 0) { Array.Clear(targetBuffer, targetBufferOffset, requestedBytes); return(requestedBytes); } // Ensure a pre-allocated ReadBuffer if (ReadBuffer == null || ReadBuffer.Length < Convert.ToInt32(requestedBytes * Constants.Controller.MaxSpeedRatio)) { ReadBuffer = new byte[Convert.ToInt32(requestedBytes * Constants.Controller.MaxSpeedRatio)]; } // First part of DSP: Perform AV Synchronization if needed if (MediaCore.State.HasVideo && Synchronize(targetBuffer, targetBufferOffset, requestedBytes, speedRatio) == false) { return(requestedBytes); } var startPosition = Position; // Perform DSP if (speedRatio < 1.0) { if (AudioProcessor != null) { ReadAndUseAudioProcessor(requestedBytes, speedRatio); } else { ReadAndSlowDown(requestedBytes, speedRatio); } } else if (speedRatio > 1.0) { if (AudioProcessor != null) { ReadAndUseAudioProcessor(requestedBytes, speedRatio); } else { ReadAndSpeedUp(requestedBytes, true, speedRatio); } } else { if (requestedBytes > AudioBuffer.ReadableCount) { Array.Clear(targetBuffer, targetBufferOffset, requestedBytes); return(requestedBytes); } AudioBuffer.Read(requestedBytes, ReadBuffer, 0); } ApplyVolumeAndBalance(targetBuffer, targetBufferOffset, requestedBytes); MediaElement.RaiseRenderingAudioEvent( targetBuffer, requestedBytes, startPosition, WaveFormat.ConvertByteSizeToDuration(requestedBytes)); } catch (Exception ex) { this.LogError(Aspects.AudioRenderer, $"{nameof(AudioRenderer)}.{nameof(Read)} has faulted.", ex); Array.Clear(targetBuffer, targetBufferOffset, requestedBytes); } finally { Monitor.Exit(SyncLock); } return(requestedBytes); }
/// <summary> /// Performs the continuous playback. /// </summary> private void PerformContinuousPlayback() { var queued = 0; PlaybackState = PlaybackState.Playing; try { while (IsCancellationPending == false) { if (DriverCallbackEvent.WaitOne(DesiredLatency) == false) { if (IsCancellationPending == true) { break; } Renderer?.MediaCore?.Log(MediaLogMessageType.Warning, $"{nameof(AudioPlaybackThread)}:{nameof(DriverCallbackEvent)} timed out. Desired Latency: {DesiredLatency}ms"); continue; } // Reset the queue count queued = 0; if (IsCancellationPending == true) { break; } foreach (var buffer in Buffers) { if (buffer.IsQueued || buffer.ReadWaveStream()) { queued++; } } // Detect an end of playback if (queued <= 0) { break; } } } catch (Exception ex) { Renderer?.MediaCore?.Log(MediaLogMessageType.Error, $"{nameof(LegacyAudioPlayer)} faulted. {ex.GetType().Name}: {ex.Message}"); throw; } finally { // Update the state PlaybackState = PlaybackState.Stopped; // Immediately stop the audio driver. Pause it first to // avoid quirky repetitive samples try { WaveInterop.PauseAudioDevice(DeviceHandle); } catch { } try { WaveInterop.ResetAudioDevice(DeviceHandle); } catch { } // Dispose of buffers foreach (var buffer in Buffers) { try { buffer.Dispose(); } catch { } } // Close the device try { WaveInterop.CloseAudioDevice(DeviceHandle); } catch { } // Dispose of managed state DeviceHandle = IntPtr.Zero; PlaybackFinished.Complete(); } }
/// <summary> /// Called whenever the audio driver requests samples. /// Do not call this method directly. /// </summary> /// <param name="targetBuffer">The render buffer.</param> /// <param name="targetBufferOffset">The render buffer offset.</param> /// <param name="requestedBytes">The requested bytes.</param> /// <returns>The number of bytes that were read.</returns> public int Read(byte[] targetBuffer, int targetBufferOffset, int requestedBytes) { // We sync-lock the reads to avoid null reference exceptions as detaroy might have been called lock (SyncLock) { try { WaitForReadyEvent.Complete(); var speedRatio = MediaCore?.State.SpeedRatio ?? 0; // Render silence if we don't need to output samples if (MediaCore.State.IsPlaying == false || speedRatio <= 0d || MediaCore.State.HasAudio == false || AudioBuffer.ReadableCount <= 0) { Array.Clear(targetBuffer, targetBufferOffset, requestedBytes); return(requestedBytes); } // Ensure a preallocated ReadBuffer if (ReadBuffer == null || ReadBuffer.Length < Convert.ToInt32(requestedBytes * Constants.Controller.MaxSpeedRatio)) { ReadBuffer = new byte[Convert.ToInt32(requestedBytes * Constants.Controller.MaxSpeedRatio)]; } // First part of DSP: Perform AV Synchronization if needed if (MediaCore.State.HasVideo && Synchronize(targetBuffer, targetBufferOffset, requestedBytes, speedRatio) == false) { return(requestedBytes); } // Perform DSP if (speedRatio < 1.0) { if (AudioProcessor != null) { ReadAndUseAudioProcessor(requestedBytes, speedRatio); } else { ReadAndSlowDown(requestedBytes, speedRatio); } } else if (speedRatio > 1.0) { if (AudioProcessor != null) { ReadAndUseAudioProcessor(requestedBytes, speedRatio); } else { ReadAndSpeedUp(requestedBytes, true, speedRatio); } } else { if (requestedBytes > AudioBuffer.ReadableCount) { Array.Clear(targetBuffer, targetBufferOffset, requestedBytes); return(requestedBytes); } AudioBuffer.Read(requestedBytes, ReadBuffer, 0); } ApplyVolumeAndBalance(targetBuffer, targetBufferOffset, requestedBytes); } catch (Exception ex) { MediaCore?.Log(MediaLogMessageType.Error, $"{ex.GetType()} in {nameof(AudioRenderer)}.{nameof(Read)}: {ex.Message}. Stack Trace:\r\n{ex.StackTrace}"); Array.Clear(targetBuffer, targetBufferOffset, requestedBytes); } return(requestedBytes); } }