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