private void InitializeDirectSound() { // Open DirectSound lock (this.m_LockObject) { directSound = null; DirectSoundCreate(ref device, out directSound, IntPtr.Zero); if (directSound != null) { // Set Cooperative Level to PRIORITY (priority level can call the SetFormat and Compact methods) directSound.SetCooperativeLevel(GetDesktopWindow(), DirectSoundCooperativeLevel.DSSCL_PRIORITY); // ------------------------------------------------------------------------------------- // Create PrimaryBuffer // ------------------------------------------------------------------------------------- // Fill BufferDescription for PrimaryBuffer BufferDescription bufferDesc = new BufferDescription(); bufferDesc.dwSize = Marshal.SizeOf(bufferDesc); bufferDesc.dwBufferBytes = 0; bufferDesc.dwFlags = DirectSoundBufferCaps.DSBCAPS_PRIMARYBUFFER; bufferDesc.dwReserved = 0; bufferDesc.lpwfxFormat = IntPtr.Zero; bufferDesc.guidAlgo = Guid.Empty; object soundBufferObj; // Create PrimaryBuffer directSound.CreateSoundBuffer(bufferDesc, out soundBufferObj, IntPtr.Zero); primarySoundBuffer = (IDirectSoundBuffer)soundBufferObj; // Play & Loop on the PrimarySound Buffer primarySoundBuffer.Play(0, 0, DirectSoundPlayFlags.DSBPLAY_LOOPING); // ------------------------------------------------------------------------------------- // Create SecondaryBuffer // ------------------------------------------------------------------------------------- // A frame of samples equals to Desired Latency samplesFrameSize = MsToBytes(desiredLatency); // Fill BufferDescription for SecondaryBuffer BufferDescription bufferDesc2 = new BufferDescription(); bufferDesc2.dwSize = Marshal.SizeOf(bufferDesc2); bufferDesc2.dwBufferBytes = (uint)(samplesFrameSize * 2); bufferDesc2.dwFlags = DirectSoundBufferCaps.DSBCAPS_GETCURRENTPOSITION2 | DirectSoundBufferCaps.DSBCAPS_CTRLPOSITIONNOTIFY | DirectSoundBufferCaps.DSBCAPS_GLOBALFOCUS | DirectSoundBufferCaps.DSBCAPS_CTRLVOLUME | DirectSoundBufferCaps.DSBCAPS_STICKYFOCUS | DirectSoundBufferCaps.DSBCAPS_GETCURRENTPOSITION2; bufferDesc2.dwReserved = 0; GCHandle handleOnWaveFormat = GCHandle.Alloc(waveFormat, GCHandleType.Pinned); // Ptr to waveFormat bufferDesc2.lpwfxFormat = handleOnWaveFormat.AddrOfPinnedObject(); // set Ptr to waveFormat bufferDesc2.guidAlgo = Guid.Empty; // Create SecondaryBuffer directSound.CreateSoundBuffer(bufferDesc2, out soundBufferObj, IntPtr.Zero); secondaryBuffer = (IDirectSoundBuffer)soundBufferObj; handleOnWaveFormat.Free(); // Get effective SecondaryBuffer size BufferCaps dsbCaps = new BufferCaps(); dsbCaps.dwSize = Marshal.SizeOf(dsbCaps); secondaryBuffer.GetCaps(dsbCaps); nextSamplesWriteIndex = 0; samplesTotalSize = dsbCaps.dwBufferBytes; samples = new byte[samplesTotalSize]; System.Diagnostics.Debug.Assert(samplesTotalSize == (2 * samplesFrameSize), "Invalid SamplesTotalSize vs SamplesFrameSize"); // ------------------------------------------------------------------------------------- // Create double buffering notification. // Use DirectSoundNotify at Position [0, 1/2] and Stop Position (0xFFFFFFFF) // ------------------------------------------------------------------------------------- IDirectSoundNotify notify = (IDirectSoundNotify)soundBufferObj; frameEventWaitHandle1 = new EventWaitHandle(false, EventResetMode.AutoReset); frameEventWaitHandle2 = new EventWaitHandle(false, EventResetMode.AutoReset); endEventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); DirectSoundBufferPositionNotify[] notifies = new DirectSoundBufferPositionNotify[3]; notifies[0] = new DirectSoundBufferPositionNotify(); notifies[0].dwOffset = 0; notifies[0].hEventNotify = frameEventWaitHandle1.SafeWaitHandle.DangerousGetHandle(); notifies[1] = new DirectSoundBufferPositionNotify(); notifies[1].dwOffset = (uint)samplesFrameSize; notifies[1].hEventNotify = frameEventWaitHandle2.SafeWaitHandle.DangerousGetHandle(); notifies[2] = new DirectSoundBufferPositionNotify(); notifies[2].dwOffset = 0xFFFFFFFF; notifies[2].hEventNotify = endEventWaitHandle.SafeWaitHandle.DangerousGetHandle(); notify.SetNotificationPositions(3, notifies); } } }
/// <summary> /// Processes the samples in a separate thread. /// </summary> private void PlaybackThreadFunc() { // Used to determine if playback is halted bool lPlaybackHalted = false; Exception exception = null; // Incase the thread is killed try { InitializeDirectSound(); int lResult = 1; if (PlaybackState == PlaybackState.Stopped) { secondaryBuffer.SetCurrentPosition(0); nextSamplesWriteIndex = 0; lResult = Feed(samplesTotalSize); } // Incase the previous Feed method returns 0 if (lResult > 0) { lock (m_LockObject) { playbackState = PlaybackState.Playing; } secondaryBuffer.Play(0, 0, DirectSoundPlayFlags.DSBPLAY_LOOPING); WaitHandle[] waitHandles = new WaitHandle[] { frameEventWaitHandle1, frameEventWaitHandle2, endEventWaitHandle }; bool lContinuePlayback = true; while (PlaybackState != PlaybackState.Stopped && lContinuePlayback) { // Wait for signals on frameEventWaitHandle1 (Position 0), frameEventWaitHandle2 (Position 1/2) int indexHandle = WaitHandle.WaitAny(waitHandles, 3 * desiredLatency, false); // TimeOut is ok if (indexHandle != WaitHandle.WaitTimeout) { // Buffer is Stopped if (indexHandle == 2) { // (Gee) - Not sure whether to stop playback in this case or not! StopPlayback(); lPlaybackHalted = true; lContinuePlayback = false; } else { indexHandle = (indexHandle == 0) ? 1 : 0; nextSamplesWriteIndex = indexHandle * samplesFrameSize; // Only carry on playing if we can! if (Feed(samplesFrameSize) == 0) { StopPlayback(); lPlaybackHalted = true; lContinuePlayback = false; } } } else { // Timed out! StopPlayback(); lPlaybackHalted = true; lContinuePlayback = false; // report this as an error in the Playback Stopped // seems to happen when device is unplugged throw new Exception("DirectSound buffer timeout"); } } } } catch (Exception e) { // Do nothing (except report error) Debug.WriteLine(e.ToString()); exception = e; } finally { if (!lPlaybackHalted) { StopPlayback(); } lock (m_LockObject) { playbackState = PlaybackState.Stopped; } // Fire playback stopped event RaisePlaybackStopped(exception); } }
/// <summary> /// Processes the samples in a separate thread. /// </summary> private void processSamples() { // Used to determine if playback is halted bool lPlaybackHalted = false; // Incase the thread is killed try { InitialiseDirectSound(); int lResult = 1; if (PlaybackState == PlaybackState.Stopped) { secondaryBuffer.SetCurrentPosition(0); nextSamplesWriteIndex = 0; lResult = Feed(samplesTotalSize); } // Incase the previous Feed method returns 0 if (lResult > 0) { lock (m_LockObject) { playbackState = PlaybackState.Playing; } secondaryBuffer.Play(0, 0, DirectSoundPlayFlags.DSBPLAY_LOOPING); WaitHandle[] waitHandles = new WaitHandle[] { frameEventWaitHandle1, frameEventWaitHandle2, endEventWaitHandle }; bool lContinuePlayback = true; while (PlaybackState != PlaybackState.Stopped && lContinuePlayback) { // Wait for signals on frameEventWaitHandle1 (Position 0), frameEventWaitHandle2 (Position 1/2) int indexHandle = WaitHandle.WaitAny(waitHandles, 3 * desiredLatency, false); // TimeOut is ok if (indexHandle != WaitHandle.WaitTimeout) { // Buffer is Stopped if (indexHandle == 2) { // (Gee) - Not sure whether to stop playback in this case or not! StopPlayback(); lPlaybackHalted = true; lContinuePlayback = false; } else { indexHandle = (indexHandle == 0) ? 1 : 0; nextSamplesWriteIndex = indexHandle * samplesFrameSize; // Only carry on playing if we can! if (Feed(samplesFrameSize) == 0) { StopPlayback(); lPlaybackHalted = true; lContinuePlayback = false; } } } else { // Timed out! StopPlayback(); lPlaybackHalted = true; lContinuePlayback = false; } } } } catch (Exception) { // Do nothing! } finally { if (!lPlaybackHalted) { StopPlayback(); } lock (m_LockObject) { playbackState = PlaybackState.Stopped; } // Fire playback stopped event if (PlaybackStopped != null) { PlaybackStopped(this, EventArgs.Empty); } } }
private void InitializeDirectSound() { // Open DirectSound lock (this.m_LockObject) { directSound = null; DirectSoundCreate(ref device, out directSound, IntPtr.Zero); if (directSound != null) { // Set Cooperative Level to PRIORITY (priority level can call the SetFormat and Compact methods) directSound.SetCooperativeLevel(GetDesktopWindow(), DirectSoundCooperativeLevel.DSSCL_PRIORITY); // ------------------------------------------------------------------------------------- // Create PrimaryBuffer // ------------------------------------------------------------------------------------- // Fill BufferDescription for PrimaryBuffer BufferDescription bufferDesc = new BufferDescription(); bufferDesc.dwSize = Marshal.SizeOf(bufferDesc); bufferDesc.dwBufferBytes = 0; bufferDesc.dwFlags = DirectSoundBufferCaps.DSBCAPS_PRIMARYBUFFER; bufferDesc.dwReserved = 0; bufferDesc.lpwfxFormat = IntPtr.Zero; bufferDesc.guidAlgo = Guid.Empty; object soundBufferObj; // Create PrimaryBuffer directSound.CreateSoundBuffer(bufferDesc, out soundBufferObj, IntPtr.Zero); primarySoundBuffer = (IDirectSoundBuffer)soundBufferObj; // Play & Loop on the PrimarySound Buffer primarySoundBuffer.Play(0, 0, DirectSoundPlayFlags.DSBPLAY_LOOPING); // ------------------------------------------------------------------------------------- // Create SecondaryBuffer // ------------------------------------------------------------------------------------- // A frame of samples equals to Desired Latency samplesFrameSize = MsToBytes(desiredLatency); // Fill BufferDescription for SecondaryBuffer BufferDescription bufferDesc2 = new BufferDescription(); bufferDesc2.dwSize = Marshal.SizeOf(bufferDesc2); bufferDesc2.dwBufferBytes = (uint)(samplesFrameSize * 2); bufferDesc2.dwFlags = DirectSoundBufferCaps.DSBCAPS_GETCURRENTPOSITION2 | DirectSoundBufferCaps.DSBCAPS_CTRLPOSITIONNOTIFY | DirectSoundBufferCaps.DSBCAPS_GLOBALFOCUS | DirectSoundBufferCaps.DSBCAPS_CTRLVOLUME | DirectSoundBufferCaps.DSBCAPS_STICKYFOCUS; bufferDesc2.dwReserved = 0; GCHandle handleOnWaveFormat = GCHandle.Alloc(waveFormat, GCHandleType.Pinned); // Ptr to waveFormat bufferDesc2.lpwfxFormat = handleOnWaveFormat.AddrOfPinnedObject(); // set Ptr to waveFormat bufferDesc2.guidAlgo = Guid.Empty; // Create SecondaryBuffer directSound.CreateSoundBuffer(bufferDesc2, out soundBufferObj, IntPtr.Zero); secondaryBuffer = (IDirectSoundBuffer)soundBufferObj; handleOnWaveFormat.Free(); // Get effective SecondaryBuffer size BufferCaps dsbCaps = new BufferCaps(); dsbCaps.dwSize = Marshal.SizeOf(dsbCaps); secondaryBuffer.GetCaps(dsbCaps); nextSamplesWriteIndex = 0; samplesTotalSize = dsbCaps.dwBufferBytes; samples = new byte[samplesTotalSize]; System.Diagnostics.Debug.Assert(samplesTotalSize == (2 * samplesFrameSize), "Invalid SamplesTotalSize vs SamplesFrameSize"); // ------------------------------------------------------------------------------------- // Create double buffering notification. // Use DirectSoundNotify at Position [0, 1/2] and Stop Position (0xFFFFFFFF) // ------------------------------------------------------------------------------------- IDirectSoundNotify notify = (IDirectSoundNotify)soundBufferObj; frameEventWaitHandle1 = new EventWaitHandle(false, EventResetMode.AutoReset); frameEventWaitHandle2 = new EventWaitHandle(false, EventResetMode.AutoReset); endEventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); DirectSoundBufferPositionNotify[] notifies = new DirectSoundBufferPositionNotify[3]; notifies[0] = new DirectSoundBufferPositionNotify(); notifies[0].dwOffset = 0; notifies[0].hEventNotify = frameEventWaitHandle1.SafeWaitHandle.DangerousGetHandle(); notifies[1] = new DirectSoundBufferPositionNotify(); notifies[1].dwOffset = (uint)samplesFrameSize; notifies[1].hEventNotify = frameEventWaitHandle2.SafeWaitHandle.DangerousGetHandle(); notifies[2] = new DirectSoundBufferPositionNotify(); notifies[2].dwOffset = 0xFFFFFFFF; notifies[2].hEventNotify = endEventWaitHandle.SafeWaitHandle.DangerousGetHandle(); notify.SetNotificationPositions(3, notifies); } } }
/// <summary> /// Processes the samples in a separate thread. /// </summary> private void PlaybackThreadFunc() { // Used to determine if playback is halted bool lPlaybackHalted = false; bool firstBufferStarted = false; bytesPlayed = 0; Exception exception = null; // Incase the thread is killed try { InitializeDirectSound(); int lResult = 1; if (PlaybackState == PlaybackState.Stopped) { secondaryBuffer.SetCurrentPosition(0); nextSamplesWriteIndex = 0; lResult = Feed(samplesTotalSize); } // Incase the previous Feed method returns 0 if (lResult > 0) { lock (m_LockObject) { playbackState = PlaybackState.Playing; } secondaryBuffer.Play(0, 0, DirectSoundPlayFlags.DSBPLAY_LOOPING); var waitHandles = new WaitHandle[] { frameEventWaitHandle1, frameEventWaitHandle2, endEventWaitHandle }; bool lContinuePlayback = true; while (PlaybackState != PlaybackState.Stopped && lContinuePlayback) { // Wait for signals on frameEventWaitHandle1 (Position 0), frameEventWaitHandle2 (Position 1/2) int indexHandle = WaitHandle.WaitAny(waitHandles, 3 * desiredLatency, false); // TimeOut is ok if (indexHandle != WaitHandle.WaitTimeout) { // Buffer is Stopped if (indexHandle == 2) { // (Gee) - Not sure whether to stop playback in this case or not! StopPlayback(); lPlaybackHalted = true; lContinuePlayback = false; } else { if (indexHandle == 0) { // we're at the beginning of the buffer... if (firstBufferStarted) { // because this notification is based on the *playback" cursor, this should be reasonably accurate bytesPlayed += samplesFrameSize * 2; } } else { firstBufferStarted = true; } indexHandle = (indexHandle == 0) ? 1 : 0; nextSamplesWriteIndex = indexHandle * samplesFrameSize; // Only carry on playing if we can! if (Feed(samplesFrameSize) == 0) { StopPlayback(); lPlaybackHalted = true; lContinuePlayback = false; } } } else { // Timed out! StopPlayback(); lPlaybackHalted = true; lContinuePlayback = false; // report this as an error in the Playback Stopped // seems to happen when device is unplugged throw new Exception("DirectSound buffer timeout"); } } } } catch (Exception e) { // Do nothing (except report error) NAudioLogger.Instance.LogError(e.Message); exception = e; } finally { if (!lPlaybackHalted) { try { StopPlayback(); } catch (Exception e) { NAudioLogger.Instance.LogError(e.Message); // don't overwrite the original reason we exited the playback loop if (exception == null) { exception = e; } } } lock (m_LockObject) { playbackState = PlaybackState.Stopped; } bytesPlayed = 0; // Fire playback stopped event RaisePlaybackStopped(exception); } }