/// <summary> /// Pause the audio /// </summary> public void Pause() { if (m_PlaybackState != PlaybackState.Playing) { return; } MmResult result; m_PlaybackState = PlaybackState.Paused; // set this here to avoid a deadlock problem with some drivers lock (WaveOutLock) result = WaveInterop.waveOutPause(DeviceHandle); if (result != MmResult.NoError) { throw new MmException(result, nameof(WaveInterop.waveOutPause)); } }
/// <summary> /// Resume playing after a pause from the same position /// </summary> private void Resume() { if (m_PlaybackState != PlaybackState.Paused) { return; } MmResult result; lock (WaveOutLock) result = WaveInterop.waveOutRestart(DeviceHandle); if (result != MmResult.NoError) { throw new MmException(result, nameof(WaveInterop.waveOutRestart)); } m_PlaybackState = PlaybackState.Playing; }
private GCHandle hThis; // for the user callback /// <summary> /// creates a new wavebuffer /// </summary> /// <param name="hWaveOut">WaveOut device to write to</param> /// <param name="bufferSize">Buffer size in bytes</param> /// <param name="bufferFillStream">Stream to provide more data</param> /// <param name="waveOutLock">Lock to protect WaveOut API's from being called on >1 thread</param> public WaveOutBuffer(IntPtr hWaveOut, Int32 bufferSize, IWaveProvider bufferFillStream, object waveOutLock) { this.bufferSize = bufferSize; buffer = new byte[bufferSize]; hBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned); this.hWaveOut = hWaveOut; waveStream = bufferFillStream; this.waveOutLock = waveOutLock; header = new WaveHeader(); hHeader = GCHandle.Alloc(header, GCHandleType.Pinned); header.dataBuffer = hBuffer.AddrOfPinnedObject(); header.bufferLength = bufferSize; header.loops = 1; hThis = GCHandle.Alloc(this); header.userData = (IntPtr)hThis; lock (waveOutLock) { MmException.Try(WaveInterop.waveOutPrepareHeader(hWaveOut, header, Marshal.SizeOf(header)), "waveOutPrepareHeader"); } }
/// <summary> /// Initializes the specified wave provider. /// </summary> /// <param name="waveProvider">The wave provider.</param> /// <exception cref="System.InvalidOperationException">Can't re-initialize during playback</exception> public void Init(IWaveProvider waveProvider) { if (m_PlaybackState != PlaybackState.Stopped) { throw new InvalidOperationException("Can't re-initialize during playback"); } if (DeviceHandle != IntPtr.Zero) { // normally we don't allow calling Init twice, but as experiment, see if we can clean up and go again // try to allow reuse of this waveOut device // n.b. risky if Playback thread has not exited DisposeBuffers(); CloseWaveOut(); } CallbackEvent = new AutoResetEvent(false); WaveStream = waveProvider; var bufferSize = waveProvider.WaveFormat.ConvertLatencyToByteSize((DesiredLatency + NumberOfBuffers - 1) / NumberOfBuffers); MmResult result; lock (WaveOutLock) { result = WaveInterop.waveOutOpenWindow(out DeviceHandle, (IntPtr)DeviceNumber, WaveStream.WaveFormat, CallbackEvent.SafeWaitHandle.DangerousGetHandle(), IntPtr.Zero, WaveInterop.WaveInOutOpenFlags.CallbackEvent); } MmException.Try(result, nameof(WaveInterop.waveOutOpen)); Buffers = new WaveOutBuffer[NumberOfBuffers]; m_PlaybackState = PlaybackState.Stopped; for (var n = 0; n < NumberOfBuffers; n++) { Buffers[n] = new WaveOutBuffer(DeviceHandle, bufferSize, WaveStream, WaveOutLock); } }
/// <summary> /// Stop and reset the WaveOut device /// </summary> public void Stop() { if (m_PlaybackState != PlaybackState.Stopped) { return; } // in the call to waveOutReset with function callbacks // some drivers will block here until OnDone is called // for every buffer m_PlaybackState = PlaybackState.Stopped; // set this here to avoid a problem with some drivers whereby MmResult result; lock (WaveOutLock) result = WaveInterop.waveOutReset(DeviceHandle); if (result != MmResult.NoError) { throw new MmException(result, nameof(WaveInterop.waveOutReset)); } CallbackEvent.Set(); // give the thread a kick, make sure we exit }