Pb WASAPI
Posted: Wed Jan 29, 2025 1:55 pm
Hi. This is very basic code to show how you can interface with Windows WASAPI. I chose Event driver Exclusive mode because it's the hardest one to do. Shared mode is easy. This code connects to the default audio capture device and request a 256 sample period. There are many validation checks for various things like samplerate, min/max buffer size supported etc, none of which are included here. Have fun
Shared Mode Render Example.

Code: Select all
EnableExplicit
; -----------------------------------------------------------------------------
; Author: AndyMK
; Date : 29/01/2025
; PB 6.20 Beta 3 (x64)
; WASAPI Exclusive Capture (Event driven Mode)
; -----------------------------------------------------------------------------
; COM & WASAPI Constants
; -----------------------------------------------------------------------------
#AUDCLNT_SHAREMODE_EXCLUSIVE= 1
#AUDCLNT_STREAMFLAGS_EVENTCALLBACK = $00040000
#WAVE_FORMAT_PCM = 1
#WAIT_OBJECT_0 = 0
#S_OK = 0
; -----------------------------------------------------------------------------
; COM Interfaces
; -----------------------------------------------------------------------------
Interface IMMDeviceEnumerator Extends IUnknown
EnumAudioEndpoints(dataFlow.l, dwStateMask.l, *ppDevices)
GetDefaultAudioEndpoint(dataFlow.l, role.l, *ppEndpoint)
GetDevice(pwstrId, ppDevice)
RegisterEndpointNotificationCallback(pClient)
UnregisterEndpointNotificationCallback(pClient)
EndInterface
Interface IMMDevice Extends IUnknown
Activate(iid.i, dwClsCtx.l, pActivationParams.i, ppInterface.i)
OpenPropertyStore(stgmAccess.l, ppProperties.i)
GetId(ppstrId.i)
GetState(pdwState.i)
EndInterface
Interface IAudioClient Extends IUnknown
Initialize(ShareMode.l, StreamFlags.l, hnsBufferDuration.q, hnsPeriodicity.q, pFormat.i, pSessionGuid.i)
GetBufferSize(*pNumBufferFrames)
GetStreamLatency(*phnsLatency.q)
GetCurrentPadding(*pNumPaddingFrames)
IsFormatSupported(ShareMode.l, pFormat.i, ppClosestMatch.i)
GetMixFormat(*ppDeviceFormat)
GetDevicePeriod(*phnsDefaultDevicePeriod.q, *phnsMinimumDevicePeriod.q)
Start()
Stop()
Reset()
SetEventHandle(EventHandle.i)
GetService(riid.i, *ppv)
EndInterface
Interface IAudioClient2 Extends IAudioClient
IsOffloadCapable(Category.l, *pbOffloadCapable.l)
SetClientProperties(*pProperties) ; AUDIO_CLIENT_PROPERTIES
GetBufferSizeLimits(pFormat.i, bEventDriven.l, *phnsMinBufferDuration.q, *phnsMaxBufferDuration.q)
EndInterface
Interface IAudioClient3 Extends IAudioClient2
GetSharedModeEnginePeriod(pFormat.i, *pPeriodInFrames.l, *pFrameSizeInBytes.l)
GetCurrentSharedModeEnginePeriod(*ppFormat.i, *pCurrentPeriodInFrames.l, *pFrameSizeInBytes.l)
InitializeSharedAudioStream(StreamFlags.l, PeriodInFrames.l, pFormat.i, AudioSessionGuid.i)
EndInterface
Interface IAudioCaptureClient Extends IUnknown
GetBuffer(*ppData, *pNumFramesToRead, *pdwFlags, *pu64DevicePosition.q, *pu64QPCPosition.q)
ReleaseBuffer(NumFramesRead.l)
GetNextPacketSize(*pNumFramesInNextPacket)
EndInterface
; -----------------------------------------------------------------------------
; Helper: Fill a WAVEFORMATEX with 16-bit PCM, stereo, at 44100 Hz
; -----------------------------------------------------------------------------
Procedure SetWaveFormatPCM44100Stereo16(*wf.WAVEFORMATEX)
*wf\wFormatTag = #WAVE_FORMAT_PCM
*wf\nChannels = 2
*wf\nSamplesPerSec = 44100
*wf\wBitsPerSample = 16
*wf\nBlockAlign = *wf\nChannels * (*wf\wBitsPerSample / 8) ; e.g. 2*(16/8) = 4
*wf\nAvgBytesPerSec = *wf\nSamplesPerSec * *wf\nBlockAlign ; e.g. 44100*4=176400
*wf\cbSize = 0
EndProcedure
; -----------------------------------------------------------------------------
; Helper: Convert Frames to Hns
; -----------------------------------------------------------------------------
Procedure.q FramesToHns(frames.l, sampleRate.l)
; frames = number of audio frames
; sampleRate= frames per second
; returns a 64-bit integer in 100-ns units
Protected.q hns
hns = (frames * 10000000) / sampleRate
ProcedureReturn hns
EndProcedure
; -----------------------------------------------------------------------------
; Procedure: CaptureMic_CustomFormat_Exclusive()
; - Fallback: IAudioClient3 -> 2 -> 1
; - Uses a manually-specified WAVEFORMATEX (44.1k, 16-bit, stereo).
; - Event-driven capture in exclusive mode.
; -----------------------------------------------------------------------------
Procedure CaptureMic_CustomFormat_Exclusive(durationMs.l)
Protected hr.l
Protected deviceEnumerator.IMMDeviceEnumerator
Protected defaultMic.IMMDevice
; Fallback interface pointers
Protected audioClient1.IAudioClient = #Null
Protected audioClient2.IAudioClient2 = #Null
Protected audioClient3.IAudioClient3 = #Null
Protected baseClient.IAudioClient = #Null
Protected captureClient.IAudioCaptureClient
Protected waveFmt.WAVEFORMATEX
Protected bufferFrames.l
Protected framesAvailable.l
Protected flags.l
Protected *data
Protected startTime.l = ElapsedMilliseconds()
Protected period.q = FramesToHns(256, 44100) ; 256 frames means 0.0058049886621315 ms of latency@44100hz (256/44100). Device dependent.
Protected bufferDuration.q = period ; must be equal or a multiple in Exclusive mode;
; 1) Initialize COM
hr = CoInitialize_(#Null)
If hr <> #S_OK
Debug "CoInitialize_ failed. hr=" + Hex(hr)
ProcedureReturn
EndIf
; 2) Create device enumerator
hr = CoCreateInstance_(?uuidof_MMDeviceEnumerator, #Null, #CLSCTX_INPROC_SERVER, ?uuidof_IMMDeviceEnumerator, @deviceEnumerator)
If hr <> #S_OK
Debug "CoCreateInstance_(MMDeviceEnumerator) failed. hr=" + Hex(hr)
Goto Cleanup
EndIf
; 3) Get default capture device
hr = deviceEnumerator\GetDefaultAudioEndpoint(1, 0, @defaultMic) ; eCapture=1, eConsole=0
If hr <> #S_OK
Debug "GetDefaultAudioEndpoint() failed. hr=" + Hex(hr)
Goto Cleanup
EndIf
; 4) Attempt IAudioClient3 => IAudioClient2 => IAudioClient
hr = defaultMic\Activate(?uuidof_IAudioClient3, #CLSCTX_INPROC_SERVER, #Null, @audioClient3)
If hr = #S_OK
Debug "Activated IAudioClient3"
baseClient = audioClient3
Else
Debug "IAudioClient3 not available hr=" + Hex(hr) + " => trying IAudioClient2..."
hr = defaultMic\Activate(?uuidof_IAudioClient2, #CLSCTX_INPROC_SERVER, #Null, @audioClient2)
If hr = #S_OK
Debug "Activated IAudioClient2"
baseClient = audioClient2
Else
Debug "IAudioClient2 not available hr=" + Hex(hr) + " => trying IAudioClient..."
hr = defaultMic\Activate(?uuidof_IAudioClient, #CLSCTX_INPROC_SERVER, #Null, @audioClient1)
If hr = #S_OK
Debug "Activated IAudioClient (legacy)"
baseClient = audioClient1
Else
Debug "All attempts failed, hr=" + Hex(hr)
Goto Cleanup
EndIf
EndIf
EndIf
; 5) Fill waveFmt with 44100/16-bit/stereo PCM
SetWaveFormatPCM44100Stereo16(@waveFmt)
; 6) Check if this format is supported in exclusive mode
hr = baseClient\IsFormatSupported(#AUDCLNT_SHAREMODE_EXCLUSIVE, @waveFmt, #Null)
If hr = #S_OK
Debug "Format is supported in exclusive mode."
Else
Debug "Format is NOT supported (hr=" + Hex(hr) + ") => likely won't Initialize() properly."
; Could fallback or bail here
EndIf
; 7) Initialize in exclusive mode + event callback
hr = baseClient\Initialize(#AUDCLNT_SHAREMODE_EXCLUSIVE, #AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, period, @waveFmt, #Null)
If hr <> #S_OK
Debug "Initialize(exclusive) failed, hr=" + Hex(hr)
Goto Cleanup
EndIf
; 8) Create an event handle
Protected hEvent.i = CreateEvent_(#Null, 0, 0, #Null)
If hEvent = 0
Debug "CreateEvent_ failed"
Goto Cleanup
EndIf
hr = baseClient\SetEventHandle(hEvent)
If hr <> #S_OK
Debug "SetEventHandle() failed hr=" + Hex(hr)
CloseHandle_(hEvent)
Goto Cleanup
EndIf
; 9) Get the buffer size in frames
hr = baseClient\GetBufferSize(@bufferFrames)
If hr <> #S_OK
Debug "GetBufferSize() failed hr=" + Hex(hr)
CloseHandle_(hEvent)
Goto Cleanup
EndIf
Debug "BufferFrames = " + Str(bufferFrames) + " at 44100/16/stereo exclusive."
; 10) Get IAudioCaptureClient
hr = baseClient\GetService(?uuidof_IAudioCaptureClient, @captureClient)
If hr <> #S_OK
Debug "GetService(IAudioCaptureClient) failed hr=" + Hex(hr)
CloseHandle_(hEvent)
Goto Cleanup
EndIf
; 11) Start capturing
hr = baseClient\Start()
If hr <> #S_OK
Debug "Start() failed hr=" + Hex(hr)
CloseHandle_(hEvent)
Goto Cleanup
EndIf
Debug "Capturing exclusively @ 44.1kHz/16-bit stereo for " + Str(durationMs) + "ms..."
; 12) Event-driven capture loop
While ElapsedMilliseconds() - startTime < durationMs
Protected waitRes = WaitForSingleObject_(hEvent, 500)
If waitRes = #WAIT_OBJECT_0
; event signaled
hr = captureClient\GetNextPacketSize(@framesAvailable)
If hr = #S_OK And framesAvailable > 0
hr = captureClient\GetBuffer(@*data, @framesAvailable, @flags, #Null, #Null)
If hr = #S_OK
; *data => PCM samples
; framesAvailable => how many frames
; For demo, we discard them
; you would typically setup a callback system here a la Portaudio.
Debug framesAvailable
captureClient\ReleaseBuffer(framesAvailable) ; Release the buffer and move on
EndIf
EndIf
EndIf
Wend
baseClient\Stop()
Debug "Capture finished."
CloseHandle_(hEvent)
Cleanup:
If captureClient
captureClient\Release()
EndIf
If audioClient3
audioClient3\Release()
EndIf
If audioClient2
audioClient2\Release()
EndIf
If audioClient1
audioClient1\Release()
EndIf
If defaultMic
defaultMic\Release()
EndIf
If deviceEnumerator
deviceEnumerator\Release()
EndIf
CoUninitialize_()
EndProcedure
; -----------------------------------------------------------------------------
; DataSection: All Required GUIDs
; -----------------------------------------------------------------------------
DataSection
; -- MMDeviceEnumerator
uuidof_MMDeviceEnumerator:
Data.l $BCDE0395
Data.w $E52F,$467C
Data.b $8E,$3D,$C4,$57,$92,$91,$69,$2E
uuidof_IMMDeviceEnumerator:
Data.l $A95664D2
Data.w $9614,$4F35
Data.b $A7,$46,$DE,$8D,$B6,$36,$17,$E6
; -- IAudioClient
uuidof_IAudioClient:
Data.l $1CB9AD4C
Data.w $DBFA,$4C32
Data.b $B1,$78,$C2,$F5,$68,$A7,$03,$B2
; -- IAudioClient2
uuidof_IAudioClient2:
Data.l $726778CD
Data.w $F60A,$4EDA
Data.b $82,$DE,$E4,$76,$10,$CD,$78,$AA
; -- IAudioClient3
uuidof_IAudioClient3:
Data.l $F4B1A599
Data.w $7266,$4319
Data.b $A8,$CA,$E7,$0A,$CB,$11,$E8,$CD
; -- IAudioCaptureClient
uuidof_IAudioCaptureClient:
Data.l $C8ADBD64
Data.w $E71E,$48A0
Data.b $A4,$DE,$18,$5C,$39,$5C,$D3,$17
EndDataSection
; -----------------------------------------------------------------------------
; Example: Capture for 5 seconds, using 44100/16-bit/stereo in exclusive mode.
; -----------------------------------------------------------------------------
CaptureMic_CustomFormat_Exclusive(5000)
End
Code: Select all
EnableExplicit
; -----------------------------------------------------------------------------
; Author: AndyMK
; Date : 29/01/2025
; PB 6.20 Beta 3 (x64)
; WASAPI Shared Render (Event driven Mode)
; -----------------------------------------------------------------------------
; COM & WASAPI Constants
; -----------------------------------------------------------------------------
#CLSCTX_INPROC_SERVER = $01
#CLSCTX_INPROC_HANDLER = $02
#CLSCTX_LOCAL_SERVER = $04
#CLSCTX_REMOTE_SERVER = $10
#CLSCTX_ALL = #CLSCTX_INPROC_SERVER | #CLSCTX_INPROC_HANDLER | #CLSCTX_LOCAL_SERVER | #CLSCTX_REMOTE_SERVER
#CLSCTX_INPROC = #CLSCTX_INPROC_SERVER | #CLSCTX_INPROC_HANDLER
#CLSCTX_SERVER = #CLSCTX_INPROC_SERVER | #CLSCTX_LOCAL_SERVER | #CLSCTX_REMOTE_SERVER
#AUDCLNT_SHAREMODE_SHARED = 0
#AUDCLNT_STREAMFLAGS_EVENTCALLBACK = $00040000
#S_OK = 0
#WAVE_FORMAT_PCM = 1
#WAIT_OBJECT_0 = 0
; -----------------------------------------------------------------------------
; COM Interfaces
; -----------------------------------------------------------------------------
Interface IMMDeviceEnumerator Extends IUnknown
EnumAudioEndpoints(dataFlow.l, dwStateMask.l, *ppDevices)
GetDefaultAudioEndpoint(dataFlow.l, role.l, *ppEndpoint)
GetDevice(pwstrId, ppDevice)
RegisterEndpointNotificationCallback(pClient)
UnregisterEndpointNotificationCallback(pClient)
EndInterface
Interface IMMDevice Extends IUnknown
Activate(iid.i, dwClsCtx.l, pActivationParams.i, ppInterface.i)
OpenPropertyStore(stgmAccess.l, ppProperties.i)
GetId(ppstrId.i)
GetState(pdwState.i)
EndInterface
Interface IAudioClient Extends IUnknown
Initialize(ShareMode.l, StreamFlags.l, hnsBufferDuration.q, hnsPeriodicity.q, pFormat.i, pSessionGuid.i)
GetBufferSize(*pNumBufferFrames)
GetStreamLatency(*phnsLatency.q)
GetCurrentPadding(*pNumPaddingFrames)
IsFormatSupported(ShareMode.l, pFormat.i, ppClosestMatch.i)
GetMixFormat(*ppDeviceFormat)
GetDevicePeriod(*phnsDefaultDevicePeriod.q, *phnsMinimumDevicePeriod.q)
Start()
Stop()
Reset()
SetEventHandle(EventHandle.i)
GetService(riid.i, *ppv)
EndInterface
Interface IAudioClient2 Extends IAudioClient
IsOffloadCapable(Category.l, *pbOffloadCapable.l)
SetClientProperties(*pProperties) ; AUDIO_CLIENT_PROPERTIES
GetBufferSizeLimits(pFormat.i, bEventDriven.l, *phnsMinBufferDuration.q, *phnsMaxBufferDuration.q)
EndInterface
Interface IAudioClient3 Extends IAudioClient2
GetSharedModeEnginePeriod(pFormat.i, *pPeriodInFrames.l, *pFrameSizeInBytes.l)
GetCurrentSharedModeEnginePeriod(*ppFormat.i, *pCurrentPeriodInFrames.l, *pFrameSizeInBytes.l)
InitializeSharedAudioStream(StreamFlags.l, PeriodInFrames.l, pFormat.i, AudioSessionGuid.i)
EndInterface
Interface IAudioRenderClient Extends IUnknown
GetBuffer(numFramesRequested.l, *ppData)
ReleaseBuffer(numFramesWritten.l, dwFlags.l)
EndInterface
; -----------------------------------------------------------------------------
; Helper: Fill a WAVEFORMATEX with 44.1kHz, 16-bit, stereo PCM
; -----------------------------------------------------------------------------
Procedure SetWaveFormatPCM44100Stereo16(*wf.WAVEFORMATEX)
*wf\wFormatTag = #WAVE_FORMAT_PCM
*wf\nChannels = 2
*wf\nSamplesPerSec = 44100
*wf\wBitsPerSample = 16
*wf\nBlockAlign = *wf\nChannels * (*wf\wBitsPerSample / 8) ; 2*(16/8)=4
*wf\nAvgBytesPerSec = *wf\nSamplesPerSec * *wf\nBlockAlign ; 44100*4=176400
*wf\cbSize = 0
EndProcedure
Procedure.q FramesToHns(frames.l, sampleRate.l)
; frames = number of audio frames
; sampleRate= frames per second
; returns a 64-bit integer in 100-ns units
Protected.q hns
hns = (frames * 10000000) / sampleRate
ProcedureReturn hns
EndProcedure
; -----------------------------------------------------------------------------
; Procedure: PlaybackSineWave_WithFallback()
; - Fallback: IAudioClient3 -> 2 -> 1
; - Plays a simple sine wave for 'durationMs' in SHARED mode, event-driven.
; -----------------------------------------------------------------------------
Procedure PlaybackSineWave_WithFallback(durationMs.l)
Protected hr.l
Protected deviceEnumerator.IMMDeviceEnumerator
Protected defaultRender.IMMDevice
Protected audioClient1.IAudioClient = #Null
Protected audioClient2.IAudioClient2 = #Null
Protected audioClient3.IAudioClient3 = #Null
Protected baseClient.IAudioClient = #Null
Protected renderClient.IAudioRenderClient
Protected waveFmt.WAVEFORMATEX
Protected *mixFormat.WAVEFORMATEX
Protected bufferFrames.l
Protected numPadding.l
Protected numFramesAvailable.l
Protected *data
Protected flags.l = 0
Protected startTime.l = ElapsedMilliseconds()
; We do SHARED mode, so typically hnsPeriodicity=0 => OS picks period
; bufferDuration ~ 100ms => 1,000,000 in 100ns
Protected hnsBufferDuration.q = 1000000 ;FramesToHns(441, 44100)
; 1) Initialize COM
hr = CoInitialize_(#Null)
If hr <> #S_OK
Debug "CoInitialize_ failed, hr=" + Hex(hr)
ProcedureReturn
EndIf
; 2) Create device enumerator
hr = CoCreateInstance_(?uuidof_MMDeviceEnumerator, #Null, #CLSCTX_INPROC_SERVER, ?uuidof_IMMDeviceEnumerator, @deviceEnumerator)
If hr <> #S_OK
Debug "CoCreateInstance_(MMDeviceEnumerator) failed, hr=" + Hex(hr)
Goto Cleanup
EndIf
; 3) Get default RENDER device (speakers/headphones)
; dataFlow=0 => eRender, role=0 => eConsole
hr = deviceEnumerator\GetDefaultAudioEndpoint(0, 0, @defaultRender)
If hr <> #S_OK
Debug "GetDefaultAudioEndpoint(eRender) failed, hr=" + Hex(hr)
Goto Cleanup
EndIf
; 4) Try IAudioClient3 => fallback => IAudioClient2 => fallback => IAudioClient
hr = defaultRender\Activate(?uuidof_IAudioClient3, #CLSCTX_INPROC_SERVER, #Null, @audioClient3)
If hr = #S_OK
Debug "Activated IAudioClient3"
baseClient = audioClient3
Else
Debug "IAudioClient3 not available hr=" + Hex(hr) + " => trying IAudioClient2..."
hr = defaultRender\Activate(?uuidof_IAudioClient2, #CLSCTX_INPROC_SERVER, #Null, @audioClient2)
If hr = #S_OK
Debug "Activated IAudioClient2"
baseClient = audioClient2
Else
Debug "IAudioClient2 not available hr=" + Hex(hr) + " => trying IAudioClient..."
hr = defaultRender\Activate(?uuidof_IAudioClient, #CLSCTX_INPROC_SERVER, #Null, @audioClient1)
If hr = #S_OK
Debug "Activated IAudioClient (legacy)"
baseClient = audioClient1
Else
Debug "All attempts failed hr=" + Hex(hr)
Goto Cleanup
EndIf
EndIf
EndIf
; For SHARED mode, we can either use:
; a) baseClient\GetMixFormat(@*mixFormat), or
; b) set our own waveFmt.
; Typically you'd just do GetMixFormat() and let OS handle conversions.
; custom waveFmt for 44.1/16/stereo, and let OS resample if needed.
SetWaveFormatPCM44100Stereo16(@waveFmt)
; 5) Initialize in SHARED mode + event callback
Protected streamFlags.l = #AUDCLNT_STREAMFLAGS_EVENTCALLBACK
hr = baseClient\Initialize(#AUDCLNT_SHAREMODE_SHARED, streamFlags, hnsBufferDuration, 0, @waveFmt, #Null)
If hr <> #S_OK
Debug "Initialize(shared) failed, hr=" + Hex(hr)
Goto Cleanup
EndIf
; 6) Create an event for event-driven playback
Protected hEvent.i = CreateEvent_(#Null, 0, 0, #Null)
If hEvent = 0
Debug "CreateEvent_ failed"
Goto Cleanup
EndIf
hr = baseClient\SetEventHandle(hEvent)
If hr <> #S_OK
Debug "SetEventHandle() failed hr=" + Hex(hr)
CloseHandle_(hEvent)
Goto Cleanup
EndIf
; 7) Get buffer size (in frames)
hr = baseClient\GetBufferSize(@bufferFrames)
If hr <> #S_OK
Debug "GetBufferSize() failed hr=" + Hex(hr)
CloseHandle_(hEvent)
Goto Cleanup
EndIf
Debug "Render BufferFrames = " + Str(bufferFrames)
; 8) Get IAudioRenderClient
hr = baseClient\GetService(?uuidof_IAudioRenderClient, @renderClient)
If hr <> #S_OK
Debug "GetService(IAudioRenderClient) failed hr=" + Hex(hr)
CloseHandle_(hEvent)
Goto Cleanup
EndIf
; 9) Start the stream
hr = baseClient\Start()
If hr <> #S_OK
Debug "IAudioClient\Start() failed hr=" + Hex(hr)
CloseHandle_(hEvent)
Goto Cleanup
EndIf
Debug "Playing a sine wave for " + Str(durationMs) + " ms..."
; We'll generate and write a 440 Hz sine wave at 44.1 kHz, stereo, 16-bit
Protected freq.f = 440.0
Protected twoPi.f = 6.283185307
Protected t.f = 0.0
Protected increment.f = freq * twoPi / 44100.0
Protected currentMs.l
Protected sampleCount.l
Protected maxSamples.l = 44100 * (durationMs / 1000.0) ; approximate total samples for the entire play
; 10) Main playback loop
While ElapsedMilliseconds() - startTime < durationMs
; Wait ~ up to 500ms for event
Protected waitRes = WaitForSingleObject_(hEvent, 500)
If waitRes = #WAIT_OBJECT_0
; The event is signaled, means we can fill more data
; Check how many frames are "already used" => padding
hr = baseClient\GetCurrentPadding(@numPadding)
If hr = #S_OK
numFramesAvailable = bufferFrames - numPadding
If numFramesAvailable > 0
; Get buffer from IAudioRenderClient
hr = renderClient\GetBuffer(numFramesAvailable, @*data)
If hr = #S_OK
Debug numFramesAvailable
; Fill that buffer with our sine wave
; each frame => 2 channels => 2 x 16-bit
Protected frame.i
Protected *ptr.Word = *data
For frame = 0 To numFramesAvailable - 1
Protected s.f = Sin(t) * 0.5 ; amplitude
Protected sample.w = Round(s * 32767.0, #PB_Round_Nearest)
; stereo => left, then right channel
*ptr\w = sample : *ptr + SizeOf(Word)
*ptr\w = sample : *ptr + SizeOf(Word)
t + increment
sampleCount + 1
If sampleCount >= maxSamples
; We can fade out or just keep going with silence
; For demo, let's keep going with sine, ignoring duration
EndIf
Next
renderClient\ReleaseBuffer(numFramesAvailable, 0)
EndIf
EndIf
EndIf
EndIf
Wend
; 11) Stop playing
baseClient\Stop()
Debug "Playback finished."
If hEvent
CloseHandle_(hEvent)
EndIf
; Cleanup
Cleanup:
If renderClient
renderClient\Release()
EndIf
If audioClient3
audioClient3\Release()
EndIf
If audioClient2
audioClient2\Release()
EndIf
If audioClient1
audioClient1\Release()
EndIf
If defaultRender
defaultRender\Release()
EndIf
If deviceEnumerator
deviceEnumerator\Release()
EndIf
CoUninitialize_()
EndProcedure
; -----------------------------------------------------------------------------
; DataSection: All Required GUIDs
; -----------------------------------------------------------------------------
DataSection
; MMDeviceEnumerator
uuidof_MMDeviceEnumerator:
Data.l $BCDE0395
Data.w $E52F,$467C
Data.b $8E,$3D,$C4,$57,$92,$91,$69,$2E
uuidof_IMMDeviceEnumerator:
Data.l $A95664D2
Data.w $9614,$4F35
Data.b $A7,$46,$DE,$8D,$B6,$36,$17,$E6
; IAudioClient {1CB9AD4C-DBFA-4c32-B178-C2F568A703B2}
uuidof_IAudioClient:
Data.l $1CB9AD4C
Data.w $DBFA,$4C32
Data.b $B1,$78,$C2,$F5,$68,$A7,$03,$B2
; IAudioClient2 {726778CD-F60A-4eda-82DE-E47610CD78AA}
uuidof_IAudioClient2:
Data.l $726778CD
Data.w $F60A,$4EDA
Data.b $82,$DE,$E4,$76,$10,$CD,$78,$AA
; IAudioClient3 {F4B1A599-7266-4319-A8CA-E70ACB11E8CD}
uuidof_IAudioClient3:
Data.l $F4B1A599
Data.w $7266,$4319
Data.b $A8,$CA,$E7,$0A,$CB,$11,$E8,$CD
; IAudioRenderClient {F294ACFC-3146-4483-A7BF-ADDCA7C260E2}
; Official: DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2)
uuidof_IAudioRenderClient:
Data.l $F294ACFC
Data.w $3146,$4483
Data.b $A7,$BF,$AD,$DC,$A7,$C2,$60,$E2
EndDataSection
; -----------------------------------------------------------------------------
; Demo: Play 5 seconds of a 440Hz sine wave
; -----------------------------------------------------------------------------
PlaybackSineWave_WithFallback(5000)
End