private GCHandle hHeader; // we need to pin the header structure /// <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; bufferPtr = Marshal.AllocHGlobal(bufferSize); this.hWaveOut = hWaveOut; waveStream = bufferFillStream; this.waveOutLock = waveOutLock; header = new WaveHeader(); hHeader = GCHandle.Alloc(header, GCHandleType.Pinned); header.dataBuffer = bufferPtr; header.bufferLength = bufferSize; header.loops = 1; lock (waveOutLock) { MmException.Try(WaveInterop.waveOutPrepareHeader(hWaveOut, header, Marshal.SizeOf(header)), "waveOutPrepareHeader"); } }
/// <summary> /// Releases resources held by this WaveBuffer /// </summary> protected void Dispose(bool disposing) { if (disposing) { // free managed resources } // free unmanaged resources if (hHeader.IsAllocated) { hHeader.Free(); } if (hWaveOut != IntPtr.Zero) { lock (waveOutLock) { WaveInterop.waveOutUnprepareHeader(hWaveOut, header, Marshal.SizeOf(header)); } hWaveOut = IntPtr.Zero; } }
/// <summary> /// Stop and reset the WaveOut device /// </summary> public void Stop() { if (playbackState != PlaybackState.Stopped) { // in the call to waveOutReset with function callbacks // some drivers will block here until OnDone is called // for every buffer playbackState = PlaybackState.Stopped; // set this here to avoid a problem with some drivers whereby MmResult result; lock (waveOutLock) { result = WaveInterop.waveOutReset(hWaveOut); } if (result != MmResult.NoError) { throw new MmException(result, "waveOutReset"); } callbackEvent.Set(); // give the thread a kick, make sure we exit } }
internal static void SetWaveOutVolume(float value, IntPtr hWaveOut, object lockObject) { if (value < 0) { throw new ArgumentOutOfRangeException(nameof(value), "Volume must be between 0.0 and 1.0"); } if (value > 1) { throw new ArgumentOutOfRangeException(nameof(value), "Volume must be between 0.0 and 1.0"); } float left = value; float right = value; int stereoVolume = (int)(left * 0xFFFF) + ((int)(right * 0xFFFF) << 16); MmResult result; lock (lockObject) { result = WaveInterop.waveOutSetVolume(hWaveOut, stereoVolume); } MmException.Try(result, "waveOutSetVolume"); }
/// <summary> /// Initialises the WaveOut device /// </summary> /// <param name="waveProvider">WaveProvider to play</param> public Task InitAsync(IWaveProvider waveProvider) { if (playbackState != PlaybackState.Stopped) { throw new InvalidOperationException("Can't re-initialize during playback"); } if (hWaveOut != 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; int bufferSize = waveProvider.WaveFormat.ConvertLatencyToByteSize((DesiredLatency + NumberOfBuffers - 1) / NumberOfBuffers); MmResult result; lock (waveOutLock) { result = WaveInterop.waveOutOpenWindow(out hWaveOut, (IntPtr)DeviceNumber, waveStream.WaveFormat, callbackEvent.SafeWaitHandle.DangerousGetHandle(), IntPtr.Zero, WaveInterop.WaveInOutOpenFlags.CallbackEvent); } MmException.Try(result, "waveOutOpen"); buffers = new WaveOutBuffer[NumberOfBuffers]; playbackState = PlaybackState.Stopped; for (var n = 0; n < NumberOfBuffers; n++) { buffers[n] = new WaveOutBuffer(hWaveOut, bufferSize, waveStream, waveOutLock); } return(Task.CompletedTask); }