3 * PortAudio Windows WDM-KS interface
5 * Author: Andrew Baldwin, Robert Bielik (WaveRT)
6 * Based on the Open Source API proposed by Ross Bencina
7 * Copyright (c) 1999-2004 Andrew Baldwin, Ross Bencina, Phil Burk
9 * Permission is hereby granted, free of charge, to any person obtaining
10 * a copy of this software and associated documentation files
11 * (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
24 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 * The text above constitutes the entire PortAudio license; however,
31 * the PortAudio community also makes the following non-binding requests:
33 * Any person wishing to distribute modifications to the Software is
34 * requested to send the modifications to the original developer so that
35 * they can be incorporated into the canonical version. It is also
36 * requested that these non-binding requests be included along with the
42 @brief Portaudio WDM-KS host API.
44 @note This is the implementation of the Portaudio host API using the
45 Windows WDM/Kernel Streaming API in order to enable very low latency
46 playback and recording on all modern Windows platforms (e.g. 2K, XP, Vista, Win7)
47 Note: This API accesses the device drivers below the usual KMIXER
48 component which is normally used to enable multi-client mixing and
49 format conversion. That means that it will lock out all other users
50 of a device for the duration of active stream using those devices
55 #if (defined(_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
56 #pragma comment( lib, "setupapi.lib" )
59 /* Debugging/tracing support */
66 #define _WIN32_WINNT 0x0501
71 #include <string.h> /* strlen() */
73 #include <wchar.h> /* iswspace() */
76 #include "pa_allocation.h"
77 #include "pa_hostapi.h"
78 #include "pa_stream.h"
79 #include "pa_cpuload.h"
80 #include "pa_process.h"
81 #include "portaudio.h"
82 #include "pa_debugprint.h"
83 #include "pa_memorybarrier.h"
84 #include "pa_ringbuffer.h"
86 #include "pa_win_waveformat.h"
88 #include "pa_win_wdmks.h"
90 #ifndef DRV_QUERYDEVICEINTERFACE
91 #define DRV_QUERYDEVICEINTERFACE (DRV_RESERVED + 12)
93 #ifndef DRV_QUERYDEVICEINTERFACESIZE
94 #define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13)
99 #ifndef __GNUC__ /* Fix for ticket #257: MinGW-w64: Inclusion of <winioctl.h> triggers multiple redefinition errors. */
100 #include <winioctl.h>
107 #define snprintf _snprintf
108 #define vsnprintf _vsnprintf
111 /* The PA_HP_TRACE macro is used in RT parts, so it can be switched off without affecting
112 the rest of the debug tracing */
114 #define PA_HP_TRACE(x) PaUtil_AddHighSpeedLogMessage x ;
116 #define PA_HP_TRACE(x)
119 /* A define that selects whether the resulting pin names are chosen from pin category
120 instead of the available pin names, who sometimes can be quite cheesy, like "Volume control".
121 Default is to use the pin category.
123 #ifndef PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES
124 #define PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES 1
129 #define PA_LOGE_ PA_DEBUG(("%s {\n",__FUNCTION__))
131 #define PA_LOGL_ PA_DEBUG(("} %s\n",__FUNCTION__))
132 /* These defines are set in order to allow the WIndows DirectX
133 * headers to compile with a GCC compiler such as MinGW
134 * NOTE: The headers may generate a few warning in GCC, but
135 * they should compile */
136 #define _INC_MMSYSTEM
138 #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
139 #define DEFINE_GUID_THUNK(name,guid) DEFINE_GUID(name,guid)
140 #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK( n, STATIC_##n )
141 #if !defined( DEFINE_WAVEFORMATEX_GUID )
142 #define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
144 #define WAVE_FORMAT_ADPCM 0x0002
145 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
146 #define WAVE_FORMAT_ALAW 0x0006
147 #define WAVE_FORMAT_MULAW 0x0007
148 #define WAVE_FORMAT_MPEG 0x0050
149 #define WAVE_FORMAT_DRM 0x0009
150 #define DYNAMIC_GUID_THUNK(l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
151 #define DYNAMIC_GUID(data) DYNAMIC_GUID_THUNK(data)
154 /* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */
155 #if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
156 #define CREATE_THREAD_FUNCTION (HANDLE)_beginthreadex
157 #define PA_THREAD_FUNC static unsigned WINAPI
159 #define CREATE_THREAD_FUNCTION CreateThread
160 #define PA_THREAD_FUNC static DWORD WINAPI
165 #define DYNAMIC_GUID(data) {data}
166 #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
168 #define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
169 #define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
170 #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
173 #include <setupapi.h>
174 #include <winioctl.h>
177 #define EXTERN_C extern
180 #if defined(__GNUC__)
182 /* For MinGW we reference mingw-include files supplied with WASAPI */
185 #include "../wasapi/mingw-include/ks.h"
186 #include "../wasapi/mingw-include/ksmedia.h"
193 /* Note that Windows SDK V6.0A or later is needed for WaveRT specific structs to be present in
194 ksmedia.h. Also make sure that the SDK include path is before other include paths (that may contain
195 an "old" ksmedia.h), so the proper ksmedia.h is used */
203 /* These next definitions allow the use of the KSUSER DLL */
204 typedef /*KSDDKAPI*/ DWORD WINAPI KSCREATEPIN(HANDLE, PKSPIN_CONNECT, ACCESS_MASK, PHANDLE);
205 extern HMODULE DllKsUser;
206 extern KSCREATEPIN* FunctionKsCreatePin;
208 /* These definitions allows the use of AVRT.DLL on Vista and later OSs */
209 typedef enum _PA_AVRT_PRIORITY
211 PA_AVRT_PRIORITY_LOW = -1,
212 PA_AVRT_PRIORITY_NORMAL,
213 PA_AVRT_PRIORITY_HIGH,
214 PA_AVRT_PRIORITY_CRITICAL
215 } PA_AVRT_PRIORITY, *PPA_AVRT_PRIORITY;
221 HANDLE (WINAPI *AvSetMmThreadCharacteristics) (LPCSTR, LPDWORD);
222 BOOL (WINAPI *AvRevertMmThreadCharacteristics) (HANDLE);
223 BOOL (WINAPI *AvSetMmThreadPriority) (HANDLE, PA_AVRT_PRIORITY);
224 } PaWinWDMKSAvRtEntryPoints;
226 static PaWinWDMKSAvRtEntryPoints paWinWDMKSAvRtEntryPoints = {0};
228 /* An unspecified channel count (-1) is not treated correctly, so we replace it with
229 * an arbitrarily large number */
230 #define MAXIMUM_NUMBER_OF_CHANNELS 256
232 /* Forward definition to break circular type reference between pin and filter */
233 struct __PaWinWdmFilter;
234 typedef struct __PaWinWdmFilter PaWinWdmFilter;
236 struct __PaWinWdmPin;
237 typedef struct __PaWinWdmPin PaWinWdmPin;
239 struct __PaWinWdmStream;
240 typedef struct __PaWinWdmStream PaWinWdmStream;
242 /* Function prototype for getting audio position */
243 typedef PaError (*FunctionGetPinAudioPosition)(PaWinWdmPin*, unsigned long*);
245 /* Function prototype for memory barrier */
246 typedef void (*FunctionMemoryBarrier)(void);
248 struct __PaProcessThreadInfo;
249 typedef struct __PaProcessThreadInfo PaProcessThreadInfo;
251 typedef PaError (*FunctionPinHandler)(PaProcessThreadInfo* pInfo, unsigned eventIndex);
253 typedef enum __PaStreamStartEnum
260 /* Multiplexed input structure.
261 * Very often several physical inputs are multiplexed through a MUX node (represented in the topology filter) */
262 typedef struct __PaWinWdmMuxedInput
264 wchar_t friendlyName[MAX_PATH];
268 } PaWinWdmMuxedInput;
271 * A pin is an input or output node, e.g. for audio flow */
275 PaWinWdmMuxedInput** inputs;
277 wchar_t friendlyName[MAX_PATH];
279 PaWinWdmFilter* parentFilter;
280 PaWDMKSSubType pinKsSubType;
282 unsigned long endpointPinId; /* For output pins */
283 KSPIN_CONNECT* pinConnect;
284 unsigned long pinConnectSize;
285 KSDATAFORMAT_WAVEFORMATEX* ksDataFormatWfx;
286 KSPIN_COMMUNICATION communication;
287 KSDATARANGE* dataRanges;
288 KSMULTIPLE_ITEM* dataRangesItem;
289 KSPIN_DATAFLOW dataFlow;
290 KSPIN_CINSTANCES instances;
291 unsigned long frameSize;
293 unsigned long formats;
294 int defaultSampleRate;
295 ULONG *positionRegister; /* WaveRT */
296 ULONG hwLatency; /* WaveRT */
297 FunctionMemoryBarrier fnMemBarrier; /* WaveRT */
298 FunctionGetPinAudioPosition fnAudioPosition; /* WaveRT */
299 FunctionPinHandler fnEventHandler;
300 FunctionPinHandler fnSubmitHandler;
303 /* The Filter structure
304 * A filter has a number of pins and a "friendly name" */
305 struct __PaWinWdmFilter
308 PaWinWDMKSDeviceInfo devInfo; /* This will hold information that is exposed in PaDeviceInfo */
313 PaWinWdmFilter* topologyFilter;
314 wchar_t friendlyName[MAX_PATH];
317 KSMULTIPLE_ITEM* connections;
318 KSMULTIPLE_ITEM* nodes;
323 typedef struct __PaWinWdmDeviceInfo
325 PaDeviceInfo inheritedDeviceInfo;
326 char compositeName[MAX_PATH]; /* Composite name consists of pin name + device name in utf8 */
327 PaWinWdmFilter* filter;
329 int muxPosition; /* Used only for input devices */
334 /* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */
335 typedef struct __PaWinWdmHostApiRepresentation
337 PaUtilHostApiRepresentation inheritedHostApiRep;
338 PaUtilStreamInterface callbackStreamInterface;
339 PaUtilStreamInterface blockingStreamInterface;
341 PaUtilAllocationGroup* allocations;
344 PaWinWdmHostApiRepresentation;
346 typedef struct __DATAPACKET
348 KSSTREAM_HEADER Header;
352 typedef struct __PaIOPacket
356 unsigned lengthBytes;
359 typedef struct __PaWinWdmIOInfo
363 unsigned hostBufferSize;
364 unsigned framesPerBuffer;
365 unsigned bytesPerFrame;
366 unsigned bytesPerSample;
367 unsigned noOfPackets; /* Only used in WaveCyclic */
368 HANDLE *events; /* noOfPackets handles (WaveCyclic) 1 (WaveRT) */
369 DATAPACKET *packets; /* noOfPackets packets (WaveCyclic) 2 (WaveRT) */
370 /* WaveRT polled mode */
371 unsigned lastPosition;
375 /* PaWinWdmStream - a stream data structure specifically for this implementation */
376 struct __PaWinWdmStream
378 PaUtilStreamRepresentation streamRepresentation;
379 PaWDMKSSpecificStreamInfo hostApiStreamInfo; /* This holds info that is exposed through PaStreamInfo */
380 PaUtilCpuLoadMeasurer cpuLoadMeasurer;
381 PaUtilBufferProcessor bufferProcessor;
383 #if PA_TRACE_REALTIME_EVENTS
387 PaUtilAllocationGroup* allocGroup;
388 PaWinWdmIOInfo capture;
389 PaWinWdmIOInfo render;
394 int oldProcessPriority;
397 HANDLE eventStreamStart[StreamStart_kCnt]; /* 0 = OK, 1 = Failed */
398 PaError threadResult;
399 PaStreamFlags streamFlags;
401 /* Capture ring buffer */
402 PaUtilRingBuffer ringBuffer;
403 char* ringBufferData;
405 /* These values handle the case where the user wants to use fewer
406 * channels than the device has */
407 int userInputChannels;
408 int deviceInputChannels;
409 int userOutputChannels;
410 int deviceOutputChannels;
413 /* Gather all processing variables in a struct */
414 struct __PaProcessThreadInfo
416 PaWinWdmStream *stream;
417 PaStreamCallbackTimeInfo ti;
418 PaStreamCallbackFlags underover;
420 volatile int pending;
421 volatile int priming;
422 volatile int pinsStarted;
423 unsigned long timeout;
424 unsigned captureHead;
425 unsigned captureTail;
428 PaIOPacket capturePackets[4];
429 PaIOPacket renderPackets[4];
432 /* Used for transferring device infos during scanning / rescanning */
433 typedef struct __PaWinWDMScanDeviceInfosResults
435 PaDeviceInfo **deviceInfos;
436 PaDeviceIndex defaultInputDevice;
437 PaDeviceIndex defaultOutputDevice;
438 } PaWinWDMScanDeviceInfosResults;
440 static const unsigned cPacketsArrayMask = 3;
442 HMODULE DllKsUser = NULL;
443 KSCREATEPIN* FunctionKsCreatePin = NULL;
445 /* prototypes for functions declared in this file */
450 #endif /* __cplusplus */
452 PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
456 #endif /* __cplusplus */
458 /* Low level I/O functions */
459 static PaError WdmSyncIoctl(HANDLE handle,
460 unsigned long ioctlNumber,
462 unsigned long inBufferCount,
464 unsigned long outBufferCount,
465 unsigned long* bytesReturned);
467 static PaError WdmGetPropertySimple(HANDLE handle,
468 const GUID* const guidPropertySet,
469 unsigned long property,
471 unsigned long valueCount);
473 static PaError WdmSetPropertySimple(HANDLE handle,
474 const GUID* const guidPropertySet,
475 unsigned long property,
477 unsigned long valueCount,
479 unsigned long instanceCount);
481 static PaError WdmGetPinPropertySimple(HANDLE handle,
483 const GUID* const guidPropertySet,
484 unsigned long property,
486 unsigned long valueCount,
487 unsigned long* byteCount);
489 static PaError WdmGetPinPropertyMulti(HANDLE handle,
491 const GUID* const guidPropertySet,
492 unsigned long property,
493 KSMULTIPLE_ITEM** ksMultipleItem);
495 static PaError WdmGetPropertyMulti(HANDLE handle,
496 const GUID* const guidPropertySet,
497 unsigned long property,
498 KSMULTIPLE_ITEM** ksMultipleItem);
500 static PaError WdmSetMuxNodeProperty(HANDLE handle,
505 /** Pin management functions */
506 static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error);
507 static void PinFree(PaWinWdmPin* pin);
508 static void PinClose(PaWinWdmPin* pin);
509 static PaError PinInstantiate(PaWinWdmPin* pin);
510 /*static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state); NOT USED */
511 static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state);
512 static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format);
513 static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format);
515 static PaError PinQueryNotificationSupport(PaWinWdmPin* pPin, BOOL* pbResult);
516 static PaError PinGetBuffer(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier);
517 static PaError PinRegisterPositionRegister(PaWinWdmPin* pPin);
518 static PaError PinRegisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle);
519 static PaError PinUnregisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle);
520 static PaError PinGetHwLatency(PaWinWdmPin* pPin, ULONG* pFifoSize, ULONG* pChipsetDelay, ULONG* pCodecDelay);
521 static PaError PinGetAudioPositionMemoryMapped(PaWinWdmPin* pPin, ULONG* pPosition);
522 static PaError PinGetAudioPositionViaIOCTLRead(PaWinWdmPin* pPin, ULONG* pPosition);
523 static PaError PinGetAudioPositionViaIOCTLWrite(PaWinWdmPin* pPin, ULONG* pPosition);
525 /* Filter management functions */
526 static PaWinWdmFilter* FilterNew(PaWDMKSType type, DWORD devNode, const wchar_t* filterName, const wchar_t* friendlyName, PaError* error);
527 static PaError FilterInitializePins(PaWinWdmFilter* filter);
528 static void FilterFree(PaWinWdmFilter* filter);
529 static void FilterAddRef(PaWinWdmFilter* filter);
530 static PaWinWdmPin* FilterCreatePin(
531 PaWinWdmFilter* filter,
533 const WAVEFORMATEX* wfex,
535 static PaError FilterUse(PaWinWdmFilter* filter);
536 static void FilterRelease(PaWinWdmFilter* filter);
538 /* Hot plug functions */
539 static BOOL IsDeviceTheSame(const PaWinWdmDeviceInfo* pDev1,
540 const PaWinWdmDeviceInfo* pDev2);
542 /* Interface functions */
543 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
544 static PaError IsFormatSupported(
545 struct PaUtilHostApiRepresentation *hostApi,
546 const PaStreamParameters *inputParameters,
547 const PaStreamParameters *outputParameters,
550 static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void **newDeviceInfos, int *newDeviceCount );
551 static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *deviceInfos, int deviceCount );
552 static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *deviceInfos, int deviceCount );
554 static PaError OpenStream(
555 struct PaUtilHostApiRepresentation *hostApi,
557 const PaStreamParameters *inputParameters,
558 const PaStreamParameters *outputParameters,
560 unsigned long framesPerBuffer,
561 PaStreamFlags streamFlags,
562 PaStreamCallback *streamCallback,
564 static PaError CloseStream( PaStream* stream );
565 static PaError StartStream( PaStream *stream );
566 static PaError StopStream( PaStream *stream );
567 static PaError AbortStream( PaStream *stream );
568 static PaError IsStreamStopped( PaStream *s );
569 static PaError IsStreamActive( PaStream *stream );
570 static PaTime GetStreamTime( PaStream *stream );
571 static double GetStreamCpuLoad( PaStream* stream );
572 static PaError ReadStream(
575 unsigned long frames );
576 static PaError WriteStream(
579 unsigned long frames );
580 static signed long GetStreamReadAvailable( PaStream* stream );
581 static signed long GetStreamWriteAvailable( PaStream* stream );
583 /* Utility functions */
584 static unsigned long GetWfexSize(const WAVEFORMATEX* wfex);
585 static PaWinWdmFilter** BuildFilterList(int* filterCount, int* noOfPaDevices, PaError* result);
586 static BOOL PinWrite(HANDLE h, DATAPACKET* p);
587 static BOOL PinRead(HANDLE h, DATAPACKET* p);
588 static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples);
589 static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples);
590 PA_THREAD_FUNC ProcessingThread(void*);
592 /* Pin handler functions */
593 static PaError PaPinCaptureEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
594 static PaError PaPinCaptureSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
596 static PaError PaPinRenderEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
597 static PaError PaPinRenderSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
599 static PaError PaPinCaptureEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
600 static PaError PaPinCaptureEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
601 static PaError PaPinCaptureSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
602 static PaError PaPinCaptureSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
604 static PaError PaPinRenderEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
605 static PaError PaPinRenderEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
606 static PaError PaPinRenderSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
607 static PaError PaPinRenderSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
609 /* Function bodies */
611 #if defined(_DEBUG) && defined(PA_ENABLE_DEBUG_OUTPUT)
612 #define PA_WDMKS_SET_TREF
613 static PaTime tRef = 0;
615 static void PaWinWdmDebugPrintf(const char* fmt, ...)
619 PaTime t = PaUtil_GetTime() - tRef;
621 _vsnprintf(buffer, 1023, fmt, list);
623 PaUtil_DebugPrint("%6.3lf: %s", t, buffer);
628 #define PA_DEBUG(x) PaWinWdmDebugPrintf x ;
632 static BOOL IsDeviceTheSame(const PaWinWdmDeviceInfo* pDev1,
633 const PaWinWdmDeviceInfo* pDev2)
635 if (pDev1 == NULL || pDev2 == NULL)
641 if (strcmp(pDev1->compositeName, pDev2->compositeName) == 0)
647 static BOOL IsEarlierThanVista()
650 NOTE: GetVersionEx() is deprecated as of Windows 8.1 and can not be used to reliably detect
651 versions of Windows higher than Windows 8 (due to manifest requirements for reporting higher versions).
652 Microsoft recommends switching to VerifyVersionInfo (available on Win 2k and later), however GetVersionEx
653 is is faster, for now we just disable the deprecation warning.
654 See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
655 See: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
657 #pragma warning (disable : 4996) /* use of GetVersionEx */
660 osvi.dwOSVersionInfoSize = sizeof(osvi);
661 if (GetVersionEx(&osvi) && osvi.dwMajorVersion<6)
667 #pragma warning (default : 4996)
672 static void MemoryBarrierDummy(void)
677 static void MemoryBarrierRead(void)
679 PaUtil_ReadMemoryBarrier();
682 static void MemoryBarrierWrite(void)
684 PaUtil_WriteMemoryBarrier();
687 static unsigned long GetWfexSize(const WAVEFORMATEX* wfex)
689 if( wfex->wFormatTag == WAVE_FORMAT_PCM )
691 return sizeof( WAVEFORMATEX );
695 return (sizeof( WAVEFORMATEX ) + wfex->cbSize);
699 static void PaWinWDM_SetLastErrorInfo(long errCode, const char* fmt, ...)
704 _vsnprintf(buffer, 1023, fmt, list);
706 PaUtil_SetLastHostErrorInfo(paWDMKS, errCode, buffer);
710 Low level pin/filter access functions
712 static PaError WdmSyncIoctl(
714 unsigned long ioctlNumber,
716 unsigned long inBufferCount,
718 unsigned long outBufferCount,
719 unsigned long* bytesReturned)
721 PaError result = paNoError;
722 unsigned long dummyBytesReturned = 0;
727 /* Use a dummy as the caller hasn't supplied one */
728 bytesReturned = &dummyBytesReturned;
731 bRes = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount, outBuffer, outBufferCount, bytesReturned, NULL);
734 unsigned long error = GetLastError();
735 if ( !(((error == ERROR_INSUFFICIENT_BUFFER ) || ( error == ERROR_MORE_DATA )) &&
736 ( ioctlNumber == IOCTL_KS_PROPERTY ) &&
737 ( outBufferCount == 0 ) ) )
739 KSPROPERTY* ksProperty = (KSPROPERTY*)inBuffer;
741 PaWinWDM_SetLastErrorInfo(result, "WdmSyncIoctl: DeviceIoControl GLE = 0x%08X (prop_set = {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}, prop_id = %u)",
743 ksProperty->Set.Data1, ksProperty->Set.Data2, ksProperty->Set.Data3,
744 ksProperty->Set.Data4[0], ksProperty->Set.Data4[1],
745 ksProperty->Set.Data4[2], ksProperty->Set.Data4[3],
746 ksProperty->Set.Data4[4], ksProperty->Set.Data4[5],
747 ksProperty->Set.Data4[6], ksProperty->Set.Data4[7],
750 result = paUnanticipatedHostError;
756 static PaError WdmGetPropertySimple(HANDLE handle,
757 const GUID* const guidPropertySet,
758 unsigned long property,
760 unsigned long valueCount)
763 KSPROPERTY ksProperty;
765 ksProperty.Set = *guidPropertySet;
766 ksProperty.Id = property;
767 ksProperty.Flags = KSPROPERTY_TYPE_GET;
769 result = WdmSyncIoctl(
781 static PaError WdmSetPropertySimple(
783 const GUID* const guidPropertySet,
784 unsigned long property,
786 unsigned long valueCount,
788 unsigned long instanceCount)
791 KSPROPERTY* ksProperty;
792 unsigned long propertyCount = 0;
794 propertyCount = sizeof(KSPROPERTY) + instanceCount;
795 ksProperty = (KSPROPERTY*)_alloca( propertyCount );
798 return paInsufficientMemory;
801 ksProperty->Set = *guidPropertySet;
802 ksProperty->Id = property;
803 ksProperty->Flags = KSPROPERTY_TYPE_SET;
807 memcpy((void*)((char*)ksProperty + sizeof(KSPROPERTY)), instance, instanceCount);
810 result = WdmSyncIoctl(
822 static PaError WdmGetPinPropertySimple(
825 const GUID* const guidPropertySet,
826 unsigned long property,
828 unsigned long valueCount,
829 unsigned long *byteCount)
834 ksPProp.Property.Set = *guidPropertySet;
835 ksPProp.Property.Id = property;
836 ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
837 ksPProp.PinId = pinId;
838 ksPProp.Reserved = 0;
840 result = WdmSyncIoctl(
852 static PaError WdmGetPinPropertyMulti(
855 const GUID* const guidPropertySet,
856 unsigned long property,
857 KSMULTIPLE_ITEM** ksMultipleItem)
860 unsigned long multipleItemSize = 0;
863 ksPProp.Property.Set = *guidPropertySet;
864 ksPProp.Property.Id = property;
865 ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
866 ksPProp.PinId = pinId;
867 ksPProp.Reserved = 0;
869 result = WdmSyncIoctl(
877 if( result != paNoError )
882 *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
883 if( !*ksMultipleItem )
885 return paInsufficientMemory;
888 result = WdmSyncIoctl(
893 (void*)*ksMultipleItem,
897 if( result != paNoError )
899 PaUtil_FreeMemory( ksMultipleItem );
905 static PaError WdmGetPropertyMulti(HANDLE handle,
906 const GUID* const guidPropertySet,
907 unsigned long property,
908 KSMULTIPLE_ITEM** ksMultipleItem)
911 unsigned long multipleItemSize = 0;
914 ksProp.Set = *guidPropertySet;
915 ksProp.Id = property;
916 ksProp.Flags = KSPROPERTY_TYPE_GET;
918 result = WdmSyncIoctl(
926 if( result != paNoError )
931 *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
932 if( !*ksMultipleItem )
934 return paInsufficientMemory;
937 result = WdmSyncIoctl(
942 (void*)*ksMultipleItem,
946 if( result != paNoError )
948 PaUtil_FreeMemory( ksMultipleItem );
954 static PaError WdmSetMuxNodeProperty(HANDLE handle,
958 PaError result = paNoError;
960 prop.Property.Set = KSPROPSETID_Audio;
961 prop.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
962 prop.Property.Flags = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY;
963 prop.NodeId = nodeId;
966 result = WdmSyncIoctl(handle, IOCTL_KS_PROPERTY, &prop, sizeof(KSNODEPROPERTY), &pinId, sizeof(ULONG), NULL);
971 /* Used when traversing topology for outputs */
972 static const KSTOPOLOGY_CONNECTION* GetConnectionTo(const KSTOPOLOGY_CONNECTION* pFrom, PaWinWdmFilter* filter, int muxIdx)
975 const KSTOPOLOGY_CONNECTION* retval = NULL;
976 const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
978 PA_DEBUG(("GetConnectionTo: Checking %u connections... (pFrom = %p)", filter->connections->Count, pFrom));
979 for (i = 0; i < filter->connections->Count; ++i)
981 const KSTOPOLOGY_CONNECTION* pConn = connections + i;
985 if (pConn->FromNode == pFrom->ToNode)
991 PA_DEBUG(("GetConnectionTo: Returning %p\n", retval));
995 /* Used when traversing topology for inputs */
996 static const KSTOPOLOGY_CONNECTION* GetConnectionFrom(const KSTOPOLOGY_CONNECTION* pTo, PaWinWdmFilter* filter, int muxIdx)
999 const KSTOPOLOGY_CONNECTION* retval = NULL;
1000 const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
1002 PA_DEBUG(("GetConnectionFrom: Checking %u connections... (pTo = %p)\n", filter->connections->Count, pTo));
1003 for (i = 0; i < filter->connections->Count; ++i)
1005 const KSTOPOLOGY_CONNECTION* pConn = connections + i;
1009 if (pConn->ToNode == pTo->FromNode)
1013 if (muxCntr < muxIdx)
1023 PA_DEBUG(("GetConnectionFrom: Returning %p\n", retval));
1027 static ULONG GetNumberOfConnectionsTo(const KSTOPOLOGY_CONNECTION* pTo, PaWinWdmFilter* filter)
1031 const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
1032 PA_DEBUG(("GetNumberOfConnectionsTo: Checking %u connections...\n", filter->connections->Count));
1033 for (i = 0; i < filter->connections->Count; ++i)
1035 const KSTOPOLOGY_CONNECTION* pConn = connections + i;
1036 if (pConn->ToNode == pTo->FromNode &&
1037 (pTo->FromNode != KSFILTER_NODE || pConn->ToNodePin == pTo->FromNodePin))
1042 PA_DEBUG(("GetNumberOfConnectionsTo: Returning %d\n", retval));
1046 typedef const KSTOPOLOGY_CONNECTION *(*TFnGetConnection)(const KSTOPOLOGY_CONNECTION*, PaWinWdmFilter*, int);
1048 static const KSTOPOLOGY_CONNECTION* FindStartConnectionFrom(ULONG startPin, PaWinWdmFilter* filter)
1051 const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
1052 PA_DEBUG(("FindStartConnectionFrom: Startpin %u, Checking %u connections...\n", startPin, filter->connections->Count));
1053 for (i = 0; i < filter->connections->Count; ++i)
1055 const KSTOPOLOGY_CONNECTION* pConn = connections + i;
1056 if (pConn->ToNode == KSFILTER_NODE && pConn->ToNodePin == startPin)
1058 PA_DEBUG(("FindStartConnectionFrom: returning %p\n", pConn));
1063 PA_DEBUG(("FindStartConnectionFrom: returning NULL\n"));
1068 static const KSTOPOLOGY_CONNECTION* FindStartConnectionTo(ULONG startPin, PaWinWdmFilter* filter)
1071 const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
1072 PA_DEBUG(("FindStartConnectionTo: Startpin %u, Checking %u connections...\n", startPin, filter->connections->Count));
1073 for (i = 0; i < filter->connections->Count; ++i)
1075 const KSTOPOLOGY_CONNECTION* pConn = connections + i;
1076 if (pConn->FromNode == KSFILTER_NODE && pConn->FromNodePin == startPin)
1078 PA_DEBUG(("FindStartConnectionTo: returning %p\n", pConn));
1083 PA_DEBUG(("FindStartConnectionTo: returning NULL\n"));
1088 static ULONG GetConnectedPin(ULONG startPin, BOOL forward, PaWinWdmFilter* filter, int muxPosition, ULONG *muxInputPinId, ULONG *muxNodeId)
1091 const KSTOPOLOGY_CONNECTION *conn = NULL;
1092 TFnGetConnection fnGetConnection = forward ? GetConnectionTo : GetConnectionFrom ;
1098 PA_DEBUG(("GetConnectedPin: LOOP LIMIT REACHED\n"));
1104 conn = forward ? FindStartConnectionTo(startPin, filter) : FindStartConnectionFrom(startPin, filter);
1108 conn = fnGetConnection(conn, filter, -1);
1111 /* Handling case of erroneous connection list */
1117 if (forward ? conn->ToNode == KSFILTER_NODE : conn->FromNode == KSFILTER_NODE)
1119 return forward ? conn->ToNodePin : conn->FromNodePin;
1123 PA_DEBUG(("GetConnectedPin: count=%d, forward=%d, muxPosition=%d\n", filter->nodes->Count, forward, muxPosition));
1124 if (filter->nodes->Count > 0 && !forward && muxPosition >= 0)
1126 const GUID* nodes = (const GUID*)(filter->nodes + 1);
1127 if (IsEqualGUID(&nodes[conn->FromNode], &KSNODETYPE_MUX))
1129 ULONG nConn = GetNumberOfConnectionsTo(conn, filter);
1130 conn = fnGetConnection(conn, filter, muxPosition);
1135 if (muxInputPinId != 0)
1137 *muxInputPinId = conn->ToNodePin;
1141 *muxNodeId = conn->ToNode;
1148 return KSFILTER_NODE;
1151 static void DumpConnectionsAndNodes(PaWinWdmFilter* filter)
1154 const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
1155 const GUID* nodes = (const GUID*)(filter->nodes + 1);
1158 PA_DEBUG(("DumpConnectionsAndNodes: connections=%d, nodes=%d\n", filter->connections->Count, filter->nodes->Count));
1160 for (i=0; i < filter->connections->Count; ++i)
1162 const KSTOPOLOGY_CONNECTION* pConn = connections + i;
1163 PA_DEBUG((" Connection: %u - FromNode=%u,FromPin=%u -> ToNode=%u,ToPin=%u\n",
1165 pConn->FromNode, pConn->FromNodePin,
1166 pConn->ToNode, pConn->ToNodePin
1170 for (i=0; i < filter->nodes->Count; ++i)
1172 const GUID* pConn = nodes + i;
1173 PA_DEBUG((" Node: %d - {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
1175 pConn->Data1, pConn->Data2, pConn->Data3,
1176 pConn->Data4[0], pConn->Data4[1],
1177 pConn->Data4[2], pConn->Data4[3],
1178 pConn->Data4[4], pConn->Data4[5],
1179 pConn->Data4[6], pConn->Data4[7]
1186 typedef struct __PaUsbTerminalGUIDToName
1190 } PaUsbTerminalGUIDToName;
1192 static const PaUsbTerminalGUIDToName kNames[] =
1194 /* Types copied from: http://msdn.microsoft.com/en-us/library/ff537742(v=vs.85).aspx */
1195 /* Input terminal types */
1196 { 0x0201, L"Microphone" },
1197 { 0x0202, L"Desktop Microphone" },
1198 { 0x0203, L"Personal Microphone" },
1199 { 0x0204, L"Omni Directional Microphone" },
1200 { 0x0205, L"Microphone Array" },
1201 { 0x0206, L"Processing Microphone Array" },
1202 /* Output terminal types */
1203 { 0x0301, L"Speakers" },
1204 { 0x0302, L"Headphones" },
1205 { 0x0303, L"Head Mounted Display Audio" },
1206 { 0x0304, L"Desktop Speaker" },
1207 { 0x0305, L"Room Speaker" },
1208 { 0x0306, L"Communication Speaker" },
1209 { 0x0307, L"LFE Speakers" },
1210 /* External terminal types */
1211 { 0x0601, L"Analog" },
1212 { 0x0602, L"Digital" },
1213 { 0x0603, L"Line" },
1214 { 0x0604, L"Audio" },
1215 { 0x0605, L"SPDIF" },
1218 static const unsigned kNamesCnt = sizeof(kNames)/sizeof(PaUsbTerminalGUIDToName);
1220 static int PaUsbTerminalGUIDToNameCmp(const void* lhs, const void* rhs)
1222 const PaUsbTerminalGUIDToName* pL = (const PaUsbTerminalGUIDToName*)lhs;
1223 const PaUsbTerminalGUIDToName* pR = (const PaUsbTerminalGUIDToName*)rhs;
1224 return ((int)(pL->usbGUID) - (int)(pR->usbGUID));
1227 static PaError GetNameFromCategory(const GUID* pGUID, BOOL input, wchar_t* name, unsigned length)
1229 PaError result = paUnanticipatedHostError;
1230 USHORT usbTerminalGUID = (USHORT)(pGUID->Data1 - 0xDFF219E0);
1233 if (input && usbTerminalGUID >= 0x301 && usbTerminalGUID < 0x400)
1235 /* Output terminal name for an input !? Set it to Line! */
1236 usbTerminalGUID = 0x603;
1238 if (!input && usbTerminalGUID >= 0x201 && usbTerminalGUID < 0x300)
1240 /* Input terminal name for an output !? Set it to Line! */
1241 usbTerminalGUID = 0x603;
1243 if (usbTerminalGUID >= 0x201 && usbTerminalGUID < 0x713)
1245 PaUsbTerminalGUIDToName s = { usbTerminalGUID };
1246 const PaUsbTerminalGUIDToName* ptr = bsearch(
1250 sizeof(PaUsbTerminalGUIDToName),
1251 PaUsbTerminalGUIDToNameCmp
1255 PA_DEBUG(("GetNameFromCategory: USB GUID %04X -> '%S'\n", usbTerminalGUID, ptr->name));
1257 if (name != NULL && length > 0)
1259 int n = _snwprintf(name, length, L"%s", ptr->name);
1260 if (usbTerminalGUID >= 0x601 && usbTerminalGUID < 0x700)
1262 _snwprintf(name + n, length - n, L" %s", (input ? L"In":L"Out"));
1270 PaWinWDM_SetLastErrorInfo(result, "GetNameFromCategory: usbTerminalGUID = %04X ", usbTerminalGUID);
1276 static BOOL IsFrequencyWithinRange(const KSDATARANGE_AUDIO* range, int frequency)
1278 if (frequency < (int)range->MinimumSampleFrequency)
1280 if (frequency > (int)range->MaximumSampleFrequency)
1285 static BOOL IsBitsWithinRange(const KSDATARANGE_AUDIO* range, int noOfBits)
1287 if (noOfBits < (int)range->MinimumBitsPerSample)
1289 if (noOfBits > (int)range->MaximumBitsPerSample)
1294 /* Note: Somewhat different order compared to WMME implementation, as we want to focus on fidelity first */
1295 static const int defaultSampleRateSearchOrder[] =
1296 { 44100, 48000, 88200, 96000, 192000, 32000, 24000, 22050, 16000, 12000, 11025, 9600, 8000 };
1297 static const int defaultSampleRateSearchOrderCount = sizeof(defaultSampleRateSearchOrder)/sizeof(defaultSampleRateSearchOrder[0]);
1299 static int DefaultSampleFrequencyIndex(const KSDATARANGE_AUDIO* range)
1303 for(i=0; i < defaultSampleRateSearchOrderCount; ++i)
1305 int currentFrequency = defaultSampleRateSearchOrder[i];
1307 if (IsFrequencyWithinRange(range, currentFrequency))
1317 Create a new pin object belonging to a filter
1318 The pin object holds all the configuration information about the pin
1319 before it is opened, and then the handle of the pin after is opened
1321 static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error)
1326 KSMULTIPLE_ITEM* item = NULL;
1327 KSIDENTIFIER* identifier;
1328 KSDATARANGE* dataRange;
1329 const ULONG streamingId = (parentFilter->devInfo.streamingType == Type_kWaveRT) ? KSINTERFACE_STANDARD_LOOPED_STREAMING : KSINTERFACE_STANDARD_STREAMING;
1330 int defaultSampleRateIndex = defaultSampleRateSearchOrderCount;
1333 PA_DEBUG(("PinNew: Creating pin %d:\n",pinId));
1335 /* Allocate the new PIN object */
1336 pin = (PaWinWdmPin*)PaUtil_AllocateMemory( sizeof(PaWinWdmPin) );
1339 result = paInsufficientMemory;
1343 /* Zero the pin object */
1344 /* memset( (void*)pin, 0, sizeof(PaWinWdmPin) ); */
1346 pin->parentFilter = parentFilter;
1349 /* Allocate a connect structure */
1350 pin->pinConnectSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX);
1351 pin->pinConnect = (KSPIN_CONNECT*)PaUtil_AllocateMemory( pin->pinConnectSize );
1352 if( !pin->pinConnect )
1354 result = paInsufficientMemory;
1358 /* Configure the connect structure with default values */
1359 pin->pinConnect->Interface.Set = KSINTERFACESETID_Standard;
1360 pin->pinConnect->Interface.Id = streamingId;
1361 pin->pinConnect->Interface.Flags = 0;
1362 pin->pinConnect->Medium.Set = KSMEDIUMSETID_Standard;
1363 pin->pinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
1364 pin->pinConnect->Medium.Flags = 0;
1365 pin->pinConnect->PinId = pinId;
1366 pin->pinConnect->PinToHandle = NULL;
1367 pin->pinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
1368 pin->pinConnect->Priority.PrioritySubClass = 1;
1369 pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)(pin->pinConnect + 1);
1370 pin->ksDataFormatWfx->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEX);
1371 pin->ksDataFormatWfx->DataFormat.Flags = 0;
1372 pin->ksDataFormatWfx->DataFormat.Reserved = 0;
1373 pin->ksDataFormatWfx->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
1374 pin->ksDataFormatWfx->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1375 pin->ksDataFormatWfx->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
1377 pin->frameSize = 0; /* Unknown until we instantiate pin */
1379 /* Get the COMMUNICATION property */
1380 result = WdmGetPinPropertySimple(
1381 parentFilter->handle,
1384 KSPROPERTY_PIN_COMMUNICATION,
1385 &pin->communication,
1386 sizeof(KSPIN_COMMUNICATION),
1388 if( result != paNoError )
1391 if( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/
1392 (pin->communication != KSPIN_COMMUNICATION_SINK) &&
1393 (pin->communication != KSPIN_COMMUNICATION_BOTH) )
1395 PA_DEBUG(("PinNew: Not source/sink\n"));
1396 result = paInvalidDevice;
1400 /* Get dataflow information */
1401 result = WdmGetPinPropertySimple(
1402 parentFilter->handle,
1405 KSPROPERTY_PIN_DATAFLOW,
1407 sizeof(KSPIN_DATAFLOW),
1410 if( result != paNoError )
1413 /* Get the INTERFACE property list */
1414 result = WdmGetPinPropertyMulti(
1415 parentFilter->handle,
1418 KSPROPERTY_PIN_INTERFACES,
1421 if( result != paNoError )
1424 identifier = (KSIDENTIFIER*)(item+1);
1426 /* Check that at least one interface is STANDARD_STREAMING */
1427 result = paUnanticipatedHostError;
1428 for( i = 0; i < item->Count; i++ )
1430 if( IsEqualGUID(&identifier[i].Set, &KSINTERFACESETID_Standard) && ( identifier[i].Id == streamingId ) )
1437 if( result != paNoError )
1439 PA_DEBUG(("PinNew: No %s streaming\n", streamingId==KSINTERFACE_STANDARD_LOOPED_STREAMING?"looped":"standard"));
1443 /* Don't need interfaces any more */
1444 PaUtil_FreeMemory( item );
1447 /* Get the MEDIUM properties list */
1448 result = WdmGetPinPropertyMulti(
1449 parentFilter->handle,
1452 KSPROPERTY_PIN_MEDIUMS,
1455 if( result != paNoError )
1458 identifier = (KSIDENTIFIER*)(item+1); /* Not actually necessary... */
1460 /* Check that at least one medium is STANDARD_DEVIO */
1461 result = paUnanticipatedHostError;
1462 for( i = 0; i < item->Count; i++ )
1464 if( IsEqualGUID(&identifier[i].Set, &KSMEDIUMSETID_Standard) && ( identifier[i].Id == KSMEDIUM_STANDARD_DEVIO ) )
1471 if( result != paNoError )
1473 PA_DEBUG(("No standard devio\n"));
1476 /* Don't need mediums any more */
1477 PaUtil_FreeMemory( item );
1480 /* Get DATARANGES */
1481 result = WdmGetPinPropertyMulti(
1482 parentFilter->handle,
1485 KSPROPERTY_PIN_DATARANGES,
1486 &pin->dataRangesItem);
1488 if( result != paNoError )
1491 pin->dataRanges = (KSDATARANGE*)(pin->dataRangesItem +1);
1493 /* Check that at least one datarange supports audio */
1494 result = paUnanticipatedHostError;
1495 dataRange = pin->dataRanges;
1496 pin->maxChannels = 0;
1497 pin->defaultSampleRate = 0;
1499 PA_DEBUG(("PinNew: Checking %u no of dataranges...\n", pin->dataRangesItem->Count));
1500 for( i = 0; i < pin->dataRangesItem->Count; i++)
1502 PA_DEBUG(("PinNew: DR major format %x\n",*(unsigned long*)(&(dataRange->MajorFormat))));
1503 /* Check that subformat is WAVEFORMATEX, PCM or WILDCARD */
1504 if( IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) ||
1505 IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) ||
1506 IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) ||
1507 IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_WILDCARD) ||
1508 IsEqualGUID(&dataRange->MajorFormat, &KSDATAFORMAT_TYPE_AUDIO) )
1512 /* Record the maximum possible channels with this pin */
1513 if( ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels == (ULONG) -1 )
1515 pin->maxChannels = MAXIMUM_NUMBER_OF_CHANNELS;
1517 else if( (int) ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels )
1519 pin->maxChannels = (int) ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels;
1521 PA_DEBUG(("PinNew: MaxChannel: %d\n",pin->maxChannels));
1523 /* Record the formats (bit depths) that are supported */
1524 if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 8) )
1526 pin->formats |= paInt8;
1527 PA_DEBUG(("PinNew: Format PCM 8 bit supported\n"));
1529 if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 16) )
1531 pin->formats |= paInt16;
1532 PA_DEBUG(("PinNew: Format PCM 16 bit supported\n"));
1534 if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 24) )
1536 pin->formats |= paInt24;
1537 PA_DEBUG(("PinNew: Format PCM 24 bit supported\n"));
1539 if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 32) )
1541 if (IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1543 pin->formats |= paFloat32;
1544 PA_DEBUG(("PinNew: Format IEEE float 32 bit supported\n"));
1548 pin->formats |= paInt32;
1549 PA_DEBUG(("PinNew: Format PCM 32 bit supported\n"));
1553 defaultIndex = DefaultSampleFrequencyIndex((KSDATARANGE_AUDIO*)dataRange);
1554 if (defaultIndex >= 0 && defaultIndex < defaultSampleRateIndex)
1556 defaultSampleRateIndex = defaultIndex;
1559 dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
1562 if( result != paNoError )
1565 /* If none of the frequencies searched for are present, there's something seriously wrong */
1566 if (defaultSampleRateIndex == defaultSampleRateSearchOrderCount)
1568 PA_DEBUG(("PinNew: No default sample rate found, skipping pin!\n"));
1569 PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "PinNew: No default sample rate found");
1570 result = paUnanticipatedHostError;
1574 /* Set the default sample rate */
1575 pin->defaultSampleRate = defaultSampleRateSearchOrder[defaultSampleRateIndex];
1576 PA_DEBUG(("PinNew: Default sample rate = %d Hz\n", pin->defaultSampleRate));
1578 /* Get instance information */
1579 result = WdmGetPinPropertySimple(
1580 parentFilter->handle,
1583 KSPROPERTY_PIN_CINSTANCES,
1585 sizeof(KSPIN_CINSTANCES),
1588 if( result != paNoError )
1591 /* If WaveRT, check if pin supports notification mode */
1592 if (parentFilter->devInfo.streamingType == Type_kWaveRT)
1594 BOOL bSupportsNotification = FALSE;
1595 if (PinQueryNotificationSupport(pin, &bSupportsNotification) == paNoError)
1597 pin->pinKsSubType = bSupportsNotification ? SubType_kNotification : SubType_kPolled;
1601 /* Query pin name (which means we need to traverse to non IRP pin, via physical connection to topology filter pin, through
1602 its nodes to the endpoint pin, and get that ones name... phew...) */
1603 PA_DEBUG(("PinNew: Finding topology pin...\n"));
1606 ULONG topoPinId = GetConnectedPin(pinId, (pin->dataFlow == KSPIN_DATAFLOW_IN), parentFilter, -1, NULL, NULL);
1607 const wchar_t kInputName[] = L"Input";
1608 const wchar_t kOutputName[] = L"Output";
1610 if (topoPinId != KSFILTER_NODE)
1612 /* Get physical connection for topo pin */
1613 unsigned long cbBytes = 0;
1614 PA_DEBUG(("PinNew: Getting physical connection...\n"));
1615 result = WdmGetPinPropertySimple(parentFilter->handle,
1618 KSPROPERTY_PIN_PHYSICALCONNECTION,
1624 if (result != paNoError)
1626 /* No physical connection -> there is no topology filter! So we get the name of the pin! */
1627 PA_DEBUG(("PinNew: No physical connection! Getting the pin name\n"));
1628 result = WdmGetPinPropertySimple(parentFilter->handle,
1631 KSPROPERTY_PIN_NAME,
1635 if (result != paNoError)
1637 GUID category = {0};
1639 /* Get pin category information */
1640 result = WdmGetPinPropertySimple(parentFilter->handle,
1643 KSPROPERTY_PIN_CATEGORY,
1648 if (result == paNoError)
1650 result = GetNameFromCategory(&category, (pin->dataFlow == KSPIN_DATAFLOW_OUT), pin->friendlyName, MAX_PATH);
1654 /* Make sure pin gets a name here... */
1655 if (wcslen(pin->friendlyName) == 0)
1657 wcscpy(pin->friendlyName, (pin->dataFlow == KSPIN_DATAFLOW_IN) ? kOutputName : kInputName);
1659 PA_DEBUG(("PinNew: Setting pin friendly name to '%s'\n", pin->friendlyName));
1661 PA_DEBUG(("PinNew: Setting pin friendly name to '%S'\n", pin->friendlyName));
1665 /* This is then == the endpoint pin */
1666 pin->endpointPinId = (pin->dataFlow == KSPIN_DATAFLOW_IN) ? pinId : topoPinId;
1670 KSPIN_PHYSICALCONNECTION* pc = (KSPIN_PHYSICALCONNECTION*)PaUtil_AllocateMemory(cbBytes + 2);
1672 wchar_t symbLinkName[MAX_PATH];
1673 PA_DEBUG(("PinNew: Physical connection found!\n"));
1676 result = paInsufficientMemory;
1679 result = WdmGetPinPropertySimple(parentFilter->handle,
1682 KSPROPERTY_PIN_PHYSICALCONNECTION,
1689 wcsncpy(symbLinkName, pc->SymbolicLinkName, MAX_PATH);
1690 PaUtil_FreeMemory( pc );
1692 if (result != paNoError)
1694 /* Shouldn't happen, but fail if it does */
1695 PA_DEBUG(("PinNew: failed to retrieve physical connection!\n"));
1699 if (symbLinkName[1] == TEXT('?'))
1701 symbLinkName[1] = TEXT('\\');
1704 if (pin->parentFilter->topologyFilter == NULL)
1706 PA_DEBUG(("PinNew: Creating topology filter '%S'\n", symbLinkName));
1708 pin->parentFilter->topologyFilter = FilterNew(Type_kNotUsed, 0, symbLinkName, L"", &result);
1709 if (pin->parentFilter->topologyFilter == NULL)
1711 PA_DEBUG(("PinNew: Failed creating topology filter\n"));
1712 result = paUnanticipatedHostError;
1713 PaWinWDM_SetLastErrorInfo(result, "Failed to create topology filter '%S'", symbLinkName);
1717 /* Copy info so we have it in device info */
1718 wcsncpy(pin->parentFilter->devInfo.topologyPath, symbLinkName, MAX_PATH);
1722 /* Must be the same */
1723 assert(wcscmp(symbLinkName, pin->parentFilter->topologyFilter->devInfo.filterPath) == 0);
1726 PA_DEBUG(("PinNew: Opening topology filter..."));
1728 result = FilterUse(pin->parentFilter->topologyFilter);
1729 if (result == paNoError)
1731 unsigned long endpointPinId;
1733 if (pin->dataFlow == KSPIN_DATAFLOW_IN)
1735 /* The "endpointPinId" is what WASAPI looks at for pin names */
1736 GUID category = {0};
1738 PA_DEBUG(("PinNew: Checking for output endpoint pin id...\n"));
1740 endpointPinId = GetConnectedPin(pcPin, TRUE, pin->parentFilter->topologyFilter, -1, NULL, NULL);
1742 if (endpointPinId == KSFILTER_NODE)
1744 result = paUnanticipatedHostError;
1745 PaWinWDM_SetLastErrorInfo(result, "Failed to get endpoint pin ID on topology filter!");
1749 PA_DEBUG(("PinNew: Found endpoint pin id %u\n", endpointPinId));
1751 /* Get pin category information */
1752 result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1755 KSPROPERTY_PIN_CATEGORY,
1760 if (result == paNoError)
1762 #if !PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES
1763 wchar_t pinName[MAX_PATH];
1765 PA_DEBUG(("PinNew: Getting pin name property..."));
1767 /* Ok, try pin name also, and favor that if available */
1768 result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1771 KSPROPERTY_PIN_NAME,
1776 if (result == paNoError && wcslen(pinName)>0)
1778 wcsncpy(pin->friendlyName, pinName, MAX_PATH);
1783 result = GetNameFromCategory(&category, (pin->dataFlow == KSPIN_DATAFLOW_OUT), pin->friendlyName, MAX_PATH);
1787 /* Make sure we get a name for the pin */
1788 if (wcslen(pin->friendlyName) == 0)
1790 wcscpy(pin->friendlyName, kOutputName);
1793 PA_DEBUG(("PinNew: Pin name '%s'\n", pin->friendlyName));
1795 PA_DEBUG(("PinNew: Pin name '%S'\n", pin->friendlyName));
1798 /* Set endpoint pin ID (this is the topology INPUT pin, since portmixer will always traverse the
1799 filter in audio streaming direction, see http://msdn.microsoft.com/en-us/library/windows/hardware/ff536331(v=vs.85).aspx
1800 for more information)
1802 pin->endpointPinId = pcPin;
1806 unsigned muxCount = 0;
1808 /* Max 64 multiplexer inputs... sanity check :) */
1809 for (i = 0; i < 64; ++i)
1811 ULONG muxNodeIdTest = (unsigned)-1;
1812 PA_DEBUG(("PinNew: Checking for input endpoint pin id (%d)...\n", i));
1814 endpointPinId = GetConnectedPin(pcPin,
1816 pin->parentFilter->topologyFilter,
1821 if (endpointPinId == KSFILTER_NODE)
1824 PA_DEBUG(("PinNew: Done with inputs.\n", endpointPinId));
1829 /* The "endpointPinId" is what WASAPI looks at for pin names */
1830 GUID category = {0};
1832 PA_DEBUG(("PinNew: Found endpoint pin id %u\n", endpointPinId));
1834 /* Get pin category information */
1835 result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1838 KSPROPERTY_PIN_CATEGORY,
1843 if (result == paNoError)
1845 if (muxNodeIdTest == (unsigned)-1)
1847 /* Ok, try pin name, and favor that if available */
1848 result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1851 KSPROPERTY_PIN_NAME,
1856 if (result != paNoError)
1858 result = GetNameFromCategory(&category, TRUE, pin->friendlyName, MAX_PATH);
1864 result = GetNameFromCategory(&category, TRUE, NULL, 0);
1866 if (result == paNoError)
1874 PA_DEBUG(("PinNew: Failed to get pin category"));
1881 pin->endpointPinId = endpointPinId;
1882 /* Make sure we get a name for the pin */
1883 if (wcslen(pin->friendlyName) == 0)
1885 wcscpy(pin->friendlyName, kInputName);
1888 PA_DEBUG(("PinNew: Input friendly name '%s'\n", pin->friendlyName));
1890 PA_DEBUG(("PinNew: Input friendly name '%S'\n", pin->friendlyName));
1893 else // muxCount > 0
1895 PA_DEBUG(("PinNew: Setting up %u inputs\n", muxCount));
1897 /* Now we redo the operation once known how many multiplexer positions there are */
1898 pin->inputs = (PaWinWdmMuxedInput**)PaUtil_AllocateMemory(muxCount * sizeof(PaWinWdmMuxedInput*));
1899 if (pin->inputs == NULL)
1901 FilterRelease(pin->parentFilter->topologyFilter);
1902 result = paInsufficientMemory;
1905 pin->inputCount = muxCount;
1907 for (i = 0; i < muxCount; ++muxPos)
1909 PA_DEBUG(("PinNew: Setting up input %u...\n", i));
1911 if (pin->inputs[i] == NULL)
1913 pin->inputs[i] = (PaWinWdmMuxedInput*)PaUtil_AllocateMemory(sizeof(PaWinWdmMuxedInput));
1914 if (pin->inputs[i] == NULL)
1916 FilterRelease(pin->parentFilter->topologyFilter);
1917 result = paInsufficientMemory;
1922 endpointPinId = GetConnectedPin(pcPin,
1924 pin->parentFilter->topologyFilter,
1926 &pin->inputs[i]->muxPinId,
1927 &pin->inputs[i]->muxNodeId);
1929 if (endpointPinId != KSFILTER_NODE)
1931 /* The "endpointPinId" is what WASAPI looks at for pin names */
1932 GUID category = {0};
1934 /* Set input endpoint ID */
1935 pin->inputs[i]->endpointPinId = endpointPinId;
1937 /* Get pin category information */
1938 result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1941 KSPROPERTY_PIN_CATEGORY,
1946 if (result == paNoError)
1948 /* Try pin name first, and if that is not defined, use category instead */
1949 result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1952 KSPROPERTY_PIN_NAME,
1953 pin->inputs[i]->friendlyName,
1957 if (result != paNoError)
1959 result = GetNameFromCategory(&category, TRUE, pin->inputs[i]->friendlyName, MAX_PATH);
1960 if (result != paNoError)
1962 /* Only specify name, let name hash in ScanDeviceInfos fix postfix enumerators */
1963 wcscpy(pin->inputs[i]->friendlyName, kInputName);
1967 PA_DEBUG(("PinNew: Input (%u) friendly name '%s'\n", i, pin->inputs[i]->friendlyName));
1969 PA_DEBUG(("PinNew: Input (%u) friendly name '%S'\n", i, pin->inputs[i]->friendlyName));
1976 /* Should never come here! */
1987 PA_DEBUG(("PinNew: No topology pin id found. Bad...\n"));
1988 /* No TOPO pin id ??? This is bad. Ok, so we just say it is an input or output... */
1989 wcscpy(pin->friendlyName, (pin->dataFlow == KSPIN_DATAFLOW_IN) ? kOutputName : kInputName);
1993 /* Release topology filter if it has been used */
1994 if (pin->parentFilter->topologyFilter && pin->parentFilter->topologyFilter->handle != NULL)
1996 PA_DEBUG(("PinNew: Releasing topology filter...\n"));
1997 FilterRelease(pin->parentFilter->topologyFilter);
2002 PA_DEBUG(("Pin created successfully\n"));
2007 PA_DEBUG(("PinNew: Error %d\n", result));
2012 if (pin->parentFilter->topologyFilter && pin->parentFilter->topologyFilter->handle != NULL)
2014 FilterRelease(pin->parentFilter->topologyFilter);
2017 PaUtil_FreeMemory( item );
2026 Safely free all resources associated with the pin
2028 static void PinFree(PaWinWdmPin* pin)
2035 if( pin->pinConnect )
2037 PaUtil_FreeMemory( pin->pinConnect );
2039 if( pin->dataRangesItem )
2041 PaUtil_FreeMemory( pin->dataRangesItem );
2045 for (i = 0; i < pin->inputCount; ++i)
2047 PaUtil_FreeMemory( pin->inputs[i] );
2049 PaUtil_FreeMemory( pin->inputs );
2051 PaUtil_FreeMemory( pin );
2057 If the pin handle is open, close it
2059 static void PinClose(PaWinWdmPin* pin)
2064 PA_DEBUG(("Closing NULL pin!"));
2068 if( pin->handle != NULL )
2070 PinSetState( pin, KSSTATE_PAUSE );
2071 PinSetState( pin, KSSTATE_STOP );
2072 CloseHandle( pin->handle );
2074 FilterRelease(pin->parentFilter);
2080 Set the state of this (instantiated) pin
2082 static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state)
2084 PaError result = paNoError;
2088 prop.Set = KSPROPSETID_Connection;
2089 prop.Id = KSPROPERTY_CONNECTION_STATE;
2090 prop.Flags = KSPROPERTY_TYPE_SET;
2093 return paInternalError;
2094 if( pin->handle == NULL )
2095 return paInternalError;
2097 result = WdmSyncIoctl(pin->handle, IOCTL_KS_PROPERTY, &prop, sizeof(KSPROPERTY), &state, sizeof(KSSTATE), NULL);
2103 static PaError PinInstantiate(PaWinWdmPin* pin)
2106 unsigned long createResult;
2107 KSALLOCATOR_FRAMING ksaf;
2108 KSALLOCATOR_FRAMING_EX ksafex;
2113 return paInternalError;
2114 if(!pin->pinConnect)
2115 return paInternalError;
2117 FilterUse(pin->parentFilter);
2119 createResult = FunctionKsCreatePin(
2120 pin->parentFilter->handle,
2122 GENERIC_WRITE | GENERIC_READ,
2126 PA_DEBUG(("Pin create result = 0x%08x\n",createResult));
2127 if( createResult != ERROR_SUCCESS )
2129 FilterRelease(pin->parentFilter);
2131 switch (createResult)
2133 case ERROR_INVALID_PARAMETER:
2134 /* First case when pin actually don't support the format */
2135 return paSampleFormatNotSupported;
2136 case ERROR_BAD_COMMAND:
2137 /* Case when pin is occupied (by another application) */
2138 return paDeviceUnavailable;
2140 /* All other cases */
2141 return paInvalidDevice;
2145 if (pin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
2147 /* Framing size query only valid for WaveCyclic devices */
2148 result = WdmGetPropertySimple(
2150 &KSPROPSETID_Connection,
2151 KSPROPERTY_CONNECTION_ALLOCATORFRAMING,
2155 if( result != paNoError )
2157 result = WdmGetPropertySimple(
2159 &KSPROPSETID_Connection,
2160 KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX,
2163 if( result == paNoError )
2165 pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize;
2170 pin->frameSize = ksaf.FrameSize;
2179 static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format)
2187 return paInternalError;
2188 if( format == NULL )
2189 return paInternalError;
2191 size = GetWfexSize(format) + sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX) - sizeof(WAVEFORMATEX);
2193 if( pin->pinConnectSize != size )
2195 newConnect = PaUtil_AllocateMemory( size );
2196 if( newConnect == NULL )
2197 return paInsufficientMemory;
2198 memcpy( newConnect, (void*)pin->pinConnect, min(pin->pinConnectSize,size) );
2199 PaUtil_FreeMemory( pin->pinConnect );
2200 pin->pinConnect = (KSPIN_CONNECT*)newConnect;
2201 pin->pinConnectSize = size;
2202 pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)((KSPIN_CONNECT*)newConnect + 1);
2203 pin->ksDataFormatWfx->DataFormat.FormatSize = size - sizeof(KSPIN_CONNECT);
2206 memcpy( (void*)&(pin->ksDataFormatWfx->WaveFormatEx), format, GetWfexSize(format) );
2207 pin->ksDataFormatWfx->DataFormat.SampleSize = (unsigned short)(format->nChannels * (format->wBitsPerSample / 8));
2214 static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format)
2216 KSDATARANGE_AUDIO* dataRange;
2217 unsigned long count;
2218 GUID guid = DYNAMIC_GUID( DEFINE_WAVEFORMATEX_GUID(format->wFormatTag) );
2219 PaError result = paInvalidDevice;
2220 const WAVEFORMATEXTENSIBLE* pFormatExt = (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) ? (const WAVEFORMATEXTENSIBLE*)format : 0;
2224 if( pFormatExt != 0 )
2226 guid = pFormatExt->SubFormat;
2228 dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges;
2230 count<pin->dataRangesItem->Count;
2232 dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize)) /* Need to update dataRange here, due to 'continue' !! */
2234 /* Check major format*/
2235 if (!(IsEqualGUID(&(dataRange->DataRange.MajorFormat), &KSDATAFORMAT_TYPE_AUDIO) ||
2236 IsEqualGUID(&(dataRange->DataRange.MajorFormat), &KSDATAFORMAT_TYPE_WILDCARD)))
2241 /* This is an audio or wildcard datarange... */
2242 if (! (IsEqualGUID(&(dataRange->DataRange.SubFormat), &KSDATAFORMAT_SUBTYPE_WILDCARD) ||
2243 IsEqualGUID(&(dataRange->DataRange.SubFormat), &KSDATAFORMAT_SUBTYPE_PCM) ||
2244 IsEqualGUID(&(dataRange->DataRange.SubFormat), &guid) ))
2249 /* Check specifier... */
2250 if (! (IsEqualGUID(&(dataRange->DataRange.Specifier), &KSDATAFORMAT_SPECIFIER_WILDCARD) ||
2251 IsEqualGUID(&(dataRange->DataRange.Specifier), &KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) )
2256 PA_DEBUG(("Pin:%x, DataRange:%d\n",(void*)pin,count));
2257 PA_DEBUG(("\tFormatSize:%d, SampleSize:%d\n",dataRange->DataRange.FormatSize,dataRange->DataRange.SampleSize));
2258 PA_DEBUG(("\tMaxChannels:%d\n",dataRange->MaximumChannels));
2259 PA_DEBUG(("\tBits:%d-%d\n",dataRange->MinimumBitsPerSample,dataRange->MaximumBitsPerSample));
2260 PA_DEBUG(("\tSampleRate:%d-%d\n",dataRange->MinimumSampleFrequency,dataRange->MaximumSampleFrequency));
2262 if( dataRange->MaximumChannels != (ULONG)-1 &&
2263 dataRange->MaximumChannels < format->nChannels )
2265 result = paInvalidChannelCount;
2269 if (pFormatExt != 0)
2271 if (!IsBitsWithinRange(dataRange, pFormatExt->Samples.wValidBitsPerSample))
2273 result = paSampleFormatNotSupported;
2279 if (!IsBitsWithinRange(dataRange, format->wBitsPerSample))
2281 result = paSampleFormatNotSupported;
2286 if (!IsFrequencyWithinRange(dataRange, format->nSamplesPerSec))
2288 result = paInvalidSampleRate;
2301 static PaError PinQueryNotificationSupport(PaWinWdmPin* pPin, BOOL* pbResult)
2303 PaError result = paNoError;
2308 propIn.Set = KSPROPSETID_RtAudio;
2309 propIn.Id = 8; /* = KSPROPERTY_RTAUDIO_QUERY_NOTIFICATION_SUPPORT */
2310 propIn.Flags = KSPROPERTY_TYPE_GET;
2312 result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
2319 if (result != paNoError)
2321 PA_DEBUG(("Failed PinQueryNotificationSupport\n"));
2328 static PaError PinGetBufferWithNotification(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier)
2330 PaError result = paNoError;
2331 KSRTAUDIO_BUFFER_PROPERTY_WITH_NOTIFICATION propIn;
2332 KSRTAUDIO_BUFFER propOut;
2336 propIn.BaseAddress = 0;
2337 propIn.NotificationCount = 2;
2338 propIn.RequestedBufferSize = *pRequestedBufSize;
2339 propIn.Property.Set = KSPROPSETID_RtAudio;
2340 propIn.Property.Id = KSPROPERTY_RTAUDIO_BUFFER_WITH_NOTIFICATION;
2341 propIn.Property.Flags = KSPROPERTY_TYPE_GET;
2343 result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
2345 sizeof(KSRTAUDIO_BUFFER_PROPERTY_WITH_NOTIFICATION),
2347 sizeof(KSRTAUDIO_BUFFER),
2350 if (result == paNoError)
2352 *pBuffer = propOut.BufferAddress;
2353 *pRequestedBufSize = propOut.ActualBufferSize;
2354 *pbCallMemBarrier = propOut.CallMemoryBarrier;
2358 PA_DEBUG(("Failed to get buffer with notification\n"));
2365 static PaError PinGetBufferWithoutNotification(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier)
2367 PaError result = paNoError;
2368 KSRTAUDIO_BUFFER_PROPERTY propIn;
2369 KSRTAUDIO_BUFFER propOut;
2373 propIn.BaseAddress = NULL;
2374 propIn.RequestedBufferSize = *pRequestedBufSize;
2375 propIn.Property.Set = KSPROPSETID_RtAudio;
2376 propIn.Property.Id = KSPROPERTY_RTAUDIO_BUFFER;
2377 propIn.Property.Flags = KSPROPERTY_TYPE_GET;
2379 result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
2381 sizeof(KSRTAUDIO_BUFFER_PROPERTY),
2383 sizeof(KSRTAUDIO_BUFFER),
2386 if (result == paNoError)
2388 *pBuffer = propOut.BufferAddress;
2389 *pRequestedBufSize = propOut.ActualBufferSize;
2390 *pbCallMemBarrier = propOut.CallMemoryBarrier;
2394 PA_DEBUG(("Failed to get buffer without notification\n"));
2401 /* greatest common divisor - PGCD in French */
2402 static unsigned long PaWinWDMGCD( unsigned long a, unsigned long b )
2404 return (b==0) ? a : PaWinWDMGCD( b, a%b);
2408 /* This function will handle getting the cyclic buffer from a WaveRT driver. Certain WaveRT drivers needs to have
2409 requested buffer size on multiples of 128 bytes:
2412 static PaError PinGetBuffer(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier)
2414 PaError result = paNoError;
2422 PA_DEBUG(("PinGetBuffer: LOOP LIMIT REACHED\n"));
2426 if (pPin->pinKsSubType != SubType_kPolled)
2428 /* In case of unknown (or notification), we try both modes */
2429 result = PinGetBufferWithNotification(pPin, pBuffer, pRequestedBufSize, pbCallMemBarrier);
2430 if (result == paNoError)
2432 PA_DEBUG(("PinGetBuffer: SubType_kNotification\n"));
2433 pPin->pinKsSubType = SubType_kNotification;
2438 result = PinGetBufferWithoutNotification(pPin, pBuffer, pRequestedBufSize, pbCallMemBarrier);
2439 if (result == paNoError)
2441 PA_DEBUG(("PinGetBuffer: SubType_kPolled\n"));
2442 pPin->pinKsSubType = SubType_kPolled;
2446 /* Check if requested size is on a 128 byte boundary */
2447 if (((*pRequestedBufSize) % 128UL) == 0)
2449 PA_DEBUG(("Buffer size on 128 byte boundary, still fails :(\n"));
2450 /* Ok, can't do much more */
2455 /* Compute LCM so we know which sizes are on a 128 byte boundary */
2456 const unsigned gcd = PaWinWDMGCD(128UL, pPin->ksDataFormatWfx->WaveFormatEx.nBlockAlign);
2457 const unsigned lcm = (128UL * pPin->ksDataFormatWfx->WaveFormatEx.nBlockAlign) / gcd;
2458 DWORD dwOldSize = *pRequestedBufSize;
2460 /* Align size to (next larger) LCM byte boundary, and then we try again. Note that LCM is not necessarily a
2462 *pRequestedBufSize = ((*pRequestedBufSize + lcm - 1) / lcm) * lcm;
2464 PA_DEBUG(("Adjusting buffer size from %u to %u bytes (128 byte boundary, LCM=%u)\n", dwOldSize, *pRequestedBufSize, lcm));
2473 static PaError PinRegisterPositionRegister(PaWinWdmPin* pPin)
2475 PaError result = paNoError;
2476 KSRTAUDIO_HWREGISTER_PROPERTY propIn;
2477 KSRTAUDIO_HWREGISTER propOut;
2481 propIn.BaseAddress = NULL;
2482 propIn.Property.Set = KSPROPSETID_RtAudio;
2483 propIn.Property.Id = KSPROPERTY_RTAUDIO_POSITIONREGISTER;
2484 propIn.Property.Flags = KSPROPERTY_TYPE_SET;
2486 result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
2488 sizeof(KSRTAUDIO_HWREGISTER_PROPERTY),
2490 sizeof(KSRTAUDIO_HWREGISTER),
2493 if (result == paNoError)
2495 pPin->positionRegister = (ULONG*)propOut.Register;
2499 PA_DEBUG(("Failed to register position register\n"));
2507 static PaError PinRegisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle)
2509 PaError result = paNoError;
2510 KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY prop;
2514 prop.NotificationEvent = handle;
2515 prop.Property.Set = KSPROPSETID_RtAudio;
2516 prop.Property.Id = KSPROPERTY_RTAUDIO_REGISTER_NOTIFICATION_EVENT;
2517 prop.Property.Flags = KSPROPERTY_TYPE_SET;
2519 result = WdmSyncIoctl(pPin->handle,
2522 sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
2524 sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
2527 if (result != paNoError) {
2528 PA_DEBUG(("Failed to register notification handle 0x%08X\n", handle));
2536 static PaError PinUnregisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle)
2538 PaError result = paNoError;
2539 KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY prop;
2545 prop.NotificationEvent = handle;
2546 prop.Property.Set = KSPROPSETID_RtAudio;
2547 prop.Property.Id = KSPROPERTY_RTAUDIO_UNREGISTER_NOTIFICATION_EVENT;
2548 prop.Property.Flags = KSPROPERTY_TYPE_SET;
2550 result = WdmSyncIoctl(pPin->handle,
2553 sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
2555 sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
2558 if (result != paNoError) {
2559 PA_DEBUG(("Failed to unregister notification handle 0x%08X\n", handle));
2567 static PaError PinGetHwLatency(PaWinWdmPin* pPin, ULONG* pFifoSize, ULONG* pChipsetDelay, ULONG* pCodecDelay)
2569 PaError result = paNoError;
2571 KSRTAUDIO_HWLATENCY propOut;
2575 propIn.Set = KSPROPSETID_RtAudio;
2576 propIn.Id = KSPROPERTY_RTAUDIO_HWLATENCY;
2577 propIn.Flags = KSPROPERTY_TYPE_GET;
2579 result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
2583 sizeof(KSRTAUDIO_HWLATENCY),
2586 if (result == paNoError)
2588 *pFifoSize = propOut.FifoSize;
2589 *pChipsetDelay = propOut.ChipsetDelay;
2590 *pCodecDelay = propOut.CodecDelay;
2594 PA_DEBUG(("Failed to retrieve hardware FIFO size!\n"));
2602 /* This one is used for WaveRT */
2603 static PaError PinGetAudioPositionMemoryMapped(PaWinWdmPin* pPin, ULONG* pPosition)
2605 *pPosition = (*pPin->positionRegister);
2609 /* This one also, but in case the driver hasn't implemented memory mapped access to the position register */
2610 static PaError PinGetAudioPositionViaIOCTLRead(PaWinWdmPin* pPin, ULONG* pPosition)
2612 PaError result = paNoError;
2614 KSAUDIO_POSITION propOut;
2618 propIn.Set = KSPROPSETID_Audio;
2619 propIn.Id = KSPROPERTY_AUDIO_POSITION;
2620 propIn.Flags = KSPROPERTY_TYPE_GET;
2622 result = WdmSyncIoctl(pPin->handle,
2624 &propIn, sizeof(KSPROPERTY),
2625 &propOut, sizeof(KSAUDIO_POSITION),
2628 if (result == paNoError)
2630 *pPosition = (ULONG)(propOut.PlayOffset);
2634 PA_DEBUG(("Failed to get audio play position!\n"));
2643 /* This one also, but in case the driver hasn't implemented memory mapped access to the position register */
2644 static PaError PinGetAudioPositionViaIOCTLWrite(PaWinWdmPin* pPin, ULONG* pPosition)
2646 PaError result = paNoError;
2648 KSAUDIO_POSITION propOut;
2652 propIn.Set = KSPROPSETID_Audio;
2653 propIn.Id = KSPROPERTY_AUDIO_POSITION;
2654 propIn.Flags = KSPROPERTY_TYPE_GET;
2656 result = WdmSyncIoctl(pPin->handle,
2658 &propIn, sizeof(KSPROPERTY),
2659 &propOut, sizeof(KSAUDIO_POSITION),
2662 if (result == paNoError)
2664 *pPosition = (ULONG)(propOut.WriteOffset);
2668 PA_DEBUG(("Failed to get audio write position!\n"));
2677 /***********************************************************************************************/
2680 * Create a new filter object.
2682 static PaWinWdmFilter* FilterNew( PaWDMKSType type, DWORD devNode, const wchar_t* filterName, const wchar_t* friendlyName, PaError* error )
2684 PaWinWdmFilter* filter = 0;
2687 /* Allocate the new filter object */
2688 filter = (PaWinWdmFilter*)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter) );
2691 result = paInsufficientMemory;
2695 PA_DEBUG(("FilterNew: Creating filter '%S'\n", friendlyName));
2698 filter->devInfo.streamingType = type;
2700 /* Store device node */
2701 filter->deviceNode = devNode;
2703 /* Zero the filter object - done by AllocateMemory */
2704 /* memset( (void*)filter, 0, sizeof(PaWinWdmFilter) ); */
2706 /* Copy the filter name */
2707 wcsncpy(filter->devInfo.filterPath, filterName, MAX_PATH);
2709 /* Copy the friendly name */
2710 wcsncpy(filter->friendlyName, friendlyName, MAX_PATH);
2712 PA_DEBUG(("FilterNew: Opening filter...\n", friendlyName));
2714 /* Open the filter handle */
2715 result = FilterUse(filter);
2716 if( result != paNoError )
2722 result = WdmGetPinPropertySimple
2727 KSPROPERTY_PIN_CTYPES,
2729 sizeof(filter->pinCount),
2732 if( result != paNoError)
2737 /* Get connections & nodes for filter */
2738 result = WdmGetPropertyMulti(
2740 &KSPROPSETID_Topology,
2741 KSPROPERTY_TOPOLOGY_CONNECTIONS,
2742 &filter->connections);
2744 if( result != paNoError)
2749 result = WdmGetPropertyMulti(
2751 &KSPROPSETID_Topology,
2752 KSPROPERTY_TOPOLOGY_NODES,
2755 if( result != paNoError)
2760 /* For debugging purposes */
2761 DumpConnectionsAndNodes(filter);
2763 /* Get product GUID (it might not be supported) */
2765 KSCOMPONENTID compId;
2766 if (WdmGetPropertySimple(filter->handle, &KSPROPSETID_General, KSPROPERTY_GENERAL_COMPONENTID, &compId, sizeof(KSCOMPONENTID)) == paNoError)
2768 filter->devInfo.deviceProductGuid = compId.Product;
2772 /* This section is not executed for topology filters */
2773 if (type != Type_kNotUsed)
2775 /* Initialize the pins */
2776 result = FilterInitializePins(filter);
2778 if( result != paNoError)
2784 /* Close the filter handle for now
2785 * It will be opened later when needed */
2786 FilterRelease(filter);
2792 PA_DEBUG(("FilterNew: Error %d\n", result));
2803 * Add reference to filter
2805 static void FilterAddRef( PaWinWdmFilter* filter )
2809 filter->filterRefCount++;
2815 * Initialize the pins of the filter. This is separated from FilterNew because this might fail if there is another
2816 * process using the pin(s).
2818 PaError FilterInitializePins( PaWinWdmFilter* filter )
2820 PaError result = paNoError;
2823 if (filter->devInfo.streamingType == Type_kNotUsed)
2826 if (filter->pins != NULL)
2829 /* Allocate pointer array to hold the pins */
2830 filter->pins = (PaWinWdmPin**)PaUtil_AllocateMemory( sizeof(PaWinWdmPin*) * filter->pinCount );
2833 result = paInsufficientMemory;
2837 /* Create all the pins we can */
2838 for(pinId = 0; pinId < filter->pinCount; pinId++)
2840 /* Create the pin with this Id */
2841 PaWinWdmPin* newPin;
2842 newPin = PinNew(filter, pinId, &result);
2843 if( result == paInsufficientMemory )
2845 if( newPin != NULL )
2847 filter->pins[pinId] = newPin;
2848 ++filter->validPinCount;
2852 if (filter->validPinCount == 0)
2854 result = paDeviceUnavailable;
2864 for (pinId = 0; pinId < filter->pinCount; ++pinId)
2866 if (filter->pins[pinId])
2868 PinFree(filter->pins[pinId]);
2869 filter->pins[pinId] = 0;
2872 PaUtil_FreeMemory( filter->pins );
2881 * Free a previously created filter
2883 static void FilterFree(PaWinWdmFilter* filter)
2888 if (--filter->filterRefCount > 0)
2890 /* Ok, a stream has a ref count to this filter */
2894 if ( filter->topologyFilter )
2896 FilterFree(filter->topologyFilter);
2897 filter->topologyFilter = 0;
2902 for( pinId = 0; pinId < filter->pinCount; pinId++ )
2903 PinFree(filter->pins[pinId]);
2904 PaUtil_FreeMemory( filter->pins );
2907 if( filter->connections )
2909 PaUtil_FreeMemory(filter->connections);
2910 filter->connections = 0;
2914 PaUtil_FreeMemory(filter->nodes);
2917 if( filter->handle )
2918 CloseHandle( filter->handle );
2919 PaUtil_FreeMemory( filter );
2925 * Reopen the filter handle if necessary so it can be used
2927 static PaError FilterUse(PaWinWdmFilter* filter)
2932 if( filter->handle == NULL )
2934 /* Open the filter */
2935 filter->handle = CreateFileW(
2936 filter->devInfo.filterPath,
2937 GENERIC_READ | GENERIC_WRITE,
2941 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
2944 if( filter->handle == NULL )
2946 return paDeviceUnavailable;
2949 filter->usageCount++;
2955 * Release the filter handle if nobody is using it
2957 static void FilterRelease(PaWinWdmFilter* filter)
2960 assert( filter->usageCount > 0 );
2963 /* Check first topology filter, if used */
2964 if (filter->topologyFilter != NULL && filter->topologyFilter->handle != NULL)
2966 FilterRelease(filter->topologyFilter);
2969 filter->usageCount--;
2970 if( filter->usageCount == 0 )
2972 if( filter->handle != NULL )
2974 CloseHandle( filter->handle );
2975 filter->handle = NULL;
2982 * Create a render or playback pin using the supplied format
2984 static PaWinWdmPin* FilterCreatePin(PaWinWdmFilter* filter,
2986 const WAVEFORMATEX* wfex,
2989 PaError result = paNoError;
2990 PaWinWdmPin* pin = NULL;
2992 assert( pinId < filter->pinCount );
2993 pin = filter->pins[pinId];
2995 result = PinSetFormat(pin,wfex);
2996 if( result == paNoError )
2998 result = PinInstantiate(pin);
3001 return result == paNoError ? pin : 0;
3004 static const wchar_t kUsbPrefix[] = L"\\\\?\\USB";
3006 static BOOL IsUSBDevice(const wchar_t* devicePath)
3008 /* Alex Lessard pointed out that different devices might present the device path with
3009 lower case letters. */
3010 return (_wcsnicmp(devicePath, kUsbPrefix, sizeof(kUsbPrefix)/sizeof(kUsbPrefix[0]) ) == 0);
3013 /* This should make it more language tolerant, I hope... */
3014 static const wchar_t kUsbNamePrefix[] = L"USB Audio";
3016 static BOOL IsNameUSBAudioDevice(const wchar_t* friendlyName)
3018 return (_wcsnicmp(friendlyName, kUsbNamePrefix, sizeof(kUsbNamePrefix)/sizeof(kUsbNamePrefix[0])) == 0);
3021 typedef enum _tag_EAlias
3023 Alias_kRender = (1<<0),
3024 Alias_kCapture = (1<<1),
3025 Alias_kRealtime = (1<<2),
3028 /* Trim whitespace from string */
3029 static void TrimString(wchar_t* str, size_t length)
3034 /* Find start of string */
3035 while (iswspace(*s)) ++s;
3036 e=s+min(length,wcslen(s))-1;
3038 /* Find end of string */
3039 while(e>s && iswspace(*e)) --e;
3043 memmove(str, s, length * sizeof(wchar_t));
3048 * Build the list of available filters
3049 * Use the SetupDi API to enumerate all devices in the KSCATEGORY_AUDIO which
3050 * have a KSCATEGORY_RENDER or KSCATEGORY_CAPTURE alias. For each of these
3051 * devices initialise a PaWinWdmFilter structure by calling our NewFilter()
3052 * function. We enumerate devices twice, once to count how many there are,
3053 * and once to initialize the PaWinWdmFilter structures.
3055 * Vista and later: Also check KSCATEGORY_REALTIME for WaveRT devices.
3057 //PaError BuildFilterList( PaWinWdmHostApiRepresentation* wdmHostApi, int* noOfPaDevices )
3058 PaWinWdmFilter** BuildFilterList( int* pFilterCount, int* pNoOfPaDevices, PaError* pResult )
3060 PaWinWdmFilter** ppFilters = NULL;
3061 HDEVINFO handle = NULL;
3065 SP_DEVICE_INTERFACE_DATA interfaceData;
3066 SP_DEVICE_INTERFACE_DATA aliasData;
3067 SP_DEVINFO_DATA devInfoData;
3069 const int sizeInterface = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR));
3070 unsigned char interfaceDetailsArray[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR))];
3071 SP_DEVICE_INTERFACE_DETAIL_DATA_W* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)interfaceDetailsArray;
3072 const GUID* category = (const GUID*)&KSCATEGORY_AUDIO;
3073 const GUID* alias_render = (const GUID*)&KSCATEGORY_RENDER;
3074 const GUID* alias_capture = (const GUID*)&KSCATEGORY_CAPTURE;
3075 const GUID* category_realtime = (const GUID*)&KSCATEGORY_REALTIME;
3077 PaWDMKSType streamingType;
3078 int filterCount = 0;
3079 int noOfPaDevices = 0;
3083 assert(pFilterCount != NULL);
3084 assert(pNoOfPaDevices != NULL);
3085 assert(pResult != NULL);
3087 devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
3089 *pNoOfPaDevices = 0;
3091 /* Open a handle to search for devices (filters) */
3092 handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
3093 if( handle == INVALID_HANDLE_VALUE )
3095 *pResult = paUnanticipatedHostError;
3098 PA_DEBUG(("Setup called\n"));
3100 /* First let's count the number of devices so we can allocate a list */
3102 for( device = 0;;device++ )
3104 interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3105 interfaceData.Reserved = 0;
3106 aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3107 aliasData.Reserved = 0;
3108 noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
3109 PA_DEBUG(("Enum called\n"));
3111 break; /* No more devices */
3113 /* Check this one has the render or capture alias */
3115 noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
3116 PA_DEBUG(("noError = %d\n",noError));
3119 if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
3121 PA_DEBUG(("Device %d has render alias\n",device));
3122 aliasFlags |= Alias_kRender; /* Has render alias */
3126 PA_DEBUG(("Device %d has no render alias\n",device));
3129 noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
3132 if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
3134 PA_DEBUG(("Device %d has capture alias\n",device));
3135 aliasFlags |= Alias_kCapture; /* Has capture alias */
3139 PA_DEBUG(("Device %d has no capture alias\n",device));
3143 invalidDevices++; /* This was not a valid capture or render audio device */
3145 /* Remember how many there are */
3146 filterCount = device-invalidDevices;
3148 PA_DEBUG(("Interfaces found: %d\n",device-invalidDevices));
3150 /* Now allocate the list of pointers to devices */
3151 ppFilters = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * filterCount);
3152 if( ppFilters == 0 )
3155 SetupDiDestroyDeviceInfoList(handle);
3156 *pResult = paInsufficientMemory;
3160 /* Now create filter objects for each interface found */
3162 for( device = 0;;device++ )
3164 interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3165 interfaceData.Reserved = 0;
3166 aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3167 aliasData.Reserved = 0;
3168 devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
3169 devInfoData.Reserved = 0;
3170 streamingType = Type_kWaveCyclic;
3172 noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
3174 break; /* No more devices */
3176 /* Check this one has the render or capture alias */
3178 noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
3181 if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
3183 PA_DEBUG(("Device %d has render alias\n",device));
3184 aliasFlags |= Alias_kRender; /* Has render alias */
3187 noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
3190 if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
3192 PA_DEBUG(("Device %d has capture alias\n",device));
3193 aliasFlags |= Alias_kCapture; /* Has capture alias */
3198 continue; /* This was not a valid capture or render audio device */
3202 /* Check if filter is WaveRT, if not it is a WaveCyclic */
3203 noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,category_realtime,&aliasData);
3206 PA_DEBUG(("Device %d has realtime alias\n",device));
3207 aliasFlags |= Alias_kRealtime;
3208 streamingType = Type_kWaveRT;
3212 noError = SetupDiGetDeviceInterfaceDetailW(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
3216 WCHAR friendlyName[MAX_PATH] = {0};
3217 DWORD sizeFriendlyName;
3218 PaWinWdmFilter* newFilter = 0;
3220 PaError result = paNoError;
3221 /* Try to get the "friendly name" for this interface */
3222 sizeFriendlyName = sizeof(friendlyName);
3224 if (IsEarlierThanVista() && IsUSBDevice(devInterfaceDetails->DevicePath))
3226 /* XP and USB audio device needs to look elsewhere, otherwise it'll only be a "USB Audio Device". Not
3228 if (!SetupDiGetDeviceRegistryPropertyW(handle,
3230 SPDRP_LOCATION_INFORMATION,
3232 (BYTE*)friendlyName,
3233 sizeof(friendlyName),
3236 friendlyName[0] = 0;
3240 if (friendlyName[0] == 0 || IsNameUSBAudioDevice(friendlyName))
3242 /* Fix contributed by Ben Allison
3243 * Removed KEY_SET_VALUE from flags on following call
3244 * as its causes failure when running without admin rights
3245 * and it was not required */
3246 HKEY hkey=SetupDiOpenDeviceInterfaceRegKey(handle,&interfaceData,0,KEY_QUERY_VALUE);
3247 if(hkey!=INVALID_HANDLE_VALUE)
3249 noError = RegQueryValueExW(hkey,L"FriendlyName",0,&type,(BYTE*)friendlyName,&sizeFriendlyName);
3250 if( noError == ERROR_SUCCESS )
3252 PA_DEBUG(("Interface %d, Name: %s\n",device,friendlyName));
3257 friendlyName[0] = 0;
3262 TrimString(friendlyName, sizeFriendlyName);
3264 newFilter = FilterNew(streamingType,
3265 devInfoData.DevInst,
3266 devInterfaceDetails->DevicePath,
3270 if( result == paNoError )
3273 unsigned filterIOs = 0;
3275 /* Increment number of "devices" */
3276 for (pin = 0; pin < newFilter->pinCount; ++pin)
3278 PaWinWdmPin* pPin = newFilter->pins[pin];
3282 filterIOs += max(1, pPin->inputCount);
3285 noOfPaDevices += filterIOs;
3287 PA_DEBUG(("Filter (%s) created with %d valid pins (total I/Os: %u)\n", ((newFilter->devInfo.streamingType==Type_kWaveRT)?"WaveRT":"WaveCyclic"), newFilter->validPinCount, filterIOs));
3289 assert(slot < filterCount);
3291 ppFilters[slot] = newFilter;
3297 PA_DEBUG(("Filter NOT created\n"));
3298 /* As there are now less filters than we initially thought
3299 * we must reduce the count by one */
3307 SetupDiDestroyDeviceInfoList(handle);
3309 *pFilterCount = filterCount;
3310 *pNoOfPaDevices = noOfPaDevices;
3315 typedef struct PaNameHashIndex
3320 struct PaNameHashIndex *next;
3323 typedef struct PaNameHashObject
3325 PaNameHashIndex* list;
3326 PaUtilAllocationGroup* allocGroup;
3329 static ULONG GetNameHash(const wchar_t* str, const BOOL input)
3331 /* This is to make sure that a name that exists as both input & output won't get the same hash value */
3332 const ULONG fnv_prime = (input ? 0x811C9DD7 : 0x811FEB0B);
3334 for(; *str != 0; str++)
3343 static PaError CreateHashEntry(PaNameHashObject* obj, const wchar_t* name, const BOOL input)
3345 ULONG hash = GetNameHash(name, input);
3346 PaNameHashIndex * pLast = NULL;
3347 PaNameHashIndex * p = obj->list;
3350 if (p->hash == hash)
3359 p = (PaNameHashIndex*)PaUtil_GroupAllocateMemory(obj->allocGroup, sizeof(PaNameHashIndex));
3362 return paInsufficientMemory;
3368 assert(pLast->next == 0);
3383 static PaError InitNameHashObject(PaNameHashObject* obj, PaWinWdmFilter* pFilter)
3387 obj->allocGroup = PaUtil_CreateAllocationGroup();
3388 if (obj->allocGroup == NULL)
3390 return paInsufficientMemory;
3393 for (i = 0; i < pFilter->pinCount; ++i)
3396 PaWinWdmPin* pin = pFilter->pins[i];
3401 for (m = 0; m < max(1, pin->inputCount); ++m)
3403 const BOOL isInput = (pin->dataFlow == KSPIN_DATAFLOW_OUT);
3404 const wchar_t* name = (pin->inputs == NULL) ? pin->friendlyName : pin->inputs[m]->friendlyName;
3406 PaError result = CreateHashEntry(obj, name, isInput);
3408 if (result != paNoError)
3417 static void DeinitNameHashObject(PaNameHashObject* obj)
3420 PaUtil_FreeAllAllocations(obj->allocGroup);
3421 PaUtil_DestroyAllocationGroup(obj->allocGroup);
3422 memset(obj, 0, sizeof(PaNameHashObject));
3425 static unsigned GetNameIndex(PaNameHashObject* obj, const wchar_t* name, const BOOL input)
3427 ULONG hash = GetNameHash(name, input);
3428 PaNameHashIndex* p = obj->list;
3431 if (p->hash == hash)
3435 return (++p->index);
3445 // Should never get here!!
3450 static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, void **scanResults, int *newDeviceCount )
3452 PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
3453 PaError result = paNoError;
3454 PaWinWdmFilter** ppFilters = 0;
3455 PaWinWDMScanDeviceInfosResults *outArgument = 0;
3456 int filterCount = 0;
3457 int totalDeviceCount = 0;
3459 DWORD defaultInDevPathSize = 0;
3460 DWORD defaultOutDevPathSize = 0;
3461 wchar_t* defaultInDevPath = 0;
3462 wchar_t* defaultOutDevPath = 0;
3464 ppFilters = BuildFilterList( &filterCount, &totalDeviceCount, &result );
3465 if( result != paNoError )
3470 // Get hold of default device paths for capture & playback
3471 if( waveInMessage(0, DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&defaultInDevPathSize, 0 ) == MMSYSERR_NOERROR )
3473 defaultInDevPath = (wchar_t *)PaUtil_AllocateMemory((defaultInDevPathSize + 1) * sizeof(wchar_t));
3474 waveInMessage(0, DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)defaultInDevPath, defaultInDevPathSize);
3476 if( waveOutMessage(0, DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&defaultOutDevPathSize, 0 ) == MMSYSERR_NOERROR )
3478 defaultOutDevPath = (wchar_t *)PaUtil_AllocateMemory((defaultOutDevPathSize + 1) * sizeof(wchar_t));
3479 waveOutMessage(0, DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)defaultOutDevPath, defaultOutDevPathSize);
3482 if( totalDeviceCount > 0 )
3484 PaWinWdmDeviceInfo *deviceInfoArray = 0;
3487 unsigned devIsDefaultIn = 0, devIsDefaultOut = 0;
3489 /* Allocate the out param for all the info we need */
3490 outArgument = (PaWinWDMScanDeviceInfosResults *) PaUtil_GroupAllocateMemory(
3491 wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults) );
3494 result = paInsufficientMemory;
3498 outArgument->defaultInputDevice = paNoDevice;
3499 outArgument->defaultOutputDevice = paNoDevice;
3501 outArgument->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
3502 wdmHostApi->allocations, sizeof(PaDeviceInfo*) * totalDeviceCount );
3503 if( !outArgument->deviceInfos )
3505 result = paInsufficientMemory;
3509 /* allocate all device info structs in a contiguous block */
3510 deviceInfoArray = (PaWinWdmDeviceInfo*)PaUtil_GroupAllocateMemory(
3511 wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo) * totalDeviceCount );
3512 if( !deviceInfoArray )
3514 result = paInsufficientMemory;
3518 /* Make sure all items in array */
3519 for( i = 0 ; i < totalDeviceCount; ++i )
3521 PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
3522 deviceInfo->structVersion = 2;
3523 deviceInfo->hostApi = hostApiIndex;
3524 deviceInfo->name = 0;
3525 outArgument->deviceInfos[ i ] = deviceInfo;
3529 for (idxFilter = 0; idxFilter < filterCount; ++idxFilter)
3531 PaNameHashObject nameHash = {0};
3532 PaWinWdmFilter* pFilter = ppFilters[idxFilter];
3533 if( pFilter == NULL )
3536 if (InitNameHashObject(&nameHash, pFilter) != paNoError)
3538 DeinitNameHashObject(&nameHash);
3542 devIsDefaultIn = (defaultInDevPath && (_wcsicmp(pFilter->devInfo.filterPath, defaultInDevPath) == 0));
3543 devIsDefaultOut = (defaultOutDevPath && (_wcsicmp(pFilter->devInfo.filterPath, defaultOutDevPath) == 0));
3545 for (i = 0; i < pFilter->pinCount; ++i)
3548 ULONG nameIndex = 0;
3549 ULONG nameIndexHash = 0;
3550 PaWinWdmPin* pin = pFilter->pins[i];
3555 for (m = 0; m < max(1, pin->inputCount); ++m)
3557 PaWinWdmDeviceInfo *wdmDeviceInfo = (PaWinWdmDeviceInfo *)outArgument->deviceInfos[idxDevice];
3558 PaDeviceInfo *deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo;
3559 wchar_t localCompositeName[MAX_PATH];
3560 unsigned nameIndex = 0;
3561 const BOOL isInput = (pin->dataFlow == KSPIN_DATAFLOW_OUT);
3563 wdmDeviceInfo->filter = pFilter;
3565 deviceInfo->structVersion = 2;
3566 deviceInfo->hostApi = hostApiIndex;
3567 deviceInfo->name = wdmDeviceInfo->compositeName;
3568 /* deviceInfo->hostApiSpecificDeviceInfo = &pFilter->devInfo; */
3570 wdmDeviceInfo->pin = pin->pinId;
3572 /* Get the name of the "device" */
3573 if (pin->inputs == NULL)
3575 wcsncpy(localCompositeName, pin->friendlyName, MAX_PATH);
3576 wdmDeviceInfo->muxPosition = -1;
3577 wdmDeviceInfo->endpointPinId = pin->endpointPinId;
3581 PaWinWdmMuxedInput* input = pin->inputs[m];
3582 wcsncpy(localCompositeName, input->friendlyName, MAX_PATH);
3583 wdmDeviceInfo->muxPosition = (int)m;
3584 wdmDeviceInfo->endpointPinId = input->endpointPinId;
3588 /* Get base length */
3589 size_t n = wcslen(localCompositeName);
3591 /* Check if there are more entries with same name (which might very well be the case), if there
3592 are, the name will be postfixed with an index. */
3593 nameIndex = GetNameIndex(&nameHash, localCompositeName, isInput);
3596 /* This name has multiple instances, so we post fix with a number */
3597 n += _snwprintf(localCompositeName + n, MAX_PATH - n, L" %u", nameIndex);
3599 /* Postfix with filter name */
3600 _snwprintf(localCompositeName + n, MAX_PATH - n, L" (%s)", pFilter->friendlyName);
3603 /* Convert wide char string to utf-8 */
3604 WideCharToMultiByte(CP_UTF8, 0, localCompositeName, -1, wdmDeviceInfo->compositeName, MAX_PATH, NULL, NULL);
3606 /* NB! WDM/KS has no concept of a full-duplex device, each pin is either an input or an output */
3610 deviceInfo->maxInputChannels = pin->maxChannels;
3611 deviceInfo->maxOutputChannels = 0;
3613 /* RoBi NB: Due to the fact that input audio endpoints in Vista (& later OSs) can be the same device, but with
3614 different input mux settings, there might be a discrepancy between the default input device chosen, and
3615 that which will be used by Portaudio. Not much to do about that unfortunately.
3617 if ((defaultInDevPath == 0 || devIsDefaultIn) &&
3618 outArgument->defaultInputDevice == paNoDevice)
3620 outArgument->defaultInputDevice = idxDevice;
3626 deviceInfo->maxInputChannels = 0;
3627 deviceInfo->maxOutputChannels = pin->maxChannels;
3629 if ((defaultOutDevPath == 0 || devIsDefaultOut) &&
3630 outArgument->defaultOutputDevice == paNoDevice)
3632 outArgument->defaultOutputDevice = idxDevice;
3636 /* These low values are not very useful because
3637 * a) The lowest latency we end up with can depend on many factors such
3638 * as the device buffer sizes/granularities, sample rate, channels and format
3639 * b) We cannot know the device buffer sizes until we try to open/use it at
3640 * a particular setting
3641 * So: we give 512x48000Hz frames as the default low input latency
3643 switch (pFilter->devInfo.streamingType)
3645 case Type_kWaveCyclic:
3646 if (IsEarlierThanVista())
3648 /* XP doesn't tolerate low latency, unless the Process Priority Class is set to REALTIME_PRIORITY_CLASS
3649 through SetPriorityClass, then 10 ms is quite feasible. However, one should then bear in mind that ALL of
3650 the process is running in REALTIME_PRIORITY_CLASS, which might not be appropriate for an application with
3651 a GUI . In this case it is advisable to separate the audio engine in another process and use IPC to communicate
3653 deviceInfo->defaultLowInputLatency = 0.02;
3654 deviceInfo->defaultLowOutputLatency = 0.02;
3658 /* This is a conservative estimate. Most WaveCyclic drivers will limit the available latency, but f.i. my Edirol
3659 PCR-A30 can reach 3 ms latency easily... */
3660 deviceInfo->defaultLowInputLatency = 0.01;
3661 deviceInfo->defaultLowOutputLatency = 0.01;
3663 deviceInfo->defaultHighInputLatency = (4096.0/48000.0);
3664 deviceInfo->defaultHighOutputLatency = (4096.0/48000.0);
3665 deviceInfo->defaultSampleRate = (double)(pin->defaultSampleRate);
3668 /* This is also a conservative estimate, based on WaveRT polled mode. In polled mode, the latency will be dictated
3669 by the buffer size given by the driver. */
3670 deviceInfo->defaultLowInputLatency = 0.01;
3671 deviceInfo->defaultLowOutputLatency = 0.01;
3672 deviceInfo->defaultHighInputLatency = 0.04;
3673 deviceInfo->defaultHighOutputLatency = 0.04;
3674 deviceInfo->defaultSampleRate = (double)(pin->defaultSampleRate);
3681 /* Add reference to filter */
3682 FilterAddRef(wdmDeviceInfo->filter);
3684 assert(idxDevice < totalDeviceCount);
3689 /* If no one has add ref'd the filter, drop it */
3690 if (pFilter->filterRefCount == 0)
3692 FilterFree(pFilter);
3695 /* Deinitialize name hash object */
3696 DeinitNameHashObject(&nameHash);
3700 *scanResults = outArgument;
3701 *newDeviceCount = idxDevice;
3705 result = DisposeDeviceInfos(hostApi, outArgument, totalDeviceCount);
3710 static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *scanResults, int deviceCount )
3712 PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
3714 hostApi->info.deviceCount = 0;
3715 hostApi->info.defaultInputDevice = paNoDevice;
3716 hostApi->info.defaultOutputDevice = paNoDevice;
3718 /* Free any old memory which might be in the device info */
3719 if( hostApi->deviceInfos )
3721 PaWinWDMScanDeviceInfosResults* localScanResults = (PaWinWDMScanDeviceInfosResults*)PaUtil_GroupAllocateMemory(
3722 wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults));
3723 localScanResults->deviceInfos = hostApi->deviceInfos;
3725 DisposeDeviceInfos(hostApi, &localScanResults, hostApi->info.deviceCount);
3727 hostApi->deviceInfos = NULL;
3730 if( scanResults != NULL )
3732 PaWinWDMScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinWDMScanDeviceInfosResults * ) scanResults;
3734 if( deviceCount > 0 )
3736 /* use the array allocated in ScanDeviceInfos() as our deviceInfos */
3737 hostApi->deviceInfos = scanDeviceInfosResults->deviceInfos;
3739 hostApi->info.defaultInputDevice = scanDeviceInfosResults->defaultInputDevice;
3740 hostApi->info.defaultOutputDevice = scanDeviceInfosResults->defaultOutputDevice;
3742 hostApi->info.deviceCount = deviceCount;
3745 PaUtil_GroupFreeMemory( wdmHostApi->allocations, scanDeviceInfosResults );
3752 static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount )
3754 PaWinWdmHostApiRepresentation *winDsHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
3756 if( scanResults != NULL )
3758 PaWinWDMScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinWDMScanDeviceInfosResults * ) scanResults;
3760 if( scanDeviceInfosResults->deviceInfos )
3763 for (i = 0; i < deviceCount; ++i)
3765 PaWinWdmDeviceInfo* pDevice = (PaWinWdmDeviceInfo*)scanDeviceInfosResults->deviceInfos[i];
3766 if (pDevice->filter != 0)
3768 FilterFree(pDevice->filter);
3772 PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); /* all device info structs are allocated in a block so we can destroy them here */
3773 PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos );
3776 PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults );
3783 PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
3785 PaError result = paNoError;
3786 int deviceCount = 0;
3787 void *scanResults = 0;
3788 PaWinWdmHostApiRepresentation *wdmHostApi = NULL;
3792 #ifdef PA_WDMKS_SET_TREF
3793 tRef = PaUtil_GetTime();
3797 Attempt to load the KSUSER.DLL without which we cannot create pins
3798 We will unload this on termination
3800 if(DllKsUser == NULL)
3802 DllKsUser = LoadLibrary(TEXT("ksuser.dll"));
3803 if(DllKsUser == NULL)
3806 FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin");
3807 if(FunctionKsCreatePin == NULL)
3810 /* Attempt to load AVRT.DLL, if we can't, then we'll just use time critical prio instead... */
3811 if(paWinWDMKSAvRtEntryPoints.hInstance == NULL)
3813 paWinWDMKSAvRtEntryPoints.hInstance = LoadLibrary(TEXT("avrt.dll"));
3814 if (paWinWDMKSAvRtEntryPoints.hInstance != NULL)
3816 paWinWDMKSAvRtEntryPoints.AvSetMmThreadCharacteristics =
3817 (HANDLE(WINAPI*)(LPCSTR,LPDWORD))GetProcAddress(paWinWDMKSAvRtEntryPoints.hInstance,"AvSetMmThreadCharacteristicsA");
3818 paWinWDMKSAvRtEntryPoints.AvRevertMmThreadCharacteristics =
3819 (BOOL(WINAPI*)(HANDLE))GetProcAddress(paWinWDMKSAvRtEntryPoints.hInstance, "AvRevertMmThreadCharacteristics");
3820 paWinWDMKSAvRtEntryPoints.AvSetMmThreadPriority =
3821 (BOOL(WINAPI*)(HANDLE,PA_AVRT_PRIORITY))GetProcAddress(paWinWDMKSAvRtEntryPoints.hInstance, "AvSetMmThreadPriority");
3825 wdmHostApi = (PaWinWdmHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWdmHostApiRepresentation) );
3828 result = paInsufficientMemory;
3832 wdmHostApi->allocations = PaUtil_CreateAllocationGroup();
3833 if( !wdmHostApi->allocations )
3835 result = paInsufficientMemory;
3839 *hostApi = &wdmHostApi->inheritedHostApiRep;
3840 (*hostApi)->info.structVersion = 1;
3841 (*hostApi)->info.type = paWDMKS;
3842 (*hostApi)->info.name = "Windows WDM-KS";
3844 /* these are all updated by CommitDeviceInfos() */
3845 (*hostApi)->info.deviceCount = 0;
3846 (*hostApi)->info.defaultInputDevice = paNoDevice;
3847 (*hostApi)->info.defaultOutputDevice = paNoDevice;
3848 (*hostApi)->deviceInfos = 0;
3850 result = ScanDeviceInfos(&wdmHostApi->inheritedHostApiRep, hostApiIndex, &scanResults, &deviceCount);
3851 if (result != paNoError)
3856 CommitDeviceInfos(&wdmHostApi->inheritedHostApiRep, hostApiIndex, scanResults, deviceCount);
3858 (*hostApi)->Terminate = Terminate;
3859 (*hostApi)->OpenStream = OpenStream;
3860 (*hostApi)->IsFormatSupported = IsFormatSupported;
3861 /* In preparation for hotplug
3862 (*hostApi)->ScanDeviceInfos = ScanDeviceInfos;
3863 (*hostApi)->CommitDeviceInfos = CommitDeviceInfos;
3864 (*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos;
3866 PaUtil_InitializeStreamInterface( &wdmHostApi->callbackStreamInterface, CloseStream, StartStream,
3867 StopStream, AbortStream, IsStreamStopped, IsStreamActive,
3868 GetStreamTime, GetStreamCpuLoad,
3869 PaUtil_DummyRead, PaUtil_DummyWrite,
3870 PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
3872 PaUtil_InitializeStreamInterface( &wdmHostApi->blockingStreamInterface, CloseStream, StartStream,
3873 StopStream, AbortStream, IsStreamStopped, IsStreamActive,
3874 GetStreamTime, PaUtil_DummyGetCpuLoad,
3875 ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
3881 Terminate( (PaUtilHostApiRepresentation*)wdmHostApi );
3888 static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
3890 PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
3893 /* Do not unload the libraries */
3894 if( DllKsUser != NULL )
3896 FreeLibrary( DllKsUser );
3900 if( paWinWDMKSAvRtEntryPoints.hInstance != NULL )
3902 FreeLibrary( paWinWDMKSAvRtEntryPoints.hInstance );
3903 paWinWDMKSAvRtEntryPoints.hInstance = NULL;
3908 PaWinWDMScanDeviceInfosResults* localScanResults = (PaWinWDMScanDeviceInfosResults*)PaUtil_GroupAllocateMemory(
3909 wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults));
3910 localScanResults->deviceInfos = hostApi->deviceInfos;
3911 DisposeDeviceInfos(hostApi, localScanResults, hostApi->info.deviceCount);
3913 if( wdmHostApi->allocations )
3915 PaUtil_FreeAllAllocations( wdmHostApi->allocations );
3916 PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
3918 PaUtil_FreeMemory( wdmHostApi );
3923 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
3924 const PaStreamParameters *inputParameters,
3925 const PaStreamParameters *outputParameters,
3928 int inputChannelCount, outputChannelCount;
3929 PaSampleFormat inputSampleFormat, outputSampleFormat;
3930 PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
3931 PaWinWdmFilter* pFilter;
3932 int result = paFormatIsSupported;
3933 WAVEFORMATEXTENSIBLE wfx;
3934 PaWinWaveFormatChannelMask channelMask;
3938 if( inputParameters )
3940 PaWinWdmDeviceInfo* pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
3943 unsigned long testFormat = 0;
3944 unsigned validBits = 0;
3946 inputChannelCount = inputParameters->channelCount;
3947 inputSampleFormat = inputParameters->sampleFormat;
3949 /* all standard sample formats are supported by the buffer adapter,
3950 this implementation doesn't support any custom sample formats */
3951 if( inputSampleFormat & paCustomFormat )
3953 PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "IsFormatSupported: Custom input format not supported");
3954 return paSampleFormatNotSupported;
3957 /* unless alternate device specification is supported, reject the use of
3958 paUseHostApiSpecificDeviceSpecification */
3960 if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
3962 PaWinWDM_SetLastErrorInfo(paInvalidDevice, "IsFormatSupported: paUseHostApiSpecificDeviceSpecification not supported");
3963 return paInvalidDevice;
3966 /* check that input device can support inputChannelCount */
3967 if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
3969 PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "IsFormatSupported: Invalid input channel count");
3970 return paInvalidChannelCount;
3973 /* validate inputStreamInfo */
3974 if( inputParameters->hostApiSpecificStreamInfo )
3976 PaWinWDM_SetLastErrorInfo(paIncompatibleHostApiSpecificStreamInfo, "Host API stream info not supported");
3977 return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
3980 pFilter = pDeviceInfo->filter;
3981 pin = pFilter->pins[pDeviceInfo->pin];
3983 /* Find out the testing format */
3984 for (fmt = paFloat32; fmt <= paUInt8; fmt <<= 1)
3986 if ((fmt & pin->formats) != 0)
3988 /* Found a matching format! */
3993 if (testFormat == 0)
3995 PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(capture) failed: no testformat found!");
3996 return paUnanticipatedHostError;
3999 /* Due to special considerations, WaveRT devices with paInt24 should be tested with paInt32 and
4000 valid bits = 24 (instead of 24 bit samples) */
4001 if (pFilter->devInfo.streamingType == Type_kWaveRT && testFormat == paInt24)
4003 PA_DEBUG(("IsFormatSupported (capture): WaveRT overriding testFormat paInt24 with paInt32 (24 valid bits)"));
4004 testFormat = paInt32;
4008 /* Check that the input format is supported */
4009 channelMask = PaWin_DefaultChannelMask(inputChannelCount);
4010 PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
4013 PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
4018 wfx.Samples.wValidBitsPerSample = validBits;
4021 result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
4022 if( result != paNoError )
4024 /* Try a WAVE_FORMAT_PCM instead */
4025 PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
4028 PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
4033 wfx.Samples.wValidBitsPerSample = validBits;
4036 result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
4037 if( result != paNoError )
4039 PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(capture) failed: sr=%u,ch=%u,bits=%u", wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample);
4046 inputChannelCount = 0;
4049 if( outputParameters )
4051 PaWinWdmDeviceInfo* pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device];
4054 unsigned long testFormat = 0;
4055 unsigned validBits = 0;
4057 outputChannelCount = outputParameters->channelCount;
4058 outputSampleFormat = outputParameters->sampleFormat;
4060 /* all standard sample formats are supported by the buffer adapter,
4061 this implementation doesn't support any custom sample formats */
4062 if( outputSampleFormat & paCustomFormat )
4064 PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "IsFormatSupported: Custom output format not supported");
4065 return paSampleFormatNotSupported;
4068 /* unless alternate device specification is supported, reject the use of
4069 paUseHostApiSpecificDeviceSpecification */
4071 if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
4073 PaWinWDM_SetLastErrorInfo(paInvalidDevice, "IsFormatSupported: paUseHostApiSpecificDeviceSpecification not supported");
4074 return paInvalidDevice;
4077 /* check that output device can support outputChannelCount */
4078 if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
4080 PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid output channel count");
4081 return paInvalidChannelCount;
4084 /* validate outputStreamInfo */
4085 if( outputParameters->hostApiSpecificStreamInfo )
4087 PaWinWDM_SetLastErrorInfo(paIncompatibleHostApiSpecificStreamInfo, "Host API stream info not supported");
4088 return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
4091 pFilter = pDeviceInfo->filter;
4092 pin = pFilter->pins[pDeviceInfo->pin];
4094 /* Find out the testing format */
4095 for (fmt = paFloat32; fmt <= paUInt8; fmt <<= 1)
4097 if ((fmt & pin->formats) != 0)
4099 /* Found a matching format! */
4104 if (testFormat == 0)
4106 PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(render) failed: no testformat found!");
4107 return paUnanticipatedHostError;
4110 /* Due to special considerations, WaveRT devices with paInt24 should be tested with paInt32 and
4111 valid bits = 24 (instead of 24 bit samples) */
4112 if (pFilter->devInfo.streamingType == Type_kWaveRT && testFormat == paInt24)
4114 PA_DEBUG(("IsFormatSupported (render): WaveRT overriding testFormat paInt24 with paInt32 (24 valid bits)"));
4115 testFormat = paInt32;
4119 /* Check that the output format is supported */
4120 channelMask = PaWin_DefaultChannelMask(outputChannelCount);
4121 PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
4124 PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
4130 wfx.Samples.wValidBitsPerSample = validBits;
4133 result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
4134 if( result != paNoError )
4136 /* Try a WAVE_FORMAT_PCM instead */
4137 PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
4140 PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
4145 wfx.Samples.wValidBitsPerSample = validBits;
4148 result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
4149 if( result != paNoError )
4151 PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(render) failed: %u,%u,%u", wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample);
4159 outputChannelCount = 0;
4165 - if a full duplex stream is requested, check that the combination
4166 of input and output parameters is supported if necessary
4168 - check that the device supports sampleRate
4170 Because the buffer adapter handles conversion between all standard
4171 sample formats, the following checks are only required if paCustomFormat
4172 is implemented, or under some other unusual conditions.
4174 - check that input device can support inputSampleFormat, or that
4175 we have the capability to convert from inputSampleFormat to
4178 - check that output device can support outputSampleFormat, or that
4179 we have the capability to convert from outputSampleFormat to
4182 if((inputChannelCount == 0)&&(outputChannelCount == 0))
4184 PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "No input or output channels defined");
4185 result = paSampleFormatNotSupported; /* Not right error */
4192 static void ResetStreamEvents(PaWinWdmStream* stream)
4195 ResetEvent(stream->eventAbort);
4196 ResetEvent(stream->eventStreamStart[StreamStart_kOk]);
4197 ResetEvent(stream->eventStreamStart[StreamStart_kFailed]);
4199 for (i=0; i<stream->capture.noOfPackets; ++i)
4201 if (stream->capture.events && stream->capture.events[i])
4203 ResetEvent(stream->capture.events[i]);
4207 for (i=0; i<stream->render.noOfPackets; ++i)
4209 if (stream->render.events && stream->render.events[i])
4211 ResetEvent(stream->render.events[i]);
4216 static void CloseStreamEvents(PaWinWdmStream* stream)
4219 PaWinWdmIOInfo* ios[2] = { &stream->capture, &stream->render };
4221 if (stream->eventAbort)
4223 CloseHandle(stream->eventAbort);
4224 stream->eventAbort = 0;
4226 if (stream->eventStreamStart[StreamStart_kOk])
4228 CloseHandle(stream->eventStreamStart[StreamStart_kOk]);
4230 if (stream->eventStreamStart[StreamStart_kFailed])
4232 CloseHandle(stream->eventStreamStart[StreamStart_kFailed]);
4235 for (i = 0; i < 2; ++i)
4238 /* Unregister notification handles for WaveRT */
4239 if (ios[i]->pPin && ios[i]->pPin->parentFilter->devInfo.streamingType == Type_kWaveRT &&
4240 ios[i]->pPin->pinKsSubType == SubType_kNotification &&
4241 ios[i]->events != 0)
4243 PinUnregisterNotificationHandle(ios[i]->pPin, ios[i]->events[0]);
4246 for (j=0; j < ios[i]->noOfPackets; ++j)
4248 if (ios[i]->events && ios[i]->events[j])
4250 CloseHandle(ios[i]->events[j]);
4251 ios[i]->events[j] = 0;
4257 static unsigned NextPowerOf2(unsigned val)
4260 val = (val >> 1) | val;
4261 val = (val >> 2) | val;
4262 val = (val >> 4) | val;
4263 val = (val >> 8) | val;
4264 val = (val >> 16) | val;
4268 static PaError ValidateSpecificStreamParameters(
4269 const PaStreamParameters *streamParameters,
4270 const PaWinWDMKSInfo *streamInfo,
4275 if( streamInfo->size != sizeof( PaWinWDMKSInfo )
4276 || streamInfo->version != 1 )
4278 PA_DEBUG(("Stream parameters: size or version not correct"));
4279 return paIncompatibleHostApiSpecificStreamInfo;
4282 if (!!(streamInfo->flags & ~(paWinWDMKSOverrideFramesize | paWinWDMKSUseGivenChannelMask)))
4284 PA_DEBUG(("Stream parameters: non supported flags set"));
4285 return paIncompatibleHostApiSpecificStreamInfo;
4288 if (streamInfo->noOfPackets != 0 &&
4289 (streamInfo->noOfPackets < 2 || streamInfo->noOfPackets > 8))
4291 PA_DEBUG(("Stream parameters: noOfPackets %u out of range [2,8]", streamInfo->noOfPackets));
4292 return paIncompatibleHostApiSpecificStreamInfo;
4295 if (streamInfo->flags & paWinWDMKSUseGivenChannelMask)
4299 PA_DEBUG(("Stream parameters: Channels mask setting not supported for input stream"));
4300 return paIncompatibleHostApiSpecificStreamInfo;
4303 if (streamInfo->channelMask & PAWIN_SPEAKER_RESERVED)
4305 PA_DEBUG(("Stream parameters: Given channels mask 0x%08X not supported", streamInfo->channelMask));
4306 return paIncompatibleHostApiSpecificStreamInfo;
4318 /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
4320 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
4322 const PaStreamParameters *inputParameters,
4323 const PaStreamParameters *outputParameters,
4325 unsigned long framesPerUserBuffer,
4326 PaStreamFlags streamFlags,
4327 PaStreamCallback *streamCallback,
4330 PaError result = paNoError;
4331 PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
4332 PaWinWdmStream *stream = 0;
4333 /* unsigned long framesPerHostBuffer; these may not be equivalent for all implementations */
4334 PaSampleFormat inputSampleFormat, outputSampleFormat;
4335 PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
4336 int userInputChannels,userOutputChannels;
4337 WAVEFORMATEXTENSIBLE wfx;
4340 PA_DEBUG(("OpenStream:sampleRate = %f\n",sampleRate));
4341 PA_DEBUG(("OpenStream:framesPerBuffer = %lu\n",framesPerUserBuffer));
4343 if( inputParameters )
4345 userInputChannels = inputParameters->channelCount;
4346 inputSampleFormat = inputParameters->sampleFormat;
4348 /* unless alternate device specification is supported, reject the use of
4349 paUseHostApiSpecificDeviceSpecification */
4351 if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
4353 PaWinWDM_SetLastErrorInfo(paInvalidDevice, "paUseHostApiSpecificDeviceSpecification(in) not supported");
4354 return paInvalidDevice;
4357 /* check that input device can support stream->userInputChannels */
4358 if( userInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
4360 PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid input channel count");
4361 return paInvalidChannelCount;
4364 /* validate inputStreamInfo */
4365 result = ValidateSpecificStreamParameters(inputParameters, inputParameters->hostApiSpecificStreamInfo, 1 );
4366 if(result != paNoError)
4368 PaWinWDM_SetLastErrorInfo(result, "Host API stream info not supported (in)");
4369 return result; /* this implementation doesn't use custom stream info */
4374 userInputChannels = 0;
4375 inputSampleFormat = hostInputSampleFormat = paInt16; /* Supress 'uninitialised var' warnings. */
4378 if( outputParameters )
4380 userOutputChannels = outputParameters->channelCount;
4381 outputSampleFormat = outputParameters->sampleFormat;
4383 /* unless alternate device specification is supported, reject the use of
4384 paUseHostApiSpecificDeviceSpecification */
4386 if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
4388 PaWinWDM_SetLastErrorInfo(paInvalidDevice, "paUseHostApiSpecificDeviceSpecification(out) not supported");
4389 return paInvalidDevice;
4392 /* check that output device can support stream->userInputChannels */
4393 if( userOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
4395 PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid output channel count");
4396 return paInvalidChannelCount;
4399 /* validate outputStreamInfo */
4400 result = ValidateSpecificStreamParameters( outputParameters, outputParameters->hostApiSpecificStreamInfo, 0 );
4401 if (result != paNoError)
4403 PaWinWDM_SetLastErrorInfo(result, "Host API stream info not supported (out)");
4404 return result; /* this implementation doesn't use custom stream info */
4409 userOutputChannels = 0;
4410 outputSampleFormat = hostOutputSampleFormat = paInt16; /* Supress 'uninitialized var' warnings. */
4413 /* validate platform specific flags */
4414 if( (streamFlags & paPlatformSpecificFlags) != 0 )
4416 PaWinWDM_SetLastErrorInfo(paInvalidFlag, "Invalid flag supplied");
4417 return paInvalidFlag; /* unexpected platform specific flag */
4420 stream = (PaWinWdmStream*)PaUtil_AllocateMemory( sizeof(PaWinWdmStream) );
4423 result = paInsufficientMemory;
4427 /* Create allocation group */
4428 stream->allocGroup = PaUtil_CreateAllocationGroup();
4429 if( !stream->allocGroup )
4431 result = paInsufficientMemory;
4435 /* Zero the stream object */
4436 /* memset((void*)stream,0,sizeof(PaWinWdmStream)); */
4438 if( streamCallback )
4440 PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
4441 &wdmHostApi->callbackStreamInterface, streamCallback, userData );
4445 /* PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
4446 &wdmHostApi->blockingStreamInterface, streamCallback, userData ); */
4448 /* We don't support the blocking API yet */
4449 PA_DEBUG(("Blocking API not supported yet!\n"));
4450 PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Blocking API not supported yet");
4451 result = paUnanticipatedHostError;
4455 PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
4457 /* Instantiate the input pin if necessary */
4458 if(userInputChannels > 0)
4460 PaWinWdmFilter* pFilter;
4461 PaWinWdmDeviceInfo* pDeviceInfo;
4463 unsigned validBitsPerSample = 0;
4464 PaWinWaveFormatChannelMask channelMask = PaWin_DefaultChannelMask( userInputChannels );
4466 result = paSampleFormatNotSupported;
4467 pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
4468 pFilter = pDeviceInfo->filter;
4469 pPin = pFilter->pins[pDeviceInfo->pin];
4471 stream->userInputChannels = userInputChannels;
4473 hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( pPin->formats, inputSampleFormat );
4474 if (hostInputSampleFormat == paSampleFormatNotSupported)
4476 result = paUnanticipatedHostError;
4477 PaWinWDM_SetLastErrorInfo(result, "PU_SCAF(%X,%X) failed (input)", pPin->formats, inputSampleFormat);
4480 else if (pFilter->devInfo.streamingType == Type_kWaveRT && hostInputSampleFormat == paInt24)
4482 /* For WaveRT, we choose 32 bit format instead of paInt24, since we MIGHT need to align buffer on a
4483 128 byte boundary (see PinGetBuffer) */
4484 hostInputSampleFormat = paInt32;
4485 /* But we'll tell the driver that it's 24 bit in 32 bit container */
4486 validBitsPerSample = 24;
4489 while (hostInputSampleFormat <= paUInt8)
4491 unsigned channelsToProbe = stream->userInputChannels;
4492 /* Some or all KS devices can only handle the exact number of channels
4493 * they specify. But PortAudio clients expect to be able to
4494 * at least specify mono I/O on a multi-channel device
4495 * If this is the case, then we will do the channel mapping internally
4496 * The following loop tests this case
4500 PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
4502 hostInputSampleFormat,
4503 PaWin_SampleFormatToLinearWaveFormatTag(hostInputSampleFormat),
4506 stream->capture.bytesPerFrame = wfx.Format.nBlockAlign;
4507 if (validBitsPerSample != 0)
4509 wfx.Samples.wValidBitsPerSample = validBitsPerSample;
4511 stream->capture.pPin = FilterCreatePin(pFilter, pPin->pinId, (WAVEFORMATEX*)&wfx, &result);
4512 stream->deviceInputChannels = channelsToProbe;
4514 if( result != paNoError && result != paDeviceUnavailable )
4516 /* Try a WAVE_FORMAT_PCM instead */
4517 PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
4519 hostInputSampleFormat,
4520 PaWin_SampleFormatToLinearWaveFormatTag(hostInputSampleFormat),
4522 if (validBitsPerSample != 0)
4524 wfx.Samples.wValidBitsPerSample = validBitsPerSample;
4526 stream->capture.pPin = FilterCreatePin(pFilter, pPin->pinId, (const WAVEFORMATEX*)&wfx, &result);
4529 if (result == paDeviceUnavailable) goto occupied;
4531 if (result == paNoError)
4537 if (channelsToProbe < (unsigned)pPin->maxChannels)
4539 /* Go to next multiple of 2 */
4540 channelsToProbe = min((((channelsToProbe>>1)+1)<<1), (unsigned)pPin->maxChannels);
4547 if (result == paNoError)
4553 /* Go to next format in line with lower resolution */
4554 hostInputSampleFormat <<= 1;
4557 if(stream->capture.pPin == NULL)
4559 PaWinWDM_SetLastErrorInfo(result, "Failed to create capture pin: sr=%u,ch=%u,bits=%u,align=%u",
4560 wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample, wfx.Format.nBlockAlign);
4564 /* Select correct mux input on MUX node of topology filter */
4565 if (pDeviceInfo->muxPosition >= 0)
4567 assert(pPin->parentFilter->topologyFilter != NULL);
4569 result = FilterUse(pPin->parentFilter->topologyFilter);
4570 if (result != paNoError)
4572 PaWinWDM_SetLastErrorInfo(result, "Failed to open topology filter");
4576 result = WdmSetMuxNodeProperty(pPin->parentFilter->topologyFilter->handle,
4577 pPin->inputs[pDeviceInfo->muxPosition]->muxNodeId,
4578 pPin->inputs[pDeviceInfo->muxPosition]->muxPinId);
4580 FilterRelease(pPin->parentFilter->topologyFilter);
4582 if(result != paNoError)
4584 PaWinWDM_SetLastErrorInfo(result, "Failed to set topology mux node");
4589 stream->capture.bytesPerSample = stream->capture.bytesPerFrame / stream->deviceInputChannels;
4590 stream->capture.pPin->frameSize /= stream->capture.bytesPerFrame;
4591 PA_DEBUG(("Capture pin frames: %d\n",stream->capture.pPin->frameSize));
4595 stream->capture.pPin = NULL;
4596 stream->capture.bytesPerFrame = 0;
4599 /* Instantiate the output pin if necessary */
4600 if(userOutputChannels > 0)
4602 PaWinWdmFilter* pFilter;
4603 PaWinWdmDeviceInfo* pDeviceInfo;
4605 PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)(outputParameters->hostApiSpecificStreamInfo);
4606 unsigned validBitsPerSample = 0;
4607 PaWinWaveFormatChannelMask channelMask = PaWin_DefaultChannelMask( userOutputChannels );
4608 if (pInfo && (pInfo->flags & paWinWDMKSUseGivenChannelMask))
4610 PA_DEBUG(("Using channelMask 0x%08X instead of default 0x%08X\n",
4613 channelMask = pInfo->channelMask;
4616 result = paSampleFormatNotSupported;
4617 pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device];
4618 pFilter = pDeviceInfo->filter;
4619 pPin = pFilter->pins[pDeviceInfo->pin];
4621 stream->userOutputChannels = userOutputChannels;
4623 hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( pPin->formats, outputSampleFormat );
4624 if (hostOutputSampleFormat == paSampleFormatNotSupported)
4626 result = paUnanticipatedHostError;
4627 PaWinWDM_SetLastErrorInfo(result, "PU_SCAF(%X,%X) failed (output)", pPin->formats, hostOutputSampleFormat);
4630 else if (pFilter->devInfo.streamingType == Type_kWaveRT && hostOutputSampleFormat == paInt24)
4632 /* For WaveRT, we choose 32 bit format instead of paInt24, since we MIGHT need to align buffer on a
4633 128 byte boundary (see PinGetBuffer) */
4634 hostOutputSampleFormat = paInt32;
4635 /* But we'll tell the driver that it's 24 bit in 32 bit container */
4636 validBitsPerSample = 24;
4639 while (hostOutputSampleFormat <= paUInt8)
4641 unsigned channelsToProbe = stream->userOutputChannels;
4642 /* Some or all KS devices can only handle the exact number of channels
4643 * they specify. But PortAudio clients expect to be able to
4644 * at least specify mono I/O on a multi-channel device
4645 * If this is the case, then we will do the channel mapping internally
4646 * The following loop tests this case
4650 PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
4652 hostOutputSampleFormat,
4653 PaWin_SampleFormatToLinearWaveFormatTag(hostOutputSampleFormat),
4656 stream->render.bytesPerFrame = wfx.Format.nBlockAlign;
4657 if (validBitsPerSample != 0)
4659 wfx.Samples.wValidBitsPerSample = validBitsPerSample;
4661 stream->render.pPin = FilterCreatePin(pFilter, pPin->pinId, (WAVEFORMATEX*)&wfx, &result);
4662 stream->deviceOutputChannels = channelsToProbe;
4664 if( result != paNoError && result != paDeviceUnavailable )
4666 PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
4668 hostOutputSampleFormat,
4669 PaWin_SampleFormatToLinearWaveFormatTag(hostOutputSampleFormat),
4671 if (validBitsPerSample != 0)
4673 wfx.Samples.wValidBitsPerSample = validBitsPerSample;
4675 stream->render.pPin = FilterCreatePin(pFilter, pPin->pinId, (const WAVEFORMATEX*)&wfx, &result);
4678 if (result == paDeviceUnavailable) goto occupied;
4680 if (result == paNoError)
4686 if (channelsToProbe < (unsigned)pPin->maxChannels)
4688 /* Go to next multiple of 2 */
4689 channelsToProbe = min((((channelsToProbe>>1)+1)<<1), (unsigned)pPin->maxChannels);
4696 if (result == paNoError)
4702 /* Go to next format in line with lower resolution */
4703 hostOutputSampleFormat <<= 1;
4706 if(stream->render.pPin == NULL)
4708 PaWinWDM_SetLastErrorInfo(result, "Failed to create render pin: sr=%u,ch=%u,bits=%u,align=%u",
4709 wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample, wfx.Format.nBlockAlign);
4713 stream->render.bytesPerSample = stream->render.bytesPerFrame / stream->deviceOutputChannels;
4714 stream->render.pPin->frameSize /= stream->render.bytesPerFrame;
4715 PA_DEBUG(("Render pin frames: %d\n",stream->render.pPin->frameSize));
4719 stream->render.pPin = NULL;
4720 stream->render.bytesPerFrame = 0;
4723 /* Calculate the framesPerHostXxxxBuffer size based upon the suggested latency values */
4724 /* Record the buffer length */
4727 /* Calculate the frames from the user's value - add a bit to round up */
4728 stream->capture.framesPerBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001);
4729 if(stream->capture.framesPerBuffer > (unsigned long)sampleRate)
4730 { /* Upper limit is 1 second */
4731 stream->capture.framesPerBuffer = (unsigned long)sampleRate;
4733 else if(stream->capture.framesPerBuffer < stream->capture.pPin->frameSize)
4735 stream->capture.framesPerBuffer = stream->capture.pPin->frameSize;
4737 PA_DEBUG(("Input frames chosen:%ld\n",stream->capture.framesPerBuffer));
4739 /* Setup number of packets to use */
4740 stream->capture.noOfPackets = 2;
4742 if (inputParameters->hostApiSpecificStreamInfo)
4744 PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)inputParameters->hostApiSpecificStreamInfo;
4746 if (stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic &&
4747 pInfo->noOfPackets != 0)
4749 stream->capture.noOfPackets = pInfo->noOfPackets;
4754 if(outputParameters)
4756 /* Calculate the frames from the user's value - add a bit to round up */
4757 stream->render.framesPerBuffer = (unsigned long)((outputParameters->suggestedLatency*sampleRate)+0.0001);
4758 if(stream->render.framesPerBuffer > (unsigned long)sampleRate)
4759 { /* Upper limit is 1 second */
4760 stream->render.framesPerBuffer = (unsigned long)sampleRate;
4762 else if(stream->render.framesPerBuffer < stream->render.pPin->frameSize)
4764 stream->render.framesPerBuffer = stream->render.pPin->frameSize;
4766 PA_DEBUG(("Output frames chosen:%ld\n",stream->render.framesPerBuffer));
4768 /* Setup number of packets to use */
4769 stream->render.noOfPackets = 2;
4771 if (outputParameters->hostApiSpecificStreamInfo)
4773 PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)outputParameters->hostApiSpecificStreamInfo;
4775 if (stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic &&
4776 pInfo->noOfPackets != 0)
4778 stream->render.noOfPackets = pInfo->noOfPackets;
4783 /* Host buffer size is bound to the largest of the input and output frame sizes */
4784 result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
4785 stream->userInputChannels, inputSampleFormat, hostInputSampleFormat,
4786 stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat,
4787 sampleRate, streamFlags, framesPerUserBuffer,
4788 max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer),
4789 paUtilBoundedHostBufferSize,
4790 streamCallback, userData );
4791 if( result != paNoError )
4793 PaWinWDM_SetLastErrorInfo(result, "PaUtil_InitializeBufferProcessor failed: ich=%u, isf=%u, hisf=%u, och=%u, osf=%u, hosf=%u, sr=%lf, flags=0x%X, fpub=%u, fphb=%u",
4794 stream->userInputChannels, inputSampleFormat, hostInputSampleFormat,
4795 stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat,
4796 sampleRate, streamFlags, framesPerUserBuffer,
4797 max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer));
4801 /* Allocate/get all the buffers for host I/O */
4802 if (stream->userInputChannels > 0)
4804 stream->streamRepresentation.streamInfo.inputLatency = stream->capture.framesPerBuffer / sampleRate;
4806 switch (stream->capture.pPin->parentFilter->devInfo.streamingType)
4808 case Type_kWaveCyclic:
4810 unsigned size = stream->capture.noOfPackets * stream->capture.framesPerBuffer * stream->capture.bytesPerFrame;
4811 /* Allocate input host buffer */
4812 stream->capture.hostBuffer = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, size);
4813 PA_DEBUG(("Input buffer allocated (size = %u)\n", size));
4814 if( !stream->capture.hostBuffer )
4816 PA_DEBUG(("Cannot allocate host input buffer!\n"));
4817 PaWinWDM_SetLastErrorInfo(paInsufficientMemory, "Failed to allocate input buffer");
4818 result = paInsufficientMemory;
4821 stream->capture.hostBufferSize = size;
4822 PA_DEBUG(("Input buffer start = %p (size=%u)\n",stream->capture.hostBuffer, stream->capture.hostBufferSize));
4823 stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveCyclic;
4824 stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveCyclic;
4829 const DWORD dwTotalSize = 2 * stream->capture.framesPerBuffer * stream->capture.bytesPerFrame;
4830 DWORD dwRequestedSize = dwTotalSize;
4831 BOOL bCallMemoryBarrier = FALSE;
4832 ULONG hwFifoLatency = 0;
4834 result = PinGetBuffer(stream->capture.pPin, (void**)&stream->capture.hostBuffer, &dwRequestedSize, &bCallMemoryBarrier);
4837 PA_DEBUG(("Input buffer start = %p, size = %u\n", stream->capture.hostBuffer, dwRequestedSize));
4838 if (dwRequestedSize != dwTotalSize)
4840 PA_DEBUG(("Buffer length changed by driver from %u to %u !\n", dwTotalSize, dwRequestedSize));
4841 /* Recalculate to what the driver has given us */
4842 stream->capture.framesPerBuffer = dwRequestedSize / (2 * stream->capture.bytesPerFrame);
4844 stream->capture.hostBufferSize = dwRequestedSize;
4846 if (stream->capture.pPin->pinKsSubType == SubType_kPolled)
4848 stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveRTPolled;
4849 stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveRTPolled;
4853 stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveRTEvent;
4854 stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveRTEvent;
4857 stream->capture.pPin->fnMemBarrier = bCallMemoryBarrier ? MemoryBarrierRead : MemoryBarrierDummy;
4861 PA_DEBUG(("Failed to get input buffer (WaveRT)\n"));
4862 PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to get input buffer (WaveRT)");
4863 result = paUnanticipatedHostError;
4868 result = PinGetHwLatency(stream->capture.pPin, &hwFifoLatency, &dummy, &dummy);
4869 if (result == paNoError)
4871 stream->capture.pPin->hwLatency = hwFifoLatency;
4873 /* Add HW latency into total input latency */
4874 stream->streamRepresentation.streamInfo.inputLatency += ((hwFifoLatency / stream->capture.bytesPerFrame) / sampleRate);
4878 PA_DEBUG(("Failed to get size of FIFO hardware buffer (is set to zero)\n"));
4879 stream->capture.pPin->hwLatency = 0;
4884 /* Undefined wave type!! */
4886 result = paInternalError;
4887 PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
4893 stream->capture.hostBuffer = 0;
4896 if (stream->userOutputChannels > 0)
4898 stream->streamRepresentation.streamInfo.outputLatency = stream->render.framesPerBuffer / sampleRate;
4900 switch (stream->render.pPin->parentFilter->devInfo.streamingType)
4902 case Type_kWaveCyclic:
4904 unsigned size = stream->render.noOfPackets * stream->render.framesPerBuffer * stream->render.bytesPerFrame;
4905 /* Allocate output device buffer */
4906 stream->render.hostBuffer = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, size);
4907 PA_DEBUG(("Output buffer allocated (size = %u)\n", size));
4908 if( !stream->render.hostBuffer )
4910 PA_DEBUG(("Cannot allocate host output buffer!\n"));
4911 PaWinWDM_SetLastErrorInfo(paInsufficientMemory, "Failed to allocate output buffer");
4912 result = paInsufficientMemory;
4915 stream->render.hostBufferSize = size;
4916 PA_DEBUG(("Output buffer start = %p (size=%u)\n",stream->render.hostBuffer, stream->render.hostBufferSize));
4918 stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveCyclic;
4919 stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveCyclic;
4924 const DWORD dwTotalSize = 2 * stream->render.framesPerBuffer * stream->render.bytesPerFrame;
4925 DWORD dwRequestedSize = dwTotalSize;
4926 BOOL bCallMemoryBarrier = FALSE;
4927 ULONG hwFifoLatency = 0;
4929 result = PinGetBuffer(stream->render.pPin, (void**)&stream->render.hostBuffer, &dwRequestedSize, &bCallMemoryBarrier);
4932 PA_DEBUG(("Output buffer start = %p, size = %u, membarrier = %u\n", stream->render.hostBuffer, dwRequestedSize, bCallMemoryBarrier));
4933 if (dwRequestedSize != dwTotalSize)
4935 PA_DEBUG(("Buffer length changed by driver from %u to %u !\n", dwTotalSize, dwRequestedSize));
4936 /* Recalculate to what the driver has given us */
4937 stream->render.framesPerBuffer = dwRequestedSize / (2 * stream->render.bytesPerFrame);
4939 stream->render.hostBufferSize = dwRequestedSize;
4941 if (stream->render.pPin->pinKsSubType == SubType_kPolled)
4943 stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveRTPolled;
4944 stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveRTPolled;
4948 stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveRTEvent;
4949 stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveRTEvent;
4952 stream->render.pPin->fnMemBarrier = bCallMemoryBarrier ? MemoryBarrierWrite : MemoryBarrierDummy;
4956 PA_DEBUG(("Failed to get output buffer (with notification)\n"));
4957 PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to get output buffer (with notification)");
4958 result = paUnanticipatedHostError;
4963 result = PinGetHwLatency(stream->render.pPin, &hwFifoLatency, &dummy, &dummy);
4964 if (result == paNoError)
4966 stream->render.pPin->hwLatency = hwFifoLatency;
4968 /* Add HW latency into total output latency */
4969 stream->streamRepresentation.streamInfo.outputLatency += ((hwFifoLatency / stream->render.bytesPerFrame) / sampleRate);
4973 PA_DEBUG(("Failed to get size of FIFO hardware buffer (is set to zero)\n"));
4974 stream->render.pPin->hwLatency = 0;
4979 /* Undefined wave type!! */
4981 result = paInternalError;
4982 PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
4988 stream->render.hostBuffer = 0;
4991 stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
4993 PA_DEBUG(("BytesPerInputFrame = %d\n",stream->capture.bytesPerFrame));
4994 PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->render.bytesPerFrame));
4996 /* memset(stream->hostBuffer,0,size); */
4999 stream->eventAbort = CreateEvent(NULL, TRUE, FALSE, NULL);
5000 if (stream->eventAbort == 0)
5002 result = paInsufficientMemory;
5005 stream->eventStreamStart[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
5006 if (stream->eventStreamStart[0] == 0)
5008 result = paInsufficientMemory;
5011 stream->eventStreamStart[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
5012 if (stream->eventStreamStart[1] == 0)
5014 result = paInsufficientMemory;
5018 if(stream->userInputChannels > 0)
5020 const unsigned bufferSizeInBytes = stream->capture.framesPerBuffer * stream->capture.bytesPerFrame;
5021 const unsigned ringBufferFrameSize = NextPowerOf2( 1024 + 2 * max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer) );
5023 stream->capture.events = (HANDLE*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->capture.noOfPackets * sizeof(HANDLE));
5024 if (stream->capture.events == NULL)
5026 result = paInsufficientMemory;
5030 stream->capture.packets = (DATAPACKET*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->capture.noOfPackets * sizeof(DATAPACKET));
5031 if (stream->capture.packets == NULL)
5033 result = paInsufficientMemory;
5037 switch(stream->capture.pPin->parentFilter->devInfo.streamingType)
5039 case Type_kWaveCyclic:
5041 /* WaveCyclic case */
5043 for (i = 0; i < stream->capture.noOfPackets; ++i)
5045 /* Set up the packets */
5046 DATAPACKET *p = stream->capture.packets + i;
5049 stream->capture.events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
5051 p->Signal.hEvent = stream->capture.events[i];
5052 p->Header.Data = stream->capture.hostBuffer + (i*bufferSizeInBytes);
5053 p->Header.FrameExtent = bufferSizeInBytes;
5054 p->Header.DataUsed = 0;
5055 p->Header.Size = sizeof(p->Header);
5056 p->Header.PresentationTime.Numerator = 1;
5057 p->Header.PresentationTime.Denominator = 1;
5063 /* Set up the "packets" */
5064 DATAPACKET *p = stream->capture.packets + 0;
5066 /* Record event: WaveRT has a single event for 2 notification per buffer */
5067 stream->capture.events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
5069 p->Header.Data = stream->capture.hostBuffer;
5070 p->Header.FrameExtent = bufferSizeInBytes;
5071 p->Header.DataUsed = 0;
5072 p->Header.Size = sizeof(p->Header);
5073 p->Header.PresentationTime.Numerator = 1;
5074 p->Header.PresentationTime.Denominator = 1;
5077 p->Header.Data = stream->capture.hostBuffer + bufferSizeInBytes;
5078 p->Header.FrameExtent = bufferSizeInBytes;
5079 p->Header.DataUsed = 0;
5080 p->Header.Size = sizeof(p->Header);
5081 p->Header.PresentationTime.Numerator = 1;
5082 p->Header.PresentationTime.Denominator = 1;
5084 if (stream->capture.pPin->pinKsSubType == SubType_kNotification)
5086 result = PinRegisterNotificationHandle(stream->capture.pPin, stream->capture.events[0]);
5088 if (result != paNoError)
5090 PA_DEBUG(("Failed to register capture notification handle\n"));
5091 PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to register capture notification handle");
5092 result = paUnanticipatedHostError;
5097 result = PinRegisterPositionRegister(stream->capture.pPin);
5099 if (result != paNoError)
5101 unsigned long pos = 0xdeadc0de;
5102 PA_DEBUG(("Failed to register capture position register, using PinGetAudioPositionViaIOCTLWrite\n"));
5103 stream->capture.pPin->fnAudioPosition = PinGetAudioPositionViaIOCTLWrite;
5104 /* Test position function */
5105 result = (stream->capture.pPin->fnAudioPosition)(stream->capture.pPin, &pos);
5106 if (result != paNoError || pos != 0x0)
5108 PA_DEBUG(("Failed to read capture position register (IOCTL)\n"));
5109 PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to read capture position register (IOCTL)");
5110 result = paUnanticipatedHostError;
5116 stream->capture.pPin->fnAudioPosition = PinGetAudioPositionMemoryMapped;
5121 /* Undefined wave type!! */
5123 result = paInternalError;
5124 PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
5128 /* Setup the input ring buffer here */
5129 stream->ringBufferData = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, ringBufferFrameSize * stream->capture.bytesPerFrame);
5130 if (stream->ringBufferData == NULL)
5132 result = paInsufficientMemory;
5135 PaUtil_InitializeRingBuffer(&stream->ringBuffer, stream->capture.bytesPerFrame, ringBufferFrameSize, stream->ringBufferData);
5137 if(stream->userOutputChannels > 0)
5139 const unsigned bufferSizeInBytes = stream->render.framesPerBuffer * stream->render.bytesPerFrame;
5141 stream->render.events = (HANDLE*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->render.noOfPackets * sizeof(HANDLE));
5142 if (stream->render.events == NULL)
5144 result = paInsufficientMemory;
5148 stream->render.packets = (DATAPACKET*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->render.noOfPackets * sizeof(DATAPACKET));
5149 if (stream->render.packets == NULL)
5151 result = paInsufficientMemory;
5155 switch(stream->render.pPin->parentFilter->devInfo.streamingType)
5157 case Type_kWaveCyclic:
5159 /* WaveCyclic case */
5161 for (i = 0; i < stream->render.noOfPackets; ++i)
5163 /* Set up the packets */
5164 DATAPACKET *p = stream->render.packets + i;
5166 /* Playback event */
5167 stream->render.events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
5169 /* In this case, we just use the packets as ptr to the device buffer */
5170 p->Signal.hEvent = stream->render.events[i];
5171 p->Header.Data = stream->render.hostBuffer + (i*bufferSizeInBytes);
5172 p->Header.FrameExtent = bufferSizeInBytes;
5173 p->Header.DataUsed = bufferSizeInBytes;
5174 p->Header.Size = sizeof(p->Header);
5175 p->Header.PresentationTime.Numerator = 1;
5176 p->Header.PresentationTime.Denominator = 1;
5184 /* Set up the "packets" */
5185 DATAPACKET *p = stream->render.packets;
5187 /* The only playback event */
5188 stream->render.events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
5190 /* In this case, we just use the packets as ptr to the device buffer */
5191 p->Header.Data = stream->render.hostBuffer;
5192 p->Header.FrameExtent = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
5193 p->Header.DataUsed = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
5194 p->Header.Size = sizeof(p->Header);
5195 p->Header.PresentationTime.Numerator = 1;
5196 p->Header.PresentationTime.Denominator = 1;
5199 p->Header.Data = stream->render.hostBuffer + stream->render.framesPerBuffer*stream->render.bytesPerFrame;
5200 p->Header.FrameExtent = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
5201 p->Header.DataUsed = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
5202 p->Header.Size = sizeof(p->Header);
5203 p->Header.PresentationTime.Numerator = 1;
5204 p->Header.PresentationTime.Denominator = 1;
5206 if (stream->render.pPin->pinKsSubType == SubType_kNotification)
5208 result = PinRegisterNotificationHandle(stream->render.pPin, stream->render.events[0]);
5210 if (result != paNoError)
5212 PA_DEBUG(("Failed to register rendering notification handle\n"));
5213 PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to register rendering notification handle");
5214 result = paUnanticipatedHostError;
5219 result = PinRegisterPositionRegister(stream->render.pPin);
5221 if (result != paNoError)
5223 unsigned long pos = 0xdeadc0de;
5224 PA_DEBUG(("Failed to register rendering position register, using PinGetAudioPositionViaIOCTLRead\n"));
5225 stream->render.pPin->fnAudioPosition = PinGetAudioPositionViaIOCTLRead;
5226 /* Test position function */
5227 result = (stream->render.pPin->fnAudioPosition)(stream->render.pPin, &pos);
5228 if (result != paNoError || pos != 0x0)
5230 PA_DEBUG(("Failed to read render position register (IOCTL)\n"));
5231 PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to read render position register (IOCTL)");
5232 result = paUnanticipatedHostError;
5238 stream->render.pPin->fnAudioPosition = PinGetAudioPositionMemoryMapped;
5243 /* Undefined wave type!! */
5245 result = paInternalError;
5246 PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
5251 stream->streamStarted = 0;
5252 stream->streamActive = 0;
5253 stream->streamStop = 0;
5254 stream->streamAbort = 0;
5255 stream->streamFlags = streamFlags;
5256 stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
5258 /* Increase ref count on filters in use, so that a CommitDeviceInfos won't delete them */
5259 if (stream->capture.pPin != 0)
5261 FilterAddRef(stream->capture.pPin->parentFilter);
5263 if (stream->render.pPin != 0)
5265 FilterAddRef(stream->render.pPin->parentFilter);
5268 /* Ok, now update our host API specific stream info */
5269 if (stream->userInputChannels)
5271 PaWinWdmDeviceInfo *pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
5273 stream->hostApiStreamInfo.input.device = Pa_HostApiDeviceIndexToDeviceIndex(Pa_HostApiTypeIdToHostApiIndex(paWDMKS), inputParameters->device);
5274 stream->hostApiStreamInfo.input.channels = stream->deviceInputChannels;
5275 stream->hostApiStreamInfo.input.muxNodeId = -1;
5276 if (stream->capture.pPin->inputs)
5278 stream->hostApiStreamInfo.input.muxNodeId = stream->capture.pPin->inputs[pDeviceInfo->muxPosition]->muxNodeId;
5280 stream->hostApiStreamInfo.input.endpointPinId = pDeviceInfo->endpointPinId;
5281 stream->hostApiStreamInfo.input.framesPerHostBuffer = stream->capture.framesPerBuffer;
5282 stream->hostApiStreamInfo.input.streamingSubType = stream->capture.pPin->pinKsSubType;
5286 stream->hostApiStreamInfo.input.device = paNoDevice;
5288 if (stream->userOutputChannels)
5290 stream->hostApiStreamInfo.output.device = Pa_HostApiDeviceIndexToDeviceIndex(Pa_HostApiTypeIdToHostApiIndex(paWDMKS), outputParameters->device);
5291 stream->hostApiStreamInfo.output.channels = stream->deviceOutputChannels;
5292 stream->hostApiStreamInfo.output.framesPerHostBuffer = stream->render.framesPerBuffer;
5293 stream->hostApiStreamInfo.output.endpointPinId = stream->render.pPin->endpointPinId;
5294 stream->hostApiStreamInfo.output.streamingSubType = stream->render.pPin->pinKsSubType;
5298 stream->hostApiStreamInfo.output.device = paNoDevice;
5300 /*stream->streamRepresentation.streamInfo.hostApiTypeId = paWDMKS;
5301 stream->streamRepresentation.streamInfo.hostApiSpecificStreamInfo = &stream->hostApiStreamInfo;*/
5302 stream->streamRepresentation.streamInfo.structVersion = 2;
5304 *s = (PaStream*)stream;
5310 /* Ok, someone else is hogging the pin, bail out */
5311 assert (result == paDeviceUnavailable);
5312 PaWinWDM_SetLastErrorInfo(result, "Device is occupied");
5315 PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
5317 CloseStreamEvents(stream);
5319 if (stream->allocGroup)
5321 PaUtil_FreeAllAllocations(stream->allocGroup);
5322 PaUtil_DestroyAllocationGroup(stream->allocGroup);
5323 stream->allocGroup = 0;
5326 if(stream->render.pPin)
5327 PinClose(stream->render.pPin);
5328 if(stream->capture.pPin)
5329 PinClose(stream->capture.pPin);
5331 PaUtil_FreeMemory( stream );
5338 When CloseStream() is called, the multi-api layer ensures that
5339 the stream has already been stopped or aborted.
5341 static PaError CloseStream( PaStream* s )
5343 PaError result = paNoError;
5344 PaWinWdmStream *stream = (PaWinWdmStream*)s;
5348 assert(!stream->streamStarted);
5349 assert(!stream->streamActive);
5351 PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
5352 PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
5354 CloseStreamEvents(stream);
5356 if (stream->allocGroup)
5358 PaUtil_FreeAllAllocations(stream->allocGroup);
5359 PaUtil_DestroyAllocationGroup(stream->allocGroup);
5360 stream->allocGroup = 0;
5363 if(stream->render.pPin)
5365 PinClose(stream->render.pPin);
5367 if(stream->capture.pPin)
5369 PinClose(stream->capture.pPin);
5372 if (stream->render.pPin)
5374 FilterFree(stream->render.pPin->parentFilter);
5376 if (stream->capture.pPin)
5378 FilterFree(stream->capture.pPin->parentFilter);
5381 PaUtil_FreeMemory( stream );
5388 Write the supplied packet to the pin
5390 Should return paNoError on success
5392 static PaError PinWrite(HANDLE h, DATAPACKET* p)
5394 PaError result = paNoError;
5395 unsigned long cbReturned = 0;
5396 BOOL fRes = DeviceIoControl(h,
5397 IOCTL_KS_WRITE_STREAM,
5406 unsigned long error = GetLastError();
5407 if (error != ERROR_IO_PENDING)
5409 result = paInternalError;
5416 Read to the supplied packet from the pin
5418 Should return paNoError on success
5420 static PaError PinRead(HANDLE h, DATAPACKET* p)
5422 PaError result = paNoError;
5423 unsigned long cbReturned = 0;
5424 BOOL fRes = DeviceIoControl(h,
5425 IOCTL_KS_READ_STREAM,
5434 unsigned long error = GetLastError();
5435 if (error != ERROR_IO_PENDING)
5437 result = paInternalError;
5444 Copy the first interleaved channel of 16 bit data to the other channels
5446 static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples)
5448 unsigned short* data = (unsigned short*)buffer;
5450 unsigned short sourceSample;
5453 sourceSample = *data++;
5454 channel = channels-1;
5457 *data++ = sourceSample;
5463 Copy the first interleaved channel of 24 bit data to the other channels
5465 static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples)
5467 unsigned char* data = (unsigned char*)buffer;
5469 unsigned char sourceSample[3];
5472 sourceSample[0] = data[0];
5473 sourceSample[1] = data[1];
5474 sourceSample[2] = data[2];
5476 channel = channels-1;
5479 data[0] = sourceSample[0];
5480 data[1] = sourceSample[1];
5481 data[2] = sourceSample[2];
5488 Copy the first interleaved channel of 32 bit data to the other channels
5490 static void DuplicateFirstChannelInt32(void* buffer, int channels, int samples)
5492 unsigned long* data = (unsigned long*)buffer;
5494 unsigned long sourceSample;
5497 sourceSample = *data++;
5498 channel = channels-1;
5501 *data++ = sourceSample;
5507 Increase the priority of the calling thread to RT
5509 static HANDLE BumpThreadPriority()
5511 HANDLE hThread = GetCurrentThread();
5513 HANDLE hAVRT = NULL;
5515 /* If we have access to AVRT.DLL (Vista and later), use it */
5516 if (paWinWDMKSAvRtEntryPoints.AvSetMmThreadCharacteristics != NULL)
5518 hAVRT = paWinWDMKSAvRtEntryPoints.AvSetMmThreadCharacteristics("Pro Audio", &dwTask);
5519 if (hAVRT != NULL && hAVRT != INVALID_HANDLE_VALUE)
5521 BOOL bret = paWinWDMKSAvRtEntryPoints.AvSetMmThreadPriority(hAVRT, PA_AVRT_PRIORITY_CRITICAL);
5524 PA_DEBUG(("Set mm thread prio to critical failed!\n"));
5533 PA_DEBUG(("Set mm thread characteristic to 'Pro Audio' failed, reverting to SetThreadPriority\n"));
5537 /* For XP and earlier, or if AvSetMmThreadCharacteristics fails (MMCSS disabled ?) */
5538 if (timeBeginPeriod(1) != TIMERR_NOERROR) {
5539 PA_DEBUG(("timeBeginPeriod(1) failed!\n"));
5542 if (!SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL)) {
5543 PA_DEBUG(("SetThreadPriority failed!\n"));
5550 Decrease the priority of the calling thread to normal
5552 static void DropThreadPriority(HANDLE hAVRT)
5554 HANDLE hThread = GetCurrentThread();
5558 paWinWDMKSAvRtEntryPoints.AvSetMmThreadPriority(hAVRT, PA_AVRT_PRIORITY_NORMAL);
5559 paWinWDMKSAvRtEntryPoints.AvRevertMmThreadCharacteristics(hAVRT);
5563 SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL);
5567 static PaError PreparePinForStart(PaWinWdmPin* pin)
5570 result = PinSetState(pin, KSSTATE_ACQUIRE);
5571 if (result != paNoError)
5575 result = PinSetState(pin, KSSTATE_PAUSE);
5576 if (result != paNoError)
5583 PinSetState(pin, KSSTATE_STOP);
5587 static PaError PreparePinsForStart(PaProcessThreadInfo* pInfo)
5589 PaError result = paNoError;
5590 /* Submit buffers */
5591 if (pInfo->stream->capture.pPin)
5593 if ((result = PreparePinForStart(pInfo->stream->capture.pPin)) != paNoError)
5598 if (pInfo->stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
5601 for(i=0; i < pInfo->stream->capture.noOfPackets; ++i)
5603 if ((result = PinRead(pInfo->stream->capture.pPin->handle, pInfo->stream->capture.packets + i)) != paNoError)
5616 if(pInfo->stream->render.pPin)
5618 if ((result = PreparePinForStart(pInfo->stream->render.pPin)) != paNoError)
5623 pInfo->priming += pInfo->stream->render.noOfPackets;
5625 SetEvent(pInfo->stream->render.events[0]);
5626 if (pInfo->stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
5629 for(i=1; i < pInfo->stream->render.noOfPackets; ++i)
5631 SetEvent(pInfo->stream->render.events[i]);
5638 PA_DEBUG(("PreparePinsForStart = %d\n", result));
5642 static PaError StartPin(PaWinWdmPin* pin)
5644 return PinSetState(pin, KSSTATE_RUN);
5647 static PaError StartPins(PaProcessThreadInfo* pInfo)
5649 PaError result = paNoError;
5650 /* Start the pins as synced as possible */
5651 if (pInfo->stream->capture.pPin)
5653 result = StartPin(pInfo->stream->capture.pPin);
5655 if(pInfo->stream->render.pPin)
5657 result = StartPin(pInfo->stream->render.pPin);
5659 PA_DEBUG(("StartPins = %d\n", result));
5664 static PaError StopPin(PaWinWdmPin* pin)
5666 PinSetState(pin, KSSTATE_PAUSE);
5667 PinSetState(pin, KSSTATE_STOP);
5672 static PaError StopPins(PaProcessThreadInfo* pInfo)
5674 PaError result = paNoError;
5675 if(pInfo->stream->render.pPin)
5677 StopPin(pInfo->stream->render.pPin);
5679 if(pInfo->stream->capture.pPin)
5681 StopPin(pInfo->stream->capture.pPin);
5686 typedef void (*TSetInputFrameCount)(PaUtilBufferProcessor*, unsigned long);
5687 typedef void (*TSetInputChannel)(PaUtilBufferProcessor*, unsigned int, void *, unsigned int);
5688 static const TSetInputFrameCount fnSetInputFrameCount[2] = { PaUtil_SetInputFrameCount, PaUtil_Set2ndInputFrameCount };
5689 static const TSetInputChannel fnSetInputChannel[2] = { PaUtil_SetInputChannel, PaUtil_Set2ndInputChannel };
5691 static PaError PaDoProcessing(PaProcessThreadInfo* pInfo)
5693 PaError result = paNoError;
5694 int i, framesProcessed = 0, doChannelCopy = 0;
5695 ring_buffer_size_t inputFramesAvailable = PaUtil_GetRingBufferReadAvailable(&pInfo->stream->ringBuffer);
5697 /* Do necessary buffer processing (which will invoke user callback if necessary) */
5698 if (pInfo->cbResult == paContinue &&
5699 (pInfo->renderHead != pInfo->renderTail || inputFramesAvailable))
5701 unsigned processFullDuplex = pInfo->stream->capture.pPin && pInfo->stream->render.pPin && (!pInfo->priming);
5703 PA_HP_TRACE((pInfo->stream->hLog, "DoProcessing: InputFrames=%u", inputFramesAvailable));
5705 PaUtil_BeginCpuLoadMeasurement( &pInfo->stream->cpuLoadMeasurer );
5707 pInfo->ti.currentTime = PaUtil_GetTime();
5709 PaUtil_BeginBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->ti, pInfo->underover);
5710 pInfo->underover = 0; /* Reset the (under|over)flow status */
5712 if (pInfo->renderTail != pInfo->renderHead)
5714 DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet;
5716 assert(packet != 0);
5717 assert(packet->Header.Data != 0);
5719 PaUtil_SetOutputFrameCount(&pInfo->stream->bufferProcessor, pInfo->stream->render.framesPerBuffer);
5721 for(i=0;i<pInfo->stream->userOutputChannels;i++)
5723 /* Only write the user output channels. Leave the rest blank */
5724 PaUtil_SetOutputChannel(&pInfo->stream->bufferProcessor,
5726 ((unsigned char*)(packet->Header.Data))+(i*pInfo->stream->render.bytesPerSample),
5727 pInfo->stream->deviceOutputChannels);
5730 /* We will do a copy to the other channels after the data has been written */
5731 doChannelCopy = ( pInfo->stream->userOutputChannels == 1 );
5734 if (inputFramesAvailable && (!pInfo->stream->userOutputChannels || inputFramesAvailable >= (int)pInfo->stream->render.framesPerBuffer))
5736 unsigned wrapCntr = 0;
5737 void* data[2] = {0};
5738 ring_buffer_size_t size[2] = {0};
5740 /* If full-duplex, we just extract output buffer number of frames */
5741 if (pInfo->stream->userOutputChannels)
5743 inputFramesAvailable = min(inputFramesAvailable, (int)pInfo->stream->render.framesPerBuffer);
5746 inputFramesAvailable = PaUtil_GetRingBufferReadRegions(&pInfo->stream->ringBuffer,
5747 inputFramesAvailable,
5753 for (wrapCntr = 0; wrapCntr < 2; ++wrapCntr)
5755 if (size[wrapCntr] == 0)
5758 fnSetInputFrameCount[wrapCntr](&pInfo->stream->bufferProcessor, size[wrapCntr]);
5759 for(i=0;i<pInfo->stream->userInputChannels;i++)
5761 /* Only read as many channels as the user wants */
5762 fnSetInputChannel[wrapCntr](&pInfo->stream->bufferProcessor,
5764 ((unsigned char*)(data[wrapCntr]))+(i*pInfo->stream->capture.bytesPerSample),
5765 pInfo->stream->deviceInputChannels);
5771 /* We haven't consumed anything from the ring buffer... */
5772 inputFramesAvailable = 0;
5773 /* If we have full-duplex, this is at startup, so mark no-input! */
5774 if (pInfo->stream->userOutputChannels>0 && pInfo->stream->userInputChannels>0)
5776 PA_HP_TRACE((pInfo->stream->hLog, "Input startup, marking no input."));
5777 PaUtil_SetNoInput(&pInfo->stream->bufferProcessor);
5781 if (processFullDuplex) /* full duplex */
5783 /* Only call the EndBufferProcessing function when the total input frames == total output frames */
5784 const unsigned long totalInputFrameCount = pInfo->stream->bufferProcessor.hostInputFrameCount[0] + pInfo->stream->bufferProcessor.hostInputFrameCount[1];
5785 const unsigned long totalOutputFrameCount = pInfo->stream->bufferProcessor.hostOutputFrameCount[0] + pInfo->stream->bufferProcessor.hostOutputFrameCount[1];
5787 if(totalInputFrameCount == totalOutputFrameCount && totalOutputFrameCount != 0)
5789 framesProcessed = PaUtil_EndBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->cbResult);
5793 framesProcessed = 0;
5798 framesProcessed = PaUtil_EndBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->cbResult);
5801 PA_HP_TRACE((pInfo->stream->hLog, "Frames processed: %u %s", framesProcessed, (pInfo->priming ? "(priming)":"")));
5805 DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet;
5806 /* Copy the first output channel to the other channels */
5807 switch (pInfo->stream->render.bytesPerSample)
5810 DuplicateFirstChannelInt16(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer);
5813 DuplicateFirstChannelInt24(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer);
5816 DuplicateFirstChannelInt32(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer);
5819 assert(0); /* Unsupported format! */
5823 PaUtil_EndCpuLoadMeasurement( &pInfo->stream->cpuLoadMeasurer, framesProcessed );
5825 if (inputFramesAvailable)
5827 PaUtil_AdvanceRingBufferReadIndex(&pInfo->stream->ringBuffer, inputFramesAvailable);
5830 if (pInfo->renderTail != pInfo->renderHead)
5832 if (!pInfo->stream->streamStop)
5834 result = pInfo->stream->render.pPin->fnSubmitHandler(pInfo, pInfo->renderTail);
5835 if (result != paNoError)
5837 PA_HP_TRACE((pInfo->stream->hLog, "Capture submit handler failed with result %d", result));
5841 pInfo->renderTail++;
5842 if (!pInfo->pinsStarted && pInfo->priming == 0)
5844 /* We start the pins here to allow "prime time" */
5845 if ((result = StartPins(pInfo)) == paNoError)
5847 PA_HP_TRACE((pInfo->stream->hLog, "Starting pins!"));
5848 pInfo->pinsStarted = 1;
5857 static VOID CALLBACK TimerAPCWaveRTPolledMode(
5858 LPVOID lpArgToCompletionRoutine,
5859 DWORD dwTimerLowValue,
5860 DWORD dwTimerHighValue)
5862 HANDLE* pHandles = (HANDLE*)lpArgToCompletionRoutine;
5863 if (pHandles[0]) SetEvent(pHandles[0]);
5864 if (pHandles[1]) SetEvent(pHandles[1]);
5867 static DWORD GetCurrentTimeInMillisecs()
5869 return timeGetTime();
5872 PA_THREAD_FUNC ProcessingThread(void* pParam)
5874 PaError result = paNoError;
5875 HANDLE hAVRT = NULL;
5876 HANDLE hTimer = NULL;
5877 HANDLE *handleArray = NULL;
5878 HANDLE timerEventHandles[2] = {0};
5879 unsigned noOfHandles = 0;
5880 unsigned captureEvents = 0;
5881 unsigned renderEvents = 0;
5882 unsigned timerPeriod = 0;
5883 DWORD timeStamp[2] = {0};
5885 PaProcessThreadInfo info;
5886 memset(&info, 0, sizeof(PaProcessThreadInfo));
5887 info.stream = (PaWinWdmStream*)pParam;
5889 info.stream->threadResult = paNoError;
5893 info.ti.inputBufferAdcTime = 0.0;
5894 info.ti.currentTime = 0.0;
5895 info.ti.outputBufferDacTime = 0.0;
5897 PA_DEBUG(("In buffer len: %.3f ms\n",(2000*info.stream->capture.framesPerBuffer) / info.stream->streamRepresentation.streamInfo.sampleRate));
5898 PA_DEBUG(("Out buffer len: %.3f ms\n",(2000*info.stream->render.framesPerBuffer) / info.stream->streamRepresentation.streamInfo.sampleRate));
5899 info.timeout = (DWORD)max(
5900 (2000*info.stream->render.framesPerBuffer/info.stream->streamRepresentation.streamInfo.sampleRate + 0.5),
5901 (2000*info.stream->capture.framesPerBuffer/info.stream->streamRepresentation.streamInfo.sampleRate + 0.5));
5902 info.timeout = max(info.timeout*8, 100);
5903 timerPeriod = info.timeout;
5904 PA_DEBUG(("Timeout = %ld ms\n",info.timeout));
5906 /* Allocate handle array */
5907 handleArray = (HANDLE*)PaUtil_AllocateMemory((info.stream->capture.noOfPackets + info.stream->render.noOfPackets + 1) * sizeof(HANDLE));
5909 /* Setup handle array for WFMO */
5910 if (info.stream->capture.pPin != 0)
5912 handleArray[noOfHandles++] = info.stream->capture.events[0];
5913 if (info.stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
5916 for(i=1; i < info.stream->capture.noOfPackets; ++i)
5918 handleArray[noOfHandles++] = info.stream->capture.events[i];
5921 captureEvents = noOfHandles;
5922 renderEvents = noOfHandles;
5925 if (info.stream->render.pPin != 0)
5927 handleArray[noOfHandles++] = info.stream->render.events[0];
5928 if (info.stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
5931 for(i=1; i < info.stream->render.noOfPackets; ++i)
5933 handleArray[noOfHandles++] = info.stream->render.events[i];
5936 renderEvents = noOfHandles;
5938 handleArray[noOfHandles++] = info.stream->eventAbort;
5939 assert(noOfHandles <= (info.stream->capture.noOfPackets + info.stream->render.noOfPackets + 1));
5941 /* Prepare render and capture pins */
5942 if ((result = PreparePinsForStart(&info)) != paNoError)
5944 PA_DEBUG(("Failed to prepare device(s)!\n"));
5948 /* Init high speed logger */
5949 if (PaUtil_InitializeHighSpeedLog(&info.stream->hLog, 1000000) != paNoError)
5951 PA_DEBUG(("Failed to init high speed logger!\n"));
5955 /* Heighten priority here */
5956 hAVRT = BumpThreadPriority();
5958 /* If input only, we start the pins immediately */
5959 if (info.stream->render.pPin == 0)
5961 if ((result = StartPins(&info)) != paNoError)
5963 PA_DEBUG(("Failed to start device(s)!\n"));
5966 info.pinsStarted = 1;
5969 /* Handle WaveRT polled mode */
5971 const unsigned fs = (unsigned)info.stream->streamRepresentation.streamInfo.sampleRate;
5972 if (info.stream->capture.pPin != 0 && info.stream->capture.pPin->pinKsSubType == SubType_kPolled)
5974 timerEventHandles[0] = info.stream->capture.events[0];
5975 timerPeriod = min(timerPeriod, (1000*info.stream->capture.framesPerBuffer)/fs);
5978 if (info.stream->render.pPin != 0 && info.stream->render.pPin->pinKsSubType == SubType_kPolled)
5980 timerEventHandles[1] = info.stream->render.events[0];
5981 timerPeriod = min(timerPeriod, (1000*info.stream->render.framesPerBuffer)/fs);
5984 if (timerEventHandles[0] || timerEventHandles[1])
5986 LARGE_INTEGER dueTime = {0};
5988 timerPeriod=max(timerPeriod/5,1);
5989 PA_DEBUG(("Timer event handles=0x%04X,0x%04X period=%u ms", timerEventHandles[0], timerEventHandles[1], timerPeriod));
5990 hTimer = CreateWaitableTimer(0, FALSE, NULL);
5993 result = paUnanticipatedHostError;
5996 /* invoke first timeout immediately */
5997 if (!SetWaitableTimer(hTimer, &dueTime, timerPeriod, TimerAPCWaveRTPolledMode, timerEventHandles, FALSE))
5999 result = paUnanticipatedHostError;
6002 PA_DEBUG(("Waitable timer started, period = %u ms\n", timerPeriod));
6006 /* Mark stream as active */
6007 info.stream->streamActive = 1;
6008 info.stream->threadResult = paNoError;
6010 /* Up and running... */
6011 SetEvent(info.stream->eventStreamStart[StreamStart_kOk]);
6013 /* Take timestamp here */
6014 timeStamp[0] = timeStamp[1] = GetCurrentTimeInMillisecs();
6016 while(!info.stream->streamAbort)
6018 unsigned doProcessing = 1;
6019 unsigned wait = WaitForMultipleObjects(noOfHandles, handleArray, FALSE, 0);
6020 unsigned eventSignalled = wait - WAIT_OBJECT_0;
6021 DWORD dwCurrentTime = 0;
6023 if (wait == WAIT_FAILED)
6025 PA_DEBUG(("Wait failed = %ld! \n",wait));
6028 if (wait == WAIT_TIMEOUT)
6030 wait = WaitForMultipleObjectsEx(noOfHandles, handleArray, FALSE, 50, TRUE);
6031 eventSignalled = wait - WAIT_OBJECT_0;
6035 if (eventSignalled < captureEvents)
6037 if (PaUtil_GetRingBufferWriteAvailable(&info.stream->ringBuffer) == 0)
6039 PA_HP_TRACE((info.stream->hLog, "!!!!! Input overflow !!!!!"));
6040 info.underover |= paInputOverflow;
6043 else if (eventSignalled < renderEvents)
6045 if (!info.priming && info.renderHead - info.renderTail > 1)
6047 PA_HP_TRACE((info.stream->hLog, "!!!!! Output underflow !!!!!"));
6048 info.underover |= paOutputUnderflow;
6053 /* Get event time */
6054 dwCurrentTime = GetCurrentTimeInMillisecs();
6056 /* Since we can mix capture/render devices between WaveCyclic, WaveRT polled and WaveRT notification (3x3 combinations),
6057 we can't rely on the timeout of WFMO to check for device timeouts, we need to keep tally. */
6058 if (info.stream->capture.pPin && (dwCurrentTime - timeStamp[0]) >= info.timeout)
6060 PA_DEBUG(("Timeout for capture device (%u ms)!", info.timeout, (dwCurrentTime - timeStamp[0])));
6061 result = paTimedOut;
6064 if (info.stream->render.pPin && (dwCurrentTime - timeStamp[1]) >= info.timeout)
6066 PA_DEBUG(("Timeout for render device (%u ms)!", info.timeout, (dwCurrentTime - timeStamp[1])));
6067 result = paTimedOut;
6071 if (wait == WAIT_IO_COMPLETION)
6073 /* Waitable timer has fired! */
6074 PA_HP_TRACE((info.stream->hLog, "WAIT_IO_COMPLETION"));
6078 if (wait == WAIT_TIMEOUT)
6084 if (eventSignalled < captureEvents)
6086 if (info.stream->capture.pPin->fnEventHandler(&info, eventSignalled) == paNoError)
6088 timeStamp[0] = dwCurrentTime;
6090 /* Since we use the ring buffer, we can submit the buffers directly */
6091 if (!info.stream->streamStop)
6093 result = info.stream->capture.pPin->fnSubmitHandler(&info, info.captureTail);
6094 if (result != paNoError)
6096 PA_HP_TRACE((info.stream->hLog, "Capture submit handler failed with result %d", result));
6101 /* If full-duplex, let _only_ render event trigger processing. We still need the stream stop
6102 handling working, so let that be processed anyways... */
6103 if (info.stream->userOutputChannels > 0)
6109 else if (eventSignalled < renderEvents)
6111 timeStamp[1] = dwCurrentTime;
6112 eventSignalled -= captureEvents;
6113 info.stream->render.pPin->fnEventHandler(&info, eventSignalled);
6117 assert(info.stream->streamAbort);
6118 PA_HP_TRACE((info.stream->hLog, "Stream abort!"));
6123 /* Handle processing */
6126 result = PaDoProcessing(&info);
6127 if (result != paNoError)
6129 PA_HP_TRACE((info.stream->hLog, "PaDoProcessing failed!"));
6134 if(info.stream->streamStop && info.cbResult != paComplete)
6136 PA_HP_TRACE((info.stream->hLog, "Stream stop! pending=%d",info.pending));
6137 info.cbResult = paComplete; /* Stop, but play remaining buffers */
6142 PA_HP_TRACE((info.stream->hLog, "pending==0 finished..."));
6145 if((!info.stream->render.pPin)&&(info.cbResult!=paContinue))
6147 PA_HP_TRACE((info.stream->hLog, "record only cbResult=%d...",info.cbResult));
6152 PA_DEBUG(("Finished processing loop\n"));
6154 info.stream->threadResult = result;
6158 PA_DEBUG(("Error starting processing thread\n"));
6159 /* Set the "error" event together with result */
6160 info.stream->threadResult = result;
6161 SetEvent(info.stream->eventStreamStart[StreamStart_kFailed]);
6166 PA_DEBUG(("Waitable timer stopped\n", timerPeriod));
6167 CancelWaitableTimer(hTimer);
6168 CloseHandle(hTimer);
6172 if (info.pinsStarted)
6177 /* Lower prio here */
6178 DropThreadPriority(hAVRT);
6180 if (handleArray != NULL)
6182 PaUtil_FreeMemory(handleArray);
6185 #if PA_TRACE_REALTIME_EVENTS
6186 if (info.stream->hLog)
6188 PA_DEBUG(("Dumping highspeed trace...\n"));
6189 PaUtil_DumpHighSpeedLog(info.stream->hLog, "hp_trace.log");
6190 PaUtil_DiscardHighSpeedLog(info.stream->hLog);
6191 info.stream->hLog = 0;
6194 info.stream->streamActive = 0;
6196 if((!info.stream->streamStop)&&(!info.stream->streamAbort))
6198 /* Invoke the user stream finished callback */
6199 /* Only do it from here if not being stopped/aborted by user */
6200 if( info.stream->streamRepresentation.streamFinishedCallback != 0 )
6201 info.stream->streamRepresentation.streamFinishedCallback( info.stream->streamRepresentation.userData );
6203 info.stream->streamStop = 0;
6204 info.stream->streamAbort = 0;
6211 static PaError StartStream( PaStream *s )
6213 PaError result = paNoError;
6214 PaWinWdmStream *stream = (PaWinWdmStream*)s;
6218 if (stream->streamThread != NULL)
6220 return paStreamIsNotStopped;
6223 stream->streamStop = 0;
6224 stream->streamAbort = 0;
6226 ResetStreamEvents(stream);
6228 PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
6230 stream->oldProcessPriority = GetPriorityClass(GetCurrentProcess());
6231 /* Uncomment the following line to enable dynamic boosting of the process
6232 * priority to real time for best low latency support
6233 * Disabled by default because RT processes can easily block the OS */
6234 /*ret = SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
6235 PA_DEBUG(("Class ret = %d;",ret));*/
6237 stream->streamThread = CREATE_THREAD_FUNCTION (NULL, 0, ProcessingThread, stream, CREATE_SUSPENDED, NULL);
6238 if(stream->streamThread == NULL)
6240 result = paInsufficientMemory;
6243 ResumeThread(stream->streamThread);
6245 switch (WaitForMultipleObjects(2, stream->eventStreamStart, FALSE, 5000))
6247 case WAIT_OBJECT_0 + StreamStart_kOk:
6248 PA_DEBUG(("Processing thread started!\n"));
6250 /* streamActive is set in processing thread */
6251 stream->streamStarted = 1;
6253 case WAIT_OBJECT_0 + StreamStart_kFailed:
6254 PA_DEBUG(("Processing thread start failed! (result=%d)\n", stream->threadResult));
6255 result = stream->threadResult;
6256 /* Wait for the stream to really exit */
6257 WaitForSingleObject(stream->streamThread, 200);
6258 CloseHandle(stream->streamThread);
6259 stream->streamThread = 0;
6263 result = paTimedOut;
6264 PaWinWDM_SetLastErrorInfo(result, "Failed to start processing thread (timeout)!");
6274 static PaError StopStream( PaStream *s )
6276 PaError result = paNoError;
6277 PaWinWdmStream *stream = (PaWinWdmStream*)s;
6282 if(stream->streamActive)
6286 stream->streamStop = 1;
6287 if (GetExitCodeThread(stream->streamThread, &dwExitCode) && dwExitCode == STILL_ACTIVE)
6289 if (WaitForSingleObject(stream->streamThread, INFINITE) != WAIT_OBJECT_0)
6291 PA_DEBUG(("StopStream: stream thread terminated\n"));
6292 TerminateThread(stream->streamThread, -1);
6293 result = paTimedOut;
6298 PA_DEBUG(("StopStream: GECT says not active, but streamActive is not false ??"));
6299 result = paUnanticipatedHostError;
6300 PaWinWDM_SetLastErrorInfo(result, "StopStream: GECT says not active, but streamActive = %d", stream->streamActive);
6305 if (stream->threadResult != paNoError)
6307 PA_DEBUG(("StopStream: Stream not active (%d)\n", stream->threadResult));
6308 result = stream->threadResult;
6309 stream->threadResult = paNoError;
6313 if (stream->streamThread != NULL)
6315 CloseHandle(stream->streamThread);
6316 stream->streamThread = 0;
6318 stream->streamStarted = 0;
6319 stream->streamActive = 0;
6323 /* Do user callback now after all state has been reset */
6324 /* This means it should be safe for the called function */
6325 /* to invoke e.g. StartStream */
6326 if( stream->streamRepresentation.streamFinishedCallback != 0 )
6327 stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
6334 static PaError AbortStream( PaStream *s )
6336 PaError result = paNoError;
6337 PaWinWdmStream *stream = (PaWinWdmStream*)s;
6342 if(stream->streamActive)
6345 stream->streamAbort = 1;
6346 SetEvent(stream->eventAbort); /* Signal immediately */
6347 if (WaitForSingleObject(stream->streamThread, 10000) != WAIT_OBJECT_0)
6349 TerminateThread(stream->streamThread, -1);
6350 result = paTimedOut;
6352 PA_DEBUG(("AbortStream: stream thread terminated\n"));
6354 assert(!stream->streamActive);
6356 CloseHandle(stream->streamThread);
6357 stream->streamThread = NULL;
6358 stream->streamStarted = 0;
6362 /* Do user callback now after all state has been reset */
6363 /* This means it should be safe for the called function */
6364 /* to invoke e.g. StartStream */
6365 if( stream->streamRepresentation.streamFinishedCallback != 0 )
6366 stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
6369 stream->streamActive = 0;
6370 stream->streamStarted = 0;
6377 static PaError IsStreamStopped( PaStream *s )
6379 PaWinWdmStream *stream = (PaWinWdmStream*)s;
6384 if(!stream->streamStarted)
6392 static PaError IsStreamActive( PaStream *s )
6394 PaWinWdmStream *stream = (PaWinWdmStream*)s;
6399 if(stream->streamActive)
6407 static PaTime GetStreamTime( PaStream* s )
6412 return PaUtil_GetTime();
6416 static double GetStreamCpuLoad( PaStream* s )
6418 PaWinWdmStream *stream = (PaWinWdmStream*)s;
6421 result = PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
6428 As separate stream interfaces are used for blocking and callback
6429 streams, the following functions can be guaranteed to only be called
6430 for blocking streams.
6433 static PaError ReadStream( PaStream* s,
6435 unsigned long frames )
6437 PaWinWdmStream *stream = (PaWinWdmStream*)s;
6441 /* suppress unused variable warnings */
6446 /* IMPLEMENT ME, see portaudio.h for required behavior*/
6448 return paInternalError;
6452 static PaError WriteStream( PaStream* s,
6454 unsigned long frames )
6456 PaWinWdmStream *stream = (PaWinWdmStream*)s;
6460 /* suppress unused variable warnings */
6465 /* IMPLEMENT ME, see portaudio.h for required behavior*/
6467 return paInternalError;
6471 static signed long GetStreamReadAvailable( PaStream* s )
6473 PaWinWdmStream *stream = (PaWinWdmStream*)s;
6477 /* suppress unused variable warnings */
6480 /* IMPLEMENT ME, see portaudio.h for required behavior*/
6486 static signed long GetStreamWriteAvailable( PaStream* s )
6488 PaWinWdmStream *stream = (PaWinWdmStream*)s;
6491 /* suppress unused variable warnings */
6494 /* IMPLEMENT ME, see portaudio.h for required behavior*/
6499 /***************************************************************************************/
6500 /* Event and submit handlers for WaveCyclic */
6501 /***************************************************************************************/
6503 static PaError PaPinCaptureEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6505 PaError result = paNoError;
6506 ring_buffer_size_t frameCount;
6507 DATAPACKET* packet = pInfo->stream->capture.packets + eventIndex;
6509 assert( eventIndex < pInfo->stream->capture.noOfPackets );
6511 if (packet->Header.DataUsed == 0)
6513 PA_HP_TRACE((pInfo->stream->hLog, ">>> Capture bogus event (no data): idx=%u", eventIndex));
6515 /* Bogus event, reset! This is to handle the behavior of this USB mic: http://shop.xtz.se/measurement-system/microphone-to-dirac-live-room-correction-suite
6516 on startup of streaming, where it erroneously sets the event without the corresponding buffer being filled (DataUsed == 0) */
6517 ResetEvent(packet->Signal.hEvent);
6519 result = -1; /* Only need this to be NOT paNoError */
6523 pInfo->capturePackets[pInfo->captureHead & cPacketsArrayMask].packet = packet;
6525 frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer, packet->Header.Data, pInfo->stream->capture.framesPerBuffer);
6527 PA_HP_TRACE((pInfo->stream->hLog, ">>> Capture event: idx=%u (frames=%u)", eventIndex, frameCount));
6528 ++pInfo->captureHead;
6531 --pInfo->pending; /* This needs to be done in either case */
6535 static PaError PaPinCaptureSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6537 PaError result = paNoError;
6538 DATAPACKET* packet = pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet;
6539 pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0;
6540 assert(packet != 0);
6541 PA_HP_TRACE((pInfo->stream->hLog, "Capture submit: %u", eventIndex));
6542 packet->Header.DataUsed = 0; /* Reset for reuse */
6543 ResetEvent(packet->Signal.hEvent);
6544 result = PinRead(pInfo->stream->capture.pPin->handle, packet);
6549 static PaError PaPinRenderEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6551 assert( eventIndex < pInfo->stream->render.noOfPackets );
6553 pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask].packet = pInfo->stream->render.packets + eventIndex;
6554 PA_HP_TRACE((pInfo->stream->hLog, "<<< Render event : idx=%u head=%u", eventIndex, pInfo->renderHead));
6555 ++pInfo->renderHead;
6560 static PaError PaPinRenderSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6562 PaError result = paNoError;
6563 DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet;
6564 pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0;
6565 assert(packet != 0);
6567 PA_HP_TRACE((pInfo->stream->hLog, "Render submit : %u idx=%u", pInfo->renderTail, (unsigned)(packet - pInfo->stream->render.packets)));
6568 ResetEvent(packet->Signal.hEvent);
6569 result = PinWrite(pInfo->stream->render.pPin->handle, packet);
6570 /* Reset event, just in case we have an analogous situation to capture (see PaPinCaptureSubmitHandler_WaveCyclic) */
6579 /***************************************************************************************/
6580 /* Event and submit handlers for WaveRT */
6581 /***************************************************************************************/
6583 static PaError PaPinCaptureEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6587 unsigned frameCount;
6588 PaWinWdmIOInfo* pCapture = &pInfo->stream->capture;
6589 const unsigned halfInputBuffer = pCapture->hostBufferSize >> 1;
6590 PaWinWdmPin* pin = pCapture->pPin;
6591 DATAPACKET* packet = 0;
6593 /* Get hold of current ADC position */
6594 pin->fnAudioPosition(pin, &pos);
6595 /* Wrap it (robi: why not use hw latency compensation here ?? because pos then gets _way_ off from
6596 where it should be, i.e. at beginning or half buffer position. Why? No idea.) */
6598 pos %= pCapture->hostBufferSize;
6599 /* Then realInBuf will point to "other" half of double buffer */
6600 realInBuf = pos < halfInputBuffer ? 1U : 0U;
6602 packet = pInfo->stream->capture.packets + realInBuf;
6604 /* Call barrier (or dummy) */
6605 pin->fnMemBarrier();
6607 /* Put it in queue */
6608 frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer, packet->Header.Data, pCapture->framesPerBuffer);
6610 pInfo->capturePackets[pInfo->captureHead & cPacketsArrayMask].packet = packet;
6612 PA_HP_TRACE((pInfo->stream->hLog, "Capture event (WaveRT): idx=%u head=%u (pos = %4.1lf%%, frames=%u)", realInBuf, pInfo->captureHead, (pos * 100.0 / pCapture->hostBufferSize), frameCount));
6614 ++pInfo->captureHead;
6620 static PaError PaPinCaptureEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6623 unsigned bytesToRead;
6624 PaWinWdmIOInfo* pCapture = &pInfo->stream->capture;
6625 const unsigned halfInputBuffer = pCapture->hostBufferSize>>1;
6626 PaWinWdmPin* pin = pInfo->stream->capture.pPin;
6628 /* Get hold of current ADC position */
6629 pin->fnAudioPosition(pin, &pos);
6630 /* Wrap it (robi: why not use hw latency compensation here ?? because pos then gets _way_ off from
6631 where it should be, i.e. at beginning or half buffer position. Why? No idea.) */
6632 /* Compensate for HW FIFO to get to last read buffer position */
6633 pos += pin->hwLatency;
6634 pos %= pCapture->hostBufferSize;
6635 /* Need to align position on frame boundary */
6636 pos &= ~(pCapture->bytesPerFrame - 1);
6638 /* Call barrier (or dummy) */
6639 pin->fnMemBarrier();
6641 /* Put it in "queue" */
6642 bytesToRead = (pCapture->hostBufferSize + pos - pCapture->lastPosition) % pCapture->hostBufferSize;
6643 if (bytesToRead > 0)
6645 unsigned frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer,
6646 pCapture->hostBuffer + pCapture->lastPosition,
6647 bytesToRead / pCapture->bytesPerFrame);
6649 pCapture->lastPosition = (pCapture->lastPosition + frameCount * pCapture->bytesPerFrame) % pCapture->hostBufferSize;
6651 PA_HP_TRACE((pInfo->stream->hLog, "Capture event (WaveRTPolled): pos = %4.1lf%%, framesRead=%u", (pos * 100.0 / pCapture->hostBufferSize), frameCount));
6652 ++pInfo->captureHead;
6658 static PaError PaPinCaptureSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6660 pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0;
6665 static PaError PaPinCaptureSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6667 pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0;
6672 static PaError PaPinRenderEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6675 unsigned realOutBuf;
6676 PaWinWdmIOInfo* pRender = &pInfo->stream->render;
6677 const unsigned halfOutputBuffer = pRender->hostBufferSize >> 1;
6678 PaWinWdmPin* pin = pInfo->stream->render.pPin;
6679 PaIOPacket* ioPacket = &pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask];
6681 /* Get hold of current DAC position */
6682 pin->fnAudioPosition(pin, &pos);
6683 /* Compensate for HW FIFO to get to last read buffer position */
6684 pos += pin->hwLatency;
6686 pos %= pRender->hostBufferSize;
6687 /* And align it, not sure its really needed though */
6688 pos &= ~(pRender->bytesPerFrame - 1);
6689 /* Then realOutBuf will point to "other" half of double buffer */
6690 realOutBuf = pos < halfOutputBuffer ? 1U : 0U;
6694 realOutBuf = pInfo->renderHead & 0x1;
6696 ioPacket->packet = pInfo->stream->render.packets + realOutBuf;
6697 ioPacket->startByte = realOutBuf * halfOutputBuffer;
6698 ioPacket->lengthBytes = halfOutputBuffer;
6700 PA_HP_TRACE((pInfo->stream->hLog, "Render event (WaveRT) : idx=%u head=%u (pos = %4.1lf%%)", realOutBuf, pInfo->renderHead, (pos * 100.0 / pRender->hostBufferSize) ));
6702 ++pInfo->renderHead;
6707 static PaError PaPinRenderEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6710 unsigned realOutBuf;
6711 unsigned bytesToWrite;
6713 PaWinWdmIOInfo* pRender = &pInfo->stream->render;
6714 const unsigned halfOutputBuffer = pRender->hostBufferSize >> 1;
6715 PaWinWdmPin* pin = pInfo->stream->render.pPin;
6716 PaIOPacket* ioPacket = &pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask];
6718 /* Get hold of current DAC position */
6719 pin->fnAudioPosition(pin, &pos);
6720 /* Compensate for HW FIFO to get to last read buffer position */
6721 pos += pin->hwLatency;
6723 pos %= pRender->hostBufferSize;
6724 /* And align it, not sure its really needed though */
6725 pos &= ~(pRender->bytesPerFrame - 1);
6729 realOutBuf = pInfo->renderHead & 0x1;
6730 ioPacket->packet = pInfo->stream->render.packets + realOutBuf;
6731 ioPacket->startByte = realOutBuf * halfOutputBuffer;
6732 ioPacket->lengthBytes = halfOutputBuffer;
6733 ++pInfo->renderHead;
6738 bytesToWrite = (pRender->hostBufferSize + pos - pRender->lastPosition) % pRender->hostBufferSize;
6739 ++pRender->pollCntr;
6740 if (bytesToWrite >= halfOutputBuffer)
6742 realOutBuf = (pos < halfOutputBuffer) ? 1U : 0U;
6743 ioPacket->packet = pInfo->stream->render.packets + realOutBuf;
6744 pRender->lastPosition = realOutBuf ? 0U : halfOutputBuffer;
6745 ioPacket->startByte = realOutBuf * halfOutputBuffer;
6746 ioPacket->lengthBytes = halfOutputBuffer;
6747 ++pInfo->renderHead;
6749 PA_HP_TRACE((pInfo->stream->hLog, "Render event (WaveRTPolled) : idx=%u head=%u (pos = %4.1lf%%, cnt=%u)", realOutBuf, pInfo->renderHead, (pos * 100.0 / pRender->hostBufferSize), pRender->pollCntr));
6750 pRender->pollCntr = 0;
6756 static PaError PaPinRenderSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6758 PaWinWdmPin* pin = pInfo->stream->render.pPin;
6759 pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0;
6760 /* Call barrier (if needed) */
6761 pin->fnMemBarrier();
6762 PA_HP_TRACE((pInfo->stream->hLog, "Render submit (WaveRT) : submit=%u", pInfo->renderTail));
6769 PA_HP_TRACE((pInfo->stream->hLog, "Setting WaveRT event for priming (2)"));
6770 SetEvent(pInfo->stream->render.events[0]);
6776 static PaError PaPinRenderSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6778 PaWinWdmPin* pin = pInfo->stream->render.pPin;
6779 pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0;
6780 /* Call barrier (if needed) */
6781 pin->fnMemBarrier();
6782 PA_HP_TRACE((pInfo->stream->hLog, "Render submit (WaveRTPolled) : submit=%u", pInfo->renderTail));
6789 PA_HP_TRACE((pInfo->stream->hLog, "Setting WaveRT event for priming (2)"));
6790 SetEvent(pInfo->stream->render.events[0]);