/// <summary> /// Gets the current position in bytes from the wave output device. /// (n.b. this is not the same thing as the position within your reader /// stream - it calls directly into waveOutGetPosition) /// </summary> /// <returns>Position in bytes</returns> public long GetPosition() { lock (WaveOutLock) { var time = new MmTime() { Type = MmTime.TIME_BYTES }; MmException.Try(WaveInterop.NativeMethods.waveOutGetPosition(DeviceHandle, out time, Marshal.SizeOf(time)), nameof(WaveInterop.NativeMethods.waveOutGetPosition)); if (time.Type != MmTime.TIME_BYTES) { throw new Exception(string.Format($"{nameof(WaveInterop.NativeMethods.waveOutGetPosition)}: wType -> Expected {0}, Received {1}", MmTime.TIME_BYTES, time.Type)); } return(time.CB); } }
/// <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.NativeMethods.waveOutOpenWindow( out DeviceHandle, DeviceNumber, WaveStream.WaveFormat, CallbackEvent.SafeWaitHandle.DangerousGetHandle(), IntPtr.Zero, WaveInterop.WaveInOutOpenFlags.CallbackEvent); } MmException.Try(result, nameof(WaveInterop.NativeMethods.waveOutOpen)); Buffers = new WaveOutBuffer[NumberOfBuffers]; m_PlaybackState = PlaybackState.Stopped; for (var n = 0; n < NumberOfBuffers; n++) { Buffers[n] = new WaveOutBuffer(DeviceHandle, bufferSize, WaveStream, WaveOutLock); } }
private GCHandle callbackHandle; // for the user callback /// <summary> /// Initializes a new instance of the <see cref="WaveOutBuffer"/> class. /// </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]; bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); this.waveOutPtr = hWaveOut; waveStream = bufferFillStream; this.waveOutLock = waveOutLock; header = new WaveHeader(); headerHandle = GCHandle.Alloc(header, GCHandleType.Pinned); header.DataBuffer = bufferHandle.AddrOfPinnedObject(); header.BufferLength = bufferSize; header.Loops = 1; callbackHandle = GCHandle.Alloc(this); header.UserData = (IntPtr)callbackHandle; lock (waveOutLock) { MmException.Try(WaveInterop.NativeMethods.waveOutPrepareHeader(hWaveOut, header, Marshal.SizeOf(header)), "waveOutPrepareHeader"); } }