/// <summary> /// Initializes the direct sound. /// </summary> private void InitializeDirectSound() { // We will have 2 buffers: one for immediate audio out rendering, and another where we will // feed the samples. We will copy audio data from the back buffer into the immediate render // buffer. We first open the DirectSound driver, create the buffers and start the playback! // Open DirectSound DirectSoundDriver = null; var createDriverResult = NativeMethods.DirectSoundCreate(ref DeviceId, out DirectSoundDriver, IntPtr.Zero); if (DirectSoundDriver == null || createDriverResult != 0) { return; } // Set Cooperative Level to PRIORITY (priority level can call the SetFormat and Compact methods) DirectSoundDriver.SetCooperativeLevel(NativeMethods.GetDesktopWindow(), DirectSound.DirectSoundCooperativeLevel.Normal); // Fill BufferDescription for immediate, rendering buffer var renderBuffer = new DirectSound.BufferDescription { Size = Marshal.SizeOf <DirectSound.BufferDescription>(), BufferBytes = 0, Flags = DirectSound.DirectSoundBufferCaps.PrimaryBuffer, Reserved = 0, FormatHandle = IntPtr.Zero, AlgorithmId = Guid.Empty }; // Create the Render Buffer (Immediate audio out) DirectSoundDriver.CreateSoundBuffer(renderBuffer, out var audioRenderBuffer, IntPtr.Zero); AudioRenderBuffer = audioRenderBuffer as DirectSound.IDirectSoundBuffer; // Play & Loop on the render buffer AudioRenderBuffer?.Play(0, 0, DirectSound.DirectSoundPlayFlags.Looping); // A frame of samples equals to Desired Latency SamplesFrameSize = MillisToBytes(DesiredLatency); var waveFormatHandle = GCHandle.Alloc(WaveFormat, GCHandleType.Pinned); // Fill BufferDescription for sample-receiving back buffer var backBuffer = new DirectSound.BufferDescription { Size = Marshal.SizeOf <DirectSound.BufferDescription>(), BufferBytes = (uint)(SamplesFrameSize * 2), Flags = DirectSound.DirectSoundBufferCaps.GetCurrentPosition2 | DirectSound.DirectSoundBufferCaps.ControlNotifyPosition | DirectSound.DirectSoundBufferCaps.GlobalFocus | DirectSound.DirectSoundBufferCaps.ControlVolume | DirectSound.DirectSoundBufferCaps.StickyFocus | DirectSound.DirectSoundBufferCaps.GetCurrentPosition2, Reserved = 0, FormatHandle = waveFormatHandle.AddrOfPinnedObject(), AlgorithmId = Guid.Empty }; // Create back buffer where samples will be fed DirectSoundDriver.CreateSoundBuffer(backBuffer, out audioRenderBuffer, IntPtr.Zero); AudioBackBuffer = audioRenderBuffer as DirectSound.IDirectSoundBuffer; waveFormatHandle.Free(); // Get effective SecondaryBuffer size var bufferCapabilities = new DirectSound.BufferCaps { Size = Marshal.SizeOf <DirectSound.BufferCaps>() }; AudioBackBuffer?.GetCaps(bufferCapabilities); NextSamplesWriteIndex = 0; SamplesTotalSize = bufferCapabilities.BufferBytes; Samples = new byte[SamplesTotalSize]; Debug.Assert(SamplesTotalSize == (2 * SamplesFrameSize), "Invalid SamplesTotalSize vs SamplesFrameSize"); // Create double buffering notifications. // Use DirectSoundNotify at Position [0, 1/2] and Stop Position (0xFFFFFFFF) var notifier = audioRenderBuffer as DirectSound.IDirectSoundNotify; FrameStartEventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); FrameEndEventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); PlaybackEndedEventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); PlaybackWaitHandles = new WaitHandle[] { FrameStartEventWaitHandle, FrameEndEventWaitHandle, PlaybackEndedEventWaitHandle, CancelEvent }; var notificationEvents = new[] { CreatePositionNotification(FrameStartEventWaitHandle, 0), CreatePositionNotification(FrameEndEventWaitHandle, (uint)SamplesFrameSize), CreatePositionNotification(PlaybackEndedEventWaitHandle, 0xFFFFFFFF) }; notifier?.SetNotificationPositions((uint)notificationEvents.Length, notificationEvents); }
/// <summary> /// Initializes the direct sound. /// </summary> private void InitializeDirectSound() { // Open DirectSound DirectSoundDriver = null; NativeMethods.DirectSoundCreate(ref DeviceId, out DirectSoundDriver, IntPtr.Zero); if (DirectSoundDriver == null) { return; } // Set Cooperative Level to PRIORITY (priority level can call the SetFormat and Compact methods) DirectSoundDriver.SetCooperativeLevel(NativeMethods.GetDesktopWindow(), DirectSound.DirectSoundCooperativeLevel.DSSCL_PRIORITY); // ------------------------------------------------------------------------------------- // Create PrimaryBuffer // ------------------------------------------------------------------------------------- // Fill BufferDescription for PrimaryBuffer var bufferDesc = new DirectSound.BufferDescription(); bufferDesc.Size = Marshal.SizeOf(bufferDesc); bufferDesc.BufferBytes = 0; bufferDesc.Flags = DirectSound.DirectSoundBufferCaps.DSBCAPS_PRIMARYBUFFER; bufferDesc.Reserved = 0; bufferDesc.FormatHandle = IntPtr.Zero; bufferDesc.AlgorithmId = Guid.Empty; // Create PrimaryBuffer DirectSoundDriver.CreateSoundBuffer(bufferDesc, out object soundBufferObj, IntPtr.Zero); AudioPlaybackBuffer = soundBufferObj as DirectSound.IDirectSoundBuffer; // Play & Loop on the PrimarySound Buffer AudioPlaybackBuffer.Play(0, 0, DirectSound.DirectSoundPlayFlags.DSBPLAY_LOOPING); // ------------------------------------------------------------------------------------- // Create SecondaryBuffer // ------------------------------------------------------------------------------------- // A frame of samples equals to Desired Latency SamplesFrameSize = MillisToBytes(DesiredLatency); // Fill BufferDescription for SecondaryBuffer var bufferDesc2 = new DirectSound.BufferDescription(); bufferDesc2.Size = Marshal.SizeOf(bufferDesc2); bufferDesc2.BufferBytes = (uint)(SamplesFrameSize * 2); bufferDesc2.Flags = DirectSound.DirectSoundBufferCaps.DSBCAPS_GETCURRENTPOSITION2 | DirectSound.DirectSoundBufferCaps.DSBCAPS_CTRLPOSITIONNOTIFY | DirectSound.DirectSoundBufferCaps.DSBCAPS_GLOBALFOCUS | DirectSound.DirectSoundBufferCaps.DSBCAPS_CTRLVOLUME | DirectSound.DirectSoundBufferCaps.DSBCAPS_STICKYFOCUS | DirectSound.DirectSoundBufferCaps.DSBCAPS_GETCURRENTPOSITION2; bufferDesc2.Reserved = 0; var handleOnWaveFormat = GCHandle.Alloc(WaveFormat, GCHandleType.Pinned); // Ptr to waveFormat bufferDesc2.FormatHandle = handleOnWaveFormat.AddrOfPinnedObject(); // set Ptr to waveFormat bufferDesc2.AlgorithmId = Guid.Empty; // Create SecondaryBuffer DirectSoundDriver.CreateSoundBuffer(bufferDesc2, out soundBufferObj, IntPtr.Zero); AudioBackBuffer = soundBufferObj as DirectSound.IDirectSoundBuffer; handleOnWaveFormat.Free(); // Get effective SecondaryBuffer size var dsbCaps = new DirectSound.BufferCaps(); dsbCaps.Size = Marshal.SizeOf(dsbCaps); AudioBackBuffer.GetCaps(dsbCaps); NextSamplesWriteIndex = 0; SamplesTotalSize = dsbCaps.BufferBytes; Samples = new byte[SamplesTotalSize]; Debug.Assert(SamplesTotalSize == (2 * SamplesFrameSize), "Invalid SamplesTotalSize vs SamplesFrameSize"); // ------------------------------------------------------------------------------------- // Create double buffering notification. // Use DirectSoundNotify at Position [0, 1/2] and Stop Position (0xFFFFFFFF) // ------------------------------------------------------------------------------------- var notify = soundBufferObj as DirectSound.IDirectSoundNotify; FrameEventWaitHandle1 = new EventWaitHandle(false, EventResetMode.AutoReset); FrameEventWaitHandle2 = new EventWaitHandle(false, EventResetMode.AutoReset); EndEventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); var notifies = new DirectSound.DirectSoundBufferPositionNotify[3]; notifies[0] = new DirectSound.DirectSoundBufferPositionNotify { Offset = 0, NotifyHandle = FrameEventWaitHandle1.SafeWaitHandle.DangerousGetHandle() }; notifies[1] = new DirectSound.DirectSoundBufferPositionNotify { Offset = (uint)SamplesFrameSize, NotifyHandle = FrameEventWaitHandle2.SafeWaitHandle.DangerousGetHandle() }; notifies[2] = new DirectSound.DirectSoundBufferPositionNotify { Offset = 0xFFFFFFFF, NotifyHandle = EndEventWaitHandle.SafeWaitHandle.DangerousGetHandle() }; notify.SetNotificationPositions(3, notifies); }