1 /*
2 * $Id$
3 * PortAudio Windows WDM-KS interface
4 *
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
8 *
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:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
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.
27 */
28 
29 /*
30 * The text above constitutes the entire PortAudio license; however,
31 * the PortAudio community also makes the following non-binding requests:
32 *
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
37 * license above.
38 */
39 
40 /** @file
41 @ingroup hostapi_src
42 @brief Portaudio WDM-KS host API.
43 
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
51 */
52 
53 #include <stdio.h>
54 
55 #if (defined(_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
56 #pragma comment( lib, "setupapi.lib" )
57 #endif
58 
59 /* Debugging/tracing support */
60 
61 #define PA_LOGE_
62 #define PA_LOGL_
63 
64 #ifdef __GNUC__
65 #include <initguid.h>
66 #define _WIN32_WINNT 0x0501
67 #define WINVER 0x0501
68 #endif
69 
70 #include <string.h> /* strlen() */
71 #include <assert.h>
72 #include <wchar.h>  /* iswspace() */
73 
74 #include "pa_util.h"
75 #include "pa_allocation.h"
76 #include "pa_hostapi.h"
77 #include "pa_stream.h"
78 #include "pa_cpuload.h"
79 #include "pa_process.h"
80 #include "portaudio.h"
81 #include "pa_debugprint.h"
82 #include "pa_memorybarrier.h"
83 #include "pa_ringbuffer.h"
84 #include "pa_trace.h"
85 #include "pa_win_waveformat.h"
86 
87 #include "pa_win_wdmks.h"
88 
89 #ifndef DRV_QUERYDEVICEINTERFACE
90 #define DRV_QUERYDEVICEINTERFACE     (DRV_RESERVED + 12)
91 #endif
92 #ifndef DRV_QUERYDEVICEINTERFACESIZE
93 #define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13)
94 #endif
95 
96 #include <windows.h>
97 #ifndef __GNUC__ /* Fix for ticket #257: MinGW-w64: Inclusion of <winioctl.h> triggers multiple redefinition errors. */
98 #include <winioctl.h>
99 #endif
100 #include <process.h>
101 
102 #include <math.h>
103 
104 #ifdef _MSC_VER
105 #define snprintf _snprintf
106 #define vsnprintf _vsnprintf
107 #endif
108 
109 /* The PA_HP_TRACE macro is used in RT parts, so it can be switched off without affecting
110 the rest of the debug tracing */
111 #if 1
112 #define PA_HP_TRACE(x)  PaUtil_AddHighSpeedLogMessage x ;
113 #else
114 #define PA_HP_TRACE(x)
115 #endif
116 
117 /* A define that selects whether the resulting pin names are chosen from pin category
118 instead of the available pin names, who sometimes can be quite cheesy, like "Volume control".
119 Default is to use the pin category.
120 */
121 #ifndef PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES
122 #define PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES  1
123 #endif
124 
125 #ifdef __GNUC__
126 #undef PA_LOGE_
127 #define PA_LOGE_ PA_DEBUG(("%s {\n",__FUNCTION__))
128 #undef PA_LOGL_
129 #define PA_LOGL_ PA_DEBUG(("} %s\n",__FUNCTION__))
130 /* These defines are set in order to allow the WIndows DirectX
131 * headers to compile with a GCC compiler such as MinGW
132 * NOTE: The headers may generate a few warning in GCC, but
133 * they should compile */
134 #define _INC_MMSYSTEM
135 #define _INC_MMREG
136 #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
137 #define DEFINE_GUID_THUNK(name,guid) DEFINE_GUID(name,guid)
138 #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK( n, STATIC_##n )
139 #if !defined( DEFINE_WAVEFORMATEX_GUID )
140 #define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
141 #endif
142 #define  WAVE_FORMAT_ADPCM      0x0002
143 #define  WAVE_FORMAT_IEEE_FLOAT 0x0003
144 #define  WAVE_FORMAT_ALAW       0x0006
145 #define  WAVE_FORMAT_MULAW      0x0007
146 #define  WAVE_FORMAT_MPEG       0x0050
147 #define  WAVE_FORMAT_DRM        0x0009
148 #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}}
149 #define DYNAMIC_GUID(data) DYNAMIC_GUID_THUNK(data)
150 #endif
151 
152 /* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */
153 #if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
154 #define CREATE_THREAD_FUNCTION (HANDLE)_beginthreadex
155 #define PA_THREAD_FUNC static unsigned WINAPI
156 #else
157 #define CREATE_THREAD_FUNCTION CreateThread
158 #define PA_THREAD_FUNC static DWORD WINAPI
159 #endif
160 
161 #ifdef _MSC_VER
162 #define NOMMIDS
163 #define DYNAMIC_GUID(data) {data}
164 #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
165 #undef DEFINE_GUID
166 #define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
167 #define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
168 #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
169 #endif
170 
171 #include <setupapi.h>
172 
173 #ifndef EXTERN_C
174 #define EXTERN_C extern
175 #endif
176 
177 #if defined(__GNUC__)
178 
179 /* For MinGW we reference mingw-include files supplied with WASAPI */
180 #define WINBOOL BOOL
181 
182 #include "../wasapi/mingw-include/ks.h"
183 #include "../wasapi/mingw-include/ksmedia.h"
184 
185 #else
186 
187 #include <mmreg.h>
188 #include <ks.h>
189 
190 /* Note that Windows SDK V6.0A or later is needed for WaveRT specific structs to be present in
191    ksmedia.h. Also make sure that the SDK include path is before other include paths (that may contain
192    an "old" ksmedia.h), so the proper ksmedia.h is used */
193 #include <ksmedia.h>
194 
195 #endif
196 
197 #include <assert.h>
198 #include <stdio.h>
199 
200 /* These next definitions allow the use of the KSUSER DLL */
201 typedef /*KSDDKAPI*/ DWORD WINAPI KSCREATEPIN(HANDLE, PKSPIN_CONNECT, ACCESS_MASK, PHANDLE);
202 extern HMODULE      DllKsUser;
203 extern KSCREATEPIN* FunctionKsCreatePin;
204 
205 /* These definitions allows the use of AVRT.DLL on Vista and later OSs */
206 typedef enum _PA_AVRT_PRIORITY
207 {
208     PA_AVRT_PRIORITY_LOW = -1,
209     PA_AVRT_PRIORITY_NORMAL,
210     PA_AVRT_PRIORITY_HIGH,
211     PA_AVRT_PRIORITY_CRITICAL
212 } PA_AVRT_PRIORITY, *PPA_AVRT_PRIORITY;
213 
214 typedef struct
215 {
216     HINSTANCE hInstance;
217 
218     HANDLE  (WINAPI *AvSetMmThreadCharacteristics) (LPCSTR, LPDWORD);
219     BOOL    (WINAPI *AvRevertMmThreadCharacteristics) (HANDLE);
220     BOOL    (WINAPI *AvSetMmThreadPriority) (HANDLE, PA_AVRT_PRIORITY);
221 } PaWinWDMKSAvRtEntryPoints;
222 
223 static PaWinWDMKSAvRtEntryPoints paWinWDMKSAvRtEntryPoints = {0};
224 
225 /* An unspecified channel count (-1) is not treated correctly, so we replace it with
226 * an arbitrarily large number */
227 #define MAXIMUM_NUMBER_OF_CHANNELS 256
228 
229 /* Forward definition to break circular type reference between pin and filter */
230 struct __PaWinWdmFilter;
231 typedef struct __PaWinWdmFilter PaWinWdmFilter;
232 
233 struct __PaWinWdmPin;
234 typedef struct __PaWinWdmPin PaWinWdmPin;
235 
236 struct __PaWinWdmStream;
237 typedef struct __PaWinWdmStream PaWinWdmStream;
238 
239 /* Function prototype for getting audio position */
240 typedef PaError (*FunctionGetPinAudioPosition)(PaWinWdmPin*, unsigned long*);
241 
242 /* Function prototype for memory barrier */
243 typedef void (*FunctionMemoryBarrier)(void);
244 
245 struct __PaProcessThreadInfo;
246 typedef struct __PaProcessThreadInfo PaProcessThreadInfo;
247 
248 typedef PaError (*FunctionPinHandler)(PaProcessThreadInfo* pInfo, unsigned eventIndex);
249 
250 typedef enum __PaStreamStartEnum
251 {
252     StreamStart_kOk,
253     StreamStart_kFailed,
254     StreamStart_kCnt
255 } PaStreamStartEnum;
256 
257 /* Multiplexed input structure.
258 *  Very often several physical inputs are multiplexed through a MUX node (represented in the topology filter) */
259 typedef struct __PaWinWdmMuxedInput
260 {
261     wchar_t                     friendlyName[MAX_PATH];
262     ULONG                       muxPinId;
263     ULONG                       muxNodeId;
264     ULONG                       endpointPinId;
265 } PaWinWdmMuxedInput;
266 
267 /* The Pin structure
268 * A pin is an input or output node, e.g. for audio flow */
269 struct __PaWinWdmPin
270 {
271     HANDLE                      handle;
272     PaWinWdmMuxedInput**        inputs;
273     unsigned                    inputCount;
274     wchar_t                     friendlyName[MAX_PATH];
275 
276     PaWinWdmFilter*             parentFilter;
277     PaWDMKSSubType              pinKsSubType;
278     unsigned long               pinId;
279     unsigned long               endpointPinId;  /* For output pins */
280     KSPIN_CONNECT*              pinConnect;
281     unsigned long               pinConnectSize;
282     KSDATAFORMAT_WAVEFORMATEX*  ksDataFormatWfx;
283     KSPIN_COMMUNICATION         communication;
284     KSDATARANGE*                dataRanges;
285     KSMULTIPLE_ITEM*            dataRangesItem;
286     KSPIN_DATAFLOW              dataFlow;
287     KSPIN_CINSTANCES            instances;
288     unsigned long               frameSize;
289     int                         maxChannels;
290     unsigned long               formats;
291     int                         defaultSampleRate;
292     ULONG                       *positionRegister;  /* WaveRT */
293     ULONG                       hwLatency;          /* WaveRT */
294     FunctionMemoryBarrier       fnMemBarrier;       /* WaveRT */
295     FunctionGetPinAudioPosition fnAudioPosition;    /* WaveRT */
296     FunctionPinHandler          fnEventHandler;
297     FunctionPinHandler          fnSubmitHandler;
298 };
299 
300 /* The Filter structure
301 * A filter has a number of pins and a "friendly name" */
302 struct __PaWinWdmFilter
303 {
304     HANDLE         handle;
305     PaWinWDMKSDeviceInfo    devInfo;  /* This will hold information that is exposed in PaDeviceInfo */
306 
307     DWORD            deviceNode;
308     int            pinCount;
309     PaWinWdmPin**  pins;
310     PaWinWdmFilter*  topologyFilter;
311     wchar_t          friendlyName[MAX_PATH];
312     int              validPinCount;
313     int            usageCount;
314     KSMULTIPLE_ITEM* connections;
315     KSMULTIPLE_ITEM* nodes;
316     int              filterRefCount;
317 };
318 
319 
320 typedef struct __PaWinWdmDeviceInfo
321 {
322     PaDeviceInfo    inheritedDeviceInfo;
323     char            compositeName[MAX_PATH];   /* Composite name consists of pin name + device name in utf8 */
324     PaWinWdmFilter* filter;
325     unsigned long   pin;
326     int             muxPosition;    /* Used only for input devices */
327     int             endpointPinId;
328 }
329 PaWinWdmDeviceInfo;
330 
331 /* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */
332 typedef struct __PaWinWdmHostApiRepresentation
333 {
334     PaUtilHostApiRepresentation  inheritedHostApiRep;
335     PaUtilStreamInterface        callbackStreamInterface;
336     PaUtilStreamInterface        blockingStreamInterface;
337 
338     PaUtilAllocationGroup*       allocations;
339     int                          deviceCount;
340 }
341 PaWinWdmHostApiRepresentation;
342 
343 typedef struct __DATAPACKET
344 {
345     KSSTREAM_HEADER  Header;
346     OVERLAPPED       Signal;
347 } DATAPACKET;
348 
349 typedef struct __PaIOPacket
350 {
351     DATAPACKET*     packet;
352     unsigned        startByte;
353     unsigned        lengthBytes;
354 } PaIOPacket;
355 
356 typedef struct __PaWinWdmIOInfo
357 {
358     PaWinWdmPin*        pPin;
359     char*               hostBuffer;
360     unsigned            hostBufferSize;
361     unsigned            framesPerBuffer;
362     unsigned            bytesPerFrame;
363     unsigned            bytesPerSample;
364     unsigned            noOfPackets;    /* Only used in WaveCyclic */
365     HANDLE              *events;        /* noOfPackets handles (WaveCyclic) 1 (WaveRT) */
366     DATAPACKET          *packets;       /* noOfPackets packets (WaveCyclic) 2 (WaveRT) */
367     /* WaveRT polled mode */
368     unsigned            lastPosition;
369     unsigned            pollCntr;
370 } PaWinWdmIOInfo;
371 
372 /* PaWinWdmStream - a stream data structure specifically for this implementation */
373 struct __PaWinWdmStream
374 {
375     PaUtilStreamRepresentation  streamRepresentation;
376     PaWDMKSSpecificStreamInfo   hostApiStreamInfo;    /* This holds info that is exposed through PaStreamInfo */
377     PaUtilCpuLoadMeasurer       cpuLoadMeasurer;
378     PaUtilBufferProcessor       bufferProcessor;
379 
380 #if PA_TRACE_REALTIME_EVENTS
381     LogHandle                   hLog;
382 #endif
383 
384     PaUtilAllocationGroup*      allocGroup;
385     PaWinWdmIOInfo              capture;
386     PaWinWdmIOInfo              render;
387     int                         streamStarted;
388     int                         streamActive;
389     int                         streamStop;
390     int                         streamAbort;
391     int                         oldProcessPriority;
392     HANDLE                      streamThread;
393     HANDLE                      eventAbort;
394     HANDLE                      eventStreamStart[StreamStart_kCnt];        /* 0 = OK, 1 = Failed */
395     PaError                     threadResult;
396     PaStreamFlags               streamFlags;
397 
398     /* Capture ring buffer */
399     PaUtilRingBuffer            ringBuffer;
400     char*                       ringBufferData;
401 
402     /* These values handle the case where the user wants to use fewer
403     * channels than the device has */
404     int                         userInputChannels;
405     int                         deviceInputChannels;
406     int                         userOutputChannels;
407     int                         deviceOutputChannels;
408 };
409 
410 /* Gather all processing variables in a struct */
411 struct __PaProcessThreadInfo
412 {
413     PaWinWdmStream              *stream;
414     PaStreamCallbackTimeInfo    ti;
415     PaStreamCallbackFlags       underover;
416     int                         cbResult;
417     volatile int                pending;
418     volatile int                priming;
419     volatile int                pinsStarted;
420     unsigned long               timeout;
421     unsigned                    captureHead;
422     unsigned                    captureTail;
423     unsigned                    renderHead;
424     unsigned                    renderTail;
425     PaIOPacket                  capturePackets[4];
426     PaIOPacket                  renderPackets[4];
427 };
428 
429 /* Used for transferring device infos during scanning / rescanning */
430 typedef struct __PaWinWDMScanDeviceInfosResults
431 {
432     PaDeviceInfo **deviceInfos;
433     PaDeviceIndex defaultInputDevice;
434     PaDeviceIndex defaultOutputDevice;
435 } PaWinWDMScanDeviceInfosResults;
436 
437 static const unsigned cPacketsArrayMask = 3;
438 
439 HMODULE      DllKsUser = NULL;
440 KSCREATEPIN* FunctionKsCreatePin = NULL;
441 
442 /* prototypes for functions declared in this file */
443 
444 #ifdef __cplusplus
445 extern "C"
446 {
447 #endif /* __cplusplus */
448 
449     PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
450 
451 #ifdef __cplusplus
452 }
453 #endif /* __cplusplus */
454 
455 /* Low level I/O functions */
456 static PaError WdmSyncIoctl(HANDLE handle,
457                             unsigned long ioctlNumber,
458                             void* inBuffer,
459                             unsigned long inBufferCount,
460                             void* outBuffer,
461                             unsigned long outBufferCount,
462                             unsigned long* bytesReturned);
463 
464 static PaError WdmGetPropertySimple(HANDLE handle,
465                                     const GUID* const guidPropertySet,
466                                     unsigned long property,
467                                     void* value,
468                                     unsigned long valueCount);
469 
470 static PaError WdmSetPropertySimple(HANDLE handle,
471                                     const GUID* const guidPropertySet,
472                                     unsigned long property,
473                                     void* value,
474                                     unsigned long valueCount,
475                                     void* instance,
476                                     unsigned long instanceCount);
477 
478 static PaError WdmGetPinPropertySimple(HANDLE  handle,
479                                        unsigned long pinId,
480                                        const GUID* const guidPropertySet,
481                                        unsigned long property,
482                                        void* value,
483                                        unsigned long valueCount,
484                                        unsigned long* byteCount);
485 
486 static PaError WdmGetPinPropertyMulti(HANDLE  handle,
487                                       unsigned long pinId,
488                                       const GUID* const guidPropertySet,
489                                       unsigned long property,
490                                       KSMULTIPLE_ITEM** ksMultipleItem);
491 
492 static PaError WdmGetPropertyMulti(HANDLE handle,
493                                    const GUID* const guidPropertySet,
494                                    unsigned long property,
495                                    KSMULTIPLE_ITEM** ksMultipleItem);
496 
497 static PaError WdmSetMuxNodeProperty(HANDLE handle,
498                                      ULONG nodeId,
499                                      ULONG pinId);
500 
501 
502 /** Pin management functions */
503 static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error);
504 static void PinFree(PaWinWdmPin* pin);
505 static void PinClose(PaWinWdmPin* pin);
506 static PaError PinInstantiate(PaWinWdmPin* pin);
507 /*static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state); NOT USED */
508 static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state);
509 static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format);
510 static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format);
511 /* WaveRT support */
512 static PaError PinQueryNotificationSupport(PaWinWdmPin* pPin, BOOL* pbResult);
513 static PaError PinGetBuffer(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier);
514 static PaError PinRegisterPositionRegister(PaWinWdmPin* pPin);
515 static PaError PinRegisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle);
516 static PaError PinUnregisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle);
517 static PaError PinGetHwLatency(PaWinWdmPin* pPin, ULONG* pFifoSize, ULONG* pChipsetDelay, ULONG* pCodecDelay);
518 static PaError PinGetAudioPositionMemoryMapped(PaWinWdmPin* pPin, ULONG* pPosition);
519 static PaError PinGetAudioPositionViaIOCTLRead(PaWinWdmPin* pPin, ULONG* pPosition);
520 static PaError PinGetAudioPositionViaIOCTLWrite(PaWinWdmPin* pPin, ULONG* pPosition);
521 
522 /* Filter management functions */
523 static PaWinWdmFilter* FilterNew(PaWDMKSType type, DWORD devNode, const wchar_t* filterName, const wchar_t* friendlyName, PaError* error);
524 static PaError FilterInitializePins(PaWinWdmFilter* filter);
525 static void FilterFree(PaWinWdmFilter* filter);
526 static void FilterAddRef(PaWinWdmFilter* filter);
527 static PaWinWdmPin* FilterCreatePin(
528                                     PaWinWdmFilter* filter,
529                                     int pinId,
530                                     const WAVEFORMATEX* wfex,
531                                     PaError* error);
532 static PaError FilterUse(PaWinWdmFilter* filter);
533 static void FilterRelease(PaWinWdmFilter* filter);
534 
535 /* Hot plug functions */
536 static BOOL IsDeviceTheSame(const PaWinWdmDeviceInfo* pDev1,
537                             const PaWinWdmDeviceInfo* pDev2);
538 
539 /* Interface functions */
540 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
541 static PaError IsFormatSupported(
542 struct PaUtilHostApiRepresentation *hostApi,
543     const PaStreamParameters *inputParameters,
544     const PaStreamParameters *outputParameters,
545     double sampleRate );
546 
547 static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void **newDeviceInfos, int *newDeviceCount );
548 static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *deviceInfos, int deviceCount );
549 static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *deviceInfos, int deviceCount );
550 
551 static PaError OpenStream(
552 struct PaUtilHostApiRepresentation *hostApi,
553     PaStream** s,
554     const PaStreamParameters *inputParameters,
555     const PaStreamParameters *outputParameters,
556     double sampleRate,
557     unsigned long framesPerBuffer,
558     PaStreamFlags streamFlags,
559     PaStreamCallback *streamCallback,
560     void *userData );
561 static PaError CloseStream( PaStream* stream );
562 static PaError StartStream( PaStream *stream );
563 static PaError StopStream( PaStream *stream );
564 static PaError AbortStream( PaStream *stream );
565 static PaError IsStreamStopped( PaStream *s );
566 static PaError IsStreamActive( PaStream *stream );
567 static PaTime GetStreamTime( PaStream *stream );
568 static double GetStreamCpuLoad( PaStream* stream );
569 static PaError ReadStream(
570                           PaStream* stream,
571                           void *buffer,
572                           unsigned long frames );
573 static PaError WriteStream(
574                            PaStream* stream,
575                            const void *buffer,
576                            unsigned long frames );
577 static signed long GetStreamReadAvailable( PaStream* stream );
578 static signed long GetStreamWriteAvailable( PaStream* stream );
579 
580 /* Utility functions */
581 static unsigned long GetWfexSize(const WAVEFORMATEX* wfex);
582 static PaWinWdmFilter** BuildFilterList(int* filterCount, int* noOfPaDevices, PaError* result);
583 static BOOL PinWrite(HANDLE h, DATAPACKET* p);
584 static BOOL PinRead(HANDLE h, DATAPACKET* p);
585 static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples);
586 static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples);
587 PA_THREAD_FUNC ProcessingThread(void*);
588 
589 /* Pin handler functions */
590 static PaError PaPinCaptureEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
591 static PaError PaPinCaptureSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
592 
593 static PaError PaPinRenderEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
594 static PaError PaPinRenderSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
595 
596 static PaError PaPinCaptureEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
597 static PaError PaPinCaptureEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
598 static PaError PaPinCaptureSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
599 static PaError PaPinCaptureSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
600 
601 static PaError PaPinRenderEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
602 static PaError PaPinRenderEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
603 static PaError PaPinRenderSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
604 static PaError PaPinRenderSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
605 
606 /* Function bodies */
607 
608 #if defined(_DEBUG) && defined(PA_ENABLE_DEBUG_OUTPUT)
609 #define PA_WDMKS_SET_TREF
610 static PaTime tRef = 0;
611 
PaWinWdmDebugPrintf(const char * fmt,...)612 static void PaWinWdmDebugPrintf(const char* fmt, ...)
613 {
614     va_list list;
615     char buffer[1024];
616     PaTime t = PaUtil_GetTime() - tRef;
617     va_start(list, fmt);
618     _vsnprintf(buffer, 1023, fmt, list);
619     va_end(list);
620     PaUtil_DebugPrint("%6.3lf: %s", t, buffer);
621 }
622 
623 #ifdef PA_DEBUG
624 #undef PA_DEBUG
625 #define PA_DEBUG(x)    PaWinWdmDebugPrintf x ;
626 #endif
627 #endif
628 
IsDeviceTheSame(const PaWinWdmDeviceInfo * pDev1,const PaWinWdmDeviceInfo * pDev2)629 static BOOL IsDeviceTheSame(const PaWinWdmDeviceInfo* pDev1,
630                             const PaWinWdmDeviceInfo* pDev2)
631 {
632     if (pDev1 == NULL || pDev2 == NULL)
633         return FALSE;
634 
635     if (pDev1 == pDev2)
636         return TRUE;
637 
638     if (strcmp(pDev1->compositeName, pDev2->compositeName) == 0)
639         return TRUE;
640 
641     return FALSE;
642 }
643 
IsEarlierThanVista()644 static BOOL IsEarlierThanVista()
645 {
646 /*
647 NOTE: GetVersionEx() is deprecated as of Windows 8.1 and can not be used to reliably detect
648 versions of Windows higher than Windows 8 (due to manifest requirements for reporting higher versions).
649 Microsoft recommends switching to VerifyVersionInfo (available on Win 2k and later), however GetVersionEx
650 is is faster, for now we just disable the deprecation warning.
651 See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
652 See: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
653 */
654 #pragma warning (disable : 4996) /* use of GetVersionEx */
655 
656     OSVERSIONINFO osvi;
657     osvi.dwOSVersionInfoSize = sizeof(osvi);
658     if (GetVersionEx(&osvi) && osvi.dwMajorVersion<6)
659     {
660         return TRUE;
661     }
662     return FALSE;
663 
664 #pragma warning (default : 4996)
665 }
666 
667 
668 
MemoryBarrierDummy(void)669 static void MemoryBarrierDummy(void)
670 {
671     /* Do nothing */
672 }
673 
MemoryBarrierRead(void)674 static void MemoryBarrierRead(void)
675 {
676     PaUtil_ReadMemoryBarrier();
677 }
678 
MemoryBarrierWrite(void)679 static void MemoryBarrierWrite(void)
680 {
681     PaUtil_WriteMemoryBarrier();
682 }
683 
GetWfexSize(const WAVEFORMATEX * wfex)684 static unsigned long GetWfexSize(const WAVEFORMATEX* wfex)
685 {
686     if( wfex->wFormatTag == WAVE_FORMAT_PCM )
687     {
688         return sizeof( WAVEFORMATEX );
689     }
690     else
691     {
692         return (sizeof( WAVEFORMATEX ) + wfex->cbSize);
693     }
694 }
695 
PaWinWDM_SetLastErrorInfo(long errCode,const char * fmt,...)696 static void PaWinWDM_SetLastErrorInfo(long errCode, const char* fmt, ...)
697 {
698     va_list list;
699     char buffer[1024];
700     va_start(list, fmt);
701     _vsnprintf(buffer, 1023, fmt, list);
702     va_end(list);
703     PaUtil_SetLastHostErrorInfo(paWDMKS, errCode, buffer);
704 }
705 
706 /*
707 Low level pin/filter access functions
708 */
WdmSyncIoctl(HANDLE handle,unsigned long ioctlNumber,void * inBuffer,unsigned long inBufferCount,void * outBuffer,unsigned long outBufferCount,unsigned long * bytesReturned)709 static PaError WdmSyncIoctl(
710                             HANDLE handle,
711                             unsigned long ioctlNumber,
712                             void* inBuffer,
713                             unsigned long inBufferCount,
714                             void* outBuffer,
715                             unsigned long outBufferCount,
716                             unsigned long* bytesReturned)
717 {
718     PaError result = paNoError;
719     unsigned long dummyBytesReturned = 0;
720     BOOL bRes;
721 
722     if( !bytesReturned )
723     {
724         /* Use a dummy as the caller hasn't supplied one */
725         bytesReturned = &dummyBytesReturned;
726     }
727 
728     bRes = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount, outBuffer, outBufferCount, bytesReturned, NULL);
729     if (!bRes)
730     {
731         unsigned long error = GetLastError();
732         if ( !(((error == ERROR_INSUFFICIENT_BUFFER ) || ( error == ERROR_MORE_DATA )) &&
733             ( ioctlNumber == IOCTL_KS_PROPERTY ) &&
734             ( outBufferCount == 0 ) ) )
735         {
736             KSPROPERTY* ksProperty = (KSPROPERTY*)inBuffer;
737 
738             PaWinWDM_SetLastErrorInfo(result, "WdmSyncIoctl: DeviceIoControl GLE = 0x%08X (prop_set = {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}, prop_id = %u)",
739                 error,
740                 ksProperty->Set.Data1, ksProperty->Set.Data2, ksProperty->Set.Data3,
741                 ksProperty->Set.Data4[0], ksProperty->Set.Data4[1],
742                 ksProperty->Set.Data4[2], ksProperty->Set.Data4[3],
743                 ksProperty->Set.Data4[4], ksProperty->Set.Data4[5],
744                 ksProperty->Set.Data4[6], ksProperty->Set.Data4[7],
745                 ksProperty->Id
746                 );
747             result = paUnanticipatedHostError;
748         }
749     }
750     return result;
751 }
752 
WdmGetPropertySimple(HANDLE handle,const GUID * const guidPropertySet,unsigned long property,void * value,unsigned long valueCount)753 static PaError WdmGetPropertySimple(HANDLE handle,
754                                     const GUID* const guidPropertySet,
755                                     unsigned long property,
756                                     void* value,
757                                     unsigned long valueCount)
758 {
759     PaError result;
760     KSPROPERTY ksProperty;
761 
762     ksProperty.Set = *guidPropertySet;
763     ksProperty.Id = property;
764     ksProperty.Flags = KSPROPERTY_TYPE_GET;
765 
766     result = WdmSyncIoctl(
767         handle,
768         IOCTL_KS_PROPERTY,
769         &ksProperty,
770         sizeof(KSPROPERTY),
771         value,
772         valueCount,
773         NULL);
774 
775     return result;
776 }
777 
WdmSetPropertySimple(HANDLE handle,const GUID * const guidPropertySet,unsigned long property,void * value,unsigned long valueCount,void * instance,unsigned long instanceCount)778 static PaError WdmSetPropertySimple(
779                                     HANDLE handle,
780                                     const GUID* const guidPropertySet,
781                                     unsigned long property,
782                                     void* value,
783                                     unsigned long valueCount,
784                                     void* instance,
785                                     unsigned long instanceCount)
786 {
787     PaError result;
788     KSPROPERTY* ksProperty;
789     unsigned long propertyCount  = 0;
790 
791     propertyCount = sizeof(KSPROPERTY) + instanceCount;
792     ksProperty = (KSPROPERTY*)_alloca( propertyCount );
793     if( !ksProperty )
794     {
795         return paInsufficientMemory;
796     }
797 
798     ksProperty->Set = *guidPropertySet;
799     ksProperty->Id = property;
800     ksProperty->Flags = KSPROPERTY_TYPE_SET;
801 
802     if( instance )
803     {
804         memcpy((void*)((char*)ksProperty + sizeof(KSPROPERTY)), instance, instanceCount);
805     }
806 
807     result = WdmSyncIoctl(
808         handle,
809         IOCTL_KS_PROPERTY,
810         ksProperty,
811         propertyCount,
812         value,
813         valueCount,
814         NULL);
815 
816     return result;
817 }
818 
WdmGetPinPropertySimple(HANDLE handle,unsigned long pinId,const GUID * const guidPropertySet,unsigned long property,void * value,unsigned long valueCount,unsigned long * byteCount)819 static PaError WdmGetPinPropertySimple(
820                                        HANDLE  handle,
821                                        unsigned long pinId,
822                                        const GUID* const guidPropertySet,
823                                        unsigned long property,
824                                        void* value,
825                                        unsigned long valueCount,
826                                        unsigned long *byteCount)
827 {
828     PaError result;
829 
830     KSP_PIN ksPProp;
831     ksPProp.Property.Set = *guidPropertySet;
832     ksPProp.Property.Id = property;
833     ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
834     ksPProp.PinId = pinId;
835     ksPProp.Reserved = 0;
836 
837     result = WdmSyncIoctl(
838         handle,
839         IOCTL_KS_PROPERTY,
840         &ksPProp,
841         sizeof(KSP_PIN),
842         value,
843         valueCount,
844         byteCount);
845 
846     return result;
847 }
848 
WdmGetPinPropertyMulti(HANDLE handle,unsigned long pinId,const GUID * const guidPropertySet,unsigned long property,KSMULTIPLE_ITEM ** ksMultipleItem)849 static PaError WdmGetPinPropertyMulti(
850                                       HANDLE handle,
851                                       unsigned long pinId,
852                                       const GUID* const guidPropertySet,
853                                       unsigned long property,
854                                       KSMULTIPLE_ITEM** ksMultipleItem)
855 {
856     PaError result;
857     unsigned long multipleItemSize = 0;
858     KSP_PIN ksPProp;
859 
860     ksPProp.Property.Set = *guidPropertySet;
861     ksPProp.Property.Id = property;
862     ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
863     ksPProp.PinId = pinId;
864     ksPProp.Reserved = 0;
865 
866     result = WdmSyncIoctl(
867         handle,
868         IOCTL_KS_PROPERTY,
869         &ksPProp.Property,
870         sizeof(KSP_PIN),
871         NULL,
872         0,
873         &multipleItemSize);
874     if( result != paNoError )
875     {
876         return result;
877     }
878 
879     *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
880     if( !*ksMultipleItem )
881     {
882         return paInsufficientMemory;
883     }
884 
885     result = WdmSyncIoctl(
886         handle,
887         IOCTL_KS_PROPERTY,
888         &ksPProp,
889         sizeof(KSP_PIN),
890         (void*)*ksMultipleItem,
891         multipleItemSize,
892         NULL);
893 
894     if( result != paNoError )
895     {
896         PaUtil_FreeMemory( ksMultipleItem );
897     }
898 
899     return result;
900 }
901 
WdmGetPropertyMulti(HANDLE handle,const GUID * const guidPropertySet,unsigned long property,KSMULTIPLE_ITEM ** ksMultipleItem)902 static PaError WdmGetPropertyMulti(HANDLE handle,
903                                    const GUID* const guidPropertySet,
904                                    unsigned long property,
905                                    KSMULTIPLE_ITEM** ksMultipleItem)
906 {
907     PaError result;
908     unsigned long multipleItemSize = 0;
909     KSPROPERTY ksProp;
910 
911     ksProp.Set = *guidPropertySet;
912     ksProp.Id = property;
913     ksProp.Flags = KSPROPERTY_TYPE_GET;
914 
915     result = WdmSyncIoctl(
916         handle,
917         IOCTL_KS_PROPERTY,
918         &ksProp,
919         sizeof(KSPROPERTY),
920         NULL,
921         0,
922         &multipleItemSize);
923     if( result != paNoError )
924     {
925         return result;
926     }
927 
928     *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
929     if( !*ksMultipleItem )
930     {
931         return paInsufficientMemory;
932     }
933 
934     result = WdmSyncIoctl(
935         handle,
936         IOCTL_KS_PROPERTY,
937         &ksProp,
938         sizeof(KSPROPERTY),
939         (void*)*ksMultipleItem,
940         multipleItemSize,
941         NULL);
942 
943     if( result != paNoError )
944     {
945         PaUtil_FreeMemory( ksMultipleItem );
946     }
947 
948     return result;
949 }
950 
WdmSetMuxNodeProperty(HANDLE handle,ULONG nodeId,ULONG pinId)951 static PaError WdmSetMuxNodeProperty(HANDLE handle,
952                                      ULONG nodeId,
953                                      ULONG pinId)
954 {
955     PaError result = paNoError;
956     KSNODEPROPERTY prop;
957     prop.Property.Set = KSPROPSETID_Audio;
958     prop.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
959     prop.Property.Flags = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY;
960     prop.NodeId = nodeId;
961     prop.Reserved = 0;
962 
963     result = WdmSyncIoctl(handle, IOCTL_KS_PROPERTY, &prop, sizeof(KSNODEPROPERTY), &pinId, sizeof(ULONG), NULL);
964 
965     return result;
966 }
967 
968 /* Used when traversing topology for outputs */
GetConnectionTo(const KSTOPOLOGY_CONNECTION * pFrom,PaWinWdmFilter * filter,int muxIdx)969 static const KSTOPOLOGY_CONNECTION* GetConnectionTo(const KSTOPOLOGY_CONNECTION* pFrom, PaWinWdmFilter* filter, int muxIdx)
970 {
971     unsigned i;
972     const KSTOPOLOGY_CONNECTION* retval = NULL;
973     const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
974     (void)muxIdx;
975     PA_DEBUG(("GetConnectionTo: Checking %u connections... (pFrom = %p)", filter->connections->Count, pFrom));
976     for (i = 0; i < filter->connections->Count; ++i)
977     {
978         const KSTOPOLOGY_CONNECTION* pConn = connections + i;
979         if (pConn == pFrom)
980             continue;
981 
982         if (pConn->FromNode == pFrom->ToNode)
983         {
984             retval = pConn;
985             break;
986         }
987     }
988     PA_DEBUG(("GetConnectionTo: Returning %p\n", retval));
989     return retval;
990 }
991 
992 /* Used when traversing topology for inputs */
GetConnectionFrom(const KSTOPOLOGY_CONNECTION * pTo,PaWinWdmFilter * filter,int muxIdx)993 static const KSTOPOLOGY_CONNECTION* GetConnectionFrom(const KSTOPOLOGY_CONNECTION* pTo, PaWinWdmFilter* filter, int muxIdx)
994 {
995     unsigned i;
996     const KSTOPOLOGY_CONNECTION* retval = NULL;
997     const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
998     int muxCntr = 0;
999     PA_DEBUG(("GetConnectionFrom: Checking %u connections... (pTo = %p)\n", filter->connections->Count, pTo));
1000     for (i = 0; i < filter->connections->Count; ++i)
1001     {
1002         const KSTOPOLOGY_CONNECTION* pConn = connections + i;
1003         if (pConn == pTo)
1004             continue;
1005 
1006         if (pConn->ToNode == pTo->FromNode)
1007         {
1008             if (muxIdx >= 0)
1009             {
1010                 if (muxCntr < muxIdx)
1011                 {
1012                     ++muxCntr;
1013                     continue;
1014                 }
1015             }
1016             retval = pConn;
1017             break;
1018         }
1019     }
1020     PA_DEBUG(("GetConnectionFrom: Returning %p\n", retval));
1021     return retval;
1022 }
1023 
GetNumberOfConnectionsTo(const KSTOPOLOGY_CONNECTION * pTo,PaWinWdmFilter * filter)1024 static ULONG GetNumberOfConnectionsTo(const KSTOPOLOGY_CONNECTION* pTo, PaWinWdmFilter* filter)
1025 {
1026     ULONG retval = 0;
1027     unsigned i;
1028     const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
1029     PA_DEBUG(("GetNumberOfConnectionsTo: Checking %u connections...\n", filter->connections->Count));
1030     for (i = 0; i < filter->connections->Count; ++i)
1031     {
1032         const KSTOPOLOGY_CONNECTION* pConn = connections + i;
1033         if (pConn->ToNode == pTo->FromNode &&
1034             (pTo->FromNode != KSFILTER_NODE || pConn->ToNodePin == pTo->FromNodePin))
1035         {
1036             ++retval;
1037         }
1038     }
1039     PA_DEBUG(("GetNumberOfConnectionsTo: Returning %d\n", retval));
1040     return retval;
1041 }
1042 
1043 typedef const KSTOPOLOGY_CONNECTION *(*TFnGetConnection)(const KSTOPOLOGY_CONNECTION*, PaWinWdmFilter*, int);
1044 
FindStartConnectionFrom(ULONG startPin,PaWinWdmFilter * filter)1045 static const KSTOPOLOGY_CONNECTION* FindStartConnectionFrom(ULONG startPin, PaWinWdmFilter* filter)
1046 {
1047     unsigned i;
1048     const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
1049     PA_DEBUG(("FindStartConnectionFrom: Startpin %u, Checking %u connections...\n", startPin, filter->connections->Count));
1050     for (i = 0; i < filter->connections->Count; ++i)
1051     {
1052         const KSTOPOLOGY_CONNECTION* pConn = connections + i;
1053         if (pConn->ToNode == KSFILTER_NODE && pConn->ToNodePin == startPin)
1054         {
1055             PA_DEBUG(("FindStartConnectionFrom: returning %p\n", pConn));
1056             return pConn;
1057         }
1058     }
1059 
1060     PA_DEBUG(("FindStartConnectionFrom: returning NULL\n"));
1061     assert(FALSE);
1062     return 0;
1063 }
1064 
FindStartConnectionTo(ULONG startPin,PaWinWdmFilter * filter)1065 static const KSTOPOLOGY_CONNECTION* FindStartConnectionTo(ULONG startPin, PaWinWdmFilter* filter)
1066 {
1067     unsigned i;
1068     const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
1069     PA_DEBUG(("FindStartConnectionTo: Startpin %u, Checking %u connections...\n", startPin, filter->connections->Count));
1070     for (i = 0; i < filter->connections->Count; ++i)
1071     {
1072         const KSTOPOLOGY_CONNECTION* pConn = connections + i;
1073         if (pConn->FromNode == KSFILTER_NODE && pConn->FromNodePin == startPin)
1074         {
1075             PA_DEBUG(("FindStartConnectionTo: returning %p\n", pConn));
1076             return pConn;
1077         }
1078     }
1079 
1080     PA_DEBUG(("FindStartConnectionTo: returning NULL\n"));
1081     assert(FALSE);
1082     return 0;
1083 }
1084 
GetConnectedPin(ULONG startPin,BOOL forward,PaWinWdmFilter * filter,int muxPosition,ULONG * muxInputPinId,ULONG * muxNodeId)1085 static ULONG GetConnectedPin(ULONG startPin, BOOL forward, PaWinWdmFilter* filter, int muxPosition, ULONG *muxInputPinId, ULONG *muxNodeId)
1086 {
1087     int limit=1000;
1088     const KSTOPOLOGY_CONNECTION *conn = NULL;
1089     TFnGetConnection fnGetConnection = forward ? GetConnectionTo : GetConnectionFrom ;
1090     PA_LOGE_;
1091     while (1)
1092     {
1093         limit--;
1094         if (limit == 0) {
1095            PA_DEBUG(("GetConnectedPin: LOOP LIMIT REACHED\n"));
1096            break;
1097         }
1098 
1099         if (conn == NULL)
1100         {
1101             conn = forward ? FindStartConnectionTo(startPin, filter) : FindStartConnectionFrom(startPin, filter);
1102         }
1103         else
1104         {
1105             conn = fnGetConnection(conn, filter, -1);
1106         }
1107 
1108         /* Handling case of erroneous connection list */
1109         if (conn == NULL)
1110         {
1111             break;
1112         }
1113 
1114         if (forward ? conn->ToNode == KSFILTER_NODE : conn->FromNode == KSFILTER_NODE)
1115         {
1116             return forward ? conn->ToNodePin : conn->FromNodePin;
1117         }
1118         else
1119         {
1120             PA_DEBUG(("GetConnectedPin: count=%d, forward=%d, muxPosition=%d\n", filter->nodes->Count, forward, muxPosition));
1121             if (filter->nodes->Count > 0 && !forward && muxPosition >= 0)
1122             {
1123                 const GUID* nodes = (const GUID*)(filter->nodes + 1);
1124                 if (IsEqualGUID(&nodes[conn->FromNode], &KSNODETYPE_MUX))
1125                 {
1126                     ULONG nConn = GetNumberOfConnectionsTo(conn, filter);
1127                     conn = fnGetConnection(conn, filter, muxPosition);
1128                     if (conn == NULL)
1129                     {
1130                         break;
1131                     }
1132                     if (muxInputPinId != 0)
1133                     {
1134                         *muxInputPinId = conn->ToNodePin;
1135                     }
1136                     if (muxNodeId != 0)
1137                     {
1138                         *muxNodeId = conn->ToNode;
1139                     }
1140                 }
1141             }
1142         }
1143     }
1144     PA_LOGL_;
1145     return KSFILTER_NODE;
1146 }
1147 
DumpConnectionsAndNodes(PaWinWdmFilter * filter)1148 static void DumpConnectionsAndNodes(PaWinWdmFilter* filter)
1149 {
1150     unsigned i;
1151     const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
1152     const GUID* nodes = (const GUID*)(filter->nodes + 1);
1153 
1154     PA_LOGE_;
1155     PA_DEBUG(("DumpConnectionsAndNodes: connections=%d, nodes=%d\n", filter->connections->Count, filter->nodes->Count));
1156 
1157     for (i=0; i < filter->connections->Count; ++i)
1158     {
1159         const KSTOPOLOGY_CONNECTION* pConn = connections + i;
1160         PA_DEBUG(("  Connection: %u - FromNode=%u,FromPin=%u -> ToNode=%u,ToPin=%u\n",
1161             i,
1162             pConn->FromNode, pConn->FromNodePin,
1163             pConn->ToNode, pConn->ToNodePin
1164             ));
1165     }
1166 
1167     for (i=0; i < filter->nodes->Count; ++i)
1168     {
1169         const GUID* pConn = nodes + i;
1170         PA_DEBUG(("  Node: %d - {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
1171             i,
1172             pConn->Data1, pConn->Data2, pConn->Data3,
1173             pConn->Data4[0], pConn->Data4[1],
1174             pConn->Data4[2], pConn->Data4[3],
1175             pConn->Data4[4], pConn->Data4[5],
1176             pConn->Data4[6], pConn->Data4[7]
1177         ));
1178     }
1179     PA_LOGL_;
1180 
1181 }
1182 
1183 typedef struct __PaUsbTerminalGUIDToName
1184 {
1185     USHORT     usbGUID;
1186     wchar_t    name[64];
1187 } PaUsbTerminalGUIDToName;
1188 
1189 static const PaUsbTerminalGUIDToName kNames[] =
1190 {
1191     /* Types copied from: http://msdn.microsoft.com/en-us/library/ff537742(v=vs.85).aspx */
1192     /* Input terminal types */
1193     { 0x0201, L"Microphone" },
1194     { 0x0202, L"Desktop Microphone" },
1195     { 0x0203, L"Personal Microphone" },
1196     { 0x0204, L"Omni Directional Microphone" },
1197     { 0x0205, L"Microphone Array" },
1198     { 0x0206, L"Processing Microphone Array" },
1199     /* Output terminal types */
1200     { 0x0301, L"Speakers" },
1201     { 0x0302, L"Headphones" },
1202     { 0x0303, L"Head Mounted Display Audio" },
1203     { 0x0304, L"Desktop Speaker" },
1204     { 0x0305, L"Room Speaker" },
1205     { 0x0306, L"Communication Speaker" },
1206     { 0x0307, L"LFE Speakers" },
1207     /* External terminal types */
1208     { 0x0601, L"Analog" },
1209     { 0x0602, L"Digital" },
1210     { 0x0603, L"Line" },
1211     { 0x0604, L"Audio" },
1212     { 0x0605, L"SPDIF" },
1213 };
1214 
1215 static const unsigned kNamesCnt = sizeof(kNames)/sizeof(PaUsbTerminalGUIDToName);
1216 
PaUsbTerminalGUIDToNameCmp(const void * lhs,const void * rhs)1217 static int PaUsbTerminalGUIDToNameCmp(const void* lhs, const void* rhs)
1218 {
1219     const PaUsbTerminalGUIDToName* pL = (const PaUsbTerminalGUIDToName*)lhs;
1220     const PaUsbTerminalGUIDToName* pR = (const PaUsbTerminalGUIDToName*)rhs;
1221     return ((int)(pL->usbGUID) - (int)(pR->usbGUID));
1222 }
1223 
GetNameFromCategory(const GUID * pGUID,BOOL input,wchar_t * name,unsigned length)1224 static PaError GetNameFromCategory(const GUID* pGUID, BOOL input, wchar_t* name, unsigned length)
1225 {
1226     PaError result = paUnanticipatedHostError;
1227     USHORT usbTerminalGUID = (USHORT)(pGUID->Data1 - 0xDFF219E0);
1228 
1229     PA_LOGE_;
1230     if (input && usbTerminalGUID >= 0x301 && usbTerminalGUID < 0x400)
1231     {
1232         /* Output terminal name for an input !? Set it to Line! */
1233         usbTerminalGUID = 0x603;
1234     }
1235     if (!input && usbTerminalGUID >= 0x201 && usbTerminalGUID < 0x300)
1236     {
1237         /* Input terminal name for an output !? Set it to Line! */
1238         usbTerminalGUID = 0x603;
1239     }
1240     if (usbTerminalGUID >= 0x201 && usbTerminalGUID < 0x713)
1241     {
1242         PaUsbTerminalGUIDToName s = { usbTerminalGUID };
1243         const PaUsbTerminalGUIDToName* ptr = bsearch(
1244             &s,
1245             kNames,
1246             kNamesCnt,
1247             sizeof(PaUsbTerminalGUIDToName),
1248             PaUsbTerminalGUIDToNameCmp
1249             );
1250         if (ptr != 0)
1251         {
1252             PA_DEBUG(("GetNameFromCategory: USB GUID %04X -> '%S'\n", usbTerminalGUID, ptr->name));
1253 
1254             if (name != NULL && length > 0)
1255             {
1256                 int n = _snwprintf(name, length, L"%s", ptr->name);
1257                 if (usbTerminalGUID >= 0x601 && usbTerminalGUID < 0x700)
1258                 {
1259                     _snwprintf(name + n, length - n, L" %s", (input ? L"In":L"Out"));
1260                 }
1261             }
1262             result = paNoError;
1263         }
1264     }
1265     else
1266     {
1267         PaWinWDM_SetLastErrorInfo(result, "GetNameFromCategory: usbTerminalGUID = %04X ", usbTerminalGUID);
1268     }
1269     PA_LOGL_;
1270     return result;
1271 }
1272 
IsFrequencyWithinRange(const KSDATARANGE_AUDIO * range,int frequency)1273 static BOOL IsFrequencyWithinRange(const KSDATARANGE_AUDIO* range, int frequency)
1274 {
1275     if (frequency < (int)range->MinimumSampleFrequency)
1276         return FALSE;
1277     if (frequency > (int)range->MaximumSampleFrequency)
1278         return FALSE;
1279     return TRUE;
1280 }
1281 
IsBitsWithinRange(const KSDATARANGE_AUDIO * range,int noOfBits)1282 static BOOL IsBitsWithinRange(const KSDATARANGE_AUDIO* range, int noOfBits)
1283 {
1284     if (noOfBits < (int)range->MinimumBitsPerSample)
1285         return FALSE;
1286     if (noOfBits > (int)range->MaximumBitsPerSample)
1287         return FALSE;
1288     return TRUE;
1289 }
1290 
1291 /* Note: Somewhat different order compared to WMME implementation, as we want to focus on fidelity first */
1292 static const int defaultSampleRateSearchOrder[] =
1293 { 44100, 48000, 88200, 96000, 192000, 32000, 24000, 22050, 16000, 12000, 11025, 9600, 8000 };
1294 static const int defaultSampleRateSearchOrderCount = sizeof(defaultSampleRateSearchOrder)/sizeof(defaultSampleRateSearchOrder[0]);
1295 
DefaultSampleFrequencyIndex(const KSDATARANGE_AUDIO * range)1296 static int DefaultSampleFrequencyIndex(const KSDATARANGE_AUDIO* range)
1297 {
1298     int i;
1299 
1300     for(i=0; i < defaultSampleRateSearchOrderCount; ++i)
1301     {
1302         int currentFrequency = defaultSampleRateSearchOrder[i];
1303 
1304         if (IsFrequencyWithinRange(range, currentFrequency))
1305         {
1306             return i;
1307         }
1308     }
1309 
1310     return -1;
1311 }
1312 
1313 /*
1314 Create a new pin object belonging to a filter
1315 The pin object holds all the configuration information about the pin
1316 before it is opened, and then the handle of the pin after is opened
1317 */
PinNew(PaWinWdmFilter * parentFilter,unsigned long pinId,PaError * error)1318 static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error)
1319 {
1320     PaWinWdmPin* pin;
1321     PaError result;
1322     unsigned long i;
1323     KSMULTIPLE_ITEM* item = NULL;
1324     KSIDENTIFIER* identifier;
1325     KSDATARANGE* dataRange;
1326     const ULONG streamingId = (parentFilter->devInfo.streamingType == Type_kWaveRT) ? KSINTERFACE_STANDARD_LOOPED_STREAMING : KSINTERFACE_STANDARD_STREAMING;
1327     int defaultSampleRateIndex = defaultSampleRateSearchOrderCount;
1328 
1329     PA_LOGE_;
1330     PA_DEBUG(("PinNew: Creating pin %d:\n",pinId));
1331 
1332     /* Allocate the new PIN object */
1333     pin = (PaWinWdmPin*)PaUtil_AllocateMemory( sizeof(PaWinWdmPin) );
1334     if( !pin )
1335     {
1336         result = paInsufficientMemory;
1337         goto error;
1338     }
1339 
1340     /* Zero the pin object */
1341     /* memset( (void*)pin, 0, sizeof(PaWinWdmPin) ); */
1342 
1343     pin->parentFilter = parentFilter;
1344     pin->pinId = pinId;
1345 
1346     /* Allocate a connect structure */
1347     pin->pinConnectSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX);
1348     pin->pinConnect = (KSPIN_CONNECT*)PaUtil_AllocateMemory( pin->pinConnectSize );
1349     if( !pin->pinConnect )
1350     {
1351         result = paInsufficientMemory;
1352         goto error;
1353     }
1354 
1355     /* Configure the connect structure with default values */
1356     pin->pinConnect->Interface.Set               = KSINTERFACESETID_Standard;
1357     pin->pinConnect->Interface.Id                = streamingId;
1358     pin->pinConnect->Interface.Flags             = 0;
1359     pin->pinConnect->Medium.Set                  = KSMEDIUMSETID_Standard;
1360     pin->pinConnect->Medium.Id                   = KSMEDIUM_TYPE_ANYINSTANCE;
1361     pin->pinConnect->Medium.Flags                = 0;
1362     pin->pinConnect->PinId                       = pinId;
1363     pin->pinConnect->PinToHandle                 = NULL;
1364     pin->pinConnect->Priority.PriorityClass      = KSPRIORITY_NORMAL;
1365     pin->pinConnect->Priority.PrioritySubClass   = 1;
1366     pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)(pin->pinConnect + 1);
1367     pin->ksDataFormatWfx->DataFormat.FormatSize  = sizeof(KSDATAFORMAT_WAVEFORMATEX);
1368     pin->ksDataFormatWfx->DataFormat.Flags       = 0;
1369     pin->ksDataFormatWfx->DataFormat.Reserved    = 0;
1370     pin->ksDataFormatWfx->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
1371     pin->ksDataFormatWfx->DataFormat.SubFormat   = KSDATAFORMAT_SUBTYPE_PCM;
1372     pin->ksDataFormatWfx->DataFormat.Specifier   = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
1373 
1374     pin->frameSize = 0; /* Unknown until we instantiate pin */
1375 
1376     /* Get the COMMUNICATION property */
1377     result = WdmGetPinPropertySimple(
1378         parentFilter->handle,
1379         pinId,
1380         &KSPROPSETID_Pin,
1381         KSPROPERTY_PIN_COMMUNICATION,
1382         &pin->communication,
1383         sizeof(KSPIN_COMMUNICATION),
1384         NULL);
1385     if( result != paNoError )
1386         goto error;
1387 
1388     if( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/
1389         (pin->communication != KSPIN_COMMUNICATION_SINK) &&
1390         (pin->communication != KSPIN_COMMUNICATION_BOTH) )
1391     {
1392         PA_DEBUG(("PinNew: Not source/sink\n"));
1393         result = paInvalidDevice;
1394         goto error;
1395     }
1396 
1397     /* Get dataflow information */
1398     result = WdmGetPinPropertySimple(
1399         parentFilter->handle,
1400         pinId,
1401         &KSPROPSETID_Pin,
1402         KSPROPERTY_PIN_DATAFLOW,
1403         &pin->dataFlow,
1404         sizeof(KSPIN_DATAFLOW),
1405         NULL);
1406 
1407     if( result != paNoError )
1408         goto error;
1409 
1410     /* Get the INTERFACE property list */
1411     result = WdmGetPinPropertyMulti(
1412         parentFilter->handle,
1413         pinId,
1414         &KSPROPSETID_Pin,
1415         KSPROPERTY_PIN_INTERFACES,
1416         &item);
1417 
1418     if( result != paNoError )
1419         goto error;
1420 
1421     identifier = (KSIDENTIFIER*)(item+1);
1422 
1423     /* Check that at least one interface is STANDARD_STREAMING */
1424     result = paUnanticipatedHostError;
1425     for( i = 0; i < item->Count; i++ )
1426     {
1427         if( IsEqualGUID(&identifier[i].Set, &KSINTERFACESETID_Standard) && ( identifier[i].Id == streamingId ) )
1428         {
1429             result = paNoError;
1430             break;
1431         }
1432     }
1433 
1434     if( result != paNoError )
1435     {
1436         PA_DEBUG(("PinNew: No %s streaming\n", streamingId==KSINTERFACE_STANDARD_LOOPED_STREAMING?"looped":"standard"));
1437         goto error;
1438     }
1439 
1440     /* Don't need interfaces any more */
1441     PaUtil_FreeMemory( item );
1442     item = NULL;
1443 
1444     /* Get the MEDIUM properties list */
1445     result = WdmGetPinPropertyMulti(
1446         parentFilter->handle,
1447         pinId,
1448         &KSPROPSETID_Pin,
1449         KSPROPERTY_PIN_MEDIUMS,
1450         &item);
1451 
1452     if( result != paNoError )
1453         goto error;
1454 
1455     identifier = (KSIDENTIFIER*)(item+1); /* Not actually necessary... */
1456 
1457     /* Check that at least one medium is STANDARD_DEVIO */
1458     result = paUnanticipatedHostError;
1459     for( i = 0; i < item->Count; i++ )
1460     {
1461         if( IsEqualGUID(&identifier[i].Set, &KSMEDIUMSETID_Standard) && ( identifier[i].Id == KSMEDIUM_STANDARD_DEVIO ) )
1462         {
1463             result = paNoError;
1464             break;
1465         }
1466     }
1467 
1468     if( result != paNoError )
1469     {
1470         PA_DEBUG(("No standard devio\n"));
1471         goto error;
1472     }
1473     /* Don't need mediums any more */
1474     PaUtil_FreeMemory( item );
1475     item = NULL;
1476 
1477     /* Get DATARANGES */
1478     result = WdmGetPinPropertyMulti(
1479         parentFilter->handle,
1480         pinId,
1481         &KSPROPSETID_Pin,
1482         KSPROPERTY_PIN_DATARANGES,
1483         &pin->dataRangesItem);
1484 
1485     if( result != paNoError )
1486         goto error;
1487 
1488     pin->dataRanges = (KSDATARANGE*)(pin->dataRangesItem +1);
1489 
1490     /* Check that at least one datarange supports audio */
1491     result = paUnanticipatedHostError;
1492     dataRange = pin->dataRanges;
1493     pin->maxChannels = 0;
1494     pin->defaultSampleRate = 0;
1495     pin->formats = 0;
1496     PA_DEBUG(("PinNew: Checking %u no of dataranges...\n", pin->dataRangesItem->Count));
1497     for( i = 0; i < pin->dataRangesItem->Count; i++)
1498     {
1499         PA_DEBUG(("PinNew: DR major format %x\n",*(unsigned long*)(&(dataRange->MajorFormat))));
1500         /* Check that subformat is WAVEFORMATEX, PCM or WILDCARD */
1501         if( IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) ||
1502             IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) ||
1503             IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) ||
1504             IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_WILDCARD) ||
1505             IsEqualGUID(&dataRange->MajorFormat, &KSDATAFORMAT_TYPE_AUDIO) )
1506         {
1507             int defaultIndex;
1508             result = paNoError;
1509             /* Record the maximum possible channels with this pin */
1510             if( ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels == (ULONG) -1 )
1511             {
1512                 pin->maxChannels = MAXIMUM_NUMBER_OF_CHANNELS;
1513             }
1514             else if( (int) ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels )
1515             {
1516                 pin->maxChannels = (int) ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels;
1517             }
1518             PA_DEBUG(("PinNew: MaxChannel: %d\n",pin->maxChannels));
1519 
1520             /* Record the formats (bit depths) that are supported */
1521             if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 8) )
1522             {
1523                 pin->formats |= paInt8;
1524                 PA_DEBUG(("PinNew: Format PCM 8 bit supported\n"));
1525             }
1526             if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 16) )
1527             {
1528                 pin->formats |= paInt16;
1529                 PA_DEBUG(("PinNew: Format PCM 16 bit supported\n"));
1530             }
1531             if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 24) )
1532             {
1533                 pin->formats |= paInt24;
1534                 PA_DEBUG(("PinNew: Format PCM 24 bit supported\n"));
1535             }
1536             if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 32) )
1537             {
1538                 if (IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1539                 {
1540                     pin->formats |= paFloat32;
1541                     PA_DEBUG(("PinNew: Format IEEE float 32 bit supported\n"));
1542                 }
1543                 else
1544                 {
1545                     pin->formats |= paInt32;
1546                     PA_DEBUG(("PinNew: Format PCM 32 bit supported\n"));
1547                 }
1548             }
1549 
1550             defaultIndex = DefaultSampleFrequencyIndex((KSDATARANGE_AUDIO*)dataRange);
1551             if (defaultIndex >= 0 && defaultIndex < defaultSampleRateIndex)
1552             {
1553                 defaultSampleRateIndex = defaultIndex;
1554             }
1555         }
1556         dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
1557     }
1558 
1559     if( result != paNoError )
1560         goto error;
1561 
1562     /* If none of the frequencies searched for are present, there's something seriously wrong */
1563     if (defaultSampleRateIndex == defaultSampleRateSearchOrderCount)
1564     {
1565         PA_DEBUG(("PinNew: No default sample rate found, skipping pin!\n"));
1566         PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "PinNew: No default sample rate found");
1567         result = paUnanticipatedHostError;
1568         goto error;
1569     }
1570 
1571     /* Set the default sample rate */
1572     pin->defaultSampleRate = defaultSampleRateSearchOrder[defaultSampleRateIndex];
1573     PA_DEBUG(("PinNew: Default sample rate = %d Hz\n", pin->defaultSampleRate));
1574 
1575     /* Get instance information */
1576     result = WdmGetPinPropertySimple(
1577         parentFilter->handle,
1578         pinId,
1579         &KSPROPSETID_Pin,
1580         KSPROPERTY_PIN_CINSTANCES,
1581         &pin->instances,
1582         sizeof(KSPIN_CINSTANCES),
1583         NULL);
1584 
1585     if( result != paNoError )
1586         goto error;
1587 
1588     /* If WaveRT, check if pin supports notification mode */
1589     if (parentFilter->devInfo.streamingType == Type_kWaveRT)
1590     {
1591         BOOL bSupportsNotification = FALSE;
1592         if (PinQueryNotificationSupport(pin, &bSupportsNotification) == paNoError)
1593         {
1594             pin->pinKsSubType = bSupportsNotification ? SubType_kNotification : SubType_kPolled;
1595         }
1596     }
1597 
1598     /* Query pin name (which means we need to traverse to non IRP pin, via physical connection to topology filter pin, through
1599     its nodes to the endpoint pin, and get that ones name... phew...) */
1600     PA_DEBUG(("PinNew: Finding topology pin...\n"));
1601 
1602     {
1603         ULONG topoPinId = GetConnectedPin(pinId, (pin->dataFlow == KSPIN_DATAFLOW_IN), parentFilter, -1, NULL, NULL);
1604         const wchar_t kInputName[] = L"Input";
1605         const wchar_t kOutputName[] = L"Output";
1606 
1607         if (topoPinId != KSFILTER_NODE)
1608         {
1609             /* Get physical connection for topo pin */
1610             unsigned long cbBytes = 0;
1611             PA_DEBUG(("PinNew: Getting physical connection...\n"));
1612             result = WdmGetPinPropertySimple(parentFilter->handle,
1613                 topoPinId,
1614                 &KSPROPSETID_Pin,
1615                 KSPROPERTY_PIN_PHYSICALCONNECTION,
1616                 0,
1617                 0,
1618                 &cbBytes
1619                 );
1620 
1621             if (result != paNoError)
1622             {
1623                 /* No physical connection -> there is no topology filter! So we get the name of the pin! */
1624                 PA_DEBUG(("PinNew: No physical connection! Getting the pin name\n"));
1625                 result = WdmGetPinPropertySimple(parentFilter->handle,
1626                     topoPinId,
1627                     &KSPROPSETID_Pin,
1628                     KSPROPERTY_PIN_NAME,
1629                     pin->friendlyName,
1630                     MAX_PATH,
1631                     NULL);
1632                 if (result != paNoError)
1633                 {
1634                     GUID category = {0};
1635 
1636                     /* Get pin category information */
1637                     result = WdmGetPinPropertySimple(parentFilter->handle,
1638                         topoPinId,
1639                         &KSPROPSETID_Pin,
1640                         KSPROPERTY_PIN_CATEGORY,
1641                         &category,
1642                         sizeof(GUID),
1643                         NULL);
1644 
1645                     if (result == paNoError)
1646                     {
1647                         result = GetNameFromCategory(&category, (pin->dataFlow == KSPIN_DATAFLOW_OUT), pin->friendlyName, MAX_PATH);
1648                     }
1649                 }
1650 
1651                 /* Make sure pin gets a name here... */
1652                 if (wcslen(pin->friendlyName) == 0)
1653                 {
1654                     wcscpy(pin->friendlyName, (pin->dataFlow == KSPIN_DATAFLOW_IN) ? kOutputName : kInputName);
1655 #ifdef UNICODE
1656                     PA_DEBUG(("PinNew: Setting pin friendly name to '%s'\n", pin->friendlyName));
1657 #else
1658                     PA_DEBUG(("PinNew: Setting pin friendly name to '%S'\n", pin->friendlyName));
1659 #endif
1660                 }
1661 
1662                 /* This is then == the endpoint pin */
1663                 pin->endpointPinId = (pin->dataFlow == KSPIN_DATAFLOW_IN) ? pinId : topoPinId;
1664             }
1665             else
1666             {
1667                 KSPIN_PHYSICALCONNECTION* pc = (KSPIN_PHYSICALCONNECTION*)PaUtil_AllocateMemory(cbBytes + 2);
1668                 ULONG pcPin;
1669                 wchar_t symbLinkName[MAX_PATH];
1670                 PA_DEBUG(("PinNew: Physical connection found!\n"));
1671                 if (pc == NULL)
1672                 {
1673                     result = paInsufficientMemory;
1674                     goto error;
1675                 }
1676                 result = WdmGetPinPropertySimple(parentFilter->handle,
1677                     topoPinId,
1678                     &KSPROPSETID_Pin,
1679                     KSPROPERTY_PIN_PHYSICALCONNECTION,
1680                     pc,
1681                     cbBytes,
1682                     NULL
1683                     );
1684 
1685                 pcPin = pc->Pin;
1686                 wcsncpy(symbLinkName, pc->SymbolicLinkName, MAX_PATH);
1687                 PaUtil_FreeMemory( pc );
1688 
1689                 if (result != paNoError)
1690                 {
1691                     /* Shouldn't happen, but fail if it does */
1692                     PA_DEBUG(("PinNew: failed to retrieve physical connection!\n"));
1693                     goto error;
1694                 }
1695 
1696                 if (symbLinkName[1] == TEXT('?'))
1697                 {
1698                     symbLinkName[1] = TEXT('\\');
1699                 }
1700 
1701                 if (pin->parentFilter->topologyFilter == NULL)
1702                 {
1703                     PA_DEBUG(("PinNew: Creating topology filter '%S'\n", symbLinkName));
1704 
1705                     pin->parentFilter->topologyFilter = FilterNew(Type_kNotUsed, 0, symbLinkName, L"", &result);
1706                     if (pin->parentFilter->topologyFilter == NULL)
1707                     {
1708                         PA_DEBUG(("PinNew: Failed creating topology filter\n"));
1709                         result = paUnanticipatedHostError;
1710                         PaWinWDM_SetLastErrorInfo(result, "Failed to create topology filter '%S'", symbLinkName);
1711                         goto error;
1712                     }
1713 
1714                     /* Copy info so we have it in device info */
1715                     wcsncpy(pin->parentFilter->devInfo.topologyPath, symbLinkName, MAX_PATH);
1716                 }
1717                 else
1718                 {
1719                     /* Must be the same */
1720                     assert(wcscmp(symbLinkName, pin->parentFilter->topologyFilter->devInfo.filterPath) == 0);
1721                 }
1722 
1723                 PA_DEBUG(("PinNew: Opening topology filter..."));
1724 
1725                 result = FilterUse(pin->parentFilter->topologyFilter);
1726                 if (result == paNoError)
1727                 {
1728                     unsigned long endpointPinId;
1729 
1730                     if (pin->dataFlow == KSPIN_DATAFLOW_IN)
1731                     {
1732                         /* The "endpointPinId" is what WASAPI looks at for pin names */
1733                         GUID category = {0};
1734 
1735                         PA_DEBUG(("PinNew: Checking for output endpoint pin id...\n"));
1736 
1737                         endpointPinId = GetConnectedPin(pcPin, TRUE, pin->parentFilter->topologyFilter, -1, NULL, NULL);
1738 
1739                         if (endpointPinId == KSFILTER_NODE)
1740                         {
1741                             result = paUnanticipatedHostError;
1742                             PaWinWDM_SetLastErrorInfo(result, "Failed to get endpoint pin ID on topology filter!");
1743                             goto error;
1744                         }
1745 
1746                         PA_DEBUG(("PinNew: Found endpoint pin id %u\n", endpointPinId));
1747 
1748                         /* Get pin category information */
1749                         result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1750                             endpointPinId,
1751                             &KSPROPSETID_Pin,
1752                             KSPROPERTY_PIN_CATEGORY,
1753                             &category,
1754                             sizeof(GUID),
1755                             NULL);
1756 
1757                         if (result == paNoError)
1758                         {
1759 #if !PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES
1760                             wchar_t pinName[MAX_PATH];
1761 
1762                             PA_DEBUG(("PinNew: Getting pin name property..."));
1763 
1764                             /* Ok, try pin name also, and favor that if available */
1765                             result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1766                                 endpointPinId,
1767                                 &KSPROPSETID_Pin,
1768                                 KSPROPERTY_PIN_NAME,
1769                                 pinName,
1770                                 MAX_PATH,
1771                                 NULL);
1772 
1773                             if (result == paNoError && wcslen(pinName)>0)
1774                             {
1775                                 wcsncpy(pin->friendlyName, pinName, MAX_PATH);
1776                             }
1777                             else
1778 #endif
1779                             {
1780                                 result = GetNameFromCategory(&category, (pin->dataFlow == KSPIN_DATAFLOW_OUT), pin->friendlyName, MAX_PATH);
1781                             }
1782                         }
1783 
1784                         /* Make sure we get a name for the pin */
1785                         if (wcslen(pin->friendlyName) == 0)
1786                         {
1787                             wcscpy(pin->friendlyName, kOutputName);
1788                         }
1789 #ifdef UNICODE
1790                         PA_DEBUG(("PinNew: Pin name '%s'\n", pin->friendlyName));
1791 #else
1792                         PA_DEBUG(("PinNew: Pin name '%S'\n", pin->friendlyName));
1793 #endif
1794 
1795                         /* Set endpoint pin ID (this is the topology INPUT pin, since portmixer will always traverse the
1796                         filter in audio streaming direction, see http://msdn.microsoft.com/en-us/library/windows/hardware/ff536331(v=vs.85).aspx
1797                         for more information)
1798                         */
1799                         pin->endpointPinId = pcPin;
1800                     }
1801                     else
1802                     {
1803                         unsigned muxCount = 0;
1804                         int muxPos = 0;
1805                         /* Max 64 multiplexer inputs... sanity check :) */
1806                         for (i = 0; i < 64; ++i)
1807                         {
1808                             ULONG muxNodeIdTest = (unsigned)-1;
1809                             PA_DEBUG(("PinNew: Checking for input endpoint pin id (%d)...\n", i));
1810 
1811                             endpointPinId = GetConnectedPin(pcPin,
1812                                 FALSE,
1813                                 pin->parentFilter->topologyFilter,
1814                                 (int)i,
1815                                 NULL,
1816                                 &muxNodeIdTest);
1817 
1818                             if (endpointPinId == KSFILTER_NODE)
1819                             {
1820                                 /* We're done */
1821                                 PA_DEBUG(("PinNew: Done with inputs.\n", endpointPinId));
1822                                 break;
1823                             }
1824                             else
1825                             {
1826                                 /* The "endpointPinId" is what WASAPI looks at for pin names */
1827                                 GUID category = {0};
1828 
1829                                 PA_DEBUG(("PinNew: Found endpoint pin id %u\n", endpointPinId));
1830 
1831                                 /* Get pin category information */
1832                                 result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1833                                     endpointPinId,
1834                                     &KSPROPSETID_Pin,
1835                                     KSPROPERTY_PIN_CATEGORY,
1836                                     &category,
1837                                     sizeof(GUID),
1838                                     NULL);
1839 
1840                                 if (result == paNoError)
1841                                 {
1842                                     if (muxNodeIdTest == (unsigned)-1)
1843                                     {
1844                                         /* Ok, try pin name, and favor that if available */
1845                                         result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1846                                             endpointPinId,
1847                                             &KSPROPSETID_Pin,
1848                                             KSPROPERTY_PIN_NAME,
1849                                             pin->friendlyName,
1850                                             MAX_PATH,
1851                                             NULL);
1852 
1853                                         if (result != paNoError)
1854                                         {
1855                                             result = GetNameFromCategory(&category, TRUE, pin->friendlyName, MAX_PATH);
1856                                         }
1857                                         break;
1858                                     }
1859                                     else
1860                                     {
1861                                         result = GetNameFromCategory(&category, TRUE, NULL, 0);
1862 
1863                                         if (result == paNoError)
1864                                         {
1865                                             ++muxCount;
1866                                         }
1867                                     }
1868                                 }
1869                                 else
1870                                 {
1871                                     PA_DEBUG(("PinNew: Failed to get pin category"));
1872                                 }
1873                             }
1874                         }
1875 
1876                         if (muxCount == 0)
1877                         {
1878                             pin->endpointPinId = endpointPinId;
1879                             /* Make sure we get a name for the pin */
1880                             if (wcslen(pin->friendlyName) == 0)
1881                             {
1882                                 wcscpy(pin->friendlyName, kInputName);
1883                             }
1884 #ifdef UNICODE
1885                             PA_DEBUG(("PinNew: Input friendly name '%s'\n", pin->friendlyName));
1886 #else
1887                             PA_DEBUG(("PinNew: Input friendly name '%S'\n", pin->friendlyName));
1888 #endif
1889                         }
1890                         else // muxCount > 0
1891                         {
1892                             PA_DEBUG(("PinNew: Setting up %u inputs\n", muxCount));
1893 
1894                             /* Now we redo the operation once known how many multiplexer positions there are */
1895                             pin->inputs = (PaWinWdmMuxedInput**)PaUtil_AllocateMemory(muxCount * sizeof(PaWinWdmMuxedInput*));
1896                             if (pin->inputs == NULL)
1897                             {
1898                                 FilterRelease(pin->parentFilter->topologyFilter);
1899                                 result = paInsufficientMemory;
1900                                 goto error;
1901                             }
1902                             pin->inputCount = muxCount;
1903 
1904                             for (i = 0; i < muxCount; ++muxPos)
1905                             {
1906                                 PA_DEBUG(("PinNew: Setting up input %u...\n", i));
1907 
1908                                 if (pin->inputs[i] == NULL)
1909                                 {
1910                                     pin->inputs[i] = (PaWinWdmMuxedInput*)PaUtil_AllocateMemory(sizeof(PaWinWdmMuxedInput));
1911                                     if (pin->inputs[i] == NULL)
1912                                     {
1913                                         FilterRelease(pin->parentFilter->topologyFilter);
1914                                         result = paInsufficientMemory;
1915                                         goto error;
1916                                     }
1917                                 }
1918 
1919                                 endpointPinId = GetConnectedPin(pcPin,
1920                                     FALSE,
1921                                     pin->parentFilter->topologyFilter,
1922                                     muxPos,
1923                                     &pin->inputs[i]->muxPinId,
1924                                     &pin->inputs[i]->muxNodeId);
1925 
1926                                 if (endpointPinId != KSFILTER_NODE)
1927                                 {
1928                                     /* The "endpointPinId" is what WASAPI looks at for pin names */
1929                                     GUID category = {0};
1930 
1931                                     /* Set input endpoint ID */
1932                                     pin->inputs[i]->endpointPinId = endpointPinId;
1933 
1934                                     /* Get pin category information */
1935                                     result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1936                                         endpointPinId,
1937                                         &KSPROPSETID_Pin,
1938                                         KSPROPERTY_PIN_CATEGORY,
1939                                         &category,
1940                                         sizeof(GUID),
1941                                         NULL);
1942 
1943                                     if (result == paNoError)
1944                                     {
1945                                         /* Try pin name first, and if that is not defined, use category instead */
1946                                         result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
1947                                             endpointPinId,
1948                                             &KSPROPSETID_Pin,
1949                                             KSPROPERTY_PIN_NAME,
1950                                             pin->inputs[i]->friendlyName,
1951                                             MAX_PATH,
1952                                             NULL);
1953 
1954                                         if (result != paNoError)
1955                                         {
1956                                             result = GetNameFromCategory(&category, TRUE, pin->inputs[i]->friendlyName, MAX_PATH);
1957                                             if (result != paNoError)
1958                                             {
1959                                                 /* Only specify name, let name hash in ScanDeviceInfos fix postfix enumerators */
1960                                                 wcscpy(pin->inputs[i]->friendlyName, kInputName);
1961                                             }
1962                                         }
1963 #ifdef UNICODE
1964                                         PA_DEBUG(("PinNew: Input (%u) friendly name '%s'\n", i, pin->inputs[i]->friendlyName));
1965 #else
1966                                         PA_DEBUG(("PinNew: Input (%u) friendly name '%S'\n", i, pin->inputs[i]->friendlyName));
1967 #endif
1968                                         ++i;
1969                                     }
1970                                 }
1971                                 else
1972                                 {
1973                                     /* Should never come here! */
1974                                     assert(FALSE);
1975                                 }
1976                             }
1977                         }
1978                     }
1979                 }
1980             }
1981         }
1982         else
1983         {
1984             PA_DEBUG(("PinNew: No topology pin id found. Bad...\n"));
1985             /* No TOPO pin id ??? This is bad. Ok, so we just say it is an input or output... */
1986             wcscpy(pin->friendlyName, (pin->dataFlow == KSPIN_DATAFLOW_IN) ? kOutputName : kInputName);
1987         }
1988     }
1989 
1990     /* Release topology filter if it has been used */
1991     if (pin->parentFilter->topologyFilter && pin->parentFilter->topologyFilter->handle != NULL)
1992     {
1993         PA_DEBUG(("PinNew: Releasing topology filter...\n"));
1994         FilterRelease(pin->parentFilter->topologyFilter);
1995     }
1996 
1997     /* Success */
1998     *error = paNoError;
1999     PA_DEBUG(("Pin created successfully\n"));
2000     PA_LOGL_;
2001     return pin;
2002 
2003 error:
2004     PA_DEBUG(("PinNew: Error %d\n", result));
2005     /*
2006     Error cleanup
2007     */
2008 
2009     if (pin->parentFilter->topologyFilter && pin->parentFilter->topologyFilter->handle != NULL)
2010     {
2011         FilterRelease(pin->parentFilter->topologyFilter);
2012     }
2013 
2014     PaUtil_FreeMemory( item );
2015     PinFree(pin);
2016 
2017     *error = result;
2018     PA_LOGL_;
2019     return NULL;
2020 }
2021 
2022 /*
2023 Safely free all resources associated with the pin
2024 */
PinFree(PaWinWdmPin * pin)2025 static void PinFree(PaWinWdmPin* pin)
2026 {
2027     unsigned i;
2028     PA_LOGE_;
2029     if( pin )
2030     {
2031         PinClose(pin);
2032         if( pin->pinConnect )
2033         {
2034             PaUtil_FreeMemory( pin->pinConnect );
2035         }
2036         if( pin->dataRangesItem )
2037         {
2038             PaUtil_FreeMemory( pin->dataRangesItem );
2039         }
2040         if( pin->inputs )
2041         {
2042             for (i = 0; i < pin->inputCount; ++i)
2043             {
2044                 PaUtil_FreeMemory( pin->inputs[i] );
2045             }
2046             PaUtil_FreeMemory( pin->inputs );
2047         }
2048         PaUtil_FreeMemory( pin );
2049     }
2050     PA_LOGL_;
2051 }
2052 
2053 /*
2054 If the pin handle is open, close it
2055 */
PinClose(PaWinWdmPin * pin)2056 static void PinClose(PaWinWdmPin* pin)
2057 {
2058     PA_LOGE_;
2059     if( pin == NULL )
2060     {
2061         PA_DEBUG(("Closing NULL pin!"));
2062         PA_LOGL_;
2063         return;
2064     }
2065     if( pin->handle != NULL )
2066     {
2067         PinSetState( pin, KSSTATE_PAUSE );
2068         PinSetState( pin, KSSTATE_STOP );
2069         CloseHandle( pin->handle );
2070         pin->handle = NULL;
2071         FilterRelease(pin->parentFilter);
2072     }
2073     PA_LOGL_;
2074 }
2075 
2076 /*
2077 Set the state of this (instantiated) pin
2078 */
PinSetState(PaWinWdmPin * pin,KSSTATE state)2079 static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state)
2080 {
2081     PaError result = paNoError;
2082     KSPROPERTY prop;
2083 
2084     PA_LOGE_;
2085     prop.Set = KSPROPSETID_Connection;
2086     prop.Id  = KSPROPERTY_CONNECTION_STATE;
2087     prop.Flags = KSPROPERTY_TYPE_SET;
2088 
2089     if( pin == NULL )
2090         return paInternalError;
2091     if( pin->handle == NULL )
2092         return paInternalError;
2093 
2094     result = WdmSyncIoctl(pin->handle, IOCTL_KS_PROPERTY, &prop, sizeof(KSPROPERTY), &state, sizeof(KSSTATE), NULL);
2095 
2096     PA_LOGL_;
2097     return result;
2098 }
2099 
PinInstantiate(PaWinWdmPin * pin)2100 static PaError PinInstantiate(PaWinWdmPin* pin)
2101 {
2102     PaError result;
2103     unsigned long createResult;
2104     KSALLOCATOR_FRAMING ksaf;
2105     KSALLOCATOR_FRAMING_EX ksafex;
2106 
2107     PA_LOGE_;
2108 
2109     if( pin == NULL )
2110         return paInternalError;
2111     if(!pin->pinConnect)
2112         return paInternalError;
2113 
2114     FilterUse(pin->parentFilter);
2115 
2116     createResult = FunctionKsCreatePin(
2117         pin->parentFilter->handle,
2118         pin->pinConnect,
2119         GENERIC_WRITE | GENERIC_READ,
2120         &pin->handle
2121         );
2122 
2123     PA_DEBUG(("Pin create result = 0x%08x\n",createResult));
2124     if( createResult != ERROR_SUCCESS )
2125     {
2126         FilterRelease(pin->parentFilter);
2127         pin->handle = NULL;
2128         switch (createResult)
2129         {
2130         case ERROR_INVALID_PARAMETER:
2131             /* First case when pin actually don't support the format */
2132             return paSampleFormatNotSupported;
2133         case ERROR_BAD_COMMAND:
2134             /* Case when pin is occupied (by another application) */
2135             return paDeviceUnavailable;
2136         default:
2137             /* All other cases */
2138             return paInvalidDevice;
2139         }
2140     }
2141 
2142     if (pin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
2143     {
2144         /* Framing size query only valid for WaveCyclic devices */
2145         result = WdmGetPropertySimple(
2146             pin->handle,
2147             &KSPROPSETID_Connection,
2148             KSPROPERTY_CONNECTION_ALLOCATORFRAMING,
2149             &ksaf,
2150             sizeof(ksaf));
2151 
2152         if( result != paNoError )
2153         {
2154             result = WdmGetPropertySimple(
2155                 pin->handle,
2156                 &KSPROPSETID_Connection,
2157                 KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX,
2158                 &ksafex,
2159                 sizeof(ksafex));
2160             if( result == paNoError )
2161             {
2162                 pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize;
2163             }
2164         }
2165         else
2166         {
2167             pin->frameSize = ksaf.FrameSize;
2168         }
2169     }
2170 
2171     PA_LOGL_;
2172 
2173     return paNoError;
2174 }
2175 
PinSetFormat(PaWinWdmPin * pin,const WAVEFORMATEX * format)2176 static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format)
2177 {
2178     unsigned long size;
2179     void* newConnect;
2180 
2181     PA_LOGE_;
2182 
2183     if( pin == NULL )
2184         return paInternalError;
2185     if( format == NULL )
2186         return paInternalError;
2187 
2188     size = GetWfexSize(format) + sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX) - sizeof(WAVEFORMATEX);
2189 
2190     if( pin->pinConnectSize != size )
2191     {
2192         newConnect = PaUtil_AllocateMemory( size );
2193         if( newConnect == NULL )
2194             return paInsufficientMemory;
2195         memcpy( newConnect, (void*)pin->pinConnect, min(pin->pinConnectSize,size) );
2196         PaUtil_FreeMemory( pin->pinConnect );
2197         pin->pinConnect = (KSPIN_CONNECT*)newConnect;
2198         pin->pinConnectSize = size;
2199         pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)((KSPIN_CONNECT*)newConnect + 1);
2200         pin->ksDataFormatWfx->DataFormat.FormatSize = size - sizeof(KSPIN_CONNECT);
2201     }
2202 
2203     memcpy( (void*)&(pin->ksDataFormatWfx->WaveFormatEx), format, GetWfexSize(format) );
2204     pin->ksDataFormatWfx->DataFormat.SampleSize = (unsigned short)(format->nChannels * (format->wBitsPerSample / 8));
2205 
2206     PA_LOGL_;
2207 
2208     return paNoError;
2209 }
2210 
PinIsFormatSupported(PaWinWdmPin * pin,const WAVEFORMATEX * format)2211 static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format)
2212 {
2213     KSDATARANGE_AUDIO* dataRange;
2214     unsigned long count;
2215     GUID guid = DYNAMIC_GUID( DEFINE_WAVEFORMATEX_GUID(format->wFormatTag) );
2216     PaError result = paInvalidDevice;
2217     const WAVEFORMATEXTENSIBLE* pFormatExt = (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) ? (const WAVEFORMATEXTENSIBLE*)format : 0;
2218 
2219     PA_LOGE_;
2220 
2221     if( pFormatExt != 0 )
2222     {
2223         guid = pFormatExt->SubFormat;
2224     }
2225     dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges;
2226     for(count = 0;
2227         count<pin->dataRangesItem->Count;
2228         count++,
2229         dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize)) /* Need to update dataRange here, due to 'continue' !! */
2230     {
2231         /* Check major format*/
2232         if (!(IsEqualGUID(&(dataRange->DataRange.MajorFormat), &KSDATAFORMAT_TYPE_AUDIO) ||
2233             IsEqualGUID(&(dataRange->DataRange.MajorFormat), &KSDATAFORMAT_TYPE_WILDCARD)))
2234         {
2235             continue;
2236         }
2237 
2238         /* This is an audio or wildcard datarange... */
2239         if (! (IsEqualGUID(&(dataRange->DataRange.SubFormat), &KSDATAFORMAT_SUBTYPE_WILDCARD) ||
2240             IsEqualGUID(&(dataRange->DataRange.SubFormat), &KSDATAFORMAT_SUBTYPE_PCM) ||
2241             IsEqualGUID(&(dataRange->DataRange.SubFormat), &guid) ))
2242         {
2243             continue;
2244         }
2245 
2246         /* Check specifier... */
2247         if (! (IsEqualGUID(&(dataRange->DataRange.Specifier), &KSDATAFORMAT_SPECIFIER_WILDCARD) ||
2248             IsEqualGUID(&(dataRange->DataRange.Specifier), &KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) )
2249         {
2250             continue;
2251         }
2252 
2253         PA_DEBUG(("Pin:%x, DataRange:%d\n",(void*)pin,count));
2254         PA_DEBUG(("\tFormatSize:%d, SampleSize:%d\n",dataRange->DataRange.FormatSize,dataRange->DataRange.SampleSize));
2255         PA_DEBUG(("\tMaxChannels:%d\n",dataRange->MaximumChannels));
2256         PA_DEBUG(("\tBits:%d-%d\n",dataRange->MinimumBitsPerSample,dataRange->MaximumBitsPerSample));
2257         PA_DEBUG(("\tSampleRate:%d-%d\n",dataRange->MinimumSampleFrequency,dataRange->MaximumSampleFrequency));
2258 
2259         if( dataRange->MaximumChannels != (ULONG)-1 &&
2260             dataRange->MaximumChannels < format->nChannels )
2261         {
2262             result = paInvalidChannelCount;
2263             continue;
2264         }
2265 
2266         if (pFormatExt != 0)
2267         {
2268             if (!IsBitsWithinRange(dataRange, pFormatExt->Samples.wValidBitsPerSample))
2269             {
2270                 result = paSampleFormatNotSupported;
2271                 continue;
2272             }
2273         }
2274         else
2275         {
2276             if (!IsBitsWithinRange(dataRange, format->wBitsPerSample))
2277             {
2278                 result = paSampleFormatNotSupported;
2279                 continue;
2280             }
2281         }
2282 
2283         if (!IsFrequencyWithinRange(dataRange, format->nSamplesPerSec))
2284         {
2285             result = paInvalidSampleRate;
2286             continue;
2287         }
2288 
2289         /* Success! */
2290         result = paNoError;
2291         break;
2292     }
2293 
2294     PA_LOGL_;
2295     return result;
2296 }
2297 
PinQueryNotificationSupport(PaWinWdmPin * pPin,BOOL * pbResult)2298 static PaError PinQueryNotificationSupport(PaWinWdmPin* pPin, BOOL* pbResult)
2299 {
2300     PaError result = paNoError;
2301     KSPROPERTY propIn;
2302 
2303     PA_LOGE_;
2304 
2305     propIn.Set = KSPROPSETID_RtAudio;
2306     propIn.Id = 8; /* = KSPROPERTY_RTAUDIO_QUERY_NOTIFICATION_SUPPORT */
2307     propIn.Flags = KSPROPERTY_TYPE_GET;
2308 
2309     result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
2310         &propIn,
2311         sizeof(KSPROPERTY),
2312         pbResult,
2313         sizeof(BOOL),
2314         NULL);
2315 
2316     if (result != paNoError)
2317     {
2318         PA_DEBUG(("Failed PinQueryNotificationSupport\n"));
2319     }
2320 
2321     PA_LOGL_;
2322     return result;
2323 }
2324 
PinGetBufferWithNotification(PaWinWdmPin * pPin,void ** pBuffer,DWORD * pRequestedBufSize,BOOL * pbCallMemBarrier)2325 static PaError PinGetBufferWithNotification(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier)
2326 {
2327     PaError result = paNoError;
2328     KSRTAUDIO_BUFFER_PROPERTY_WITH_NOTIFICATION propIn;
2329     KSRTAUDIO_BUFFER propOut;
2330 
2331     PA_LOGE_;
2332 
2333     propIn.BaseAddress = 0;
2334     propIn.NotificationCount = 2;
2335     propIn.RequestedBufferSize = *pRequestedBufSize;
2336     propIn.Property.Set = KSPROPSETID_RtAudio;
2337     propIn.Property.Id = KSPROPERTY_RTAUDIO_BUFFER_WITH_NOTIFICATION;
2338     propIn.Property.Flags = KSPROPERTY_TYPE_GET;
2339 
2340     result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
2341         &propIn,
2342         sizeof(KSRTAUDIO_BUFFER_PROPERTY_WITH_NOTIFICATION),
2343         &propOut,
2344         sizeof(KSRTAUDIO_BUFFER),
2345         NULL);
2346 
2347     if (result == paNoError)
2348     {
2349         *pBuffer = propOut.BufferAddress;
2350         *pRequestedBufSize = propOut.ActualBufferSize;
2351         *pbCallMemBarrier = propOut.CallMemoryBarrier;
2352     }
2353     else
2354     {
2355         PA_DEBUG(("Failed to get buffer with notification\n"));
2356     }
2357 
2358     PA_LOGL_;
2359     return result;
2360 }
2361 
PinGetBufferWithoutNotification(PaWinWdmPin * pPin,void ** pBuffer,DWORD * pRequestedBufSize,BOOL * pbCallMemBarrier)2362 static PaError PinGetBufferWithoutNotification(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier)
2363 {
2364     PaError result = paNoError;
2365     KSRTAUDIO_BUFFER_PROPERTY propIn;
2366     KSRTAUDIO_BUFFER propOut;
2367 
2368     PA_LOGE_;
2369 
2370     propIn.BaseAddress = NULL;
2371     propIn.RequestedBufferSize = *pRequestedBufSize;
2372     propIn.Property.Set = KSPROPSETID_RtAudio;
2373     propIn.Property.Id = KSPROPERTY_RTAUDIO_BUFFER;
2374     propIn.Property.Flags = KSPROPERTY_TYPE_GET;
2375 
2376     result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
2377         &propIn,
2378         sizeof(KSRTAUDIO_BUFFER_PROPERTY),
2379         &propOut,
2380         sizeof(KSRTAUDIO_BUFFER),
2381         NULL);
2382 
2383     if (result == paNoError)
2384     {
2385         *pBuffer = propOut.BufferAddress;
2386         *pRequestedBufSize = propOut.ActualBufferSize;
2387         *pbCallMemBarrier = propOut.CallMemoryBarrier;
2388     }
2389     else
2390     {
2391         PA_DEBUG(("Failed to get buffer without notification\n"));
2392     }
2393 
2394     PA_LOGL_;
2395     return result;
2396 }
2397 
2398 /* greatest common divisor - PGCD in French */
PaWinWDMGCD(unsigned long a,unsigned long b)2399 static unsigned long PaWinWDMGCD( unsigned long a, unsigned long b )
2400 {
2401     return (b==0) ? a : PaWinWDMGCD( b, a%b);
2402 }
2403 
2404 
2405 /* This function will handle getting the cyclic buffer from a WaveRT driver. Certain WaveRT drivers needs to have
2406 requested buffer size on multiples of 128 bytes:
2407 
2408 */
PinGetBuffer(PaWinWdmPin * pPin,void ** pBuffer,DWORD * pRequestedBufSize,BOOL * pbCallMemBarrier)2409 static PaError PinGetBuffer(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier)
2410 {
2411     PaError result = paNoError;
2412     int limit = 1000;
2413     PA_LOGE_;
2414 
2415     while (1)
2416     {
2417         limit--;
2418         if (limit == 0) {
2419            PA_DEBUG(("PinGetBuffer: LOOP LIMIT REACHED\n"));
2420            break;
2421         }
2422 
2423         if (pPin->pinKsSubType != SubType_kPolled)
2424         {
2425             /* In case of unknown (or notification), we try both modes */
2426             result = PinGetBufferWithNotification(pPin, pBuffer, pRequestedBufSize, pbCallMemBarrier);
2427             if (result == paNoError)
2428             {
2429                 PA_DEBUG(("PinGetBuffer: SubType_kNotification\n"));
2430                 pPin->pinKsSubType = SubType_kNotification;
2431                 break;
2432             }
2433         }
2434 
2435         result = PinGetBufferWithoutNotification(pPin, pBuffer, pRequestedBufSize, pbCallMemBarrier);
2436         if (result == paNoError)
2437         {
2438             PA_DEBUG(("PinGetBuffer: SubType_kPolled\n"));
2439             pPin->pinKsSubType = SubType_kPolled;
2440             break;
2441         }
2442 
2443         /* Check if requested size is on a 128 byte boundary */
2444         if (((*pRequestedBufSize) % 128UL) == 0)
2445         {
2446             PA_DEBUG(("Buffer size on 128 byte boundary, still fails :(\n"));
2447             /* Ok, can't do much more */
2448             break;
2449         }
2450         else
2451         {
2452             /* Compute LCM so we know which sizes are on a 128 byte boundary */
2453             const unsigned gcd = PaWinWDMGCD(128UL, pPin->ksDataFormatWfx->WaveFormatEx.nBlockAlign);
2454             const unsigned lcm = (128UL * pPin->ksDataFormatWfx->WaveFormatEx.nBlockAlign) / gcd;
2455             DWORD dwOldSize = *pRequestedBufSize;
2456 
2457             /* Align size to (next larger) LCM byte boundary, and then we try again. Note that LCM is not necessarily a
2458             power of 2. */
2459             *pRequestedBufSize = ((*pRequestedBufSize + lcm - 1) / lcm) * lcm;
2460 
2461             PA_DEBUG(("Adjusting buffer size from %u to %u bytes (128 byte boundary, LCM=%u)\n", dwOldSize, *pRequestedBufSize, lcm));
2462         }
2463     }
2464 
2465     PA_LOGL_;
2466 
2467     return result;
2468 }
2469 
PinRegisterPositionRegister(PaWinWdmPin * pPin)2470 static PaError PinRegisterPositionRegister(PaWinWdmPin* pPin)
2471 {
2472     PaError result = paNoError;
2473     KSRTAUDIO_HWREGISTER_PROPERTY propIn;
2474     KSRTAUDIO_HWREGISTER propOut;
2475 
2476     PA_LOGE_;
2477 
2478     propIn.BaseAddress = NULL;
2479     propIn.Property.Set = KSPROPSETID_RtAudio;
2480     propIn.Property.Id = KSPROPERTY_RTAUDIO_POSITIONREGISTER;
2481     propIn.Property.Flags = KSPROPERTY_TYPE_SET;
2482 
2483     result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
2484         &propIn,
2485         sizeof(KSRTAUDIO_HWREGISTER_PROPERTY),
2486         &propOut,
2487         sizeof(KSRTAUDIO_HWREGISTER),
2488         NULL);
2489 
2490     if (result == paNoError)
2491     {
2492         pPin->positionRegister = (ULONG*)propOut.Register;
2493     }
2494     else
2495     {
2496         PA_DEBUG(("Failed to register position register\n"));
2497     }
2498 
2499     PA_LOGL_;
2500 
2501     return result;
2502 }
2503 
PinRegisterNotificationHandle(PaWinWdmPin * pPin,HANDLE handle)2504 static PaError PinRegisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle)
2505 {
2506     PaError result = paNoError;
2507     KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY prop;
2508 
2509     PA_LOGE_;
2510 
2511     prop.NotificationEvent = handle;
2512     prop.Property.Set = KSPROPSETID_RtAudio;
2513     prop.Property.Id = KSPROPERTY_RTAUDIO_REGISTER_NOTIFICATION_EVENT;
2514     prop.Property.Flags = KSPROPERTY_TYPE_SET;
2515 
2516     result = WdmSyncIoctl(pPin->handle,
2517         IOCTL_KS_PROPERTY,
2518         &prop,
2519         sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
2520         &prop,
2521         sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
2522         NULL);
2523 
2524     if (result != paNoError) {
2525         PA_DEBUG(("Failed to register notification handle 0x%08X\n", handle));
2526     }
2527 
2528     PA_LOGL_;
2529 
2530     return result;
2531 }
2532 
PinUnregisterNotificationHandle(PaWinWdmPin * pPin,HANDLE handle)2533 static PaError PinUnregisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle)
2534 {
2535     PaError result = paNoError;
2536     KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY prop;
2537 
2538     PA_LOGE_;
2539 
2540     if (handle != NULL)
2541     {
2542         prop.NotificationEvent = handle;
2543         prop.Property.Set = KSPROPSETID_RtAudio;
2544         prop.Property.Id = KSPROPERTY_RTAUDIO_UNREGISTER_NOTIFICATION_EVENT;
2545         prop.Property.Flags = KSPROPERTY_TYPE_SET;
2546 
2547         result = WdmSyncIoctl(pPin->handle,
2548             IOCTL_KS_PROPERTY,
2549             &prop,
2550             sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
2551             &prop,
2552             sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
2553             NULL);
2554 
2555         if (result != paNoError) {
2556             PA_DEBUG(("Failed to unregister notification handle 0x%08X\n", handle));
2557         }
2558     }
2559     PA_LOGL_;
2560 
2561     return result;
2562 }
2563 
PinGetHwLatency(PaWinWdmPin * pPin,ULONG * pFifoSize,ULONG * pChipsetDelay,ULONG * pCodecDelay)2564 static PaError PinGetHwLatency(PaWinWdmPin* pPin, ULONG* pFifoSize, ULONG* pChipsetDelay, ULONG* pCodecDelay)
2565 {
2566     PaError result = paNoError;
2567     KSPROPERTY propIn;
2568     KSRTAUDIO_HWLATENCY propOut;
2569 
2570     PA_LOGE_;
2571 
2572     propIn.Set = KSPROPSETID_RtAudio;
2573     propIn.Id = KSPROPERTY_RTAUDIO_HWLATENCY;
2574     propIn.Flags = KSPROPERTY_TYPE_GET;
2575 
2576     result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
2577         &propIn,
2578         sizeof(KSPROPERTY),
2579         &propOut,
2580         sizeof(KSRTAUDIO_HWLATENCY),
2581         NULL);
2582 
2583     if (result == paNoError)
2584     {
2585         *pFifoSize = propOut.FifoSize;
2586         *pChipsetDelay = propOut.ChipsetDelay;
2587         *pCodecDelay = propOut.CodecDelay;
2588     }
2589     else
2590     {
2591         PA_DEBUG(("Failed to retrieve hardware FIFO size!\n"));
2592     }
2593 
2594     PA_LOGL_;
2595 
2596     return result;
2597 }
2598 
2599 /* This one is used for WaveRT */
PinGetAudioPositionMemoryMapped(PaWinWdmPin * pPin,ULONG * pPosition)2600 static PaError PinGetAudioPositionMemoryMapped(PaWinWdmPin* pPin, ULONG* pPosition)
2601 {
2602     *pPosition = (*pPin->positionRegister);
2603     return paNoError;
2604 }
2605 
2606 /* This one also, but in case the driver hasn't implemented memory mapped access to the position register */
PinGetAudioPositionViaIOCTLRead(PaWinWdmPin * pPin,ULONG * pPosition)2607 static PaError PinGetAudioPositionViaIOCTLRead(PaWinWdmPin* pPin, ULONG* pPosition)
2608 {
2609     PaError result = paNoError;
2610     KSPROPERTY propIn;
2611     KSAUDIO_POSITION propOut;
2612 
2613     PA_LOGE_;
2614 
2615     propIn.Set = KSPROPSETID_Audio;
2616     propIn.Id = KSPROPERTY_AUDIO_POSITION;
2617     propIn.Flags = KSPROPERTY_TYPE_GET;
2618 
2619     result = WdmSyncIoctl(pPin->handle,
2620         IOCTL_KS_PROPERTY,
2621         &propIn, sizeof(KSPROPERTY),
2622         &propOut, sizeof(KSAUDIO_POSITION),
2623         NULL);
2624 
2625     if (result == paNoError)
2626     {
2627         *pPosition = (ULONG)(propOut.PlayOffset);
2628     }
2629     else
2630     {
2631         PA_DEBUG(("Failed to get audio play position!\n"));
2632     }
2633 
2634     PA_LOGL_;
2635 
2636     return result;
2637 
2638 }
2639 
2640 /* This one also, but in case the driver hasn't implemented memory mapped access to the position register */
PinGetAudioPositionViaIOCTLWrite(PaWinWdmPin * pPin,ULONG * pPosition)2641 static PaError PinGetAudioPositionViaIOCTLWrite(PaWinWdmPin* pPin, ULONG* pPosition)
2642 {
2643     PaError result = paNoError;
2644     KSPROPERTY propIn;
2645     KSAUDIO_POSITION propOut;
2646 
2647     PA_LOGE_;
2648 
2649     propIn.Set = KSPROPSETID_Audio;
2650     propIn.Id = KSPROPERTY_AUDIO_POSITION;
2651     propIn.Flags = KSPROPERTY_TYPE_GET;
2652 
2653     result = WdmSyncIoctl(pPin->handle,
2654         IOCTL_KS_PROPERTY,
2655         &propIn, sizeof(KSPROPERTY),
2656         &propOut, sizeof(KSAUDIO_POSITION),
2657         NULL);
2658 
2659     if (result == paNoError)
2660     {
2661         *pPosition = (ULONG)(propOut.WriteOffset);
2662     }
2663     else
2664     {
2665         PA_DEBUG(("Failed to get audio write position!\n"));
2666     }
2667 
2668     PA_LOGL_;
2669 
2670     return result;
2671 
2672 }
2673 
2674 /***********************************************************************************************/
2675 
2676 /**
2677 * Create a new filter object.
2678 */
FilterNew(PaWDMKSType type,DWORD devNode,const wchar_t * filterName,const wchar_t * friendlyName,PaError * error)2679 static PaWinWdmFilter* FilterNew( PaWDMKSType type, DWORD devNode, const wchar_t* filterName, const wchar_t* friendlyName, PaError* error )
2680 {
2681     PaWinWdmFilter* filter = 0;
2682     PaError result;
2683 
2684     /* Allocate the new filter object */
2685     filter = (PaWinWdmFilter*)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter) );
2686     if( !filter )
2687     {
2688         result = paInsufficientMemory;
2689         goto error;
2690     }
2691 
2692     PA_DEBUG(("FilterNew: Creating filter '%S'\n", friendlyName));
2693 
2694     /* Set type flag */
2695     filter->devInfo.streamingType = type;
2696 
2697     /* Store device node */
2698     filter->deviceNode = devNode;
2699 
2700     /* Zero the filter object - done by AllocateMemory */
2701     /* memset( (void*)filter, 0, sizeof(PaWinWdmFilter) ); */
2702 
2703     /* Copy the filter name */
2704     wcsncpy(filter->devInfo.filterPath, filterName, MAX_PATH);
2705 
2706     /* Copy the friendly name */
2707     wcsncpy(filter->friendlyName, friendlyName, MAX_PATH);
2708 
2709     PA_DEBUG(("FilterNew: Opening filter...\n", friendlyName));
2710 
2711     /* Open the filter handle */
2712     result = FilterUse(filter);
2713     if( result != paNoError )
2714     {
2715         goto error;
2716     }
2717 
2718     /* Get pin count */
2719     result = WdmGetPinPropertySimple
2720         (
2721         filter->handle,
2722         0,
2723         &KSPROPSETID_Pin,
2724         KSPROPERTY_PIN_CTYPES,
2725         &filter->pinCount,
2726         sizeof(filter->pinCount),
2727         NULL);
2728 
2729     if( result != paNoError)
2730     {
2731         goto error;
2732     }
2733 
2734     /* Get connections & nodes for filter */
2735     result = WdmGetPropertyMulti(
2736         filter->handle,
2737         &KSPROPSETID_Topology,
2738         KSPROPERTY_TOPOLOGY_CONNECTIONS,
2739         &filter->connections);
2740 
2741     if( result != paNoError)
2742     {
2743         goto error;
2744     }
2745 
2746     result = WdmGetPropertyMulti(
2747         filter->handle,
2748         &KSPROPSETID_Topology,
2749         KSPROPERTY_TOPOLOGY_NODES,
2750         &filter->nodes);
2751 
2752     if( result != paNoError)
2753     {
2754         goto error;
2755     }
2756 
2757     /* For debugging purposes */
2758     DumpConnectionsAndNodes(filter);
2759 
2760     /* Get product GUID (it might not be supported) */
2761     {
2762         KSCOMPONENTID compId;
2763         if (WdmGetPropertySimple(filter->handle, &KSPROPSETID_General, KSPROPERTY_GENERAL_COMPONENTID, &compId, sizeof(KSCOMPONENTID)) == paNoError)
2764         {
2765             filter->devInfo.deviceProductGuid = compId.Product;
2766         }
2767     }
2768 
2769     /* This section is not executed for topology filters */
2770     if (type != Type_kNotUsed)
2771     {
2772         /* Initialize the pins */
2773         result = FilterInitializePins(filter);
2774 
2775         if( result != paNoError)
2776         {
2777             goto error;
2778         }
2779     }
2780 
2781     /* Close the filter handle for now
2782     * It will be opened later when needed */
2783     FilterRelease(filter);
2784 
2785     *error = paNoError;
2786     return filter;
2787 
2788 error:
2789     PA_DEBUG(("FilterNew: Error %d\n", result));
2790     /*
2791     Error cleanup
2792     */
2793     FilterFree(filter);
2794 
2795     *error = result;
2796     return NULL;
2797 }
2798 
2799 /**
2800 * Add reference to filter
2801 */
FilterAddRef(PaWinWdmFilter * filter)2802 static void FilterAddRef( PaWinWdmFilter* filter )
2803 {
2804     if (filter != 0)
2805     {
2806         filter->filterRefCount++;
2807     }
2808 }
2809 
2810 
2811 /**
2812 * Initialize the pins of the filter. This is separated from FilterNew because this might fail if there is another
2813 * process using the pin(s).
2814 */
FilterInitializePins(PaWinWdmFilter * filter)2815 PaError FilterInitializePins( PaWinWdmFilter* filter )
2816 {
2817     PaError result = paNoError;
2818     int pinId;
2819 
2820     if (filter->devInfo.streamingType == Type_kNotUsed)
2821         return paNoError;
2822 
2823     if (filter->pins != NULL)
2824         return paNoError;
2825 
2826     /* Allocate pointer array to hold the pins */
2827     filter->pins = (PaWinWdmPin**)PaUtil_AllocateMemory( sizeof(PaWinWdmPin*) * filter->pinCount );
2828     if( !filter->pins )
2829     {
2830         result = paInsufficientMemory;
2831         goto error;
2832     }
2833 
2834     /* Create all the pins we can */
2835     for(pinId = 0; pinId < filter->pinCount; pinId++)
2836     {
2837         /* Create the pin with this Id */
2838         PaWinWdmPin* newPin;
2839         newPin = PinNew(filter, pinId, &result);
2840         if( result == paInsufficientMemory )
2841             goto error;
2842         if( newPin != NULL )
2843         {
2844             filter->pins[pinId] = newPin;
2845             ++filter->validPinCount;
2846         }
2847     }
2848 
2849     if (filter->validPinCount == 0)
2850     {
2851         result = paDeviceUnavailable;
2852         goto error;
2853     }
2854 
2855     return paNoError;
2856 
2857 error:
2858 
2859     if (filter->pins)
2860     {
2861         for (pinId = 0; pinId < filter->pinCount; ++pinId)
2862         {
2863             if (filter->pins[pinId])
2864             {
2865                 PinFree(filter->pins[pinId]);
2866                 filter->pins[pinId] = 0;
2867             }
2868         }
2869         PaUtil_FreeMemory( filter->pins );
2870         filter->pins = 0;
2871     }
2872 
2873     return result;
2874 }
2875 
2876 
2877 /**
2878 * Free a previously created filter
2879 */
FilterFree(PaWinWdmFilter * filter)2880 static void FilterFree(PaWinWdmFilter* filter)
2881 {
2882     PA_LOGL_;
2883     if( filter )
2884     {
2885         if (--filter->filterRefCount > 0)
2886         {
2887             /* Ok, a stream has a ref count to this filter */
2888             return;
2889         }
2890 
2891         if ( filter->topologyFilter )
2892         {
2893             FilterFree(filter->topologyFilter);
2894             filter->topologyFilter = 0;
2895         }
2896         if ( filter->pins )
2897         {
2898             int pinId;
2899             for( pinId = 0; pinId < filter->pinCount; pinId++ )
2900                 PinFree(filter->pins[pinId]);
2901             PaUtil_FreeMemory( filter->pins );
2902             filter->pins = 0;
2903         }
2904         if( filter->connections )
2905         {
2906             PaUtil_FreeMemory(filter->connections);
2907             filter->connections = 0;
2908         }
2909         if( filter->nodes )
2910         {
2911             PaUtil_FreeMemory(filter->nodes);
2912             filter->nodes = 0;
2913         }
2914         if( filter->handle )
2915             CloseHandle( filter->handle );
2916         PaUtil_FreeMemory( filter );
2917     }
2918     PA_LOGE_;
2919 }
2920 
2921 /**
2922 * Reopen the filter handle if necessary so it can be used
2923 **/
FilterUse(PaWinWdmFilter * filter)2924 static PaError FilterUse(PaWinWdmFilter* filter)
2925 {
2926     assert( filter );
2927 
2928     PA_LOGE_;
2929     if( filter->handle == NULL )
2930     {
2931         /* Open the filter */
2932         filter->handle = CreateFileW(
2933             filter->devInfo.filterPath,
2934             GENERIC_READ | GENERIC_WRITE,
2935             0,
2936             NULL,
2937             OPEN_EXISTING,
2938             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
2939             NULL);
2940 
2941         if( filter->handle == NULL )
2942         {
2943             return paDeviceUnavailable;
2944         }
2945     }
2946     filter->usageCount++;
2947     PA_LOGL_;
2948     return paNoError;
2949 }
2950 
2951 /**
2952 * Release the filter handle if nobody is using it
2953 **/
FilterRelease(PaWinWdmFilter * filter)2954 static void FilterRelease(PaWinWdmFilter* filter)
2955 {
2956     assert( filter );
2957     assert( filter->usageCount > 0 );
2958 
2959     PA_LOGE_;
2960     /* Check first topology filter, if used */
2961     if (filter->topologyFilter != NULL && filter->topologyFilter->handle != NULL)
2962     {
2963         FilterRelease(filter->topologyFilter);
2964     }
2965 
2966     filter->usageCount--;
2967     if( filter->usageCount == 0 )
2968     {
2969         if( filter->handle != NULL )
2970         {
2971             CloseHandle( filter->handle );
2972             filter->handle = NULL;
2973         }
2974     }
2975     PA_LOGL_;
2976 }
2977 
2978 /**
2979 * Create a render or playback pin using the supplied format
2980 **/
FilterCreatePin(PaWinWdmFilter * filter,int pinId,const WAVEFORMATEX * wfex,PaError * error)2981 static PaWinWdmPin* FilterCreatePin(PaWinWdmFilter* filter,
2982                                     int pinId,
2983                                     const WAVEFORMATEX* wfex,
2984                                     PaError* error)
2985 {
2986     PaError result = paNoError;
2987     PaWinWdmPin* pin = NULL;
2988     assert( filter );
2989     assert( pinId < filter->pinCount );
2990     pin = filter->pins[pinId];
2991     assert( pin );
2992     result = PinSetFormat(pin,wfex);
2993     if( result == paNoError )
2994     {
2995         result = PinInstantiate(pin);
2996     }
2997     *error = result;
2998     return result == paNoError ? pin : 0;
2999 }
3000 
3001 static const wchar_t kUsbPrefix[] = L"\\\\?\\USB";
3002 
IsUSBDevice(const wchar_t * devicePath)3003 static BOOL IsUSBDevice(const wchar_t* devicePath)
3004 {
3005     /* Alex Lessard pointed out that different devices might present the device path with
3006        lower case letters. */
3007     return (_wcsnicmp(devicePath, kUsbPrefix, sizeof(kUsbPrefix)/sizeof(kUsbPrefix[0]) ) == 0);
3008 }
3009 
3010 /* This should make it more language tolerant, I hope... */
3011 static const wchar_t kUsbNamePrefix[] = L"USB Audio";
3012 
IsNameUSBAudioDevice(const wchar_t * friendlyName)3013 static BOOL IsNameUSBAudioDevice(const wchar_t* friendlyName)
3014 {
3015     return (_wcsnicmp(friendlyName, kUsbNamePrefix, sizeof(kUsbNamePrefix)/sizeof(kUsbNamePrefix[0])) == 0);
3016 }
3017 
3018 typedef enum _tag_EAlias
3019 {
3020     Alias_kRender   = (1<<0),
3021     Alias_kCapture  = (1<<1),
3022     Alias_kRealtime = (1<<2),
3023 } EAlias;
3024 
3025 /* Trim whitespace from string */
TrimString(wchar_t * str,size_t length)3026 static void TrimString(wchar_t* str, size_t length)
3027 {
3028     wchar_t* s = str;
3029     wchar_t* e = 0;
3030 
3031     /* Find start of string */
3032     while (iswspace(*s)) ++s;
3033     e=s+min(length,wcslen(s))-1;
3034 
3035     /* Find end of string */
3036     while(e>s && iswspace(*e)) --e;
3037     ++e;
3038 
3039     length = e - s;
3040     memmove(str, s, length * sizeof(wchar_t));
3041     str[length] = 0;
3042 }
3043 
3044 /**
3045 * Build the list of available filters
3046 * Use the SetupDi API to enumerate all devices in the KSCATEGORY_AUDIO which
3047 * have a KSCATEGORY_RENDER or KSCATEGORY_CAPTURE alias. For each of these
3048 * devices initialise a PaWinWdmFilter structure by calling our NewFilter()
3049 * function. We enumerate devices twice, once to count how many there are,
3050 * and once to initialize the PaWinWdmFilter structures.
3051 *
3052 * Vista and later: Also check KSCATEGORY_REALTIME for WaveRT devices.
3053 */
3054 //PaError BuildFilterList( PaWinWdmHostApiRepresentation* wdmHostApi, int* noOfPaDevices )
BuildFilterList(int * pFilterCount,int * pNoOfPaDevices,PaError * pResult)3055 PaWinWdmFilter** BuildFilterList( int* pFilterCount, int* pNoOfPaDevices, PaError* pResult )
3056 {
3057     PaWinWdmFilter** ppFilters = NULL;
3058     HDEVINFO handle = NULL;
3059     int device;
3060     int invalidDevices;
3061     int slot;
3062     SP_DEVICE_INTERFACE_DATA interfaceData;
3063     SP_DEVICE_INTERFACE_DATA aliasData;
3064     SP_DEVINFO_DATA devInfoData;
3065     int noError;
3066     const int sizeInterface = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR));
3067     unsigned char interfaceDetailsArray[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR))];
3068     SP_DEVICE_INTERFACE_DETAIL_DATA_W* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)interfaceDetailsArray;
3069     const GUID* category = (const GUID*)&KSCATEGORY_AUDIO;
3070     const GUID* alias_render = (const GUID*)&KSCATEGORY_RENDER;
3071     const GUID* alias_capture = (const GUID*)&KSCATEGORY_CAPTURE;
3072     const GUID* category_realtime = (const GUID*)&KSCATEGORY_REALTIME;
3073     DWORD aliasFlags;
3074     PaWDMKSType streamingType;
3075     int filterCount = 0;
3076     int noOfPaDevices = 0;
3077 
3078     PA_LOGE_;
3079 
3080     assert(pFilterCount != NULL);
3081     assert(pNoOfPaDevices != NULL);
3082     assert(pResult != NULL);
3083 
3084     devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
3085     *pFilterCount = 0;
3086     *pNoOfPaDevices = 0;
3087 
3088     /* Open a handle to search for devices (filters) */
3089     handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
3090     if( handle == INVALID_HANDLE_VALUE )
3091     {
3092         *pResult = paUnanticipatedHostError;
3093         return NULL;
3094     }
3095     PA_DEBUG(("Setup called\n"));
3096 
3097     /* First let's count the number of devices so we can allocate a list */
3098     invalidDevices = 0;
3099     for( device = 0;;device++ )
3100     {
3101         interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3102         interfaceData.Reserved = 0;
3103         aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3104         aliasData.Reserved = 0;
3105         noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
3106         PA_DEBUG(("Enum called\n"));
3107         if( !noError )
3108             break; /* No more devices */
3109 
3110         /* Check this one has the render or capture alias */
3111         aliasFlags = 0;
3112         noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
3113         PA_DEBUG(("noError = %d\n",noError));
3114         if(noError)
3115         {
3116             if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
3117             {
3118                 PA_DEBUG(("Device %d has render alias\n",device));
3119                 aliasFlags |= Alias_kRender; /* Has render alias */
3120             }
3121             else
3122             {
3123                 PA_DEBUG(("Device %d has no render alias\n",device));
3124             }
3125         }
3126         noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
3127         if(noError)
3128         {
3129             if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
3130             {
3131                 PA_DEBUG(("Device %d has capture alias\n",device));
3132                 aliasFlags |= Alias_kCapture; /* Has capture alias */
3133             }
3134             else
3135             {
3136                 PA_DEBUG(("Device %d has no capture alias\n",device));
3137             }
3138         }
3139         if(!aliasFlags)
3140             invalidDevices++; /* This was not a valid capture or render audio device */
3141     }
3142     /* Remember how many there are */
3143     filterCount = device-invalidDevices;
3144 
3145     PA_DEBUG(("Interfaces found: %d\n",device-invalidDevices));
3146 
3147     /* Now allocate the list of pointers to devices */
3148     ppFilters  = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * filterCount);
3149     if( ppFilters == 0 )
3150     {
3151         if(handle != NULL)
3152             SetupDiDestroyDeviceInfoList(handle);
3153         *pResult = paInsufficientMemory;
3154         return NULL;
3155     }
3156 
3157     /* Now create filter objects for each interface found */
3158     slot = 0;
3159     for( device = 0;;device++ )
3160     {
3161         interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3162         interfaceData.Reserved = 0;
3163         aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3164         aliasData.Reserved = 0;
3165         devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
3166         devInfoData.Reserved = 0;
3167         streamingType = Type_kWaveCyclic;
3168 
3169         noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
3170         if( !noError )
3171             break; /* No more devices */
3172 
3173         /* Check this one has the render or capture alias */
3174         aliasFlags = 0;
3175         noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
3176         if(noError)
3177         {
3178             if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
3179             {
3180                 PA_DEBUG(("Device %d has render alias\n",device));
3181                 aliasFlags |= Alias_kRender; /* Has render alias */
3182             }
3183         }
3184         noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
3185         if(noError)
3186         {
3187             if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
3188             {
3189                 PA_DEBUG(("Device %d has capture alias\n",device));
3190                 aliasFlags |= Alias_kCapture; /* Has capture alias */
3191             }
3192         }
3193         if(!aliasFlags)
3194         {
3195             continue; /* This was not a valid capture or render audio device */
3196         }
3197         else
3198         {
3199             /* Check if filter is WaveRT, if not it is a WaveCyclic */
3200             noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,category_realtime,&aliasData);
3201             if (noError)
3202             {
3203                 PA_DEBUG(("Device %d has realtime alias\n",device));
3204                 aliasFlags |= Alias_kRealtime;
3205                 streamingType = Type_kWaveRT;
3206             }
3207         }
3208 
3209         noError = SetupDiGetDeviceInterfaceDetailW(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
3210         if( noError )
3211         {
3212             DWORD type;
3213             WCHAR friendlyName[MAX_PATH] = {0};
3214             DWORD sizeFriendlyName;
3215             PaWinWdmFilter* newFilter = 0;
3216 
3217             PaError result = paNoError;
3218             /* Try to get the "friendly name" for this interface */
3219             sizeFriendlyName = sizeof(friendlyName);
3220 
3221             if (IsEarlierThanVista() && IsUSBDevice(devInterfaceDetails->DevicePath))
3222             {
3223                 /* XP and USB audio device needs to look elsewhere, otherwise it'll only be a "USB Audio Device". Not
3224                 very literate. */
3225                 if (!SetupDiGetDeviceRegistryPropertyW(handle,
3226                     &devInfoData,
3227                     SPDRP_LOCATION_INFORMATION,
3228                     &type,
3229                     (BYTE*)friendlyName,
3230                     sizeof(friendlyName),
3231                     NULL))
3232                 {
3233                     friendlyName[0] = 0;
3234                 }
3235             }
3236 
3237             if (friendlyName[0] == 0 || IsNameUSBAudioDevice(friendlyName))
3238             {
3239                 /* Fix contributed by Ben Allison
3240                 * Removed KEY_SET_VALUE from flags on following call
3241                 * as its causes failure when running without admin rights
3242                 * and it was not required */
3243                 HKEY hkey=SetupDiOpenDeviceInterfaceRegKey(handle,&interfaceData,0,KEY_QUERY_VALUE);
3244                 if(hkey!=INVALID_HANDLE_VALUE)
3245                 {
3246                     noError = RegQueryValueExW(hkey,L"FriendlyName",0,&type,(BYTE*)friendlyName,&sizeFriendlyName);
3247                     if( noError == ERROR_SUCCESS )
3248                     {
3249                         PA_DEBUG(("Interface %d, Name: %s\n",device,friendlyName));
3250                         RegCloseKey(hkey);
3251                     }
3252                     else
3253                     {
3254                         friendlyName[0] = 0;
3255                     }
3256                 }
3257             }
3258 
3259             TrimString(friendlyName, sizeFriendlyName);
3260 
3261             newFilter = FilterNew(streamingType,
3262                 devInfoData.DevInst,
3263                 devInterfaceDetails->DevicePath,
3264                 friendlyName,
3265                 &result);
3266 
3267             if( result == paNoError )
3268             {
3269                 int pin;
3270                 unsigned filterIOs = 0;
3271 
3272                 /* Increment number of "devices" */
3273                 for (pin = 0; pin < newFilter->pinCount; ++pin)
3274                 {
3275                     PaWinWdmPin* pPin = newFilter->pins[pin];
3276                     if (pPin == NULL)
3277                         continue;
3278 
3279                     filterIOs += max(1, pPin->inputCount);
3280                 }
3281 
3282                 noOfPaDevices += filterIOs;
3283 
3284                 PA_DEBUG(("Filter (%s) created with %d valid pins (total I/Os: %u)\n", ((newFilter->devInfo.streamingType==Type_kWaveRT)?"WaveRT":"WaveCyclic"), newFilter->validPinCount, filterIOs));
3285 
3286                 assert(slot < filterCount);
3287 
3288                 ppFilters[slot] = newFilter;
3289 
3290                 slot++;
3291             }
3292             else
3293             {
3294                 PA_DEBUG(("Filter NOT created\n"));
3295                 /* As there are now less filters than we initially thought
3296                 * we must reduce the count by one */
3297                 filterCount--;
3298             }
3299         }
3300     }
3301 
3302     /* Clean up */
3303     if(handle != NULL)
3304         SetupDiDestroyDeviceInfoList(handle);
3305 
3306     *pFilterCount = filterCount;
3307     *pNoOfPaDevices = noOfPaDevices;
3308 
3309     return ppFilters;
3310 }
3311 
3312 typedef struct PaNameHashIndex
3313 {
3314     unsigned index;
3315     unsigned count;
3316     ULONG    hash;
3317     struct PaNameHashIndex *next;
3318 } PaNameHashIndex;
3319 
3320 typedef struct PaNameHashObject
3321 {
3322     PaNameHashIndex* list;
3323     PaUtilAllocationGroup* allocGroup;
3324 } PaNameHashObject;
3325 
GetNameHash(const wchar_t * str,const BOOL input)3326 static ULONG GetNameHash(const wchar_t* str, const BOOL input)
3327 {
3328     /* This is to make sure that a name that exists as both input & output won't get the same hash value */
3329     const ULONG fnv_prime = (input ? 0x811C9DD7 : 0x811FEB0B);
3330     ULONG hash = 0;
3331     for(; *str != 0; str++)
3332     {
3333         hash *= fnv_prime;
3334         hash ^= (*str);
3335     }
3336     assert(hash != 0);
3337     return hash;
3338 }
3339 
CreateHashEntry(PaNameHashObject * obj,const wchar_t * name,const BOOL input)3340 static PaError CreateHashEntry(PaNameHashObject* obj, const wchar_t* name, const BOOL input)
3341 {
3342     ULONG hash = GetNameHash(name, input);
3343     PaNameHashIndex * pLast = NULL;
3344     PaNameHashIndex * p = obj->list;
3345     while (p != 0)
3346     {
3347         if (p->hash == hash)
3348         {
3349             break;
3350         }
3351         pLast = p;
3352         p = p->next;
3353     }
3354     if (p == NULL)
3355     {
3356         p = (PaNameHashIndex*)PaUtil_GroupAllocateMemory(obj->allocGroup, sizeof(PaNameHashIndex));
3357         if (p == NULL)
3358         {
3359             return paInsufficientMemory;
3360         }
3361         p->hash = hash;
3362         p->count = 1;
3363         if (pLast != 0)
3364         {
3365             assert(pLast->next == 0);
3366             pLast->next = p;
3367         }
3368         if (obj->list == 0)
3369         {
3370             obj->list = p;
3371         }
3372     }
3373     else
3374     {
3375         ++p->count;
3376     }
3377     return paNoError;
3378 }
3379 
InitNameHashObject(PaNameHashObject * obj,PaWinWdmFilter * pFilter)3380 static PaError InitNameHashObject(PaNameHashObject* obj, PaWinWdmFilter* pFilter)
3381 {
3382     int i;
3383 
3384     obj->allocGroup = PaUtil_CreateAllocationGroup();
3385     if (obj->allocGroup == NULL)
3386     {
3387         return paInsufficientMemory;
3388     }
3389 
3390     for (i = 0; i < pFilter->pinCount; ++i)
3391     {
3392         unsigned m;
3393         PaWinWdmPin* pin = pFilter->pins[i];
3394 
3395         if (pin == NULL)
3396             continue;
3397 
3398         for (m = 0; m < max(1, pin->inputCount); ++m)
3399         {
3400             const BOOL isInput = (pin->dataFlow == KSPIN_DATAFLOW_OUT);
3401             const wchar_t* name = (pin->inputs == NULL) ? pin->friendlyName : pin->inputs[m]->friendlyName;
3402 
3403             PaError result = CreateHashEntry(obj, name, isInput);
3404 
3405             if (result != paNoError)
3406             {
3407                 return result;
3408             }
3409         }
3410     }
3411     return paNoError;
3412 }
3413 
DeinitNameHashObject(PaNameHashObject * obj)3414 static void DeinitNameHashObject(PaNameHashObject* obj)
3415 {
3416     assert(obj != 0);
3417     PaUtil_FreeAllAllocations(obj->allocGroup);
3418     PaUtil_DestroyAllocationGroup(obj->allocGroup);
3419     memset(obj, 0, sizeof(PaNameHashObject));
3420 }
3421 
GetNameIndex(PaNameHashObject * obj,const wchar_t * name,const BOOL input)3422 static unsigned GetNameIndex(PaNameHashObject* obj, const wchar_t* name, const BOOL input)
3423 {
3424     ULONG hash = GetNameHash(name, input);
3425     PaNameHashIndex* p = obj->list;
3426     while (p != NULL)
3427     {
3428         if (p->hash == hash)
3429         {
3430             if (p->count > 1)
3431             {
3432                 return (++p->index);
3433             }
3434             else
3435             {
3436                 return 0;
3437             }
3438         }
3439 
3440         p = p->next;
3441     }
3442     // Should never get here!!
3443     assert(FALSE);
3444     return 0;
3445 }
3446 
ScanDeviceInfos(struct PaUtilHostApiRepresentation * hostApi,PaHostApiIndex hostApiIndex,void ** scanResults,int * newDeviceCount)3447 static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, void **scanResults, int *newDeviceCount )
3448 {
3449     PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
3450     PaError result = paNoError;
3451     PaWinWdmFilter** ppFilters = 0;
3452     PaWinWDMScanDeviceInfosResults *outArgument = 0;
3453     int filterCount = 0;
3454     int totalDeviceCount = 0;
3455     int idxDevice = 0;
3456     DWORD defaultInDevPathSize = 0;
3457     DWORD defaultOutDevPathSize = 0;
3458     wchar_t* defaultInDevPath = 0;
3459     wchar_t* defaultOutDevPath = 0;
3460 
3461     ppFilters = BuildFilterList( &filterCount, &totalDeviceCount, &result );
3462     if( result != paNoError )
3463     {
3464         goto error;
3465     }
3466 
3467     // Get hold of default device paths for capture & playback
3468     if( waveInMessage(0, DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&defaultInDevPathSize, 0 ) == MMSYSERR_NOERROR )
3469     {
3470         defaultInDevPath = (wchar_t *)PaUtil_AllocateMemory((defaultInDevPathSize + 1) * sizeof(wchar_t));
3471         waveInMessage(0, DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)defaultInDevPath, defaultInDevPathSize);
3472     }
3473     if( waveOutMessage(0, DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&defaultOutDevPathSize, 0 ) == MMSYSERR_NOERROR )
3474     {
3475         defaultOutDevPath = (wchar_t *)PaUtil_AllocateMemory((defaultOutDevPathSize + 1) * sizeof(wchar_t));
3476         waveOutMessage(0, DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)defaultOutDevPath, defaultOutDevPathSize);
3477     }
3478 
3479     if( totalDeviceCount > 0 )
3480     {
3481         PaWinWdmDeviceInfo *deviceInfoArray = 0;
3482         int idxFilter;
3483         int i;
3484         unsigned devIsDefaultIn = 0, devIsDefaultOut = 0;
3485 
3486         /* Allocate the out param for all the info we need */
3487         outArgument = (PaWinWDMScanDeviceInfosResults *) PaUtil_GroupAllocateMemory(
3488             wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults) );
3489         if( !outArgument )
3490         {
3491             result = paInsufficientMemory;
3492             goto error;
3493         }
3494 
3495         outArgument->defaultInputDevice  = paNoDevice;
3496         outArgument->defaultOutputDevice = paNoDevice;
3497 
3498         outArgument->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
3499             wdmHostApi->allocations, sizeof(PaDeviceInfo*) * totalDeviceCount );
3500         if( !outArgument->deviceInfos )
3501         {
3502             result = paInsufficientMemory;
3503             goto error;
3504         }
3505 
3506         /* allocate all device info structs in a contiguous block */
3507         deviceInfoArray = (PaWinWdmDeviceInfo*)PaUtil_GroupAllocateMemory(
3508             wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo) * totalDeviceCount );
3509         if( !deviceInfoArray )
3510         {
3511             result = paInsufficientMemory;
3512             goto error;
3513         }
3514 
3515         /* Make sure all items in array */
3516         for( i = 0 ; i < totalDeviceCount; ++i )
3517         {
3518             PaDeviceInfo *deviceInfo  = &deviceInfoArray[i].inheritedDeviceInfo;
3519             deviceInfo->structVersion = 2;
3520             deviceInfo->hostApi       = hostApiIndex;
3521             deviceInfo->name          = 0;
3522             outArgument->deviceInfos[ i ] = deviceInfo;
3523         }
3524 
3525         idxDevice = 0;
3526         for (idxFilter = 0; idxFilter < filterCount; ++idxFilter)
3527         {
3528             PaNameHashObject nameHash = {0};
3529             PaWinWdmFilter* pFilter = ppFilters[idxFilter];
3530             if( pFilter == NULL )
3531                 continue;
3532 
3533             if (InitNameHashObject(&nameHash, pFilter) != paNoError)
3534             {
3535                 DeinitNameHashObject(&nameHash);
3536                 continue;
3537             }
3538 
3539             devIsDefaultIn = (defaultInDevPath && (_wcsicmp(pFilter->devInfo.filterPath, defaultInDevPath) == 0));
3540             devIsDefaultOut = (defaultOutDevPath && (_wcsicmp(pFilter->devInfo.filterPath, defaultOutDevPath) == 0));
3541 
3542             for (i = 0; i < pFilter->pinCount; ++i)
3543             {
3544                 unsigned m;
3545                 ULONG nameIndex = 0;
3546                 ULONG nameIndexHash = 0;
3547                 PaWinWdmPin* pin = pFilter->pins[i];
3548 
3549                 if (pin == NULL)
3550                     continue;
3551 
3552                 for (m = 0; m < max(1, pin->inputCount); ++m)
3553                 {
3554                     PaWinWdmDeviceInfo *wdmDeviceInfo = (PaWinWdmDeviceInfo *)outArgument->deviceInfos[idxDevice];
3555                     PaDeviceInfo *deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo;
3556                     wchar_t localCompositeName[MAX_PATH];
3557                     unsigned nameIndex = 0;
3558                     const BOOL isInput = (pin->dataFlow == KSPIN_DATAFLOW_OUT);
3559 
3560                     wdmDeviceInfo->filter = pFilter;
3561 
3562                     deviceInfo->structVersion = 2;
3563                     deviceInfo->hostApi = hostApiIndex;
3564                     deviceInfo->name = wdmDeviceInfo->compositeName;
3565                     /* deviceInfo->hostApiSpecificDeviceInfo = &pFilter->devInfo; */
3566 
3567                     wdmDeviceInfo->pin = pin->pinId;
3568 
3569                     /* Get the name of the "device" */
3570                     if (pin->inputs == NULL)
3571                     {
3572                         wcsncpy(localCompositeName, pin->friendlyName, MAX_PATH);
3573                         wdmDeviceInfo->muxPosition = -1;
3574                         wdmDeviceInfo->endpointPinId = pin->endpointPinId;
3575                     }
3576                     else
3577                     {
3578                         PaWinWdmMuxedInput* input = pin->inputs[m];
3579                         wcsncpy(localCompositeName, input->friendlyName, MAX_PATH);
3580                         wdmDeviceInfo->muxPosition = (int)m;
3581                         wdmDeviceInfo->endpointPinId = input->endpointPinId;
3582                     }
3583 
3584                     {
3585                         /* Get base length */
3586                         size_t n = wcslen(localCompositeName);
3587 
3588                         /* Check if there are more entries with same name (which might very well be the case), if there
3589                         are, the name will be postfixed with an index. */
3590                         nameIndex = GetNameIndex(&nameHash, localCompositeName, isInput);
3591                         if (nameIndex > 0)
3592                         {
3593                             /* This name has multiple instances, so we post fix with a number */
3594                             n += _snwprintf(localCompositeName + n, MAX_PATH - n, L" %u", nameIndex);
3595                         }
3596                         /* Postfix with filter name */
3597                         _snwprintf(localCompositeName + n, MAX_PATH - n, L" (%s)", pFilter->friendlyName);
3598                     }
3599 
3600                     /* Convert wide char string to utf-8 */
3601                     WideCharToMultiByte(CP_UTF8, 0, localCompositeName, -1, wdmDeviceInfo->compositeName, MAX_PATH, NULL, NULL);
3602 
3603                     /* NB! WDM/KS has no concept of a full-duplex device, each pin is either an input or an output */
3604                     if (isInput)
3605                     {
3606                         /* INPUT ! */
3607                         deviceInfo->maxInputChannels  = pin->maxChannels;
3608                         deviceInfo->maxOutputChannels = 0;
3609 
3610                         /* RoBi NB: Due to the fact that input audio endpoints in Vista (& later OSs) can be the same device, but with
3611                            different input mux settings, there might be a discrepancy between the default input device chosen, and
3612                            that which will be used by Portaudio. Not much to do about that unfortunately.
3613                         */
3614                         if ((defaultInDevPath == 0 || devIsDefaultIn) &&
3615                              outArgument->defaultInputDevice == paNoDevice)
3616                         {
3617                             outArgument->defaultInputDevice = idxDevice;
3618                         }
3619                     }
3620                     else
3621                     {
3622                         /* OUTPUT ! */
3623                         deviceInfo->maxInputChannels  = 0;
3624                         deviceInfo->maxOutputChannels = pin->maxChannels;
3625 
3626                         if ((defaultOutDevPath == 0 || devIsDefaultOut) &&
3627                             outArgument->defaultOutputDevice == paNoDevice)
3628                         {
3629                             outArgument->defaultOutputDevice = idxDevice;
3630                         }
3631                     }
3632 
3633                     /* These low values are not very useful because
3634                     * a) The lowest latency we end up with can depend on many factors such
3635                     *    as the device buffer sizes/granularities, sample rate, channels and format
3636                     * b) We cannot know the device buffer sizes until we try to open/use it at
3637                     *    a particular setting
3638                     * So: we give 512x48000Hz frames as the default low input latency
3639                     **/
3640                     switch (pFilter->devInfo.streamingType)
3641                     {
3642                     case Type_kWaveCyclic:
3643                         if (IsEarlierThanVista())
3644                         {
3645                             /* XP doesn't tolerate low latency, unless the Process Priority Class is set to REALTIME_PRIORITY_CLASS
3646                             through SetPriorityClass, then 10 ms is quite feasible. However, one should then bear in mind that ALL of
3647                             the process is running in REALTIME_PRIORITY_CLASS, which might not be appropriate for an application with
3648                             a GUI . In this case it is advisable to separate the audio engine in another process and use IPC to communicate
3649                             with it. */
3650                             deviceInfo->defaultLowInputLatency = 0.02;
3651                             deviceInfo->defaultLowOutputLatency = 0.02;
3652                         }
3653                         else
3654                         {
3655                             /* This is a conservative estimate. Most WaveCyclic drivers will limit the available latency, but f.i. my Edirol
3656                             PCR-A30 can reach 3 ms latency easily... */
3657                             deviceInfo->defaultLowInputLatency = 0.01;
3658                             deviceInfo->defaultLowOutputLatency = 0.01;
3659                         }
3660                         deviceInfo->defaultHighInputLatency = (4096.0/48000.0);
3661                         deviceInfo->defaultHighOutputLatency = (4096.0/48000.0);
3662                         deviceInfo->defaultSampleRate = (double)(pin->defaultSampleRate);
3663                         break;
3664                     case Type_kWaveRT:
3665                         /* This is also a conservative estimate, based on WaveRT polled mode. In polled mode, the latency will be dictated
3666                         by the buffer size given by the driver. */
3667                         deviceInfo->defaultLowInputLatency = 0.01;
3668                         deviceInfo->defaultLowOutputLatency = 0.01;
3669                         deviceInfo->defaultHighInputLatency = 0.04;
3670                         deviceInfo->defaultHighOutputLatency = 0.04;
3671                         deviceInfo->defaultSampleRate = (double)(pin->defaultSampleRate);
3672                         break;
3673                     default:
3674                         assert(0);
3675                         break;
3676                     }
3677 
3678                     /* Add reference to filter */
3679                     FilterAddRef(wdmDeviceInfo->filter);
3680 
3681                     assert(idxDevice < totalDeviceCount);
3682                     ++idxDevice;
3683                 }
3684             }
3685 
3686             /* If no one has add ref'd the filter, drop it */
3687             if (pFilter->filterRefCount == 0)
3688             {
3689                 FilterFree(pFilter);
3690             }
3691 
3692             /* Deinitialize name hash object */
3693             DeinitNameHashObject(&nameHash);
3694         }
3695     }
3696 
3697     *scanResults = outArgument;
3698     *newDeviceCount = idxDevice;
3699     return result;
3700 
3701 error:
3702     result = DisposeDeviceInfos(hostApi, outArgument, totalDeviceCount);
3703 
3704     return result;
3705 }
3706 
CommitDeviceInfos(struct PaUtilHostApiRepresentation * hostApi,PaHostApiIndex index,void * scanResults,int deviceCount)3707 static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *scanResults, int deviceCount )
3708 {
3709     PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
3710 
3711     hostApi->info.deviceCount = 0;
3712     hostApi->info.defaultInputDevice = paNoDevice;
3713     hostApi->info.defaultOutputDevice = paNoDevice;
3714 
3715     /* Free any old memory which might be in the device info */
3716     if( hostApi->deviceInfos )
3717     {
3718         PaWinWDMScanDeviceInfosResults* localScanResults = (PaWinWDMScanDeviceInfosResults*)PaUtil_GroupAllocateMemory(
3719             wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults));
3720         localScanResults->deviceInfos = hostApi->deviceInfos;
3721 
3722         DisposeDeviceInfos(hostApi, &localScanResults, hostApi->info.deviceCount);
3723 
3724         hostApi->deviceInfos = NULL;
3725     }
3726 
3727     if( scanResults != NULL )
3728     {
3729         PaWinWDMScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinWDMScanDeviceInfosResults * ) scanResults;
3730 
3731         if( deviceCount > 0 )
3732         {
3733             /* use the array allocated in ScanDeviceInfos() as our deviceInfos */
3734             hostApi->deviceInfos = scanDeviceInfosResults->deviceInfos;
3735 
3736             hostApi->info.defaultInputDevice = scanDeviceInfosResults->defaultInputDevice;
3737             hostApi->info.defaultOutputDevice = scanDeviceInfosResults->defaultOutputDevice;
3738 
3739             hostApi->info.deviceCount = deviceCount;
3740         }
3741 
3742         PaUtil_GroupFreeMemory( wdmHostApi->allocations, scanDeviceInfosResults );
3743     }
3744 
3745     return paNoError;
3746 
3747 }
3748 
DisposeDeviceInfos(struct PaUtilHostApiRepresentation * hostApi,void * scanResults,int deviceCount)3749 static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount )
3750 {
3751     PaWinWdmHostApiRepresentation *winDsHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
3752 
3753     if( scanResults != NULL )
3754     {
3755         PaWinWDMScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinWDMScanDeviceInfosResults * ) scanResults;
3756 
3757         if( scanDeviceInfosResults->deviceInfos )
3758         {
3759             int i;
3760             for (i = 0; i < deviceCount; ++i)
3761             {
3762                 PaWinWdmDeviceInfo* pDevice = (PaWinWdmDeviceInfo*)scanDeviceInfosResults->deviceInfos[i];
3763                 if (pDevice->filter != 0)
3764                 {
3765                     FilterFree(pDevice->filter);
3766                 }
3767             }
3768 
3769             PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); /* all device info structs are allocated in a block so we can destroy them here */
3770             PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos );
3771         }
3772 
3773         PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults );
3774     }
3775 
3776     return paNoError;
3777 
3778 }
3779 
PaWinWdm_Initialize(PaUtilHostApiRepresentation ** hostApi,PaHostApiIndex hostApiIndex)3780 PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
3781 {
3782     PaError result = paNoError;
3783     int deviceCount = 0;
3784     void *scanResults = 0;
3785     PaWinWdmHostApiRepresentation *wdmHostApi = NULL;
3786 
3787     PA_LOGE_;
3788 
3789 #ifdef PA_WDMKS_SET_TREF
3790     tRef = PaUtil_GetTime();
3791 #endif
3792 
3793     /*
3794     Attempt to load the KSUSER.DLL without which we cannot create pins
3795     We will unload this on termination
3796     */
3797     if(DllKsUser == NULL)
3798     {
3799         DllKsUser = LoadLibrary(TEXT("ksuser.dll"));
3800         if(DllKsUser == NULL)
3801             goto error;
3802     }
3803     FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin");
3804     if(FunctionKsCreatePin == NULL)
3805         goto error;
3806 
3807     /* Attempt to load AVRT.DLL, if we can't, then we'll just use time critical prio instead... */
3808     if(paWinWDMKSAvRtEntryPoints.hInstance == NULL)
3809     {
3810         paWinWDMKSAvRtEntryPoints.hInstance = LoadLibrary(TEXT("avrt.dll"));
3811         if (paWinWDMKSAvRtEntryPoints.hInstance != NULL)
3812         {
3813             paWinWDMKSAvRtEntryPoints.AvSetMmThreadCharacteristics =
3814                 (HANDLE(WINAPI*)(LPCSTR,LPDWORD))GetProcAddress(paWinWDMKSAvRtEntryPoints.hInstance,"AvSetMmThreadCharacteristicsA");
3815             paWinWDMKSAvRtEntryPoints.AvRevertMmThreadCharacteristics =
3816                 (BOOL(WINAPI*)(HANDLE))GetProcAddress(paWinWDMKSAvRtEntryPoints.hInstance, "AvRevertMmThreadCharacteristics");
3817             paWinWDMKSAvRtEntryPoints.AvSetMmThreadPriority =
3818                 (BOOL(WINAPI*)(HANDLE,PA_AVRT_PRIORITY))GetProcAddress(paWinWDMKSAvRtEntryPoints.hInstance, "AvSetMmThreadPriority");
3819         }
3820     }
3821 
3822     wdmHostApi = (PaWinWdmHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWdmHostApiRepresentation) );
3823     if( !wdmHostApi )
3824     {
3825         result = paInsufficientMemory;
3826         goto error;
3827     }
3828 
3829     wdmHostApi->allocations = PaUtil_CreateAllocationGroup();
3830     if( !wdmHostApi->allocations )
3831     {
3832         result = paInsufficientMemory;
3833         goto error;
3834     }
3835 
3836     *hostApi = &wdmHostApi->inheritedHostApiRep;
3837     (*hostApi)->info.structVersion = 1;
3838     (*hostApi)->info.type = paWDMKS;
3839     (*hostApi)->info.name = "Windows WDM-KS";
3840 
3841     /* these are all updated by CommitDeviceInfos() */
3842     (*hostApi)->info.deviceCount = 0;
3843     (*hostApi)->info.defaultInputDevice = paNoDevice;
3844     (*hostApi)->info.defaultOutputDevice = paNoDevice;
3845     (*hostApi)->deviceInfos = 0;
3846 
3847     result = ScanDeviceInfos(&wdmHostApi->inheritedHostApiRep, hostApiIndex, &scanResults, &deviceCount);
3848     if (result != paNoError)
3849     {
3850         goto error;
3851     }
3852 
3853     CommitDeviceInfos(&wdmHostApi->inheritedHostApiRep, hostApiIndex, scanResults, deviceCount);
3854 
3855     (*hostApi)->Terminate = Terminate;
3856     (*hostApi)->OpenStream = OpenStream;
3857     (*hostApi)->IsFormatSupported = IsFormatSupported;
3858     /* In preparation for hotplug
3859     (*hostApi)->ScanDeviceInfos = ScanDeviceInfos;
3860     (*hostApi)->CommitDeviceInfos = CommitDeviceInfos;
3861     (*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos;
3862     */
3863     PaUtil_InitializeStreamInterface( &wdmHostApi->callbackStreamInterface, CloseStream, StartStream,
3864         StopStream, AbortStream, IsStreamStopped, IsStreamActive,
3865         GetStreamTime, GetStreamCpuLoad,
3866         PaUtil_DummyRead, PaUtil_DummyWrite,
3867         PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
3868 
3869     PaUtil_InitializeStreamInterface( &wdmHostApi->blockingStreamInterface, CloseStream, StartStream,
3870         StopStream, AbortStream, IsStreamStopped, IsStreamActive,
3871         GetStreamTime, PaUtil_DummyGetCpuLoad,
3872         ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
3873 
3874     PA_LOGL_;
3875     return result;
3876 
3877 error:
3878     Terminate( (PaUtilHostApiRepresentation*)wdmHostApi );
3879 
3880     PA_LOGL_;
3881     return result;
3882 }
3883 
3884 
Terminate(struct PaUtilHostApiRepresentation * hostApi)3885 static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
3886 {
3887     PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
3888     PA_LOGE_;
3889 
3890     /* Do not unload the libraries */
3891     if( DllKsUser != NULL )
3892     {
3893         FreeLibrary( DllKsUser );
3894         DllKsUser = NULL;
3895     }
3896 
3897     if( paWinWDMKSAvRtEntryPoints.hInstance != NULL )
3898     {
3899         FreeLibrary( paWinWDMKSAvRtEntryPoints.hInstance );
3900         paWinWDMKSAvRtEntryPoints.hInstance = NULL;
3901     }
3902 
3903     if( wdmHostApi)
3904     {
3905         PaWinWDMScanDeviceInfosResults* localScanResults = (PaWinWDMScanDeviceInfosResults*)PaUtil_GroupAllocateMemory(
3906             wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults));
3907         localScanResults->deviceInfos = hostApi->deviceInfos;
3908         DisposeDeviceInfos(hostApi, localScanResults, hostApi->info.deviceCount);
3909 
3910         if( wdmHostApi->allocations )
3911         {
3912             PaUtil_FreeAllAllocations( wdmHostApi->allocations );
3913             PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
3914         }
3915         PaUtil_FreeMemory( wdmHostApi );
3916     }
3917     PA_LOGL_;
3918 }
3919 
IsFormatSupported(struct PaUtilHostApiRepresentation * hostApi,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate)3920 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
3921                                  const PaStreamParameters *inputParameters,
3922                                  const PaStreamParameters *outputParameters,
3923                                  double sampleRate )
3924 {
3925     int inputChannelCount, outputChannelCount;
3926     PaSampleFormat inputSampleFormat, outputSampleFormat;
3927     PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
3928     PaWinWdmFilter* pFilter;
3929     int result = paFormatIsSupported;
3930     WAVEFORMATEXTENSIBLE wfx;
3931     PaWinWaveFormatChannelMask channelMask;
3932 
3933     PA_LOGE_;
3934 
3935     if( inputParameters )
3936     {
3937         PaWinWdmDeviceInfo* pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
3938         PaWinWdmPin* pin;
3939         unsigned fmt;
3940         unsigned long testFormat = 0;
3941         unsigned validBits = 0;
3942 
3943         inputChannelCount = inputParameters->channelCount;
3944         inputSampleFormat = inputParameters->sampleFormat;
3945 
3946         /* all standard sample formats are supported by the buffer adapter,
3947         this implementation doesn't support any custom sample formats */
3948         if( inputSampleFormat & paCustomFormat )
3949         {
3950             PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "IsFormatSupported: Custom input format not supported");
3951             return paSampleFormatNotSupported;
3952         }
3953 
3954         /* unless alternate device specification is supported, reject the use of
3955         paUseHostApiSpecificDeviceSpecification */
3956 
3957         if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
3958         {
3959             PaWinWDM_SetLastErrorInfo(paInvalidDevice, "IsFormatSupported: paUseHostApiSpecificDeviceSpecification not supported");
3960             return paInvalidDevice;
3961         }
3962 
3963         /* check that input device can support inputChannelCount */
3964         if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
3965         {
3966             PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "IsFormatSupported: Invalid input channel count");
3967             return paInvalidChannelCount;
3968         }
3969 
3970         /* validate inputStreamInfo */
3971         if( inputParameters->hostApiSpecificStreamInfo )
3972         {
3973             PaWinWDM_SetLastErrorInfo(paIncompatibleHostApiSpecificStreamInfo, "Host API stream info not supported");
3974             return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
3975         }
3976 
3977         pFilter = pDeviceInfo->filter;
3978         pin = pFilter->pins[pDeviceInfo->pin];
3979 
3980         /* Find out the testing format */
3981         for (fmt = paFloat32; fmt <= paUInt8; fmt <<= 1)
3982         {
3983             if ((fmt & pin->formats) != 0)
3984             {
3985                 /* Found a matching format! */
3986                 testFormat = fmt;
3987                 break;
3988             }
3989         }
3990         if (testFormat == 0)
3991         {
3992             PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(capture) failed: no testformat found!");
3993             return paUnanticipatedHostError;
3994         }
3995 
3996         /* Due to special considerations, WaveRT devices with paInt24 should be tested with paInt32 and
3997         valid bits = 24 (instead of 24 bit samples) */
3998         if (pFilter->devInfo.streamingType == Type_kWaveRT && testFormat == paInt24)
3999         {
4000             PA_DEBUG(("IsFormatSupported (capture): WaveRT overriding testFormat paInt24 with paInt32 (24 valid bits)"));
4001             testFormat = paInt32;
4002             validBits = 24;
4003         }
4004 
4005         /* Check that the input format is supported */
4006         channelMask = PaWin_DefaultChannelMask(inputChannelCount);
4007         PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
4008             inputChannelCount,
4009             testFormat,
4010             PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
4011             sampleRate,
4012             channelMask );
4013         if (validBits != 0)
4014         {
4015             wfx.Samples.wValidBitsPerSample = validBits;
4016         }
4017 
4018         result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
4019         if( result != paNoError )
4020         {
4021             /* Try a WAVE_FORMAT_PCM instead */
4022             PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
4023                 inputChannelCount,
4024                 testFormat,
4025                 PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
4026                 sampleRate);
4027 
4028             if (validBits != 0)
4029             {
4030                 wfx.Samples.wValidBitsPerSample = validBits;
4031             }
4032 
4033             result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
4034             if( result != paNoError )
4035             {
4036                 PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(capture) failed: sr=%u,ch=%u,bits=%u", wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample);
4037                 return result;
4038             }
4039         }
4040     }
4041     else
4042     {
4043         inputChannelCount = 0;
4044     }
4045 
4046     if( outputParameters )
4047     {
4048         PaWinWdmDeviceInfo* pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device];
4049         PaWinWdmPin* pin;
4050         unsigned fmt;
4051         unsigned long testFormat = 0;
4052         unsigned validBits = 0;
4053 
4054         outputChannelCount = outputParameters->channelCount;
4055         outputSampleFormat = outputParameters->sampleFormat;
4056 
4057         /* all standard sample formats are supported by the buffer adapter,
4058         this implementation doesn't support any custom sample formats */
4059         if( outputSampleFormat & paCustomFormat )
4060         {
4061             PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "IsFormatSupported: Custom output format not supported");
4062             return paSampleFormatNotSupported;
4063         }
4064 
4065         /* unless alternate device specification is supported, reject the use of
4066         paUseHostApiSpecificDeviceSpecification */
4067 
4068         if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
4069         {
4070             PaWinWDM_SetLastErrorInfo(paInvalidDevice, "IsFormatSupported: paUseHostApiSpecificDeviceSpecification not supported");
4071             return paInvalidDevice;
4072         }
4073 
4074         /* check that output device can support outputChannelCount */
4075         if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
4076         {
4077             PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid output channel count");
4078             return paInvalidChannelCount;
4079         }
4080 
4081         /* validate outputStreamInfo */
4082         if( outputParameters->hostApiSpecificStreamInfo )
4083         {
4084             PaWinWDM_SetLastErrorInfo(paIncompatibleHostApiSpecificStreamInfo, "Host API stream info not supported");
4085             return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
4086         }
4087 
4088         pFilter = pDeviceInfo->filter;
4089         pin = pFilter->pins[pDeviceInfo->pin];
4090 
4091         /* Find out the testing format */
4092         for (fmt = paFloat32; fmt <= paUInt8; fmt <<= 1)
4093         {
4094             if ((fmt & pin->formats) != 0)
4095             {
4096                 /* Found a matching format! */
4097                 testFormat = fmt;
4098                 break;
4099             }
4100         }
4101         if (testFormat == 0)
4102         {
4103             PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(render) failed: no testformat found!");
4104             return paUnanticipatedHostError;
4105         }
4106 
4107         /* Due to special considerations, WaveRT devices with paInt24 should be tested with paInt32 and
4108         valid bits = 24 (instead of 24 bit samples) */
4109         if (pFilter->devInfo.streamingType == Type_kWaveRT && testFormat == paInt24)
4110         {
4111             PA_DEBUG(("IsFormatSupported (render): WaveRT overriding testFormat paInt24 with paInt32 (24 valid bits)"));
4112             testFormat = paInt32;
4113             validBits = 24;
4114         }
4115 
4116         /* Check that the output format is supported */
4117         channelMask = PaWin_DefaultChannelMask(outputChannelCount);
4118         PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
4119             outputChannelCount,
4120             testFormat,
4121             PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
4122             sampleRate,
4123             channelMask );
4124 
4125         if (validBits != 0)
4126         {
4127             wfx.Samples.wValidBitsPerSample = validBits;
4128         }
4129 
4130         result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
4131         if( result != paNoError )
4132         {
4133             /* Try a WAVE_FORMAT_PCM instead */
4134             PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
4135                 outputChannelCount,
4136                 testFormat,
4137                 PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
4138                 sampleRate);
4139 
4140             if (validBits != 0)
4141             {
4142                 wfx.Samples.wValidBitsPerSample = validBits;
4143             }
4144 
4145             result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
4146             if( result != paNoError )
4147             {
4148                 PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(render) failed: %u,%u,%u", wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample);
4149                 return result;
4150             }
4151         }
4152 
4153     }
4154     else
4155     {
4156         outputChannelCount = 0;
4157     }
4158 
4159     /*
4160     IMPLEMENT ME:
4161 
4162     - if a full duplex stream is requested, check that the combination
4163     of input and output parameters is supported if necessary
4164 
4165     - check that the device supports sampleRate
4166 
4167     Because the buffer adapter handles conversion between all standard
4168     sample formats, the following checks are only required if paCustomFormat
4169     is implemented, or under some other unusual conditions.
4170 
4171     - check that input device can support inputSampleFormat, or that
4172     we have the capability to convert from inputSampleFormat to
4173     a native format
4174 
4175     - check that output device can support outputSampleFormat, or that
4176     we have the capability to convert from outputSampleFormat to
4177     a native format
4178     */
4179     if((inputChannelCount == 0)&&(outputChannelCount == 0))
4180     {
4181         PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "No input or output channels defined");
4182         result = paSampleFormatNotSupported; /* Not right error */
4183     }
4184 
4185     PA_LOGL_;
4186     return result;
4187 }
4188 
ResetStreamEvents(PaWinWdmStream * stream)4189 static void ResetStreamEvents(PaWinWdmStream* stream)
4190 {
4191     unsigned i;
4192     ResetEvent(stream->eventAbort);
4193     ResetEvent(stream->eventStreamStart[StreamStart_kOk]);
4194     ResetEvent(stream->eventStreamStart[StreamStart_kFailed]);
4195 
4196     for (i=0; i<stream->capture.noOfPackets; ++i)
4197     {
4198         if (stream->capture.events && stream->capture.events[i])
4199         {
4200             ResetEvent(stream->capture.events[i]);
4201         }
4202     }
4203 
4204     for (i=0; i<stream->render.noOfPackets; ++i)
4205     {
4206         if (stream->render.events && stream->render.events[i])
4207         {
4208             ResetEvent(stream->render.events[i]);
4209         }
4210     }
4211 }
4212 
CloseStreamEvents(PaWinWdmStream * stream)4213 static void CloseStreamEvents(PaWinWdmStream* stream)
4214 {
4215     unsigned i;
4216     PaWinWdmIOInfo* ios[2] = { &stream->capture, &stream->render };
4217 
4218     if (stream->eventAbort)
4219     {
4220         CloseHandle(stream->eventAbort);
4221         stream->eventAbort = 0;
4222     }
4223     if (stream->eventStreamStart[StreamStart_kOk])
4224     {
4225         CloseHandle(stream->eventStreamStart[StreamStart_kOk]);
4226     }
4227     if (stream->eventStreamStart[StreamStart_kFailed])
4228     {
4229         CloseHandle(stream->eventStreamStart[StreamStart_kFailed]);
4230     }
4231 
4232     for (i = 0; i < 2; ++i)
4233     {
4234         unsigned j;
4235         /* Unregister notification handles for WaveRT */
4236         if (ios[i]->pPin && ios[i]->pPin->parentFilter->devInfo.streamingType == Type_kWaveRT &&
4237             ios[i]->pPin->pinKsSubType == SubType_kNotification &&
4238             ios[i]->events != 0)
4239         {
4240             PinUnregisterNotificationHandle(ios[i]->pPin, ios[i]->events[0]);
4241         }
4242 
4243         for (j=0; j < ios[i]->noOfPackets; ++j)
4244         {
4245             if (ios[i]->events && ios[i]->events[j])
4246             {
4247                 CloseHandle(ios[i]->events[j]);
4248                 ios[i]->events[j] = 0;
4249             }
4250         }
4251     }
4252 }
4253 
NextPowerOf2(unsigned val)4254 static unsigned NextPowerOf2(unsigned val)
4255 {
4256     val--;
4257     val = (val >> 1) | val;
4258     val = (val >> 2) | val;
4259     val = (val >> 4) | val;
4260     val = (val >> 8) | val;
4261     val = (val >> 16) | val;
4262     return ++val;
4263 }
4264 
ValidateSpecificStreamParameters(const PaStreamParameters * streamParameters,const PaWinWDMKSInfo * streamInfo,unsigned isInput)4265 static PaError ValidateSpecificStreamParameters(
4266     const PaStreamParameters *streamParameters,
4267     const PaWinWDMKSInfo *streamInfo,
4268     unsigned isInput)
4269 {
4270     if( streamInfo )
4271     {
4272         if( streamInfo->size != sizeof( PaWinWDMKSInfo )
4273             || streamInfo->version != 1 )
4274         {
4275             PA_DEBUG(("Stream parameters: size or version not correct"));
4276             return paIncompatibleHostApiSpecificStreamInfo;
4277         }
4278 
4279         if (!!(streamInfo->flags & ~(paWinWDMKSOverrideFramesize | paWinWDMKSUseGivenChannelMask)))
4280         {
4281             PA_DEBUG(("Stream parameters: non supported flags set"));
4282             return paIncompatibleHostApiSpecificStreamInfo;
4283         }
4284 
4285         if (streamInfo->noOfPackets != 0 &&
4286             (streamInfo->noOfPackets < 2 || streamInfo->noOfPackets > 8))
4287         {
4288             PA_DEBUG(("Stream parameters: noOfPackets %u out of range [2,8]", streamInfo->noOfPackets));
4289             return paIncompatibleHostApiSpecificStreamInfo;
4290         }
4291 
4292         if (streamInfo->flags & paWinWDMKSUseGivenChannelMask)
4293         {
4294             if (isInput)
4295             {
4296                 PA_DEBUG(("Stream parameters: Channels mask setting not supported for input stream"));
4297                 return paIncompatibleHostApiSpecificStreamInfo;
4298             }
4299 
4300             if (streamInfo->channelMask & PAWIN_SPEAKER_RESERVED)
4301             {
4302                 PA_DEBUG(("Stream parameters: Given channels mask 0x%08X not supported", streamInfo->channelMask));
4303                 return paIncompatibleHostApiSpecificStreamInfo;
4304             }
4305         }
4306 
4307     }
4308 
4309     return paNoError;
4310 }
4311 
4312 
4313 
4314 
4315 /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
4316 
OpenStream(struct PaUtilHostApiRepresentation * hostApi,PaStream ** s,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate,unsigned long framesPerUserBuffer,PaStreamFlags streamFlags,PaStreamCallback * streamCallback,void * userData)4317 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
4318                           PaStream** s,
4319                           const PaStreamParameters *inputParameters,
4320                           const PaStreamParameters *outputParameters,
4321                           double sampleRate,
4322                           unsigned long framesPerUserBuffer,
4323                           PaStreamFlags streamFlags,
4324                           PaStreamCallback *streamCallback,
4325                           void *userData )
4326 {
4327     PaError result = paNoError;
4328     PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
4329     PaWinWdmStream *stream = 0;
4330     /* unsigned long framesPerHostBuffer; these may not be equivalent for all implementations */
4331     PaSampleFormat inputSampleFormat, outputSampleFormat;
4332     PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
4333     int userInputChannels,userOutputChannels;
4334     WAVEFORMATEXTENSIBLE wfx;
4335 
4336     PA_LOGE_;
4337     PA_DEBUG(("OpenStream:sampleRate = %f\n",sampleRate));
4338     PA_DEBUG(("OpenStream:framesPerBuffer = %lu\n",framesPerUserBuffer));
4339 
4340     if( inputParameters )
4341     {
4342         userInputChannels = inputParameters->channelCount;
4343         inputSampleFormat = inputParameters->sampleFormat;
4344 
4345         /* unless alternate device specification is supported, reject the use of
4346         paUseHostApiSpecificDeviceSpecification */
4347 
4348         if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
4349         {
4350             PaWinWDM_SetLastErrorInfo(paInvalidDevice, "paUseHostApiSpecificDeviceSpecification(in) not supported");
4351             return paInvalidDevice;
4352         }
4353 
4354         /* check that input device can support stream->userInputChannels */
4355         if( userInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
4356         {
4357             PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid input channel count");
4358             return paInvalidChannelCount;
4359         }
4360 
4361         /* validate inputStreamInfo */
4362         result = ValidateSpecificStreamParameters(inputParameters, inputParameters->hostApiSpecificStreamInfo, 1 );
4363         if(result != paNoError)
4364         {
4365             PaWinWDM_SetLastErrorInfo(result, "Host API stream info not supported (in)");
4366             return result; /* this implementation doesn't use custom stream info */
4367         }
4368     }
4369     else
4370     {
4371         userInputChannels = 0;
4372         inputSampleFormat = hostInputSampleFormat = paInt16; /* Supress 'uninitialised var' warnings. */
4373     }
4374 
4375     if( outputParameters )
4376     {
4377         userOutputChannels = outputParameters->channelCount;
4378         outputSampleFormat = outputParameters->sampleFormat;
4379 
4380         /* unless alternate device specification is supported, reject the use of
4381         paUseHostApiSpecificDeviceSpecification */
4382 
4383         if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
4384         {
4385             PaWinWDM_SetLastErrorInfo(paInvalidDevice, "paUseHostApiSpecificDeviceSpecification(out) not supported");
4386             return paInvalidDevice;
4387         }
4388 
4389         /* check that output device can support stream->userInputChannels */
4390         if( userOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
4391         {
4392             PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid output channel count");
4393             return paInvalidChannelCount;
4394         }
4395 
4396         /* validate outputStreamInfo */
4397         result = ValidateSpecificStreamParameters( outputParameters, outputParameters->hostApiSpecificStreamInfo, 0 );
4398         if (result != paNoError)
4399         {
4400             PaWinWDM_SetLastErrorInfo(result, "Host API stream info not supported (out)");
4401             return result; /* this implementation doesn't use custom stream info */
4402         }
4403     }
4404     else
4405     {
4406         userOutputChannels = 0;
4407         outputSampleFormat = hostOutputSampleFormat = paInt16; /* Supress 'uninitialized var' warnings. */
4408     }
4409 
4410     /* validate platform specific flags */
4411     if( (streamFlags & paPlatformSpecificFlags) != 0 )
4412     {
4413         PaWinWDM_SetLastErrorInfo(paInvalidFlag, "Invalid flag supplied");
4414         return paInvalidFlag; /* unexpected platform specific flag */
4415     }
4416 
4417     stream = (PaWinWdmStream*)PaUtil_AllocateMemory( sizeof(PaWinWdmStream) );
4418     if( !stream )
4419     {
4420         result = paInsufficientMemory;
4421         goto error;
4422     }
4423 
4424     /* Create allocation group */
4425     stream->allocGroup = PaUtil_CreateAllocationGroup();
4426     if( !stream->allocGroup )
4427     {
4428         result = paInsufficientMemory;
4429         goto error;
4430     }
4431 
4432     /* Zero the stream object */
4433     /* memset((void*)stream,0,sizeof(PaWinWdmStream)); */
4434 
4435     if( streamCallback )
4436     {
4437         PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
4438             &wdmHostApi->callbackStreamInterface, streamCallback, userData );
4439     }
4440     else
4441     {
4442         /* PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
4443         &wdmHostApi->blockingStreamInterface, streamCallback, userData ); */
4444 
4445         /* We don't support the blocking API yet */
4446         PA_DEBUG(("Blocking API not supported yet!\n"));
4447         PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Blocking API not supported yet");
4448         result = paUnanticipatedHostError;
4449         goto error;
4450     }
4451 
4452     PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
4453 
4454     /* Instantiate the input pin if necessary */
4455     if(userInputChannels > 0)
4456     {
4457         PaWinWdmFilter* pFilter;
4458         PaWinWdmDeviceInfo* pDeviceInfo;
4459         PaWinWdmPin* pPin;
4460         unsigned validBitsPerSample = 0;
4461         PaWinWaveFormatChannelMask channelMask = PaWin_DefaultChannelMask( userInputChannels );
4462 
4463         result = paSampleFormatNotSupported;
4464         pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
4465         pFilter = pDeviceInfo->filter;
4466         pPin = pFilter->pins[pDeviceInfo->pin];
4467 
4468         stream->userInputChannels = userInputChannels;
4469 
4470         hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( pPin->formats, inputSampleFormat );
4471         if (hostInputSampleFormat == paSampleFormatNotSupported)
4472         {
4473             result = paUnanticipatedHostError;
4474             PaWinWDM_SetLastErrorInfo(result, "PU_SCAF(%X,%X) failed (input)", pPin->formats, inputSampleFormat);
4475             goto error;
4476         }
4477         else if (pFilter->devInfo.streamingType == Type_kWaveRT && hostInputSampleFormat == paInt24)
4478         {
4479             /* For WaveRT, we choose 32 bit format instead of paInt24, since we MIGHT need to align buffer on a
4480             128 byte boundary (see PinGetBuffer) */
4481             hostInputSampleFormat = paInt32;
4482             /* But we'll tell the driver that it's 24 bit in 32 bit container */
4483             validBitsPerSample = 24;
4484         }
4485 
4486         while (hostInputSampleFormat <= paUInt8)
4487         {
4488             unsigned channelsToProbe = stream->userInputChannels;
4489             /* Some or all KS devices can only handle the exact number of channels
4490             * they specify. But PortAudio clients expect to be able to
4491             * at least specify mono I/O on a multi-channel device
4492             * If this is the case, then we will do the channel mapping internally
4493             * The following loop tests this case
4494             **/
4495             while (1)
4496             {
4497                 PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
4498                     channelsToProbe,
4499                     hostInputSampleFormat,
4500                     PaWin_SampleFormatToLinearWaveFormatTag(hostInputSampleFormat),
4501                     sampleRate,
4502                     channelMask );
4503                 stream->capture.bytesPerFrame = wfx.Format.nBlockAlign;
4504                 if (validBitsPerSample != 0)
4505                 {
4506                     wfx.Samples.wValidBitsPerSample = validBitsPerSample;
4507                 }
4508                 stream->capture.pPin = FilterCreatePin(pFilter, pPin->pinId, (WAVEFORMATEX*)&wfx, &result);
4509                 stream->deviceInputChannels = channelsToProbe;
4510 
4511                 if( result != paNoError && result != paDeviceUnavailable )
4512                 {
4513                     /* Try a WAVE_FORMAT_PCM instead */
4514                     PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
4515                         channelsToProbe,
4516                         hostInputSampleFormat,
4517                         PaWin_SampleFormatToLinearWaveFormatTag(hostInputSampleFormat),
4518                         sampleRate);
4519                     if (validBitsPerSample != 0)
4520                     {
4521                         wfx.Samples.wValidBitsPerSample = validBitsPerSample;
4522                     }
4523                     stream->capture.pPin = FilterCreatePin(pFilter, pPin->pinId, (const WAVEFORMATEX*)&wfx, &result);
4524                 }
4525 
4526                 if (result == paDeviceUnavailable) goto occupied;
4527 
4528                 if (result == paNoError)
4529                 {
4530                     /* We're done */
4531                     break;
4532                 }
4533 
4534                 if (channelsToProbe < (unsigned)pPin->maxChannels)
4535                 {
4536                     /* Go to next multiple of 2 */
4537                     channelsToProbe = min((((channelsToProbe>>1)+1)<<1), (unsigned)pPin->maxChannels);
4538                     continue;
4539                 }
4540 
4541                 break;
4542             }
4543 
4544             if (result == paNoError)
4545             {
4546                 /* We're done */
4547                 break;
4548             }
4549 
4550             /* Go to next format in line with lower resolution */
4551             hostInputSampleFormat <<= 1;
4552         }
4553 
4554         if(stream->capture.pPin == NULL)
4555         {
4556             PaWinWDM_SetLastErrorInfo(result, "Failed to create capture pin: sr=%u,ch=%u,bits=%u,align=%u",
4557                 wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample, wfx.Format.nBlockAlign);
4558             goto error;
4559         }
4560 
4561         /* Select correct mux input on MUX node of topology filter */
4562         if (pDeviceInfo->muxPosition >= 0)
4563         {
4564             assert(pPin->parentFilter->topologyFilter != NULL);
4565 
4566             result = FilterUse(pPin->parentFilter->topologyFilter);
4567             if (result != paNoError)
4568             {
4569                 PaWinWDM_SetLastErrorInfo(result, "Failed to open topology filter");
4570                 goto error;
4571             }
4572 
4573             result = WdmSetMuxNodeProperty(pPin->parentFilter->topologyFilter->handle,
4574                 pPin->inputs[pDeviceInfo->muxPosition]->muxNodeId,
4575                 pPin->inputs[pDeviceInfo->muxPosition]->muxPinId);
4576 
4577             FilterRelease(pPin->parentFilter->topologyFilter);
4578 
4579             if(result != paNoError)
4580             {
4581                 PaWinWDM_SetLastErrorInfo(result, "Failed to set topology mux node");
4582                 goto error;
4583             }
4584         }
4585 
4586         stream->capture.bytesPerSample = stream->capture.bytesPerFrame / stream->deviceInputChannels;
4587         stream->capture.pPin->frameSize /= stream->capture.bytesPerFrame;
4588         PA_DEBUG(("Capture pin frames: %d\n",stream->capture.pPin->frameSize));
4589     }
4590     else
4591     {
4592         stream->capture.pPin = NULL;
4593         stream->capture.bytesPerFrame = 0;
4594     }
4595 
4596     /* Instantiate the output pin if necessary */
4597     if(userOutputChannels > 0)
4598     {
4599         PaWinWdmFilter* pFilter;
4600         PaWinWdmDeviceInfo* pDeviceInfo;
4601         PaWinWdmPin* pPin;
4602         PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)(outputParameters->hostApiSpecificStreamInfo);
4603         unsigned validBitsPerSample = 0;
4604         PaWinWaveFormatChannelMask channelMask = PaWin_DefaultChannelMask( userOutputChannels );
4605         if (pInfo && (pInfo->flags & paWinWDMKSUseGivenChannelMask))
4606         {
4607             PA_DEBUG(("Using channelMask 0x%08X instead of default 0x%08X\n",
4608                 pInfo->channelMask,
4609                 channelMask));
4610             channelMask = pInfo->channelMask;
4611         }
4612 
4613         result = paSampleFormatNotSupported;
4614         pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device];
4615         pFilter = pDeviceInfo->filter;
4616         pPin = pFilter->pins[pDeviceInfo->pin];
4617 
4618         stream->userOutputChannels = userOutputChannels;
4619 
4620         hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( pPin->formats, outputSampleFormat );
4621         if (hostOutputSampleFormat == paSampleFormatNotSupported)
4622         {
4623             result = paUnanticipatedHostError;
4624             PaWinWDM_SetLastErrorInfo(result, "PU_SCAF(%X,%X) failed (output)", pPin->formats, hostOutputSampleFormat);
4625             goto error;
4626         }
4627         else if (pFilter->devInfo.streamingType == Type_kWaveRT && hostOutputSampleFormat == paInt24)
4628         {
4629             /* For WaveRT, we choose 32 bit format instead of paInt24, since we MIGHT need to align buffer on a
4630             128 byte boundary (see PinGetBuffer) */
4631             hostOutputSampleFormat = paInt32;
4632             /* But we'll tell the driver that it's 24 bit in 32 bit container */
4633             validBitsPerSample = 24;
4634         }
4635 
4636         while (hostOutputSampleFormat <= paUInt8)
4637         {
4638             unsigned channelsToProbe = stream->userOutputChannels;
4639             /* Some or all KS devices can only handle the exact number of channels
4640             * they specify. But PortAudio clients expect to be able to
4641             * at least specify mono I/O on a multi-channel device
4642             * If this is the case, then we will do the channel mapping internally
4643             * The following loop tests this case
4644             **/
4645             while (1)
4646             {
4647                 PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
4648                     channelsToProbe,
4649                     hostOutputSampleFormat,
4650                     PaWin_SampleFormatToLinearWaveFormatTag(hostOutputSampleFormat),
4651                     sampleRate,
4652                     channelMask );
4653                 stream->render.bytesPerFrame = wfx.Format.nBlockAlign;
4654                 if (validBitsPerSample != 0)
4655                 {
4656                     wfx.Samples.wValidBitsPerSample = validBitsPerSample;
4657                 }
4658                 stream->render.pPin = FilterCreatePin(pFilter, pPin->pinId, (WAVEFORMATEX*)&wfx, &result);
4659                 stream->deviceOutputChannels = channelsToProbe;
4660 
4661                 if( result != paNoError && result != paDeviceUnavailable )
4662                 {
4663                     PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
4664                         channelsToProbe,
4665                         hostOutputSampleFormat,
4666                         PaWin_SampleFormatToLinearWaveFormatTag(hostOutputSampleFormat),
4667                         sampleRate);
4668                     if (validBitsPerSample != 0)
4669                     {
4670                         wfx.Samples.wValidBitsPerSample = validBitsPerSample;
4671                     }
4672                     stream->render.pPin = FilterCreatePin(pFilter, pPin->pinId, (const WAVEFORMATEX*)&wfx, &result);
4673                 }
4674 
4675                 if (result == paDeviceUnavailable) goto occupied;
4676 
4677                 if (result == paNoError)
4678                 {
4679                     /* We're done */
4680                     break;
4681                 }
4682 
4683                 if (channelsToProbe < (unsigned)pPin->maxChannels)
4684                 {
4685                     /* Go to next multiple of 2 */
4686                     channelsToProbe = min((((channelsToProbe>>1)+1)<<1), (unsigned)pPin->maxChannels);
4687                     continue;
4688                 }
4689 
4690                 break;
4691             };
4692 
4693             if (result == paNoError)
4694             {
4695                 /* We're done */
4696                 break;
4697             }
4698 
4699             /* Go to next format in line with lower resolution */
4700             hostOutputSampleFormat <<= 1;
4701         }
4702 
4703         if(stream->render.pPin == NULL)
4704         {
4705             PaWinWDM_SetLastErrorInfo(result, "Failed to create render pin: sr=%u,ch=%u,bits=%u,align=%u",
4706                 wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample, wfx.Format.nBlockAlign);
4707             goto error;
4708         }
4709 
4710         stream->render.bytesPerSample = stream->render.bytesPerFrame / stream->deviceOutputChannels;
4711         stream->render.pPin->frameSize /= stream->render.bytesPerFrame;
4712         PA_DEBUG(("Render pin frames: %d\n",stream->render.pPin->frameSize));
4713     }
4714     else
4715     {
4716         stream->render.pPin = NULL;
4717         stream->render.bytesPerFrame = 0;
4718     }
4719 
4720     /* Calculate the framesPerHostXxxxBuffer size based upon the suggested latency values */
4721     /* Record the buffer length */
4722     if(inputParameters)
4723     {
4724         /* Calculate the frames from the user's value - add a bit to round up */
4725         stream->capture.framesPerBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001);
4726         if(stream->capture.framesPerBuffer > (unsigned long)sampleRate)
4727         { /* Upper limit is 1 second */
4728             stream->capture.framesPerBuffer = (unsigned long)sampleRate;
4729         }
4730         else if(stream->capture.framesPerBuffer < stream->capture.pPin->frameSize)
4731         {
4732             stream->capture.framesPerBuffer = stream->capture.pPin->frameSize;
4733         }
4734         PA_DEBUG(("Input frames chosen:%ld\n",stream->capture.framesPerBuffer));
4735 
4736         /* Setup number of packets to use */
4737         stream->capture.noOfPackets = 2;
4738 
4739         if (inputParameters->hostApiSpecificStreamInfo)
4740         {
4741             PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)inputParameters->hostApiSpecificStreamInfo;
4742 
4743             if (stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic &&
4744                 pInfo->noOfPackets != 0)
4745             {
4746                 stream->capture.noOfPackets = pInfo->noOfPackets;
4747             }
4748         }
4749     }
4750 
4751     if(outputParameters)
4752     {
4753         /* Calculate the frames from the user's value - add a bit to round up */
4754         stream->render.framesPerBuffer = (unsigned long)((outputParameters->suggestedLatency*sampleRate)+0.0001);
4755         if(stream->render.framesPerBuffer > (unsigned long)sampleRate)
4756         { /* Upper limit is 1 second */
4757             stream->render.framesPerBuffer = (unsigned long)sampleRate;
4758         }
4759         else if(stream->render.framesPerBuffer < stream->render.pPin->frameSize)
4760         {
4761             stream->render.framesPerBuffer = stream->render.pPin->frameSize;
4762         }
4763         PA_DEBUG(("Output frames chosen:%ld\n",stream->render.framesPerBuffer));
4764 
4765         /* Setup number of packets to use */
4766         stream->render.noOfPackets = 2;
4767 
4768         if (outputParameters->hostApiSpecificStreamInfo)
4769         {
4770             PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)outputParameters->hostApiSpecificStreamInfo;
4771 
4772             if (stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic &&
4773                 pInfo->noOfPackets != 0)
4774             {
4775                 stream->render.noOfPackets = pInfo->noOfPackets;
4776             }
4777         }
4778     }
4779 
4780     /* Host buffer size is bound to the largest of the input and output frame sizes */
4781     result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
4782         stream->userInputChannels, inputSampleFormat, hostInputSampleFormat,
4783         stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat,
4784         sampleRate, streamFlags, framesPerUserBuffer,
4785         max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer),
4786         paUtilBoundedHostBufferSize,
4787         streamCallback, userData );
4788     if( result != paNoError )
4789     {
4790         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",
4791             stream->userInputChannels, inputSampleFormat, hostInputSampleFormat,
4792             stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat,
4793             sampleRate, streamFlags, framesPerUserBuffer,
4794             max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer));
4795         goto error;
4796     }
4797 
4798     /* Allocate/get all the buffers for host I/O */
4799     if (stream->userInputChannels > 0)
4800     {
4801         stream->streamRepresentation.streamInfo.inputLatency = stream->capture.framesPerBuffer / sampleRate;
4802 
4803         switch (stream->capture.pPin->parentFilter->devInfo.streamingType)
4804         {
4805         case Type_kWaveCyclic:
4806             {
4807                 unsigned size = stream->capture.noOfPackets * stream->capture.framesPerBuffer * stream->capture.bytesPerFrame;
4808                 /* Allocate input host buffer */
4809                 stream->capture.hostBuffer = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, size);
4810                 PA_DEBUG(("Input buffer allocated (size = %u)\n", size));
4811                 if( !stream->capture.hostBuffer )
4812                 {
4813                     PA_DEBUG(("Cannot allocate host input buffer!\n"));
4814                     PaWinWDM_SetLastErrorInfo(paInsufficientMemory, "Failed to allocate input buffer");
4815                     result = paInsufficientMemory;
4816                     goto error;
4817                 }
4818                 stream->capture.hostBufferSize = size;
4819                 PA_DEBUG(("Input buffer start = %p (size=%u)\n",stream->capture.hostBuffer, stream->capture.hostBufferSize));
4820                 stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveCyclic;
4821                 stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveCyclic;
4822             }
4823             break;
4824         case Type_kWaveRT:
4825             {
4826                 const DWORD dwTotalSize = 2 * stream->capture.framesPerBuffer * stream->capture.bytesPerFrame;
4827                 DWORD dwRequestedSize = dwTotalSize;
4828                 BOOL bCallMemoryBarrier = FALSE;
4829                 ULONG hwFifoLatency = 0;
4830                 ULONG dummy;
4831                 result = PinGetBuffer(stream->capture.pPin, (void**)&stream->capture.hostBuffer, &dwRequestedSize, &bCallMemoryBarrier);
4832                 if (!result)
4833                 {
4834                     PA_DEBUG(("Input buffer start = %p, size = %u\n", stream->capture.hostBuffer, dwRequestedSize));
4835                     if (dwRequestedSize != dwTotalSize)
4836                     {
4837                         PA_DEBUG(("Buffer length changed by driver from %u to %u !\n", dwTotalSize, dwRequestedSize));
4838                         /* Recalculate to what the driver has given us */
4839                         stream->capture.framesPerBuffer = dwRequestedSize / (2 * stream->capture.bytesPerFrame);
4840                     }
4841                     stream->capture.hostBufferSize = dwRequestedSize;
4842 
4843                     if (stream->capture.pPin->pinKsSubType == SubType_kPolled)
4844                     {
4845                         stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveRTPolled;
4846                         stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveRTPolled;
4847                     }
4848                     else
4849                     {
4850                         stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveRTEvent;
4851                         stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveRTEvent;
4852                     }
4853 
4854                     stream->capture.pPin->fnMemBarrier = bCallMemoryBarrier ? MemoryBarrierRead : MemoryBarrierDummy;
4855                 }
4856                 else
4857                 {
4858                     PA_DEBUG(("Failed to get input buffer (WaveRT)\n"));
4859                     PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to get input buffer (WaveRT)");
4860                     result = paUnanticipatedHostError;
4861                     goto error;
4862                 }
4863 
4864                 /* Get latency */
4865                 result = PinGetHwLatency(stream->capture.pPin, &hwFifoLatency, &dummy, &dummy);
4866                 if (result == paNoError)
4867                 {
4868                     stream->capture.pPin->hwLatency = hwFifoLatency;
4869 
4870                     /* Add HW latency into total input latency */
4871                     stream->streamRepresentation.streamInfo.inputLatency += ((hwFifoLatency / stream->capture.bytesPerFrame) / sampleRate);
4872                 }
4873                 else
4874                 {
4875                     PA_DEBUG(("Failed to get size of FIFO hardware buffer (is set to zero)\n"));
4876                     stream->capture.pPin->hwLatency = 0;
4877                 }
4878             }
4879             break;
4880         default:
4881             /* Undefined wave type!! */
4882             assert(0);
4883             result = paInternalError;
4884             PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
4885             goto error;
4886         }
4887     }
4888     else
4889     {
4890         stream->capture.hostBuffer = 0;
4891     }
4892 
4893     if (stream->userOutputChannels > 0)
4894     {
4895         stream->streamRepresentation.streamInfo.outputLatency = stream->render.framesPerBuffer / sampleRate;
4896 
4897         switch (stream->render.pPin->parentFilter->devInfo.streamingType)
4898         {
4899         case Type_kWaveCyclic:
4900             {
4901                 unsigned size = stream->render.noOfPackets * stream->render.framesPerBuffer * stream->render.bytesPerFrame;
4902                 /* Allocate output device buffer */
4903                 stream->render.hostBuffer = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, size);
4904                 PA_DEBUG(("Output buffer allocated (size = %u)\n", size));
4905                 if( !stream->render.hostBuffer )
4906                 {
4907                     PA_DEBUG(("Cannot allocate host output buffer!\n"));
4908                     PaWinWDM_SetLastErrorInfo(paInsufficientMemory, "Failed to allocate output buffer");
4909                     result = paInsufficientMemory;
4910                     goto error;
4911                 }
4912                 stream->render.hostBufferSize = size;
4913                 PA_DEBUG(("Output buffer start = %p (size=%u)\n",stream->render.hostBuffer, stream->render.hostBufferSize));
4914 
4915                 stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveCyclic;
4916                 stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveCyclic;
4917             }
4918             break;
4919         case Type_kWaveRT:
4920             {
4921                 const DWORD dwTotalSize = 2 * stream->render.framesPerBuffer * stream->render.bytesPerFrame;
4922                 DWORD dwRequestedSize = dwTotalSize;
4923                 BOOL bCallMemoryBarrier = FALSE;
4924                 ULONG hwFifoLatency = 0;
4925                 ULONG dummy;
4926                 result = PinGetBuffer(stream->render.pPin, (void**)&stream->render.hostBuffer, &dwRequestedSize, &bCallMemoryBarrier);
4927                 if (!result)
4928                 {
4929                     PA_DEBUG(("Output buffer start = %p, size = %u, membarrier = %u\n", stream->render.hostBuffer, dwRequestedSize, bCallMemoryBarrier));
4930                     if (dwRequestedSize != dwTotalSize)
4931                     {
4932                         PA_DEBUG(("Buffer length changed by driver from %u to %u !\n", dwTotalSize, dwRequestedSize));
4933                         /* Recalculate to what the driver has given us */
4934                         stream->render.framesPerBuffer = dwRequestedSize / (2 * stream->render.bytesPerFrame);
4935                     }
4936                     stream->render.hostBufferSize = dwRequestedSize;
4937 
4938                     if (stream->render.pPin->pinKsSubType == SubType_kPolled)
4939                     {
4940                         stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveRTPolled;
4941                         stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveRTPolled;
4942                     }
4943                     else
4944                     {
4945                         stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveRTEvent;
4946                         stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveRTEvent;
4947                     }
4948 
4949                     stream->render.pPin->fnMemBarrier = bCallMemoryBarrier ? MemoryBarrierWrite : MemoryBarrierDummy;
4950                 }
4951                 else
4952                 {
4953                     PA_DEBUG(("Failed to get output buffer (with notification)\n"));
4954                     PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to get output buffer (with notification)");
4955                     result = paUnanticipatedHostError;
4956                     goto error;
4957                 }
4958 
4959                 /* Get latency */
4960                 result = PinGetHwLatency(stream->render.pPin, &hwFifoLatency, &dummy, &dummy);
4961                 if (result == paNoError)
4962                 {
4963                     stream->render.pPin->hwLatency = hwFifoLatency;
4964 
4965                     /* Add HW latency into total output latency */
4966                     stream->streamRepresentation.streamInfo.outputLatency += ((hwFifoLatency / stream->render.bytesPerFrame) / sampleRate);
4967                 }
4968                 else
4969                 {
4970                     PA_DEBUG(("Failed to get size of FIFO hardware buffer (is set to zero)\n"));
4971                     stream->render.pPin->hwLatency = 0;
4972                 }
4973             }
4974             break;
4975         default:
4976             /* Undefined wave type!! */
4977             assert(0);
4978             result = paInternalError;
4979             PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
4980             goto error;
4981         }
4982     }
4983     else
4984     {
4985         stream->render.hostBuffer = 0;
4986     }
4987 
4988     stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
4989 
4990     PA_DEBUG(("BytesPerInputFrame = %d\n",stream->capture.bytesPerFrame));
4991     PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->render.bytesPerFrame));
4992 
4993     /* memset(stream->hostBuffer,0,size); */
4994 
4995     /* Abort */
4996     stream->eventAbort          = CreateEvent(NULL, TRUE, FALSE, NULL);
4997     if (stream->eventAbort == 0)
4998     {
4999         result = paInsufficientMemory;
5000         goto error;
5001     }
5002     stream->eventStreamStart[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
5003     if (stream->eventStreamStart[0] == 0)
5004     {
5005         result = paInsufficientMemory;
5006         goto error;
5007     }
5008     stream->eventStreamStart[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
5009     if (stream->eventStreamStart[1] == 0)
5010     {
5011         result = paInsufficientMemory;
5012         goto error;
5013     }
5014 
5015     if(stream->userInputChannels > 0)
5016     {
5017         const unsigned bufferSizeInBytes = stream->capture.framesPerBuffer * stream->capture.bytesPerFrame;
5018         const unsigned ringBufferFrameSize = NextPowerOf2( 1024 + 2 * max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer) );
5019 
5020         stream->capture.events = (HANDLE*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->capture.noOfPackets * sizeof(HANDLE));
5021         if (stream->capture.events == NULL)
5022         {
5023             result = paInsufficientMemory;
5024             goto error;
5025         }
5026 
5027         stream->capture.packets = (DATAPACKET*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->capture.noOfPackets * sizeof(DATAPACKET));
5028         if (stream->capture.packets == NULL)
5029         {
5030             result = paInsufficientMemory;
5031             goto error;
5032         }
5033 
5034         switch(stream->capture.pPin->parentFilter->devInfo.streamingType)
5035         {
5036         case Type_kWaveCyclic:
5037             {
5038                 /* WaveCyclic case */
5039                 unsigned i;
5040                 for (i = 0; i < stream->capture.noOfPackets; ++i)
5041                 {
5042                     /* Set up the packets */
5043                     DATAPACKET *p = stream->capture.packets + i;
5044 
5045                     /* Record event */
5046                     stream->capture.events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
5047 
5048                     p->Signal.hEvent = stream->capture.events[i];
5049                     p->Header.Data = stream->capture.hostBuffer + (i*bufferSizeInBytes);
5050                     p->Header.FrameExtent = bufferSizeInBytes;
5051                     p->Header.DataUsed = 0;
5052                     p->Header.Size = sizeof(p->Header);
5053                     p->Header.PresentationTime.Numerator = 1;
5054                     p->Header.PresentationTime.Denominator = 1;
5055                 }
5056             }
5057             break;
5058         case Type_kWaveRT:
5059             {
5060                 /* Set up the "packets" */
5061                 DATAPACKET *p = stream->capture.packets + 0;
5062 
5063                 /* Record event: WaveRT has a single event for 2 notification per buffer */
5064                 stream->capture.events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
5065 
5066                 p->Header.Data = stream->capture.hostBuffer;
5067                 p->Header.FrameExtent = bufferSizeInBytes;
5068                 p->Header.DataUsed = 0;
5069                 p->Header.Size = sizeof(p->Header);
5070                 p->Header.PresentationTime.Numerator = 1;
5071                 p->Header.PresentationTime.Denominator = 1;
5072 
5073                 ++p;
5074                 p->Header.Data = stream->capture.hostBuffer + bufferSizeInBytes;
5075                 p->Header.FrameExtent = bufferSizeInBytes;
5076                 p->Header.DataUsed = 0;
5077                 p->Header.Size = sizeof(p->Header);
5078                 p->Header.PresentationTime.Numerator = 1;
5079                 p->Header.PresentationTime.Denominator = 1;
5080 
5081                 if (stream->capture.pPin->pinKsSubType == SubType_kNotification)
5082                 {
5083                     result = PinRegisterNotificationHandle(stream->capture.pPin, stream->capture.events[0]);
5084 
5085                     if (result != paNoError)
5086                     {
5087                         PA_DEBUG(("Failed to register capture notification handle\n"));
5088                         PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to register capture notification handle");
5089                         result = paUnanticipatedHostError;
5090                         goto error;
5091                     }
5092                 }
5093 
5094                 result = PinRegisterPositionRegister(stream->capture.pPin);
5095 
5096                 if (result != paNoError)
5097                 {
5098                     unsigned long pos = 0xdeadc0de;
5099                     PA_DEBUG(("Failed to register capture position register, using PinGetAudioPositionViaIOCTLWrite\n"));
5100                     stream->capture.pPin->fnAudioPosition = PinGetAudioPositionViaIOCTLWrite;
5101                     /* Test position function */
5102                     result = (stream->capture.pPin->fnAudioPosition)(stream->capture.pPin, &pos);
5103                     if (result != paNoError || pos != 0x0)
5104                     {
5105                         PA_DEBUG(("Failed to read capture position register (IOCTL)\n"));
5106                         PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to read capture position register (IOCTL)");
5107                         result = paUnanticipatedHostError;
5108                         goto error;
5109                     }
5110                 }
5111                 else
5112                 {
5113                     stream->capture.pPin->fnAudioPosition = PinGetAudioPositionMemoryMapped;
5114                 }
5115             }
5116             break;
5117         default:
5118             /* Undefined wave type!! */
5119             assert(0);
5120             result = paInternalError;
5121             PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
5122             goto error;
5123         }
5124 
5125         /* Setup the input ring buffer here */
5126         stream->ringBufferData = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, ringBufferFrameSize * stream->capture.bytesPerFrame);
5127         if (stream->ringBufferData == NULL)
5128         {
5129             result = paInsufficientMemory;
5130             goto error;
5131         }
5132         PaUtil_InitializeRingBuffer(&stream->ringBuffer, stream->capture.bytesPerFrame, ringBufferFrameSize, stream->ringBufferData);
5133     }
5134     if(stream->userOutputChannels > 0)
5135     {
5136         const unsigned bufferSizeInBytes = stream->render.framesPerBuffer * stream->render.bytesPerFrame;
5137 
5138         stream->render.events = (HANDLE*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->render.noOfPackets * sizeof(HANDLE));
5139         if (stream->render.events == NULL)
5140         {
5141             result = paInsufficientMemory;
5142             goto error;
5143         }
5144 
5145         stream->render.packets = (DATAPACKET*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->render.noOfPackets * sizeof(DATAPACKET));
5146         if (stream->render.packets == NULL)
5147         {
5148             result = paInsufficientMemory;
5149             goto error;
5150         }
5151 
5152         switch(stream->render.pPin->parentFilter->devInfo.streamingType)
5153         {
5154         case Type_kWaveCyclic:
5155             {
5156                 /* WaveCyclic case */
5157                 unsigned i;
5158                 for (i = 0; i < stream->render.noOfPackets; ++i)
5159                 {
5160                     /* Set up the packets */
5161                     DATAPACKET *p = stream->render.packets + i;
5162 
5163                     /* Playback event */
5164                     stream->render.events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
5165 
5166                     /* In this case, we just use the packets as ptr to the device buffer */
5167                     p->Signal.hEvent = stream->render.events[i];
5168                     p->Header.Data = stream->render.hostBuffer + (i*bufferSizeInBytes);
5169                     p->Header.FrameExtent = bufferSizeInBytes;
5170                     p->Header.DataUsed = bufferSizeInBytes;
5171                     p->Header.Size = sizeof(p->Header);
5172                     p->Header.PresentationTime.Numerator = 1;
5173                     p->Header.PresentationTime.Denominator = 1;
5174                 }
5175             }
5176             break;
5177         case Type_kWaveRT:
5178             {
5179                 /* WaveRT case */
5180 
5181                 /* Set up the "packets" */
5182                 DATAPACKET *p = stream->render.packets;
5183 
5184                 /* The only playback event */
5185                 stream->render.events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
5186 
5187                 /* In this case, we just use the packets as ptr to the device buffer */
5188                 p->Header.Data = stream->render.hostBuffer;
5189                 p->Header.FrameExtent = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
5190                 p->Header.DataUsed = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
5191                 p->Header.Size = sizeof(p->Header);
5192                 p->Header.PresentationTime.Numerator = 1;
5193                 p->Header.PresentationTime.Denominator = 1;
5194 
5195                 ++p;
5196                 p->Header.Data = stream->render.hostBuffer + stream->render.framesPerBuffer*stream->render.bytesPerFrame;
5197                 p->Header.FrameExtent = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
5198                 p->Header.DataUsed = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
5199                 p->Header.Size = sizeof(p->Header);
5200                 p->Header.PresentationTime.Numerator = 1;
5201                 p->Header.PresentationTime.Denominator = 1;
5202 
5203                 if (stream->render.pPin->pinKsSubType == SubType_kNotification)
5204                 {
5205                     result = PinRegisterNotificationHandle(stream->render.pPin, stream->render.events[0]);
5206 
5207                     if (result != paNoError)
5208                     {
5209                         PA_DEBUG(("Failed to register rendering notification handle\n"));
5210                         PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to register rendering notification handle");
5211                         result = paUnanticipatedHostError;
5212                         goto error;
5213                     }
5214                 }
5215 
5216                 result = PinRegisterPositionRegister(stream->render.pPin);
5217 
5218                 if (result != paNoError)
5219                 {
5220                     unsigned long pos = 0xdeadc0de;
5221                     PA_DEBUG(("Failed to register rendering position register, using PinGetAudioPositionViaIOCTLRead\n"));
5222                     stream->render.pPin->fnAudioPosition = PinGetAudioPositionViaIOCTLRead;
5223                     /* Test position function */
5224                     result = (stream->render.pPin->fnAudioPosition)(stream->render.pPin, &pos);
5225                     if (result != paNoError || pos != 0x0)
5226                     {
5227                         PA_DEBUG(("Failed to read render position register (IOCTL)\n"));
5228                         PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to read render position register (IOCTL)");
5229                         result = paUnanticipatedHostError;
5230                         goto error;
5231                     }
5232                 }
5233                 else
5234                 {
5235                     stream->render.pPin->fnAudioPosition = PinGetAudioPositionMemoryMapped;
5236                 }
5237             }
5238             break;
5239         default:
5240             /* Undefined wave type!! */
5241             assert(0);
5242             result = paInternalError;
5243             PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
5244             goto error;
5245         }
5246     }
5247 
5248     stream->streamStarted = 0;
5249     stream->streamActive = 0;
5250     stream->streamStop = 0;
5251     stream->streamAbort = 0;
5252     stream->streamFlags = streamFlags;
5253     stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
5254 
5255     /* Increase ref count on filters in use, so that a CommitDeviceInfos won't delete them */
5256     if (stream->capture.pPin != 0)
5257     {
5258         FilterAddRef(stream->capture.pPin->parentFilter);
5259     }
5260     if (stream->render.pPin != 0)
5261     {
5262         FilterAddRef(stream->render.pPin->parentFilter);
5263     }
5264 
5265     /* Ok, now update our host API specific stream info */
5266     if (stream->userInputChannels)
5267     {
5268         PaWinWdmDeviceInfo *pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
5269 
5270         stream->hostApiStreamInfo.input.device = Pa_HostApiDeviceIndexToDeviceIndex(Pa_HostApiTypeIdToHostApiIndex(paWDMKS), inputParameters->device);
5271         stream->hostApiStreamInfo.input.channels = stream->deviceInputChannels;
5272         stream->hostApiStreamInfo.input.muxNodeId = -1;
5273         if (stream->capture.pPin->inputs)
5274         {
5275             stream->hostApiStreamInfo.input.muxNodeId = stream->capture.pPin->inputs[pDeviceInfo->muxPosition]->muxNodeId;
5276         }
5277         stream->hostApiStreamInfo.input.endpointPinId = pDeviceInfo->endpointPinId;
5278         stream->hostApiStreamInfo.input.framesPerHostBuffer = stream->capture.framesPerBuffer;
5279         stream->hostApiStreamInfo.input.streamingSubType = stream->capture.pPin->pinKsSubType;
5280     }
5281     else
5282     {
5283         stream->hostApiStreamInfo.input.device = paNoDevice;
5284     }
5285     if (stream->userOutputChannels)
5286     {
5287         stream->hostApiStreamInfo.output.device = Pa_HostApiDeviceIndexToDeviceIndex(Pa_HostApiTypeIdToHostApiIndex(paWDMKS), outputParameters->device);
5288         stream->hostApiStreamInfo.output.channels = stream->deviceOutputChannels;
5289         stream->hostApiStreamInfo.output.framesPerHostBuffer = stream->render.framesPerBuffer;
5290         stream->hostApiStreamInfo.output.endpointPinId = stream->render.pPin->endpointPinId;
5291         stream->hostApiStreamInfo.output.streamingSubType = stream->render.pPin->pinKsSubType;
5292     }
5293     else
5294     {
5295         stream->hostApiStreamInfo.output.device = paNoDevice;
5296     }
5297     /*stream->streamRepresentation.streamInfo.hostApiTypeId = paWDMKS;
5298     stream->streamRepresentation.streamInfo.hostApiSpecificStreamInfo = &stream->hostApiStreamInfo;*/
5299     stream->streamRepresentation.streamInfo.structVersion = 2;
5300 
5301     *s = (PaStream*)stream;
5302 
5303     PA_LOGL_;
5304     return result;
5305 
5306 occupied:
5307     /* Ok, someone else is hogging the pin, bail out */
5308     assert (result == paDeviceUnavailable);
5309     PaWinWDM_SetLastErrorInfo(result, "Device is occupied");
5310 
5311 error:
5312     PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
5313 
5314     CloseStreamEvents(stream);
5315 
5316     if (stream->allocGroup)
5317     {
5318         PaUtil_FreeAllAllocations(stream->allocGroup);
5319         PaUtil_DestroyAllocationGroup(stream->allocGroup);
5320         stream->allocGroup = 0;
5321     }
5322 
5323     if(stream->render.pPin)
5324         PinClose(stream->render.pPin);
5325     if(stream->capture.pPin)
5326         PinClose(stream->capture.pPin);
5327 
5328     PaUtil_FreeMemory( stream );
5329 
5330     PA_LOGL_;
5331     return result;
5332 }
5333 
5334 /*
5335 When CloseStream() is called, the multi-api layer ensures that
5336 the stream has already been stopped or aborted.
5337 */
CloseStream(PaStream * s)5338 static PaError CloseStream( PaStream* s )
5339 {
5340     PaError result = paNoError;
5341     PaWinWdmStream *stream = (PaWinWdmStream*)s;
5342 
5343     PA_LOGE_;
5344 
5345     assert(!stream->streamStarted);
5346     assert(!stream->streamActive);
5347 
5348     PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
5349     PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
5350 
5351     CloseStreamEvents(stream);
5352 
5353     if (stream->allocGroup)
5354     {
5355         PaUtil_FreeAllAllocations(stream->allocGroup);
5356         PaUtil_DestroyAllocationGroup(stream->allocGroup);
5357         stream->allocGroup = 0;
5358     }
5359 
5360     if(stream->render.pPin)
5361     {
5362         PinClose(stream->render.pPin);
5363     }
5364     if(stream->capture.pPin)
5365     {
5366         PinClose(stream->capture.pPin);
5367     }
5368 
5369     if (stream->render.pPin)
5370     {
5371         FilterFree(stream->render.pPin->parentFilter);
5372     }
5373     if (stream->capture.pPin)
5374     {
5375         FilterFree(stream->capture.pPin->parentFilter);
5376     }
5377 
5378     PaUtil_FreeMemory( stream );
5379 
5380     PA_LOGL_;
5381     return result;
5382 }
5383 
5384 /*
5385 Write the supplied packet to the pin
5386 Asynchronous
5387 Should return paNoError on success
5388 */
PinWrite(HANDLE h,DATAPACKET * p)5389 static PaError PinWrite(HANDLE h, DATAPACKET* p)
5390 {
5391     PaError result = paNoError;
5392     unsigned long cbReturned = 0;
5393     BOOL fRes = DeviceIoControl(h,
5394         IOCTL_KS_WRITE_STREAM,
5395         NULL,
5396         0,
5397         &p->Header,
5398         p->Header.Size,
5399         &cbReturned,
5400         &p->Signal);
5401     if (!fRes)
5402     {
5403         unsigned long error = GetLastError();
5404         if (error != ERROR_IO_PENDING)
5405         {
5406             result = paInternalError;
5407         }
5408     }
5409     return result;
5410 }
5411 
5412 /*
5413 Read to the supplied packet from the pin
5414 Asynchronous
5415 Should return paNoError on success
5416 */
PinRead(HANDLE h,DATAPACKET * p)5417 static PaError PinRead(HANDLE h, DATAPACKET* p)
5418 {
5419     PaError result = paNoError;
5420     unsigned long cbReturned = 0;
5421     BOOL fRes = DeviceIoControl(h,
5422         IOCTL_KS_READ_STREAM,
5423         NULL,
5424         0,
5425         &p->Header,
5426         p->Header.Size,
5427         &cbReturned,
5428         &p->Signal);
5429     if (!fRes)
5430     {
5431         unsigned long error = GetLastError();
5432         if (error != ERROR_IO_PENDING)
5433         {
5434             result = paInternalError;
5435         }
5436     }
5437     return result;
5438 }
5439 
5440 /*
5441 Copy the first interleaved channel of 16 bit data to the other channels
5442 */
DuplicateFirstChannelInt16(void * buffer,int channels,int samples)5443 static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples)
5444 {
5445     unsigned short* data = (unsigned short*)buffer;
5446     int channel;
5447     unsigned short sourceSample;
5448     while( samples-- )
5449     {
5450         sourceSample = *data++;
5451         channel = channels-1;
5452         while( channel-- )
5453         {
5454             *data++ = sourceSample;
5455         }
5456     }
5457 }
5458 
5459 /*
5460 Copy the first interleaved channel of 24 bit data to the other channels
5461 */
DuplicateFirstChannelInt24(void * buffer,int channels,int samples)5462 static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples)
5463 {
5464     unsigned char* data = (unsigned char*)buffer;
5465     int channel;
5466     unsigned char sourceSample[3];
5467     while( samples-- )
5468     {
5469         sourceSample[0] = data[0];
5470         sourceSample[1] = data[1];
5471         sourceSample[2] = data[2];
5472         data += 3;
5473         channel = channels-1;
5474         while( channel-- )
5475         {
5476             data[0] = sourceSample[0];
5477             data[1] = sourceSample[1];
5478             data[2] = sourceSample[2];
5479             data += 3;
5480         }
5481     }
5482 }
5483 
5484 /*
5485 Copy the first interleaved channel of 32 bit data to the other channels
5486 */
DuplicateFirstChannelInt32(void * buffer,int channels,int samples)5487 static void DuplicateFirstChannelInt32(void* buffer, int channels, int samples)
5488 {
5489     unsigned long* data = (unsigned long*)buffer;
5490     int channel;
5491     unsigned long sourceSample;
5492     while( samples-- )
5493     {
5494         sourceSample = *data++;
5495         channel = channels-1;
5496         while( channel-- )
5497         {
5498             *data++ = sourceSample;
5499         }
5500     }
5501 }
5502 
5503 /*
5504 Increase the priority of the calling thread to RT
5505 */
BumpThreadPriority()5506 static HANDLE BumpThreadPriority()
5507 {
5508     HANDLE hThread = GetCurrentThread();
5509     DWORD dwTask = 0;
5510     HANDLE hAVRT = NULL;
5511 
5512     /* If we have access to AVRT.DLL (Vista and later), use it */
5513     if (paWinWDMKSAvRtEntryPoints.AvSetMmThreadCharacteristics != NULL)
5514     {
5515         hAVRT = paWinWDMKSAvRtEntryPoints.AvSetMmThreadCharacteristics("Pro Audio", &dwTask);
5516         if (hAVRT != NULL && hAVRT != INVALID_HANDLE_VALUE)
5517         {
5518             BOOL bret = paWinWDMKSAvRtEntryPoints.AvSetMmThreadPriority(hAVRT, PA_AVRT_PRIORITY_CRITICAL);
5519             if (!bret)
5520             {
5521                 PA_DEBUG(("Set mm thread prio to critical failed!\n"));
5522             }
5523             else
5524             {
5525                 return hAVRT;
5526             }
5527         }
5528         else
5529         {
5530             PA_DEBUG(("Set mm thread characteristic to 'Pro Audio' failed, reverting to SetThreadPriority\n"));
5531         }
5532     }
5533 
5534     /* For XP and earlier, or if AvSetMmThreadCharacteristics fails (MMCSS disabled ?) */
5535     if (timeBeginPeriod(1) != TIMERR_NOERROR) {
5536         PA_DEBUG(("timeBeginPeriod(1) failed!\n"));
5537     }
5538 
5539     if (!SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL)) {
5540         PA_DEBUG(("SetThreadPriority failed!\n"));
5541     }
5542 
5543     return hAVRT;
5544 }
5545 
5546 /*
5547 Decrease the priority of the calling thread to normal
5548 */
DropThreadPriority(HANDLE hAVRT)5549 static void DropThreadPriority(HANDLE hAVRT)
5550 {
5551     HANDLE hThread = GetCurrentThread();
5552 
5553     if (hAVRT != NULL)
5554     {
5555         paWinWDMKSAvRtEntryPoints.AvSetMmThreadPriority(hAVRT, PA_AVRT_PRIORITY_NORMAL);
5556         paWinWDMKSAvRtEntryPoints.AvRevertMmThreadCharacteristics(hAVRT);
5557         return;
5558     }
5559 
5560     SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL);
5561     timeEndPeriod(1);
5562 }
5563 
PreparePinForStart(PaWinWdmPin * pin)5564 static PaError PreparePinForStart(PaWinWdmPin* pin)
5565 {
5566     PaError result;
5567     result = PinSetState(pin, KSSTATE_ACQUIRE);
5568     if (result != paNoError)
5569     {
5570         goto error;
5571     }
5572     result = PinSetState(pin, KSSTATE_PAUSE);
5573     if (result != paNoError)
5574     {
5575         goto error;
5576     }
5577     return result;
5578 
5579 error:
5580     PinSetState(pin, KSSTATE_STOP);
5581     return result;
5582 }
5583 
PreparePinsForStart(PaProcessThreadInfo * pInfo)5584 static PaError PreparePinsForStart(PaProcessThreadInfo* pInfo)
5585 {
5586     PaError result = paNoError;
5587     /* Submit buffers */
5588     if (pInfo->stream->capture.pPin)
5589     {
5590         if ((result = PreparePinForStart(pInfo->stream->capture.pPin)) != paNoError)
5591         {
5592             goto error;
5593         }
5594 
5595         if (pInfo->stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
5596         {
5597             unsigned i;
5598             for(i=0; i < pInfo->stream->capture.noOfPackets; ++i)
5599             {
5600                 if ((result = PinRead(pInfo->stream->capture.pPin->handle, pInfo->stream->capture.packets + i)) != paNoError)
5601                 {
5602                     goto error;
5603                 }
5604                 ++pInfo->pending;
5605             }
5606         }
5607         else
5608         {
5609             pInfo->pending = 2;
5610         }
5611     }
5612 
5613     if(pInfo->stream->render.pPin)
5614     {
5615         if ((result = PreparePinForStart(pInfo->stream->render.pPin)) != paNoError)
5616         {
5617             goto error;
5618         }
5619 
5620         pInfo->priming += pInfo->stream->render.noOfPackets;
5621         ++pInfo->pending;
5622         SetEvent(pInfo->stream->render.events[0]);
5623         if (pInfo->stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
5624         {
5625             unsigned i;
5626             for(i=1; i < pInfo->stream->render.noOfPackets; ++i)
5627             {
5628                 SetEvent(pInfo->stream->render.events[i]);
5629                 ++pInfo->pending;
5630             }
5631         }
5632     }
5633 
5634 error:
5635     PA_DEBUG(("PreparePinsForStart = %d\n", result));
5636     return result;
5637 }
5638 
StartPin(PaWinWdmPin * pin)5639 static PaError StartPin(PaWinWdmPin* pin)
5640 {
5641     return PinSetState(pin, KSSTATE_RUN);
5642 }
5643 
StartPins(PaProcessThreadInfo * pInfo)5644 static PaError StartPins(PaProcessThreadInfo* pInfo)
5645 {
5646     PaError result = paNoError;
5647     /* Start the pins as synced as possible */
5648     if (pInfo->stream->capture.pPin)
5649     {
5650         result = StartPin(pInfo->stream->capture.pPin);
5651     }
5652     if(pInfo->stream->render.pPin)
5653     {
5654         result = StartPin(pInfo->stream->render.pPin);
5655     }
5656     PA_DEBUG(("StartPins = %d\n", result));
5657     return result;
5658 }
5659 
5660 
StopPin(PaWinWdmPin * pin)5661 static PaError StopPin(PaWinWdmPin* pin)
5662 {
5663     PinSetState(pin, KSSTATE_PAUSE);
5664     PinSetState(pin, KSSTATE_STOP);
5665     return paNoError;
5666 }
5667 
5668 
StopPins(PaProcessThreadInfo * pInfo)5669 static PaError StopPins(PaProcessThreadInfo* pInfo)
5670 {
5671     PaError result = paNoError;
5672     if(pInfo->stream->render.pPin)
5673     {
5674         StopPin(pInfo->stream->render.pPin);
5675     }
5676     if(pInfo->stream->capture.pPin)
5677     {
5678         StopPin(pInfo->stream->capture.pPin);
5679     }
5680     return result;
5681 }
5682 
5683 typedef void (*TSetInputFrameCount)(PaUtilBufferProcessor*, unsigned long);
5684 typedef void (*TSetInputChannel)(PaUtilBufferProcessor*, unsigned int, void *, unsigned int);
5685 static const TSetInputFrameCount fnSetInputFrameCount[2] = { PaUtil_SetInputFrameCount, PaUtil_Set2ndInputFrameCount };
5686 static const TSetInputChannel fnSetInputChannel[2] = { PaUtil_SetInputChannel, PaUtil_Set2ndInputChannel };
5687 
PaDoProcessing(PaProcessThreadInfo * pInfo)5688 static PaError PaDoProcessing(PaProcessThreadInfo* pInfo)
5689 {
5690     PaError result = paNoError;
5691     int i, framesProcessed = 0, doChannelCopy = 0;
5692     ring_buffer_size_t inputFramesAvailable = PaUtil_GetRingBufferReadAvailable(&pInfo->stream->ringBuffer);
5693 
5694     /* Do necessary buffer processing (which will invoke user callback if necessary) */
5695     if (pInfo->cbResult == paContinue &&
5696         (pInfo->renderHead != pInfo->renderTail || inputFramesAvailable))
5697     {
5698         unsigned processFullDuplex = pInfo->stream->capture.pPin && pInfo->stream->render.pPin && (!pInfo->priming);
5699 
5700         PA_HP_TRACE((pInfo->stream->hLog, "DoProcessing: InputFrames=%u", inputFramesAvailable));
5701 
5702         PaUtil_BeginCpuLoadMeasurement( &pInfo->stream->cpuLoadMeasurer );
5703 
5704         pInfo->ti.currentTime = PaUtil_GetTime();
5705 
5706         PaUtil_BeginBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->ti, pInfo->underover);
5707         pInfo->underover = 0; /* Reset the (under|over)flow status */
5708 
5709         if (pInfo->renderTail != pInfo->renderHead)
5710         {
5711             DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet;
5712 
5713             assert(packet != 0);
5714             assert(packet->Header.Data != 0);
5715 
5716             PaUtil_SetOutputFrameCount(&pInfo->stream->bufferProcessor, pInfo->stream->render.framesPerBuffer);
5717 
5718             for(i=0;i<pInfo->stream->userOutputChannels;i++)
5719             {
5720                 /* Only write the user output channels. Leave the rest blank */
5721                 PaUtil_SetOutputChannel(&pInfo->stream->bufferProcessor,
5722                     i,
5723                     ((unsigned char*)(packet->Header.Data))+(i*pInfo->stream->render.bytesPerSample),
5724                     pInfo->stream->deviceOutputChannels);
5725             }
5726 
5727             /* We will do a copy to the other channels after the data has been written */
5728             doChannelCopy = ( pInfo->stream->userOutputChannels == 1 );
5729         }
5730 
5731         if (inputFramesAvailable && (!pInfo->stream->userOutputChannels || inputFramesAvailable >= (int)pInfo->stream->render.framesPerBuffer))
5732         {
5733             unsigned wrapCntr = 0;
5734             void* data[2] = {0};
5735             ring_buffer_size_t size[2] = {0};
5736 
5737             /* If full-duplex, we just extract output buffer number of frames */
5738             if (pInfo->stream->userOutputChannels)
5739             {
5740                 inputFramesAvailable = min(inputFramesAvailable, (int)pInfo->stream->render.framesPerBuffer);
5741             }
5742 
5743             inputFramesAvailable = PaUtil_GetRingBufferReadRegions(&pInfo->stream->ringBuffer,
5744                 inputFramesAvailable,
5745                 &data[0],
5746                 &size[0],
5747                 &data[1],
5748                 &size[1]);
5749 
5750             for (wrapCntr = 0; wrapCntr < 2; ++wrapCntr)
5751             {
5752                 if (size[wrapCntr] == 0)
5753                     break;
5754 
5755                 fnSetInputFrameCount[wrapCntr](&pInfo->stream->bufferProcessor, size[wrapCntr]);
5756                 for(i=0;i<pInfo->stream->userInputChannels;i++)
5757                 {
5758                     /* Only read as many channels as the user wants */
5759                     fnSetInputChannel[wrapCntr](&pInfo->stream->bufferProcessor,
5760                         i,
5761                         ((unsigned char*)(data[wrapCntr]))+(i*pInfo->stream->capture.bytesPerSample),
5762                         pInfo->stream->deviceInputChannels);
5763                 }
5764             }
5765         }
5766         else
5767         {
5768             /* We haven't consumed anything from the ring buffer... */
5769             inputFramesAvailable = 0;
5770             /* If we have full-duplex, this is at startup, so mark no-input! */
5771             if (pInfo->stream->userOutputChannels>0 && pInfo->stream->userInputChannels>0)
5772             {
5773                 PA_HP_TRACE((pInfo->stream->hLog, "Input startup, marking no input."));
5774                 PaUtil_SetNoInput(&pInfo->stream->bufferProcessor);
5775             }
5776         }
5777 
5778         if (processFullDuplex) /* full duplex */
5779         {
5780             /* Only call the EndBufferProcessing function when the total input frames == total output frames */
5781             const unsigned long totalInputFrameCount = pInfo->stream->bufferProcessor.hostInputFrameCount[0] + pInfo->stream->bufferProcessor.hostInputFrameCount[1];
5782             const unsigned long totalOutputFrameCount = pInfo->stream->bufferProcessor.hostOutputFrameCount[0] + pInfo->stream->bufferProcessor.hostOutputFrameCount[1];
5783 
5784             if(totalInputFrameCount == totalOutputFrameCount && totalOutputFrameCount != 0)
5785             {
5786                 framesProcessed = PaUtil_EndBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->cbResult);
5787             }
5788             else
5789             {
5790                 framesProcessed = 0;
5791             }
5792         }
5793         else
5794         {
5795             framesProcessed = PaUtil_EndBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->cbResult);
5796         }
5797 
5798         PA_HP_TRACE((pInfo->stream->hLog, "Frames processed: %u %s", framesProcessed, (pInfo->priming ? "(priming)":"")));
5799 
5800         if( doChannelCopy )
5801         {
5802             DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet;
5803             /* Copy the first output channel to the other channels */
5804             switch (pInfo->stream->render.bytesPerSample)
5805             {
5806             case 2:
5807                 DuplicateFirstChannelInt16(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer);
5808                 break;
5809             case 3:
5810                 DuplicateFirstChannelInt24(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer);
5811                 break;
5812             case 4:
5813                 DuplicateFirstChannelInt32(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer);
5814                 break;
5815             default:
5816                 assert(0); /* Unsupported format! */
5817                 break;
5818             }
5819         }
5820         PaUtil_EndCpuLoadMeasurement( &pInfo->stream->cpuLoadMeasurer, framesProcessed );
5821 
5822         if (inputFramesAvailable)
5823         {
5824             PaUtil_AdvanceRingBufferReadIndex(&pInfo->stream->ringBuffer, inputFramesAvailable);
5825         }
5826 
5827         if (pInfo->renderTail != pInfo->renderHead)
5828         {
5829             if (!pInfo->stream->streamStop)
5830             {
5831                 result = pInfo->stream->render.pPin->fnSubmitHandler(pInfo, pInfo->renderTail);
5832                 if (result != paNoError)
5833                 {
5834                     PA_HP_TRACE((pInfo->stream->hLog, "Capture submit handler failed with result %d", result));
5835                     return result;
5836                 }
5837             }
5838             pInfo->renderTail++;
5839             if (!pInfo->pinsStarted && pInfo->priming == 0)
5840             {
5841                 /* We start the pins here to allow "prime time" */
5842                 if ((result = StartPins(pInfo)) == paNoError)
5843                 {
5844                     PA_HP_TRACE((pInfo->stream->hLog, "Starting pins!"));
5845                     pInfo->pinsStarted = 1;
5846                 }
5847             }
5848         }
5849     }
5850 
5851     return result;
5852 }
5853 
TimerAPCWaveRTPolledMode(LPVOID lpArgToCompletionRoutine,DWORD dwTimerLowValue,DWORD dwTimerHighValue)5854 static VOID CALLBACK TimerAPCWaveRTPolledMode(
5855     LPVOID lpArgToCompletionRoutine,
5856     DWORD dwTimerLowValue,
5857     DWORD dwTimerHighValue)
5858 {
5859     HANDLE* pHandles = (HANDLE*)lpArgToCompletionRoutine;
5860     if (pHandles[0]) SetEvent(pHandles[0]);
5861     if (pHandles[1]) SetEvent(pHandles[1]);
5862 }
5863 
GetCurrentTimeInMillisecs()5864 static DWORD GetCurrentTimeInMillisecs()
5865 {
5866     return timeGetTime();
5867 }
5868 
ProcessingThread(void * pParam)5869 PA_THREAD_FUNC ProcessingThread(void* pParam)
5870 {
5871     PaError result = paNoError;
5872     HANDLE hAVRT = NULL;
5873     HANDLE hTimer = NULL;
5874     HANDLE *handleArray = NULL;
5875     HANDLE timerEventHandles[2] = {0};
5876     unsigned noOfHandles = 0;
5877     unsigned captureEvents = 0;
5878     unsigned renderEvents = 0;
5879     unsigned timerPeriod = 0;
5880     DWORD timeStamp[2] = {0};
5881 
5882     PaProcessThreadInfo info;
5883     memset(&info, 0, sizeof(PaProcessThreadInfo));
5884     info.stream = (PaWinWdmStream*)pParam;
5885 
5886     info.stream->threadResult = paNoError;
5887 
5888     PA_LOGE_;
5889 
5890     info.ti.inputBufferAdcTime = 0.0;
5891     info.ti.currentTime = 0.0;
5892     info.ti.outputBufferDacTime = 0.0;
5893 
5894     PA_DEBUG(("In  buffer len: %.3f ms\n",(2000*info.stream->capture.framesPerBuffer) / info.stream->streamRepresentation.streamInfo.sampleRate));
5895     PA_DEBUG(("Out buffer len: %.3f ms\n",(2000*info.stream->render.framesPerBuffer) / info.stream->streamRepresentation.streamInfo.sampleRate));
5896     info.timeout = (DWORD)max(
5897         (2000*info.stream->render.framesPerBuffer/info.stream->streamRepresentation.streamInfo.sampleRate + 0.5),
5898         (2000*info.stream->capture.framesPerBuffer/info.stream->streamRepresentation.streamInfo.sampleRate + 0.5));
5899     info.timeout = max(info.timeout*8, 100);
5900     timerPeriod = info.timeout;
5901     PA_DEBUG(("Timeout = %ld ms\n",info.timeout));
5902 
5903     /* Allocate handle array */
5904     handleArray = (HANDLE*)PaUtil_AllocateMemory((info.stream->capture.noOfPackets + info.stream->render.noOfPackets + 1) * sizeof(HANDLE));
5905 
5906     /* Setup handle array for WFMO */
5907     if (info.stream->capture.pPin != 0)
5908     {
5909         handleArray[noOfHandles++] = info.stream->capture.events[0];
5910         if (info.stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
5911         {
5912             unsigned i;
5913             for(i=1; i < info.stream->capture.noOfPackets; ++i)
5914             {
5915                 handleArray[noOfHandles++] = info.stream->capture.events[i];
5916             }
5917         }
5918         captureEvents = noOfHandles;
5919         renderEvents = noOfHandles;
5920     }
5921 
5922     if (info.stream->render.pPin != 0)
5923     {
5924         handleArray[noOfHandles++] = info.stream->render.events[0];
5925         if (info.stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
5926         {
5927             unsigned i;
5928             for(i=1; i < info.stream->render.noOfPackets; ++i)
5929             {
5930                 handleArray[noOfHandles++] = info.stream->render.events[i];
5931             }
5932         }
5933         renderEvents = noOfHandles;
5934     }
5935     handleArray[noOfHandles++] = info.stream->eventAbort;
5936     assert(noOfHandles <= (info.stream->capture.noOfPackets + info.stream->render.noOfPackets + 1));
5937 
5938     /* Prepare render and capture pins */
5939     if ((result = PreparePinsForStart(&info)) != paNoError)
5940     {
5941         PA_DEBUG(("Failed to prepare device(s)!\n"));
5942         goto error;
5943     }
5944 
5945     /* Init high speed logger */
5946     if (PaUtil_InitializeHighSpeedLog(&info.stream->hLog, 1000000) != paNoError)
5947     {
5948         PA_DEBUG(("Failed to init high speed logger!\n"));
5949         goto error;
5950     }
5951 
5952     /* Heighten priority here */
5953     hAVRT = BumpThreadPriority();
5954 
5955     /* If input only, we start the pins immediately */
5956     if (info.stream->render.pPin == 0)
5957     {
5958         if ((result = StartPins(&info)) != paNoError)
5959         {
5960             PA_DEBUG(("Failed to start device(s)!\n"));
5961             goto error;
5962         }
5963         info.pinsStarted = 1;
5964     }
5965 
5966     /* Handle WaveRT polled mode */
5967     {
5968         const unsigned fs = (unsigned)info.stream->streamRepresentation.streamInfo.sampleRate;
5969         if (info.stream->capture.pPin != 0 && info.stream->capture.pPin->pinKsSubType == SubType_kPolled)
5970         {
5971             timerEventHandles[0] = info.stream->capture.events[0];
5972             timerPeriod = min(timerPeriod, (1000*info.stream->capture.framesPerBuffer)/fs);
5973         }
5974 
5975         if (info.stream->render.pPin != 0 && info.stream->render.pPin->pinKsSubType == SubType_kPolled)
5976         {
5977             timerEventHandles[1] = info.stream->render.events[0];
5978             timerPeriod = min(timerPeriod, (1000*info.stream->render.framesPerBuffer)/fs);
5979         }
5980 
5981         if (timerEventHandles[0] || timerEventHandles[1])
5982         {
5983             LARGE_INTEGER dueTime = {0};
5984 
5985             timerPeriod=max(timerPeriod/5,1);
5986             PA_DEBUG(("Timer event handles=0x%04X,0x%04X period=%u ms", timerEventHandles[0], timerEventHandles[1], timerPeriod));
5987             hTimer = CreateWaitableTimer(0, FALSE, NULL);
5988             if (hTimer == NULL)
5989             {
5990                 result = paUnanticipatedHostError;
5991                 goto error;
5992             }
5993             /* invoke first timeout immediately */
5994             if (!SetWaitableTimer(hTimer, &dueTime, timerPeriod, TimerAPCWaveRTPolledMode, timerEventHandles, FALSE))
5995             {
5996                 result = paUnanticipatedHostError;
5997                 goto error;
5998             }
5999             PA_DEBUG(("Waitable timer started, period = %u ms\n", timerPeriod));
6000         }
6001     }
6002 
6003     /* Mark stream as active */
6004     info.stream->streamActive = 1;
6005     info.stream->threadResult = paNoError;
6006 
6007     /* Up and running... */
6008     SetEvent(info.stream->eventStreamStart[StreamStart_kOk]);
6009 
6010     /* Take timestamp here */
6011     timeStamp[0] = timeStamp[1] = GetCurrentTimeInMillisecs();
6012 
6013     while(!info.stream->streamAbort)
6014     {
6015         unsigned doProcessing = 1;
6016         unsigned wait = WaitForMultipleObjects(noOfHandles, handleArray, FALSE, 0);
6017         unsigned eventSignalled = wait - WAIT_OBJECT_0;
6018         DWORD dwCurrentTime = 0;
6019 
6020         if (wait == WAIT_FAILED)
6021         {
6022             PA_DEBUG(("Wait failed = %ld! \n",wait));
6023             break;
6024         }
6025         if (wait == WAIT_TIMEOUT)
6026         {
6027             wait = WaitForMultipleObjectsEx(noOfHandles, handleArray, FALSE, 50, TRUE);
6028             eventSignalled = wait - WAIT_OBJECT_0;
6029         }
6030         else
6031         {
6032             if (eventSignalled < captureEvents)
6033             {
6034                 if (PaUtil_GetRingBufferWriteAvailable(&info.stream->ringBuffer) == 0)
6035                 {
6036                     PA_HP_TRACE((info.stream->hLog, "!!!!! Input overflow !!!!!"));
6037                     info.underover |= paInputOverflow;
6038                 }
6039             }
6040             else if (eventSignalled < renderEvents)
6041             {
6042                 if (!info.priming && info.renderHead - info.renderTail > 1)
6043                 {
6044                     PA_HP_TRACE((info.stream->hLog, "!!!!! Output underflow !!!!!"));
6045                     info.underover |= paOutputUnderflow;
6046                 }
6047             }
6048         }
6049 
6050         /* Get event time */
6051         dwCurrentTime = GetCurrentTimeInMillisecs();
6052 
6053         /* Since we can mix capture/render devices between WaveCyclic, WaveRT polled and WaveRT notification (3x3 combinations),
6054         we can't rely on the timeout of WFMO to check for device timeouts, we need to keep tally. */
6055         if (info.stream->capture.pPin && (dwCurrentTime - timeStamp[0]) >= info.timeout)
6056         {
6057             PA_DEBUG(("Timeout for capture device (%u ms)!", info.timeout, (dwCurrentTime - timeStamp[0])));
6058             result = paTimedOut;
6059             break;
6060         }
6061         if (info.stream->render.pPin && (dwCurrentTime - timeStamp[1]) >= info.timeout)
6062         {
6063             PA_DEBUG(("Timeout for render device (%u ms)!", info.timeout, (dwCurrentTime - timeStamp[1])));
6064             result = paTimedOut;
6065             break;
6066         }
6067 
6068         if (wait == WAIT_IO_COMPLETION)
6069         {
6070             /* Waitable timer has fired! */
6071             PA_HP_TRACE((info.stream->hLog, "WAIT_IO_COMPLETION"));
6072             continue;
6073         }
6074 
6075         if (wait == WAIT_TIMEOUT)
6076         {
6077             continue;
6078         }
6079         else
6080         {
6081             if (eventSignalled < captureEvents)
6082             {
6083                 if (info.stream->capture.pPin->fnEventHandler(&info, eventSignalled) == paNoError)
6084                 {
6085                     timeStamp[0] = dwCurrentTime;
6086 
6087                     /* Since we use the ring buffer, we can submit the buffers directly */
6088                     if (!info.stream->streamStop)
6089                     {
6090                         result = info.stream->capture.pPin->fnSubmitHandler(&info, info.captureTail);
6091                         if (result != paNoError)
6092                         {
6093                             PA_HP_TRACE((info.stream->hLog, "Capture submit handler failed with result %d", result));
6094                             break;
6095                         }
6096                     }
6097                     ++info.captureTail;
6098                     /* If full-duplex, let _only_ render event trigger processing. We still need the stream stop
6099                     handling working, so let that be processed anyways... */
6100                     if (info.stream->userOutputChannels > 0)
6101                     {
6102                         doProcessing = 0;
6103                     }
6104                 }
6105             }
6106             else if (eventSignalled < renderEvents)
6107             {
6108                 timeStamp[1] = dwCurrentTime;
6109                 eventSignalled -= captureEvents;
6110                 info.stream->render.pPin->fnEventHandler(&info, eventSignalled);
6111             }
6112             else
6113             {
6114                 assert(info.stream->streamAbort);
6115                 PA_HP_TRACE((info.stream->hLog, "Stream abort!"));
6116                 continue;
6117             }
6118         }
6119 
6120         /* Handle processing */
6121         if (doProcessing)
6122         {
6123             result = PaDoProcessing(&info);
6124             if (result != paNoError)
6125             {
6126                 PA_HP_TRACE((info.stream->hLog, "PaDoProcessing failed!"));
6127                 break;
6128             }
6129         }
6130 
6131         if(info.stream->streamStop && info.cbResult != paComplete)
6132         {
6133             PA_HP_TRACE((info.stream->hLog, "Stream stop! pending=%d",info.pending));
6134             info.cbResult = paComplete; /* Stop, but play remaining buffers */
6135         }
6136 
6137         if(info.pending<=0)
6138         {
6139             PA_HP_TRACE((info.stream->hLog, "pending==0 finished..."));
6140             break;
6141         }
6142         if((!info.stream->render.pPin)&&(info.cbResult!=paContinue))
6143         {
6144             PA_HP_TRACE((info.stream->hLog, "record only cbResult=%d...",info.cbResult));
6145             break;
6146         }
6147     }
6148 
6149     PA_DEBUG(("Finished processing loop\n"));
6150 
6151     info.stream->threadResult = result;
6152     goto bailout;
6153 
6154 error:
6155     PA_DEBUG(("Error starting processing thread\n"));
6156     /* Set the "error" event together with result */
6157     info.stream->threadResult = result;
6158     SetEvent(info.stream->eventStreamStart[StreamStart_kFailed]);
6159 
6160 bailout:
6161     if (hTimer)
6162     {
6163         PA_DEBUG(("Waitable timer stopped\n", timerPeriod));
6164         CancelWaitableTimer(hTimer);
6165         CloseHandle(hTimer);
6166         hTimer = 0;
6167     }
6168 
6169     if (info.pinsStarted)
6170     {
6171         StopPins(&info);
6172     }
6173 
6174     /* Lower prio here */
6175     DropThreadPriority(hAVRT);
6176 
6177     if (handleArray != NULL)
6178     {
6179         PaUtil_FreeMemory(handleArray);
6180     }
6181 
6182 #if PA_TRACE_REALTIME_EVENTS
6183     if (info.stream->hLog)
6184     {
6185         PA_DEBUG(("Dumping highspeed trace...\n"));
6186         PaUtil_DumpHighSpeedLog(info.stream->hLog, "hp_trace.log");
6187         PaUtil_DiscardHighSpeedLog(info.stream->hLog);
6188         info.stream->hLog = 0;
6189     }
6190 #endif
6191     info.stream->streamActive = 0;
6192 
6193     if((!info.stream->streamStop)&&(!info.stream->streamAbort))
6194     {
6195         /* Invoke the user stream finished callback */
6196         /* Only do it from here if not being stopped/aborted by user */
6197         if( info.stream->streamRepresentation.streamFinishedCallback != 0 )
6198             info.stream->streamRepresentation.streamFinishedCallback( info.stream->streamRepresentation.userData );
6199     }
6200     info.stream->streamStop = 0;
6201     info.stream->streamAbort = 0;
6202 
6203     PA_LOGL_;
6204     return 0;
6205 }
6206 
6207 
StartStream(PaStream * s)6208 static PaError StartStream( PaStream *s )
6209 {
6210     PaError result = paNoError;
6211     PaWinWdmStream *stream = (PaWinWdmStream*)s;
6212 
6213     PA_LOGE_;
6214 
6215     if (stream->streamThread != NULL)
6216     {
6217         return paStreamIsNotStopped;
6218     }
6219 
6220     stream->streamStop = 0;
6221     stream->streamAbort = 0;
6222 
6223     ResetStreamEvents(stream);
6224 
6225     PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
6226 
6227     stream->oldProcessPriority = GetPriorityClass(GetCurrentProcess());
6228     /* Uncomment the following line to enable dynamic boosting of the process
6229     * priority to real time for best low latency support
6230     * Disabled by default because RT processes can easily block the OS */
6231     /*ret = SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
6232     PA_DEBUG(("Class ret = %d;",ret));*/
6233 
6234     stream->streamThread = CREATE_THREAD_FUNCTION (NULL, 0, ProcessingThread, stream, CREATE_SUSPENDED, NULL);
6235     if(stream->streamThread == NULL)
6236     {
6237         result = paInsufficientMemory;
6238         goto end;
6239     }
6240     ResumeThread(stream->streamThread);
6241 
6242     switch (WaitForMultipleObjects(2, stream->eventStreamStart, FALSE, 5000))
6243     {
6244     case WAIT_OBJECT_0 + StreamStart_kOk:
6245         PA_DEBUG(("Processing thread started!\n"));
6246         result = paNoError;
6247         /* streamActive is set in processing thread */
6248         stream->streamStarted = 1;
6249         break;
6250     case WAIT_OBJECT_0 + StreamStart_kFailed:
6251         PA_DEBUG(("Processing thread start failed! (result=%d)\n", stream->threadResult));
6252         result = stream->threadResult;
6253         /* Wait for the stream to really exit */
6254         WaitForSingleObject(stream->streamThread, 200);
6255         CloseHandle(stream->streamThread);
6256         stream->streamThread = 0;
6257         break;
6258     case WAIT_TIMEOUT:
6259     default:
6260         result = paTimedOut;
6261         PaWinWDM_SetLastErrorInfo(result, "Failed to start processing thread (timeout)!");
6262         break;
6263     }
6264 
6265 end:
6266     PA_LOGL_;
6267     return result;
6268 }
6269 
6270 
StopStream(PaStream * s)6271 static PaError StopStream( PaStream *s )
6272 {
6273     PaError result = paNoError;
6274     PaWinWdmStream *stream = (PaWinWdmStream*)s;
6275     BOOL doCb = FALSE;
6276 
6277     PA_LOGE_;
6278 
6279     if(stream->streamActive)
6280     {
6281         DWORD dwExitCode;
6282         doCb = TRUE;
6283         stream->streamStop = 1;
6284         if (GetExitCodeThread(stream->streamThread, &dwExitCode) && dwExitCode == STILL_ACTIVE)
6285         {
6286             if (WaitForSingleObject(stream->streamThread, INFINITE) != WAIT_OBJECT_0)
6287             {
6288                 PA_DEBUG(("StopStream: stream thread terminated\n"));
6289                 TerminateThread(stream->streamThread, -1);
6290                 result = paTimedOut;
6291             }
6292         }
6293         else
6294         {
6295             PA_DEBUG(("StopStream: GECT says not active, but streamActive is not false ??"));
6296             result = paUnanticipatedHostError;
6297             PaWinWDM_SetLastErrorInfo(result, "StopStream: GECT says not active, but streamActive = %d", stream->streamActive);
6298         }
6299     }
6300     else
6301     {
6302         if (stream->threadResult != paNoError)
6303         {
6304             PA_DEBUG(("StopStream: Stream not active (%d)\n", stream->threadResult));
6305             result = stream->threadResult;
6306             stream->threadResult = paNoError;
6307         }
6308     }
6309 
6310     if (stream->streamThread != NULL)
6311     {
6312         CloseHandle(stream->streamThread);
6313         stream->streamThread = 0;
6314     }
6315     stream->streamStarted = 0;
6316     stream->streamActive = 0;
6317 
6318     if(doCb)
6319     {
6320         /* Do user callback now after all state has been reset */
6321         /* This means it should be safe for the called function */
6322         /* to invoke e.g. StartStream */
6323         if( stream->streamRepresentation.streamFinishedCallback != 0 )
6324             stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
6325     }
6326 
6327     PA_LOGL_;
6328     return result;
6329 }
6330 
AbortStream(PaStream * s)6331 static PaError AbortStream( PaStream *s )
6332 {
6333     PaError result = paNoError;
6334     PaWinWdmStream *stream = (PaWinWdmStream*)s;
6335     int doCb = 0;
6336 
6337     PA_LOGE_;
6338 
6339     if(stream->streamActive)
6340     {
6341         doCb = 1;
6342         stream->streamAbort = 1;
6343         SetEvent(stream->eventAbort); /* Signal immediately */
6344         if (WaitForSingleObject(stream->streamThread, 10000) != WAIT_OBJECT_0)
6345         {
6346             TerminateThread(stream->streamThread, -1);
6347             result = paTimedOut;
6348 
6349             PA_DEBUG(("AbortStream: stream thread terminated\n"));
6350         }
6351         assert(!stream->streamActive);
6352     }
6353     CloseHandle(stream->streamThread);
6354     stream->streamThread = NULL;
6355     stream->streamStarted = 0;
6356 
6357     if(doCb)
6358     {
6359         /* Do user callback now after all state has been reset */
6360         /* This means it should be safe for the called function */
6361         /* to invoke e.g. StartStream */
6362         if( stream->streamRepresentation.streamFinishedCallback != 0 )
6363             stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
6364     }
6365 
6366     stream->streamActive = 0;
6367     stream->streamStarted = 0;
6368 
6369     PA_LOGL_;
6370     return result;
6371 }
6372 
6373 
IsStreamStopped(PaStream * s)6374 static PaError IsStreamStopped( PaStream *s )
6375 {
6376     PaWinWdmStream *stream = (PaWinWdmStream*)s;
6377     int result = 0;
6378 
6379     PA_LOGE_;
6380 
6381     if(!stream->streamStarted)
6382         result = 1;
6383 
6384     PA_LOGL_;
6385     return result;
6386 }
6387 
6388 
IsStreamActive(PaStream * s)6389 static PaError IsStreamActive( PaStream *s )
6390 {
6391     PaWinWdmStream *stream = (PaWinWdmStream*)s;
6392     int result = 0;
6393 
6394     PA_LOGE_;
6395 
6396     if(stream->streamActive)
6397         result = 1;
6398 
6399     PA_LOGL_;
6400     return result;
6401 }
6402 
6403 
GetStreamTime(PaStream * s)6404 static PaTime GetStreamTime( PaStream* s )
6405 {
6406     PA_LOGE_;
6407     PA_LOGL_;
6408     (void)s;
6409     return PaUtil_GetTime();
6410 }
6411 
6412 
GetStreamCpuLoad(PaStream * s)6413 static double GetStreamCpuLoad( PaStream* s )
6414 {
6415     PaWinWdmStream *stream = (PaWinWdmStream*)s;
6416     double result;
6417     PA_LOGE_;
6418     result = PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
6419     PA_LOGL_;
6420     return result;
6421 }
6422 
6423 
6424 /*
6425 As separate stream interfaces are used for blocking and callback
6426 streams, the following functions can be guaranteed to only be called
6427 for blocking streams.
6428 */
6429 
ReadStream(PaStream * s,void * buffer,unsigned long frames)6430 static PaError ReadStream( PaStream* s,
6431                           void *buffer,
6432                           unsigned long frames )
6433 {
6434     PaWinWdmStream *stream = (PaWinWdmStream*)s;
6435 
6436     PA_LOGE_;
6437 
6438     /* suppress unused variable warnings */
6439     (void) buffer;
6440     (void) frames;
6441     (void) stream;
6442 
6443     /* IMPLEMENT ME, see portaudio.h for required behavior*/
6444     PA_LOGL_;
6445     return paInternalError;
6446 }
6447 
6448 
WriteStream(PaStream * s,const void * buffer,unsigned long frames)6449 static PaError WriteStream( PaStream* s,
6450                            const void *buffer,
6451                            unsigned long frames )
6452 {
6453     PaWinWdmStream *stream = (PaWinWdmStream*)s;
6454 
6455     PA_LOGE_;
6456 
6457     /* suppress unused variable warnings */
6458     (void) buffer;
6459     (void) frames;
6460     (void) stream;
6461 
6462     /* IMPLEMENT ME, see portaudio.h for required behavior*/
6463     PA_LOGL_;
6464     return paInternalError;
6465 }
6466 
6467 
GetStreamReadAvailable(PaStream * s)6468 static signed long GetStreamReadAvailable( PaStream* s )
6469 {
6470     PaWinWdmStream *stream = (PaWinWdmStream*)s;
6471 
6472     PA_LOGE_;
6473 
6474     /* suppress unused variable warnings */
6475     (void) stream;
6476 
6477     /* IMPLEMENT ME, see portaudio.h for required behavior*/
6478     PA_LOGL_;
6479     return 0;
6480 }
6481 
6482 
GetStreamWriteAvailable(PaStream * s)6483 static signed long GetStreamWriteAvailable( PaStream* s )
6484 {
6485     PaWinWdmStream *stream = (PaWinWdmStream*)s;
6486 
6487     PA_LOGE_;
6488     /* suppress unused variable warnings */
6489     (void) stream;
6490 
6491     /* IMPLEMENT ME, see portaudio.h for required behavior*/
6492     PA_LOGL_;
6493     return 0;
6494 }
6495 
6496 /***************************************************************************************/
6497 /* Event and submit handlers for WaveCyclic                                            */
6498 /***************************************************************************************/
6499 
PaPinCaptureEventHandler_WaveCyclic(PaProcessThreadInfo * pInfo,unsigned eventIndex)6500 static PaError PaPinCaptureEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6501 {
6502     PaError result = paNoError;
6503     ring_buffer_size_t frameCount;
6504     DATAPACKET* packet = pInfo->stream->capture.packets + eventIndex;
6505 
6506     assert( eventIndex < pInfo->stream->capture.noOfPackets );
6507 
6508     if (packet->Header.DataUsed == 0)
6509     {
6510         PA_HP_TRACE((pInfo->stream->hLog, ">>> Capture bogus event (no data): idx=%u", eventIndex));
6511 
6512         /* 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
6513            on startup of streaming, where it erroneously sets the event without the corresponding buffer being filled (DataUsed == 0) */
6514         ResetEvent(packet->Signal.hEvent);
6515 
6516         result = -1;    /* Only need this to be NOT paNoError */
6517     }
6518     else
6519     {
6520         pInfo->capturePackets[pInfo->captureHead & cPacketsArrayMask].packet = packet;
6521 
6522         frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer, packet->Header.Data, pInfo->stream->capture.framesPerBuffer);
6523 
6524         PA_HP_TRACE((pInfo->stream->hLog, ">>> Capture event: idx=%u (frames=%u)", eventIndex, frameCount));
6525         ++pInfo->captureHead;
6526     }
6527 
6528     --pInfo->pending; /* This needs to be done in either case */
6529     return result;
6530 }
6531 
PaPinCaptureSubmitHandler_WaveCyclic(PaProcessThreadInfo * pInfo,unsigned eventIndex)6532 static PaError PaPinCaptureSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6533 {
6534     PaError result = paNoError;
6535     DATAPACKET* packet = pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet;
6536     pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0;
6537     assert(packet != 0);
6538     PA_HP_TRACE((pInfo->stream->hLog, "Capture submit: %u", eventIndex));
6539     packet->Header.DataUsed = 0; /* Reset for reuse */
6540     ResetEvent(packet->Signal.hEvent);
6541     result = PinRead(pInfo->stream->capture.pPin->handle, packet);
6542     ++pInfo->pending;
6543     return result;
6544 }
6545 
PaPinRenderEventHandler_WaveCyclic(PaProcessThreadInfo * pInfo,unsigned eventIndex)6546 static PaError PaPinRenderEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6547 {
6548     assert( eventIndex < pInfo->stream->render.noOfPackets );
6549 
6550     pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask].packet = pInfo->stream->render.packets + eventIndex;
6551     PA_HP_TRACE((pInfo->stream->hLog, "<<< Render event : idx=%u head=%u", eventIndex, pInfo->renderHead));
6552     ++pInfo->renderHead;
6553     --pInfo->pending;
6554     return paNoError;
6555 }
6556 
PaPinRenderSubmitHandler_WaveCyclic(PaProcessThreadInfo * pInfo,unsigned eventIndex)6557 static PaError PaPinRenderSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6558 {
6559     PaError result = paNoError;
6560     DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet;
6561     pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0;
6562     assert(packet != 0);
6563 
6564     PA_HP_TRACE((pInfo->stream->hLog, "Render submit : %u idx=%u", pInfo->renderTail, (unsigned)(packet - pInfo->stream->render.packets)));
6565     ResetEvent(packet->Signal.hEvent);
6566     result = PinWrite(pInfo->stream->render.pPin->handle, packet);
6567     /* Reset event, just in case we have an analogous situation to capture (see PaPinCaptureSubmitHandler_WaveCyclic) */
6568     ++pInfo->pending;
6569     if (pInfo->priming)
6570     {
6571         --pInfo->priming;
6572     }
6573     return result;
6574 }
6575 
6576 /***************************************************************************************/
6577 /* Event and submit handlers for WaveRT                                                */
6578 /***************************************************************************************/
6579 
PaPinCaptureEventHandler_WaveRTEvent(PaProcessThreadInfo * pInfo,unsigned eventIndex)6580 static PaError PaPinCaptureEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6581 {
6582     unsigned long pos;
6583     unsigned realInBuf;
6584     unsigned frameCount;
6585     PaWinWdmIOInfo* pCapture = &pInfo->stream->capture;
6586     const unsigned halfInputBuffer = pCapture->hostBufferSize >> 1;
6587     PaWinWdmPin* pin = pCapture->pPin;
6588     DATAPACKET* packet = 0;
6589 
6590     /* Get hold of current ADC position */
6591     pin->fnAudioPosition(pin, &pos);
6592     /* Wrap it (robi: why not use hw latency compensation here ?? because pos then gets _way_ off from
6593     where it should be, i.e. at beginning or half buffer position. Why? No idea.)  */
6594 
6595     pos %= pCapture->hostBufferSize;
6596     /* Then realInBuf will point to "other" half of double buffer */
6597     realInBuf = pos < halfInputBuffer ? 1U : 0U;
6598 
6599     packet = pInfo->stream->capture.packets + realInBuf;
6600 
6601     /* Call barrier (or dummy) */
6602     pin->fnMemBarrier();
6603 
6604     /* Put it in queue */
6605     frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer, packet->Header.Data, pCapture->framesPerBuffer);
6606 
6607     pInfo->capturePackets[pInfo->captureHead & cPacketsArrayMask].packet = packet;
6608 
6609     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));
6610 
6611     ++pInfo->captureHead;
6612     --pInfo->pending;
6613 
6614     return paNoError;
6615 }
6616 
PaPinCaptureEventHandler_WaveRTPolled(PaProcessThreadInfo * pInfo,unsigned eventIndex)6617 static PaError PaPinCaptureEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6618 {
6619     unsigned long pos;
6620     unsigned bytesToRead;
6621     PaWinWdmIOInfo* pCapture = &pInfo->stream->capture;
6622     const unsigned halfInputBuffer = pCapture->hostBufferSize>>1;
6623     PaWinWdmPin* pin = pInfo->stream->capture.pPin;
6624 
6625     /* Get hold of current ADC position */
6626     pin->fnAudioPosition(pin, &pos);
6627     /* Wrap it (robi: why not use hw latency compensation here ?? because pos then gets _way_ off from
6628     where it should be, i.e. at beginning or half buffer position. Why? No idea.)  */
6629     /* Compensate for HW FIFO to get to last read buffer position */
6630     pos += pin->hwLatency;
6631     pos %= pCapture->hostBufferSize;
6632     /* Need to align position on frame boundary */
6633     pos &= ~(pCapture->bytesPerFrame - 1);
6634 
6635     /* Call barrier (or dummy) */
6636     pin->fnMemBarrier();
6637 
6638     /* Put it in "queue" */
6639     bytesToRead = (pCapture->hostBufferSize + pos - pCapture->lastPosition) % pCapture->hostBufferSize;
6640     if (bytesToRead > 0)
6641     {
6642         unsigned frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer,
6643             pCapture->hostBuffer + pCapture->lastPosition,
6644             bytesToRead / pCapture->bytesPerFrame);
6645 
6646         pCapture->lastPosition = (pCapture->lastPosition + frameCount * pCapture->bytesPerFrame) % pCapture->hostBufferSize;
6647 
6648         PA_HP_TRACE((pInfo->stream->hLog, "Capture event (WaveRTPolled): pos = %4.1lf%%, framesRead=%u", (pos * 100.0 / pCapture->hostBufferSize), frameCount));
6649         ++pInfo->captureHead;
6650         --pInfo->pending;
6651     }
6652     return paNoError;
6653 }
6654 
PaPinCaptureSubmitHandler_WaveRTEvent(PaProcessThreadInfo * pInfo,unsigned eventIndex)6655 static PaError PaPinCaptureSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6656 {
6657     pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0;
6658     ++pInfo->pending;
6659     return paNoError;
6660 }
6661 
PaPinCaptureSubmitHandler_WaveRTPolled(PaProcessThreadInfo * pInfo,unsigned eventIndex)6662 static PaError PaPinCaptureSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6663 {
6664     pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0;
6665     ++pInfo->pending;
6666     return paNoError;
6667 }
6668 
PaPinRenderEventHandler_WaveRTEvent(PaProcessThreadInfo * pInfo,unsigned eventIndex)6669 static PaError PaPinRenderEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6670 {
6671     unsigned long pos;
6672     unsigned realOutBuf;
6673     PaWinWdmIOInfo* pRender = &pInfo->stream->render;
6674     const unsigned halfOutputBuffer = pRender->hostBufferSize >> 1;
6675     PaWinWdmPin* pin = pInfo->stream->render.pPin;
6676     PaIOPacket* ioPacket = &pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask];
6677 
6678     /* Get hold of current DAC position */
6679     pin->fnAudioPosition(pin, &pos);
6680     /* Compensate for HW FIFO to get to last read buffer position */
6681     pos += pin->hwLatency;
6682     /* Wrap it */
6683     pos %= pRender->hostBufferSize;
6684     /* And align it, not sure its really needed though */
6685     pos &= ~(pRender->bytesPerFrame - 1);
6686     /* Then realOutBuf will point to "other" half of double buffer */
6687     realOutBuf = pos < halfOutputBuffer ? 1U : 0U;
6688 
6689     if (pInfo->priming)
6690     {
6691         realOutBuf = pInfo->renderHead & 0x1;
6692     }
6693     ioPacket->packet = pInfo->stream->render.packets + realOutBuf;
6694     ioPacket->startByte = realOutBuf * halfOutputBuffer;
6695     ioPacket->lengthBytes = halfOutputBuffer;
6696 
6697     PA_HP_TRACE((pInfo->stream->hLog, "Render event (WaveRT) : idx=%u head=%u (pos = %4.1lf%%)", realOutBuf, pInfo->renderHead, (pos * 100.0 / pRender->hostBufferSize) ));
6698 
6699     ++pInfo->renderHead;
6700     --pInfo->pending;
6701     return paNoError;
6702 }
6703 
PaPinRenderEventHandler_WaveRTPolled(PaProcessThreadInfo * pInfo,unsigned eventIndex)6704 static PaError PaPinRenderEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6705 {
6706     unsigned long pos;
6707     unsigned realOutBuf;
6708     unsigned bytesToWrite;
6709 
6710     PaWinWdmIOInfo* pRender = &pInfo->stream->render;
6711     const unsigned halfOutputBuffer = pRender->hostBufferSize >> 1;
6712     PaWinWdmPin* pin = pInfo->stream->render.pPin;
6713     PaIOPacket* ioPacket = &pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask];
6714 
6715     /* Get hold of current DAC position */
6716     pin->fnAudioPosition(pin, &pos);
6717     /* Compensate for HW FIFO to get to last read buffer position */
6718     pos += pin->hwLatency;
6719     /* Wrap it */
6720     pos %= pRender->hostBufferSize;
6721     /* And align it, not sure its really needed though */
6722     pos &= ~(pRender->bytesPerFrame - 1);
6723 
6724     if (pInfo->priming)
6725     {
6726         realOutBuf = pInfo->renderHead & 0x1;
6727         ioPacket->packet = pInfo->stream->render.packets + realOutBuf;
6728         ioPacket->startByte = realOutBuf * halfOutputBuffer;
6729         ioPacket->lengthBytes = halfOutputBuffer;
6730         ++pInfo->renderHead;
6731         --pInfo->pending;
6732     }
6733     else
6734     {
6735         bytesToWrite = (pRender->hostBufferSize + pos - pRender->lastPosition) % pRender->hostBufferSize;
6736         ++pRender->pollCntr;
6737         if (bytesToWrite >= halfOutputBuffer)
6738         {
6739             realOutBuf = (pos < halfOutputBuffer) ? 1U : 0U;
6740             ioPacket->packet = pInfo->stream->render.packets + realOutBuf;
6741             pRender->lastPosition = realOutBuf ? 0U : halfOutputBuffer;
6742             ioPacket->startByte = realOutBuf * halfOutputBuffer;
6743             ioPacket->lengthBytes = halfOutputBuffer;
6744             ++pInfo->renderHead;
6745             --pInfo->pending;
6746             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));
6747             pRender->pollCntr = 0;
6748         }
6749     }
6750     return paNoError;
6751 }
6752 
PaPinRenderSubmitHandler_WaveRTEvent(PaProcessThreadInfo * pInfo,unsigned eventIndex)6753 static PaError PaPinRenderSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6754 {
6755     PaWinWdmPin* pin = pInfo->stream->render.pPin;
6756     pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0;
6757     /* Call barrier (if needed) */
6758     pin->fnMemBarrier();
6759     PA_HP_TRACE((pInfo->stream->hLog, "Render submit (WaveRT) : submit=%u", pInfo->renderTail));
6760     ++pInfo->pending;
6761     if (pInfo->priming)
6762     {
6763         --pInfo->priming;
6764         if (pInfo->priming)
6765         {
6766             PA_HP_TRACE((pInfo->stream->hLog, "Setting WaveRT event for priming (2)"));
6767             SetEvent(pInfo->stream->render.events[0]);
6768         }
6769     }
6770     return paNoError;
6771 }
6772 
PaPinRenderSubmitHandler_WaveRTPolled(PaProcessThreadInfo * pInfo,unsigned eventIndex)6773 static PaError PaPinRenderSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
6774 {
6775     PaWinWdmPin* pin = pInfo->stream->render.pPin;
6776     pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0;
6777     /* Call barrier (if needed) */
6778     pin->fnMemBarrier();
6779     PA_HP_TRACE((pInfo->stream->hLog, "Render submit (WaveRTPolled) : submit=%u", pInfo->renderTail));
6780     ++pInfo->pending;
6781     if (pInfo->priming)
6782     {
6783         --pInfo->priming;
6784         if (pInfo->priming)
6785         {
6786             PA_HP_TRACE((pInfo->stream->hLog, "Setting WaveRT event for priming (2)"));
6787             SetEvent(pInfo->stream->render.events[0]);
6788         }
6789     }
6790     return paNoError;
6791 }
6792