1 /*
2 * $Id: pa_win_wdmks.c 1606 2011-02-17 15:56:04Z rob_bielik $
3 * PortAudio Windows WDM-KS interface
4 *
5 * Author: Andrew Baldwin
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)
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 /* Debugging/tracing support */
56
57 #define PA_LOGE_
58 #define PA_LOGL_
59
60 #ifdef __GNUC__
61 #include <initguid.h>
62 #define _WIN32_WINNT 0x0501
63 #define WINVER 0x0501
64 #endif
65
66 #include <string.h> /* strlen() */
67 #include <assert.h>
68
69 #include "pa_util.h"
70 #include "pa_allocation.h"
71 #include "pa_hostapi.h"
72 #include "pa_stream.h"
73 #include "pa_cpuload.h"
74 #include "pa_process.h"
75 #include "portaudio.h"
76 #include "pa_debugprint.h"
77
78 #include <windows.h>
79 #include <winioctl.h>
80 #include <process.h>
81
82 #ifdef __GNUC__
83 #undef PA_LOGE_
84 #define PA_LOGE_ PA_DEBUG(("%s {\n",__FUNCTION__))
85 #undef PA_LOGL_
86 #define PA_LOGL_ PA_DEBUG(("} %s\n",__FUNCTION__))
87 /* These defines are set in order to allow the WIndows DirectX
88 * headers to compile with a GCC compiler such as MinGW
89 * NOTE: The headers may generate a few warning in GCC, but
90 * they should compile */
91 #define _INC_MMSYSTEM
92 #define _INC_MMREG
93 #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
94 #define DEFINE_GUID_THUNK(name,guid) DEFINE_GUID(name,guid)
95 #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK( n, STATIC_##n )
96 #if !defined( DEFINE_WAVEFORMATEX_GUID )
97 #define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
98 #endif
99 #define WAVE_FORMAT_ADPCM 0x0002
100 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
101 #define WAVE_FORMAT_ALAW 0x0006
102 #define WAVE_FORMAT_MULAW 0x0007
103 #define WAVE_FORMAT_MPEG 0x0050
104 #define WAVE_FORMAT_DRM 0x0009
105 #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}}
106 #define DYNAMIC_GUID(data) DYNAMIC_GUID_THUNK(data)
107 #endif
108
109 #if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
110 #pragma comment( lib, "setupapi.lib" )
111 #endif
112
113 /* use CreateThread for CYGWIN, _beginthreadex for all others */
114 #ifndef __CYGWIN__
115 #define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
116 #else
117 #define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
118 #endif
119
120 /* use ExitThread for CYGWIN, _endthreadex for all others */
121 #ifndef __CYGWIN__
122 #define EXIT_THREAD _endthreadex(0)
123 #else
124 #define EXIT_THREAD ExitThread(0)
125 #endif
126
127 #ifdef _MSC_VER
128 #define NOMMIDS
129 #define DYNAMIC_GUID(data) {data}
130 #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
131 #undef DEFINE_GUID
132 #define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
133 #define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
134 #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
135 #endif
136
137 #include <mmreg.h>
138 #include <ks.h>
139 #include <ksmedia.h>
140 #include <tchar.h>
141 #include <assert.h>
142 #include <stdio.h>
143
144 /* These next definitions allow the use of the KSUSER DLL */
145 typedef KSDDKAPI DWORD WINAPI KSCREATEPIN(HANDLE, PKSPIN_CONNECT, ACCESS_MASK, PHANDLE);
146 extern HMODULE DllKsUser;
147 extern KSCREATEPIN* FunctionKsCreatePin;
148
149 /* Forward definition to break circular type reference between pin and filter */
150 struct __PaWinWdmFilter;
151 typedef struct __PaWinWdmFilter PaWinWdmFilter;
152
153 /* The Pin structure
154 * A pin is an input or output node, e.g. for audio flow */
155 typedef struct __PaWinWdmPin
156 {
157 HANDLE handle;
158 PaWinWdmFilter* parentFilter;
159 unsigned long pinId;
160 KSPIN_CONNECT* pinConnect;
161 unsigned long pinConnectSize;
162 KSDATAFORMAT_WAVEFORMATEX* ksDataFormatWfx;
163 KSPIN_COMMUNICATION communication;
164 KSDATARANGE* dataRanges;
165 KSMULTIPLE_ITEM* dataRangesItem;
166 KSPIN_DATAFLOW dataFlow;
167 KSPIN_CINSTANCES instances;
168 unsigned long frameSize;
169 int maxChannels;
170 unsigned long formats;
171 int bestSampleRate;
172 }
173 PaWinWdmPin;
174
175 /* The Filter structure
176 * A filter has a number of pins and a "friendly name" */
177 struct __PaWinWdmFilter
178 {
179 HANDLE handle;
180 int pinCount;
181 PaWinWdmPin** pins;
182 TCHAR filterName[MAX_PATH];
183 TCHAR friendlyName[MAX_PATH];
184 int maxInputChannels;
185 int maxOutputChannels;
186 unsigned long formats;
187 int usageCount;
188 int bestSampleRate;
189 };
190
191 /* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */
192 typedef struct __PaWinWdmHostApiRepresentation
193 {
194 PaUtilHostApiRepresentation inheritedHostApiRep;
195 PaUtilStreamInterface callbackStreamInterface;
196 PaUtilStreamInterface blockingStreamInterface;
197
198 PaUtilAllocationGroup* allocations;
199 PaWinWdmFilter** filters;
200 int filterCount;
201 }
202 PaWinWdmHostApiRepresentation;
203
204 typedef struct __PaWinWdmDeviceInfo
205 {
206 PaDeviceInfo inheritedDeviceInfo;
207 PaWinWdmFilter* filter;
208 }
209 PaWinWdmDeviceInfo;
210
211 typedef struct __DATAPACKET
212 {
213 KSSTREAM_HEADER Header;
214 OVERLAPPED Signal;
215 } DATAPACKET;
216
217 /* PaWinWdmStream - a stream data structure specifically for this implementation */
218 typedef struct __PaWinWdmStream
219 {
220 PaUtilStreamRepresentation streamRepresentation;
221 PaUtilCpuLoadMeasurer cpuLoadMeasurer;
222 PaUtilBufferProcessor bufferProcessor;
223
224 PaWinWdmPin* recordingPin;
225 PaWinWdmPin* playbackPin;
226 char* hostBuffer;
227 unsigned long framesPerHostIBuffer;
228 unsigned long framesPerHostOBuffer;
229 int bytesPerInputFrame;
230 int bytesPerOutputFrame;
231 int streamStarted;
232 int streamActive;
233 int streamStop;
234 int streamAbort;
235 int oldProcessPriority;
236 HANDLE streamThread;
237 HANDLE events[5]; /* 2 play + 2 record packets + abort events */
238 DATAPACKET packets[4]; /* 2 play + 2 record */
239 PaStreamFlags streamFlags;
240 /* These values handle the case where the user wants to use fewer
241 * channels than the device has */
242 int userInputChannels;
243 int deviceInputChannels;
244 int userOutputChannels;
245 int deviceOutputChannels;
246 int inputSampleSize;
247 int outputSampleSize;
248 }
249 PaWinWdmStream;
250
251 #include <setupapi.h>
252
253 HMODULE DllKsUser = NULL;
254 KSCREATEPIN* FunctionKsCreatePin = NULL;
255
256 /* prototypes for functions declared in this file */
257
258 #ifdef __cplusplus
259 extern "C"
260 {
261 #endif /* __cplusplus */
262
263 PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
264
265 #ifdef __cplusplus
266 }
267 #endif /* __cplusplus */
268
269 /* Low level I/O functions */
270 static PaError WdmSyncIoctl(HANDLE handle,
271 unsigned long ioctlNumber,
272 void* inBuffer,
273 unsigned long inBufferCount,
274 void* outBuffer,
275 unsigned long outBufferCount,
276 unsigned long* bytesReturned);
277 static PaError WdmGetPropertySimple(HANDLE handle,
278 const GUID* const guidPropertySet,
279 unsigned long property,
280 void* value,
281 unsigned long valueCount,
282 void* instance,
283 unsigned long instanceCount);
284 static PaError WdmSetPropertySimple(HANDLE handle,
285 const GUID* const guidPropertySet,
286 unsigned long property,
287 void* value,
288 unsigned long valueCount,
289 void* instance,
290 unsigned long instanceCount);
291 static PaError WdmGetPinPropertySimple(HANDLE handle,
292 unsigned long pinId,
293 const GUID* const guidPropertySet,
294 unsigned long property,
295 void* value,
296 unsigned long valueCount);
297 static PaError WdmGetPinPropertyMulti(HANDLE handle,
298 unsigned long pinId,
299 const GUID* const guidPropertySet,
300 unsigned long property,
301 KSMULTIPLE_ITEM** ksMultipleItem);
302
303 /** Pin management functions */
304 static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error);
305 static void PinFree(PaWinWdmPin* pin);
306 static void PinClose(PaWinWdmPin* pin);
307 static PaError PinInstantiate(PaWinWdmPin* pin);
308 /*static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state); NOT USED */
309 static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state);
310 static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format);
311 static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format);
312
313 /* Filter management functions */
314 static PaWinWdmFilter* FilterNew(
315 TCHAR* filterName,
316 TCHAR* friendlyName,
317 PaError* error);
318 static void FilterFree(PaWinWdmFilter* filter);
319 static PaWinWdmPin* FilterCreateRenderPin(
320 PaWinWdmFilter* filter,
321 const WAVEFORMATEX* wfex,
322 PaError* error);
323 static PaWinWdmPin* FilterFindViableRenderPin(
324 PaWinWdmFilter* filter,
325 const WAVEFORMATEX* wfex,
326 PaError* error);
327 static PaError FilterCanCreateRenderPin(
328 PaWinWdmFilter* filter,
329 const WAVEFORMATEX* wfex);
330 static PaWinWdmPin* FilterCreateCapturePin(
331 PaWinWdmFilter* filter,
332 const WAVEFORMATEX* wfex,
333 PaError* error);
334 static PaWinWdmPin* FilterFindViableCapturePin(
335 PaWinWdmFilter* filter,
336 const WAVEFORMATEX* wfex,
337 PaError* error);
338 static PaError FilterCanCreateCapturePin(
339 PaWinWdmFilter* filter,
340 const WAVEFORMATEX* pwfx);
341 static PaError FilterUse(
342 PaWinWdmFilter* filter);
343 static void FilterRelease(
344 PaWinWdmFilter* filter);
345
346 /* Interface functions */
347 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
348 static PaError IsFormatSupported(
349 struct PaUtilHostApiRepresentation *hostApi,
350 const PaStreamParameters *inputParameters,
351 const PaStreamParameters *outputParameters,
352 double sampleRate );
353 static PaError OpenStream(
354 struct PaUtilHostApiRepresentation *hostApi,
355 PaStream** s,
356 const PaStreamParameters *inputParameters,
357 const PaStreamParameters *outputParameters,
358 double sampleRate,
359 unsigned long framesPerBuffer,
360 PaStreamFlags streamFlags,
361 PaStreamCallback *streamCallback,
362 void *userData );
363 static PaError CloseStream( PaStream* stream );
364 static PaError StartStream( PaStream *stream );
365 static PaError StopStream( PaStream *stream );
366 static PaError AbortStream( PaStream *stream );
367 static PaError IsStreamStopped( PaStream *s );
368 static PaError IsStreamActive( PaStream *stream );
369 static PaTime GetStreamTime( PaStream *stream );
370 static double GetStreamCpuLoad( PaStream* stream );
371 static PaError ReadStream(
372 PaStream* stream,
373 void *buffer,
374 unsigned long frames );
375 static PaError WriteStream(
376 PaStream* stream,
377 const void *buffer,
378 unsigned long frames );
379 static signed long GetStreamReadAvailable( PaStream* stream );
380 static signed long GetStreamWriteAvailable( PaStream* stream );
381
382 /* Utility functions */
383 static unsigned long GetWfexSize(const WAVEFORMATEX* wfex);
384 static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi);
385 static BOOL PinWrite(HANDLE h, DATAPACKET* p);
386 static BOOL PinRead(HANDLE h, DATAPACKET* p);
387 static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples);
388 static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples);
389 static DWORD WINAPI ProcessingThread(LPVOID pParam);
390
391 /* Function bodies */
392
GetWfexSize(const WAVEFORMATEX * wfex)393 static unsigned long GetWfexSize(const WAVEFORMATEX* wfex)
394 {
395 if( wfex->wFormatTag == WAVE_FORMAT_PCM )
396 {
397 return sizeof( WAVEFORMATEX );
398 }
399 else
400 {
401 return (sizeof( WAVEFORMATEX ) + wfex->cbSize);
402 }
403 }
404
405 /*
406 Low level pin/filter access functions
407 */
WdmSyncIoctl(HANDLE handle,unsigned long ioctlNumber,void * inBuffer,unsigned long inBufferCount,void * outBuffer,unsigned long outBufferCount,unsigned long * bytesReturned)408 static PaError WdmSyncIoctl(
409 HANDLE handle,
410 unsigned long ioctlNumber,
411 void* inBuffer,
412 unsigned long inBufferCount,
413 void* outBuffer,
414 unsigned long outBufferCount,
415 unsigned long* bytesReturned)
416 {
417 PaError result = paNoError;
418 OVERLAPPED overlapped;
419 int boolResult;
420 unsigned long dummyBytesReturned;
421 unsigned long error;
422
423 if( !bytesReturned )
424 {
425 /* User a dummy as the caller hasn't supplied one */
426 bytesReturned = &dummyBytesReturned;
427 }
428
429 FillMemory((void *)&overlapped,sizeof(overlapped),0);
430 overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
431 if( !overlapped.hEvent )
432 {
433 result = paInsufficientMemory;
434 goto error;
435 }
436 overlapped.hEvent = (HANDLE)((DWORD_PTR)overlapped.hEvent | 0x1);
437
438 boolResult = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount,
439 outBuffer, outBufferCount, bytesReturned, &overlapped);
440 if( !boolResult )
441 {
442 error = GetLastError();
443 if( error == ERROR_IO_PENDING )
444 {
445 error = WaitForSingleObject(overlapped.hEvent,INFINITE);
446 if( error != WAIT_OBJECT_0 )
447 {
448 result = paUnanticipatedHostError;
449 goto error;
450 }
451 }
452 else if((( error == ERROR_INSUFFICIENT_BUFFER ) ||
453 ( error == ERROR_MORE_DATA )) &&
454 ( ioctlNumber == IOCTL_KS_PROPERTY ) &&
455 ( outBufferCount == 0 ))
456 {
457 boolResult = TRUE;
458 }
459 else
460 {
461 result = paUnanticipatedHostError;
462 }
463 }
464 if( !boolResult )
465 *bytesReturned = 0;
466
467 error:
468 if( overlapped.hEvent )
469 {
470 CloseHandle( overlapped.hEvent );
471 }
472 return result;
473 }
474
WdmGetPropertySimple(HANDLE handle,const GUID * const guidPropertySet,unsigned long property,void * value,unsigned long valueCount,void * instance,unsigned long instanceCount)475 static PaError WdmGetPropertySimple(HANDLE handle,
476 const GUID* const guidPropertySet,
477 unsigned long property,
478 void* value,
479 unsigned long valueCount,
480 void* instance,
481 unsigned long instanceCount)
482 {
483 PaError result;
484 KSPROPERTY* ksProperty;
485 unsigned long propertyCount;
486
487 propertyCount = sizeof(KSPROPERTY) + instanceCount;
488 ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
489 if( !ksProperty )
490 {
491 return paInsufficientMemory;
492 }
493
494 FillMemory((void*)ksProperty,sizeof(ksProperty),0);
495 ksProperty->Set = *guidPropertySet;
496 ksProperty->Id = property;
497 ksProperty->Flags = KSPROPERTY_TYPE_GET;
498
499 if( instance )
500 {
501 memcpy( (void*)(((char*)ksProperty)+sizeof(KSPROPERTY)), instance, instanceCount );
502 }
503
504 result = WdmSyncIoctl(
505 handle,
506 IOCTL_KS_PROPERTY,
507 ksProperty,
508 propertyCount,
509 value,
510 valueCount,
511 NULL);
512
513 PaUtil_FreeMemory( ksProperty );
514 return result;
515 }
516
WdmSetPropertySimple(HANDLE handle,const GUID * const guidPropertySet,unsigned long property,void * value,unsigned long valueCount,void * instance,unsigned long instanceCount)517 static PaError WdmSetPropertySimple(
518 HANDLE handle,
519 const GUID* const guidPropertySet,
520 unsigned long property,
521 void* value,
522 unsigned long valueCount,
523 void* instance,
524 unsigned long instanceCount)
525 {
526 PaError result;
527 KSPROPERTY* ksProperty;
528 unsigned long propertyCount = 0;
529
530 propertyCount = sizeof(KSPROPERTY) + instanceCount;
531 ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
532 if( !ksProperty )
533 {
534 return paInsufficientMemory;
535 }
536
537 ksProperty->Set = *guidPropertySet;
538 ksProperty->Id = property;
539 ksProperty->Flags = KSPROPERTY_TYPE_SET;
540
541 if( instance )
542 {
543 memcpy((void*)((char*)ksProperty + sizeof(KSPROPERTY)), instance, instanceCount);
544 }
545
546 result = WdmSyncIoctl(
547 handle,
548 IOCTL_KS_PROPERTY,
549 ksProperty,
550 propertyCount,
551 value,
552 valueCount,
553 NULL);
554
555 PaUtil_FreeMemory( ksProperty );
556 return result;
557 }
558
WdmGetPinPropertySimple(HANDLE handle,unsigned long pinId,const GUID * const guidPropertySet,unsigned long property,void * value,unsigned long valueCount)559 static PaError WdmGetPinPropertySimple(
560 HANDLE handle,
561 unsigned long pinId,
562 const GUID* const guidPropertySet,
563 unsigned long property,
564 void* value,
565 unsigned long valueCount)
566 {
567 PaError result;
568
569 KSP_PIN ksPProp;
570 ksPProp.Property.Set = *guidPropertySet;
571 ksPProp.Property.Id = property;
572 ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
573 ksPProp.PinId = pinId;
574 ksPProp.Reserved = 0;
575
576 result = WdmSyncIoctl(
577 handle,
578 IOCTL_KS_PROPERTY,
579 &ksPProp,
580 sizeof(KSP_PIN),
581 value,
582 valueCount,
583 NULL);
584
585 return result;
586 }
587
WdmGetPinPropertyMulti(HANDLE handle,unsigned long pinId,const GUID * const guidPropertySet,unsigned long property,KSMULTIPLE_ITEM ** ksMultipleItem)588 static PaError WdmGetPinPropertyMulti(
589 HANDLE handle,
590 unsigned long pinId,
591 const GUID* const guidPropertySet,
592 unsigned long property,
593 KSMULTIPLE_ITEM** ksMultipleItem)
594 {
595 PaError result;
596 unsigned long multipleItemSize = 0;
597 KSP_PIN ksPProp;
598
599 ksPProp.Property.Set = *guidPropertySet;
600 ksPProp.Property.Id = property;
601 ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
602 ksPProp.PinId = pinId;
603 ksPProp.Reserved = 0;
604
605 result = WdmSyncIoctl(
606 handle,
607 IOCTL_KS_PROPERTY,
608 &ksPProp.Property,
609 sizeof(KSP_PIN),
610 NULL,
611 0,
612 &multipleItemSize);
613 if( result != paNoError )
614 {
615 return result;
616 }
617
618 *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
619 if( !*ksMultipleItem )
620 {
621 return paInsufficientMemory;
622 }
623
624 result = WdmSyncIoctl(
625 handle,
626 IOCTL_KS_PROPERTY,
627 &ksPProp,
628 sizeof(KSP_PIN),
629 (void*)*ksMultipleItem,
630 multipleItemSize,
631 NULL);
632
633 if( result != paNoError )
634 {
635 PaUtil_FreeMemory( ksMultipleItem );
636 }
637
638 return result;
639 }
640
641
642 /*
643 Create a new pin object belonging to a filter
644 The pin object holds all the configuration information about the pin
645 before it is opened, and then the handle of the pin after is opened
646 */
PinNew(PaWinWdmFilter * parentFilter,unsigned long pinId,PaError * error)647 static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error)
648 {
649 PaWinWdmPin* pin;
650 PaError result;
651 unsigned long i;
652 KSMULTIPLE_ITEM* item = NULL;
653 KSIDENTIFIER* identifier;
654 KSDATARANGE* dataRange;
655
656 PA_LOGE_;
657 PA_DEBUG(("Creating pin %d:\n",pinId));
658
659 /* Allocate the new PIN object */
660 pin = (PaWinWdmPin*)PaUtil_AllocateMemory( sizeof(PaWinWdmPin) );
661 if( !pin )
662 {
663 result = paInsufficientMemory;
664 goto error;
665 }
666
667 /* Zero the pin object */
668 /* memset( (void*)pin, 0, sizeof(PaWinWdmPin) ); */
669
670 pin->parentFilter = parentFilter;
671 pin->pinId = pinId;
672
673 /* Allocate a connect structure */
674 pin->pinConnectSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX);
675 pin->pinConnect = (KSPIN_CONNECT*)PaUtil_AllocateMemory( pin->pinConnectSize );
676 if( !pin->pinConnect )
677 {
678 result = paInsufficientMemory;
679 goto error;
680 }
681
682 /* Configure the connect structure with default values */
683 pin->pinConnect->Interface.Set = KSINTERFACESETID_Standard;
684 pin->pinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
685 pin->pinConnect->Interface.Flags = 0;
686 pin->pinConnect->Medium.Set = KSMEDIUMSETID_Standard;
687 pin->pinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
688 pin->pinConnect->Medium.Flags = 0;
689 pin->pinConnect->PinId = pinId;
690 pin->pinConnect->PinToHandle = NULL;
691 pin->pinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
692 pin->pinConnect->Priority.PrioritySubClass = 1;
693 pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)(pin->pinConnect + 1);
694 pin->ksDataFormatWfx->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEX);
695 pin->ksDataFormatWfx->DataFormat.Flags = 0;
696 pin->ksDataFormatWfx->DataFormat.Reserved = 0;
697 pin->ksDataFormatWfx->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
698 pin->ksDataFormatWfx->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
699 pin->ksDataFormatWfx->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
700
701 pin->frameSize = 0; /* Unknown until we instantiate pin */
702
703 /* Get the COMMUNICATION property */
704 result = WdmGetPinPropertySimple(
705 parentFilter->handle,
706 pinId,
707 &KSPROPSETID_Pin,
708 KSPROPERTY_PIN_COMMUNICATION,
709 &pin->communication,
710 sizeof(KSPIN_COMMUNICATION));
711 if( result != paNoError )
712 goto error;
713
714 if( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/
715 (pin->communication != KSPIN_COMMUNICATION_SINK) &&
716 (pin->communication != KSPIN_COMMUNICATION_BOTH) )
717 {
718 PA_DEBUG(("Not source/sink\n"));
719 result = paInvalidDevice;
720 goto error;
721 }
722
723 /* Get dataflow information */
724 result = WdmGetPinPropertySimple(
725 parentFilter->handle,
726 pinId,
727 &KSPROPSETID_Pin,
728 KSPROPERTY_PIN_DATAFLOW,
729 &pin->dataFlow,
730 sizeof(KSPIN_DATAFLOW));
731
732 if( result != paNoError )
733 goto error;
734
735 /* Get the INTERFACE property list */
736 result = WdmGetPinPropertyMulti(
737 parentFilter->handle,
738 pinId,
739 &KSPROPSETID_Pin,
740 KSPROPERTY_PIN_INTERFACES,
741 &item);
742
743 if( result != paNoError )
744 goto error;
745
746 identifier = (KSIDENTIFIER*)(item+1);
747
748 /* Check that at least one interface is STANDARD_STREAMING */
749 result = paUnanticipatedHostError;
750 for( i = 0; i < item->Count; i++ )
751 {
752 if( !memcmp( (void*)&identifier[i].Set, (void*)&KSINTERFACESETID_Standard, sizeof( GUID ) ) &&
753 ( identifier[i].Id == KSINTERFACE_STANDARD_STREAMING ) )
754 {
755 result = paNoError;
756 break;
757 }
758 }
759
760 if( result != paNoError )
761 {
762 PA_DEBUG(("No standard streaming\n"));
763 goto error;
764 }
765
766 /* Don't need interfaces any more */
767 PaUtil_FreeMemory( item );
768 item = NULL;
769
770 /* Get the MEDIUM properties list */
771 result = WdmGetPinPropertyMulti(
772 parentFilter->handle,
773 pinId,
774 &KSPROPSETID_Pin,
775 KSPROPERTY_PIN_MEDIUMS,
776 &item);
777
778 if( result != paNoError )
779 goto error;
780
781 identifier = (KSIDENTIFIER*)(item+1); /* Not actually necessary... */
782
783 /* Check that at least one medium is STANDARD_DEVIO */
784 result = paUnanticipatedHostError;
785 for( i = 0; i < item->Count; i++ )
786 {
787 if( !memcmp( (void*)&identifier[i].Set, (void*)&KSMEDIUMSETID_Standard, sizeof( GUID ) ) &&
788 ( identifier[i].Id == KSMEDIUM_STANDARD_DEVIO ) )
789 {
790 result = paNoError;
791 break;
792 }
793 }
794
795 if( result != paNoError )
796 {
797 PA_DEBUG(("No standard devio\n"));
798 goto error;
799 }
800 /* Don't need mediums any more */
801 PaUtil_FreeMemory( item );
802 item = NULL;
803
804 /* Get DATARANGES */
805 result = WdmGetPinPropertyMulti(
806 parentFilter->handle,
807 pinId,
808 &KSPROPSETID_Pin,
809 KSPROPERTY_PIN_DATARANGES,
810 &pin->dataRangesItem);
811
812 if( result != paNoError )
813 goto error;
814
815 pin->dataRanges = (KSDATARANGE*)(pin->dataRangesItem +1);
816
817 /* Check that at least one datarange supports audio */
818 result = paUnanticipatedHostError;
819 dataRange = pin->dataRanges;
820 pin->maxChannels = 0;
821 pin->bestSampleRate = 0;
822 pin->formats = 0;
823 for( i = 0; i <pin->dataRangesItem->Count; i++)
824 {
825 PA_DEBUG(("DR major format %x\n",*(unsigned long*)(&(dataRange->MajorFormat))));
826 /* Check that subformat is WAVEFORMATEX, PCM or WILDCARD */
827 if( IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) ||
828 !memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_PCM, sizeof ( GUID ) ) ||
829 ( !memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof ( GUID ) ) &&
830 ( !memcmp((void*)&dataRange->MajorFormat, (void*)&KSDATAFORMAT_TYPE_AUDIO, sizeof ( GUID ) ) ) ) )
831 {
832 result = paNoError;
833 /* Record the maximum possible channels with this pin */
834 PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));
835 if( (int)((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels )
836 {
837 pin->maxChannels = ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels;
838 /*PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));*/
839 }
840 /* Record the formats (bit depths) that are supported */
841 if( ((KSDATARANGE_AUDIO*)dataRange)->MinimumBitsPerSample <= 16 )
842 {
843 pin->formats |= paInt16;
844 PA_DEBUG(("Format 16 bit supported\n"));
845 }
846 if( ((KSDATARANGE_AUDIO*)dataRange)->MaximumBitsPerSample >= 24 )
847 {
848 pin->formats |= paInt24;
849 PA_DEBUG(("Format 24 bit supported\n"));
850 }
851 if( ( pin->bestSampleRate != 48000) &&
852 (((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 48000) &&
853 (((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 48000) )
854 {
855 pin->bestSampleRate = 48000;
856 PA_DEBUG(("48kHz supported\n"));
857 }
858 else if(( pin->bestSampleRate != 48000) && ( pin->bestSampleRate != 44100 ) &&
859 (((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 44100) &&
860 (((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 44100) )
861 {
862 pin->bestSampleRate = 44100;
863 PA_DEBUG(("44.1kHz supported\n"));
864 }
865 else
866 {
867 pin->bestSampleRate = ((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency;
868 }
869 }
870 dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
871 }
872
873 if( result != paNoError )
874 goto error;
875
876 /* Get instance information */
877 result = WdmGetPinPropertySimple(
878 parentFilter->handle,
879 pinId,
880 &KSPROPSETID_Pin,
881 KSPROPERTY_PIN_CINSTANCES,
882 &pin->instances,
883 sizeof(KSPIN_CINSTANCES));
884
885 if( result != paNoError )
886 goto error;
887
888 /* Success */
889 *error = paNoError;
890 PA_DEBUG(("Pin created successfully\n"));
891 PA_LOGL_;
892 return pin;
893
894 error:
895 /*
896 Error cleanup
897 */
898 PaUtil_FreeMemory( item );
899 if( pin )
900 {
901 PaUtil_FreeMemory( pin->pinConnect );
902 PaUtil_FreeMemory( pin->dataRangesItem );
903 PaUtil_FreeMemory( pin );
904 }
905 *error = result;
906 PA_LOGL_;
907 return NULL;
908 }
909
910 /*
911 Safely free all resources associated with the pin
912 */
PinFree(PaWinWdmPin * pin)913 static void PinFree(PaWinWdmPin* pin)
914 {
915 PA_LOGE_;
916 if( pin )
917 {
918 PinClose(pin);
919 if( pin->pinConnect )
920 {
921 PaUtil_FreeMemory( pin->pinConnect );
922 }
923 if( pin->dataRangesItem )
924 {
925 PaUtil_FreeMemory( pin->dataRangesItem );
926 }
927 PaUtil_FreeMemory( pin );
928 }
929 PA_LOGL_;
930 }
931
932 /*
933 If the pin handle is open, close it
934 */
PinClose(PaWinWdmPin * pin)935 static void PinClose(PaWinWdmPin* pin)
936 {
937 PA_LOGE_;
938 if( pin == NULL )
939 {
940 PA_DEBUG(("Closing NULL pin!"));
941 PA_LOGL_;
942 return;
943 }
944 if( pin->handle != NULL )
945 {
946 PinSetState( pin, KSSTATE_PAUSE );
947 PinSetState( pin, KSSTATE_STOP );
948 CloseHandle( pin->handle );
949 pin->handle = NULL;
950 FilterRelease(pin->parentFilter);
951 }
952 PA_LOGL_;
953 }
954
955 /*
956 Set the state of this (instantiated) pin
957 */
PinSetState(PaWinWdmPin * pin,KSSTATE state)958 static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state)
959 {
960 PaError result;
961
962 PA_LOGE_;
963 if( pin == NULL )
964 return paInternalError;
965 if( pin->handle == NULL )
966 return paInternalError;
967
968 result = WdmSetPropertySimple(
969 pin->handle,
970 &KSPROPSETID_Connection,
971 KSPROPERTY_CONNECTION_STATE,
972 &state,
973 sizeof(state),
974 NULL,
975 0);
976 PA_LOGL_;
977 return result;
978 }
979
PinInstantiate(PaWinWdmPin * pin)980 static PaError PinInstantiate(PaWinWdmPin* pin)
981 {
982 PaError result;
983 unsigned long createResult;
984 KSALLOCATOR_FRAMING ksaf;
985 KSALLOCATOR_FRAMING_EX ksafex;
986
987 PA_LOGE_;
988
989 if( pin == NULL )
990 return paInternalError;
991 if(!pin->pinConnect)
992 return paInternalError;
993
994 FilterUse(pin->parentFilter);
995
996 createResult = FunctionKsCreatePin(
997 pin->parentFilter->handle,
998 pin->pinConnect,
999 GENERIC_WRITE | GENERIC_READ,
1000 &pin->handle
1001 );
1002
1003 PA_DEBUG(("Pin create result = %x\n",createResult));
1004 if( createResult != ERROR_SUCCESS )
1005 {
1006 FilterRelease(pin->parentFilter);
1007 pin->handle = NULL;
1008 return paInvalidDevice;
1009 }
1010
1011 result = WdmGetPropertySimple(
1012 pin->handle,
1013 &KSPROPSETID_Connection,
1014 KSPROPERTY_CONNECTION_ALLOCATORFRAMING,
1015 &ksaf,
1016 sizeof(ksaf),
1017 NULL,
1018 0);
1019
1020 if( result != paNoError )
1021 {
1022 result = WdmGetPropertySimple(
1023 pin->handle,
1024 &KSPROPSETID_Connection,
1025 KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX,
1026 &ksafex,
1027 sizeof(ksafex),
1028 NULL,
1029 0);
1030 if( result == paNoError )
1031 {
1032 pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize;
1033 }
1034 }
1035 else
1036 {
1037 pin->frameSize = ksaf.FrameSize;
1038 }
1039
1040 PA_LOGL_;
1041
1042 return paNoError;
1043 }
1044
1045 /* NOT USED
1046 static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state)
1047 {
1048 PaError result;
1049
1050 if( state == NULL )
1051 return paInternalError;
1052 if( pin == NULL )
1053 return paInternalError;
1054 if( pin->handle == NULL )
1055 return paInternalError;
1056
1057 result = WdmGetPropertySimple(
1058 pin->handle,
1059 KSPROPSETID_Connection,
1060 KSPROPERTY_CONNECTION_STATE,
1061 state,
1062 sizeof(KSSTATE),
1063 NULL,
1064 0);
1065
1066 return result;
1067 }
1068 */
PinSetFormat(PaWinWdmPin * pin,const WAVEFORMATEX * format)1069 static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format)
1070 {
1071 unsigned long size;
1072 void* newConnect;
1073
1074 PA_LOGE_;
1075
1076 if( pin == NULL )
1077 return paInternalError;
1078 if( format == NULL )
1079 return paInternalError;
1080
1081 size = GetWfexSize(format) + sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX) - sizeof(WAVEFORMATEX);
1082
1083 if( pin->pinConnectSize != size )
1084 {
1085 newConnect = PaUtil_AllocateMemory( size );
1086 if( newConnect == NULL )
1087 return paInsufficientMemory;
1088 memcpy( newConnect, (void*)pin->pinConnect, min(pin->pinConnectSize,size) );
1089 PaUtil_FreeMemory( pin->pinConnect );
1090 pin->pinConnect = (KSPIN_CONNECT*)newConnect;
1091 pin->pinConnectSize = size;
1092 pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)((KSPIN_CONNECT*)newConnect + 1);
1093 pin->ksDataFormatWfx->DataFormat.FormatSize = size - sizeof(KSPIN_CONNECT);
1094 }
1095
1096 memcpy( (void*)&(pin->ksDataFormatWfx->WaveFormatEx), format, GetWfexSize(format) );
1097 pin->ksDataFormatWfx->DataFormat.SampleSize = (unsigned short)(format->nChannels * (format->wBitsPerSample / 8));
1098
1099 PA_LOGL_;
1100
1101 return paNoError;
1102 }
1103
PinIsFormatSupported(PaWinWdmPin * pin,const WAVEFORMATEX * format)1104 static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format)
1105 {
1106 KSDATARANGE_AUDIO* dataRange;
1107 unsigned long count;
1108 GUID guid = DYNAMIC_GUID( DEFINE_WAVEFORMATEX_GUID(format->wFormatTag) );
1109 PaError result = paInvalidDevice;
1110
1111 PA_LOGE_;
1112
1113 if( format->wFormatTag == WAVE_FORMAT_EXTENSIBLE )
1114 {
1115 guid = ((WAVEFORMATEXTENSIBLE*)format)->SubFormat;
1116 }
1117 dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges;
1118 for(count = 0; count<pin->dataRangesItem->Count; count++)
1119 {
1120 if(( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_AUDIO,sizeof(GUID)) ) ||
1121 ( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_WILDCARD,sizeof(GUID)) ))
1122 {
1123 /* This is an audio or wildcard datarange... */
1124 if(( !memcmp(&(dataRange->DataRange.SubFormat),&KSDATAFORMAT_SUBTYPE_WILDCARD,sizeof(GUID)) ) ||
1125 ( !memcmp(&(dataRange->DataRange.SubFormat),&guid,sizeof(GUID)) ))
1126 {
1127 if(( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WILDCARD,sizeof(GUID)) ) ||
1128 ( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX,sizeof(GUID) )))
1129 {
1130
1131 PA_DEBUG(("Pin:%x, DataRange:%d\n",(void*)pin,count));
1132 PA_DEBUG(("\tFormatSize:%d, SampleSize:%d\n",dataRange->DataRange.FormatSize,dataRange->DataRange.SampleSize));
1133 PA_DEBUG(("\tMaxChannels:%d\n",dataRange->MaximumChannels));
1134 PA_DEBUG(("\tBits:%d-%d\n",dataRange->MinimumBitsPerSample,dataRange->MaximumBitsPerSample));
1135 PA_DEBUG(("\tSampleRate:%d-%d\n",dataRange->MinimumSampleFrequency,dataRange->MaximumSampleFrequency));
1136
1137 if( dataRange->MaximumChannels < format->nChannels )
1138 {
1139 result = paInvalidChannelCount;
1140 continue;
1141 }
1142 if( dataRange->MinimumBitsPerSample > format->wBitsPerSample )
1143 {
1144 result = paSampleFormatNotSupported;
1145 continue;
1146 }
1147 if( dataRange->MaximumBitsPerSample < format->wBitsPerSample )
1148 {
1149 result = paSampleFormatNotSupported;
1150 continue;
1151 }
1152 if( dataRange->MinimumSampleFrequency > format->nSamplesPerSec )
1153 {
1154 result = paInvalidSampleRate;
1155 continue;
1156 }
1157 if( dataRange->MaximumSampleFrequency < format->nSamplesPerSec )
1158 {
1159 result = paInvalidSampleRate;
1160 continue;
1161 }
1162 /* Success! */
1163 PA_LOGL_;
1164 return paNoError;
1165 }
1166 }
1167 }
1168 dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize);
1169 }
1170
1171 PA_LOGL_;
1172
1173 return result;
1174 }
1175
1176 /**
1177 * Create a new filter object
1178 */
FilterNew(TCHAR * filterName,TCHAR * friendlyName,PaError * error)1179 static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError* error)
1180 {
1181 PaWinWdmFilter* filter;
1182 PaError result;
1183 int pinId;
1184 int valid;
1185
1186
1187 /* Allocate the new filter object */
1188 filter = (PaWinWdmFilter*)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter) );
1189 if( !filter )
1190 {
1191 result = paInsufficientMemory;
1192 goto error;
1193 }
1194
1195 /* Zero the filter object - done by AllocateMemory */
1196 /* memset( (void*)filter, 0, sizeof(PaWinWdmFilter) ); */
1197
1198 /* Copy the filter name */
1199 _tcsncpy(filter->filterName, filterName, MAX_PATH);
1200
1201 /* Copy the friendly name */
1202 _tcsncpy(filter->friendlyName, friendlyName, MAX_PATH);
1203
1204 /* Open the filter handle */
1205 result = FilterUse(filter);
1206 if( result != paNoError )
1207 {
1208 goto error;
1209 }
1210
1211 /* Get pin count */
1212 result = WdmGetPinPropertySimple
1213 (
1214 filter->handle,
1215 0,
1216 &KSPROPSETID_Pin,
1217 KSPROPERTY_PIN_CTYPES,
1218 &filter->pinCount,
1219 sizeof(filter->pinCount)
1220 );
1221
1222 if( result != paNoError)
1223 {
1224 goto error;
1225 }
1226
1227 /* Allocate pointer array to hold the pins */
1228 filter->pins = (PaWinWdmPin**)PaUtil_AllocateMemory( sizeof(PaWinWdmPin*) * filter->pinCount );
1229 if( !filter->pins )
1230 {
1231 result = paInsufficientMemory;
1232 goto error;
1233 }
1234
1235 /* Create all the pins we can */
1236 filter->maxInputChannels = 0;
1237 filter->maxOutputChannels = 0;
1238 filter->bestSampleRate = 0;
1239
1240 valid = 0;
1241 for(pinId = 0; pinId < filter->pinCount; pinId++)
1242 {
1243 /* Create the pin with this Id */
1244 PaWinWdmPin* newPin;
1245 newPin = PinNew(filter, pinId, &result);
1246 if( result == paInsufficientMemory )
1247 goto error;
1248 if( newPin != NULL )
1249 {
1250 filter->pins[pinId] = newPin;
1251 valid = 1;
1252
1253 /* Get the max output channel count */
1254 if(( newPin->dataFlow == KSPIN_DATAFLOW_IN ) &&
1255 (( newPin->communication == KSPIN_COMMUNICATION_SINK) ||
1256 ( newPin->communication == KSPIN_COMMUNICATION_BOTH)))
1257 {
1258 if(newPin->maxChannels > filter->maxOutputChannels)
1259 filter->maxOutputChannels = newPin->maxChannels;
1260 filter->formats |= newPin->formats;
1261 }
1262 /* Get the max input channel count */
1263 if(( newPin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
1264 (( newPin->communication == KSPIN_COMMUNICATION_SINK) ||
1265 ( newPin->communication == KSPIN_COMMUNICATION_BOTH)))
1266 {
1267 if(newPin->maxChannels > filter->maxInputChannels)
1268 filter->maxInputChannels = newPin->maxChannels;
1269 filter->formats |= newPin->formats;
1270 }
1271
1272 if(newPin->bestSampleRate > filter->bestSampleRate)
1273 {
1274 filter->bestSampleRate = newPin->bestSampleRate;
1275 }
1276 }
1277 }
1278
1279 if(( filter->maxInputChannels == 0) && ( filter->maxOutputChannels == 0))
1280 {
1281 /* No input or output... not valid */
1282 valid = 0;
1283 }
1284
1285 if( !valid )
1286 {
1287 /* No valid pin was found on this filter so we destroy it */
1288 result = paDeviceUnavailable;
1289 goto error;
1290 }
1291
1292 /* Close the filter handle for now
1293 * It will be opened later when needed */
1294 FilterRelease(filter);
1295
1296 *error = paNoError;
1297 return filter;
1298
1299 error:
1300 /*
1301 Error cleanup
1302 */
1303 if( filter )
1304 {
1305 for( pinId = 0; pinId < filter->pinCount; pinId++ )
1306 PinFree(filter->pins[pinId]);
1307 PaUtil_FreeMemory( filter->pins );
1308 if( filter->handle )
1309 CloseHandle( filter->handle );
1310 PaUtil_FreeMemory( filter );
1311 }
1312 *error = result;
1313 return NULL;
1314 }
1315
1316 /**
1317 * Free a previously created filter
1318 */
FilterFree(PaWinWdmFilter * filter)1319 static void FilterFree(PaWinWdmFilter* filter)
1320 {
1321 int pinId;
1322 PA_LOGL_;
1323 if( filter )
1324 {
1325 for( pinId = 0; pinId < filter->pinCount; pinId++ )
1326 PinFree(filter->pins[pinId]);
1327 PaUtil_FreeMemory( filter->pins );
1328 if( filter->handle )
1329 CloseHandle( filter->handle );
1330 PaUtil_FreeMemory( filter );
1331 }
1332 PA_LOGE_;
1333 }
1334
1335 /**
1336 * Reopen the filter handle if necessary so it can be used
1337 **/
FilterUse(PaWinWdmFilter * filter)1338 static PaError FilterUse(PaWinWdmFilter* filter)
1339 {
1340 assert( filter );
1341
1342 PA_LOGE_;
1343 if( filter->handle == NULL )
1344 {
1345 /* Open the filter */
1346 filter->handle = CreateFile(
1347 filter->filterName,
1348 GENERIC_READ | GENERIC_WRITE,
1349 0,
1350 NULL,
1351 OPEN_EXISTING,
1352 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
1353 NULL);
1354
1355 if( filter->handle == NULL )
1356 {
1357 return paDeviceUnavailable;
1358 }
1359 }
1360 filter->usageCount++;
1361 PA_LOGL_;
1362 return paNoError;
1363 }
1364
1365 /**
1366 * Release the filter handle if nobody is using it
1367 **/
FilterRelease(PaWinWdmFilter * filter)1368 static void FilterRelease(PaWinWdmFilter* filter)
1369 {
1370 assert( filter );
1371 assert( filter->usageCount > 0 );
1372
1373 PA_LOGE_;
1374 filter->usageCount--;
1375 if( filter->usageCount == 0 )
1376 {
1377 if( filter->handle != NULL )
1378 {
1379 CloseHandle( filter->handle );
1380 filter->handle = NULL;
1381 }
1382 }
1383 PA_LOGL_;
1384 }
1385
1386 /**
1387 * Create a render (playback) Pin using the supplied format
1388 **/
FilterCreateRenderPin(PaWinWdmFilter * filter,const WAVEFORMATEX * wfex,PaError * error)1389 static PaWinWdmPin* FilterCreateRenderPin(PaWinWdmFilter* filter,
1390 const WAVEFORMATEX* wfex,
1391 PaError* error)
1392 {
1393 PaError result;
1394 PaWinWdmPin* pin;
1395
1396 assert( filter );
1397
1398 pin = FilterFindViableRenderPin(filter,wfex,&result);
1399 if(!pin)
1400 {
1401 goto error;
1402 }
1403 result = PinSetFormat(pin,wfex);
1404 if( result != paNoError )
1405 {
1406 goto error;
1407 }
1408 result = PinInstantiate(pin);
1409 if( result != paNoError )
1410 {
1411 goto error;
1412 }
1413
1414 *error = paNoError;
1415 return pin;
1416
1417 error:
1418 *error = result;
1419 return NULL;
1420 }
1421
1422 /**
1423 * Find a pin that supports the given format
1424 **/
FilterFindViableRenderPin(PaWinWdmFilter * filter,const WAVEFORMATEX * wfex,PaError * error)1425 static PaWinWdmPin* FilterFindViableRenderPin(PaWinWdmFilter* filter,
1426 const WAVEFORMATEX* wfex,
1427 PaError* error)
1428 {
1429 int pinId;
1430 PaWinWdmPin* pin;
1431 PaError result = paDeviceUnavailable;
1432 *error = paNoError;
1433
1434 assert( filter );
1435
1436 for( pinId = 0; pinId<filter->pinCount; pinId++ )
1437 {
1438 pin = filter->pins[pinId];
1439 if( pin != NULL )
1440 {
1441 if(( pin->dataFlow == KSPIN_DATAFLOW_IN ) &&
1442 (( pin->communication == KSPIN_COMMUNICATION_SINK) ||
1443 ( pin->communication == KSPIN_COMMUNICATION_BOTH)))
1444 {
1445 result = PinIsFormatSupported( pin, wfex );
1446 if( result == paNoError )
1447 {
1448 return pin;
1449 }
1450 }
1451 }
1452 }
1453
1454 *error = result;
1455 return NULL;
1456 }
1457
1458 /**
1459 * Check if there is a pin that should playback
1460 * with the supplied format
1461 **/
FilterCanCreateRenderPin(PaWinWdmFilter * filter,const WAVEFORMATEX * wfex)1462 static PaError FilterCanCreateRenderPin(PaWinWdmFilter* filter,
1463 const WAVEFORMATEX* wfex)
1464 {
1465 PaWinWdmPin* pin;
1466 PaError result;
1467
1468 assert ( filter );
1469
1470 pin = FilterFindViableRenderPin(filter,wfex,&result);
1471 /* result will be paNoError if pin found
1472 * or else an error code indicating what is wrong with the format
1473 **/
1474 return result;
1475 }
1476
1477 /**
1478 * Create a capture (record) Pin using the supplied format
1479 **/
FilterCreateCapturePin(PaWinWdmFilter * filter,const WAVEFORMATEX * wfex,PaError * error)1480 static PaWinWdmPin* FilterCreateCapturePin(PaWinWdmFilter* filter,
1481 const WAVEFORMATEX* wfex,
1482 PaError* error)
1483 {
1484 PaError result;
1485 PaWinWdmPin* pin;
1486
1487 assert( filter );
1488
1489 pin = FilterFindViableCapturePin(filter,wfex,&result);
1490 if(!pin)
1491 {
1492 goto error;
1493 }
1494
1495 result = PinSetFormat(pin,wfex);
1496 if( result != paNoError )
1497 {
1498 goto error;
1499 }
1500
1501 result = PinInstantiate(pin);
1502 if( result != paNoError )
1503 {
1504 goto error;
1505 }
1506
1507 *error = paNoError;
1508 return pin;
1509
1510 error:
1511 *error = result;
1512 return NULL;
1513 }
1514
1515 /**
1516 * Find a capture pin that supports the given format
1517 **/
FilterFindViableCapturePin(PaWinWdmFilter * filter,const WAVEFORMATEX * wfex,PaError * error)1518 static PaWinWdmPin* FilterFindViableCapturePin(PaWinWdmFilter* filter,
1519 const WAVEFORMATEX* wfex,
1520 PaError* error)
1521 {
1522 int pinId;
1523 PaWinWdmPin* pin;
1524 PaError result = paDeviceUnavailable;
1525 *error = paNoError;
1526
1527 assert( filter );
1528
1529 for( pinId = 0; pinId<filter->pinCount; pinId++ )
1530 {
1531 pin = filter->pins[pinId];
1532 if( pin != NULL )
1533 {
1534 if(( pin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
1535 (( pin->communication == KSPIN_COMMUNICATION_SINK) ||
1536 ( pin->communication == KSPIN_COMMUNICATION_BOTH)))
1537 {
1538 result = PinIsFormatSupported( pin, wfex );
1539 if( result == paNoError )
1540 {
1541 return pin;
1542 }
1543 }
1544 }
1545 }
1546
1547 *error = result;
1548 return NULL;
1549 }
1550
1551 /**
1552 * Check if there is a pin that should playback
1553 * with the supplied format
1554 **/
FilterCanCreateCapturePin(PaWinWdmFilter * filter,const WAVEFORMATEX * wfex)1555 static PaError FilterCanCreateCapturePin(PaWinWdmFilter* filter,
1556 const WAVEFORMATEX* wfex)
1557 {
1558 PaWinWdmPin* pin;
1559 PaError result;
1560
1561 assert ( filter );
1562
1563 pin = FilterFindViableCapturePin(filter,wfex,&result);
1564 /* result will be paNoError if pin found
1565 * or else an error code indicating what is wrong with the format
1566 **/
1567 return result;
1568 }
1569
1570 /**
1571 * Build the list of available filters
1572 * Use the SetupDi API to enumerate all devices in the KSCATEGORY_AUDIO which
1573 * have a KSCATEGORY_RENDER or KSCATEGORY_CAPTURE alias. For each of these
1574 * devices initialise a PaWinWdmFilter structure by calling our NewFilter()
1575 * function. We enumerate devices twice, once to count how many there are,
1576 * and once to initialize the PaWinWdmFilter structures.
1577 */
BuildFilterList(PaWinWdmHostApiRepresentation * wdmHostApi)1578 static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
1579 {
1580 PaError result = paNoError;
1581 HDEVINFO handle = NULL;
1582 int device;
1583 int invalidDevices;
1584 int slot;
1585 SP_DEVICE_INTERFACE_DATA interfaceData;
1586 SP_DEVICE_INTERFACE_DATA aliasData;
1587 SP_DEVINFO_DATA devInfoData;
1588 int noError;
1589 const int sizeInterface = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR));
1590 unsigned char interfaceDetailsArray[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR))];
1591 SP_DEVICE_INTERFACE_DETAIL_DATA* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA*)interfaceDetailsArray;
1592 TCHAR friendlyName[MAX_PATH];
1593 HKEY hkey;
1594 DWORD sizeFriendlyName;
1595 DWORD type;
1596 PaWinWdmFilter* newFilter;
1597 GUID* category = (GUID*)&KSCATEGORY_AUDIO;
1598 GUID* alias_render = (GUID*)&KSCATEGORY_RENDER;
1599 GUID* alias_capture = (GUID*)&KSCATEGORY_CAPTURE;
1600 DWORD hasAlias;
1601
1602 PA_LOGE_;
1603
1604 devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
1605
1606 /* Open a handle to search for devices (filters) */
1607 handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
1608 if( handle == NULL )
1609 {
1610 return paUnanticipatedHostError;
1611 }
1612 PA_DEBUG(("Setup called\n"));
1613
1614 /* First let's count the number of devices so we can allocate a list */
1615 invalidDevices = 0;
1616 for( device = 0;;device++ )
1617 {
1618 interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1619 interfaceData.Reserved = 0;
1620 aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1621 aliasData.Reserved = 0;
1622 noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
1623 PA_DEBUG(("Enum called\n"));
1624 if( !noError )
1625 break; /* No more devices */
1626
1627 /* Check this one has the render or capture alias */
1628 hasAlias = 0;
1629 noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
1630 PA_DEBUG(("noError = %d\n",noError));
1631 if(noError)
1632 {
1633 if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1634 {
1635 PA_DEBUG(("Device %d has render alias\n",device));
1636 hasAlias |= 1; /* Has render alias */
1637 }
1638 else
1639 {
1640 PA_DEBUG(("Device %d has no render alias\n",device));
1641 }
1642 }
1643 noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
1644 if(noError)
1645 {
1646 if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1647 {
1648 PA_DEBUG(("Device %d has capture alias\n",device));
1649 hasAlias |= 2; /* Has capture alias */
1650 }
1651 else
1652 {
1653 PA_DEBUG(("Device %d has no capture alias\n",device));
1654 }
1655 }
1656 if(!hasAlias)
1657 invalidDevices++; /* This was not a valid capture or render audio device */
1658
1659 }
1660 /* Remember how many there are */
1661 wdmHostApi->filterCount = device-invalidDevices;
1662
1663 PA_DEBUG(("Interfaces found: %d\n",device-invalidDevices));
1664
1665 /* Now allocate the list of pointers to devices */
1666 wdmHostApi->filters = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * device );
1667 if( !wdmHostApi->filters )
1668 {
1669 if(handle != NULL)
1670 SetupDiDestroyDeviceInfoList(handle);
1671 return paInsufficientMemory;
1672 }
1673
1674 /* Now create filter objects for each interface found */
1675 slot = 0;
1676 for( device = 0;;device++ )
1677 {
1678 interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1679 interfaceData.Reserved = 0;
1680 aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1681 aliasData.Reserved = 0;
1682 devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1683 devInfoData.Reserved = 0;
1684
1685 noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
1686 if( !noError )
1687 break; /* No more devices */
1688
1689 /* Check this one has the render or capture alias */
1690 hasAlias = 0;
1691 noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
1692 if(noError)
1693 {
1694 if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1695 {
1696 PA_DEBUG(("Device %d has render alias\n",device));
1697 hasAlias |= 1; /* Has render alias */
1698 }
1699 }
1700 noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
1701 if(noError)
1702 {
1703 if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1704 {
1705 PA_DEBUG(("Device %d has capture alias\n",device));
1706 hasAlias |= 2; /* Has capture alias */
1707 }
1708 }
1709 if(!hasAlias)
1710 continue; /* This was not a valid capture or render audio device */
1711
1712 noError = SetupDiGetDeviceInterfaceDetail(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
1713 if( noError )
1714 {
1715 /* Try to get the "friendly name" for this interface */
1716 sizeFriendlyName = sizeof(friendlyName);
1717 /* Fix contributed by Ben Allison
1718 * Removed KEY_SET_VALUE from flags on following call
1719 * as its causes failure when running without admin rights
1720 * and it was not required */
1721 hkey=SetupDiOpenDeviceInterfaceRegKey(handle,&interfaceData,0,KEY_QUERY_VALUE);
1722 if(hkey!=INVALID_HANDLE_VALUE)
1723 {
1724 noError = RegQueryValueEx(hkey,TEXT("FriendlyName"),0,&type,(BYTE*)friendlyName,&sizeFriendlyName);
1725 if( noError == ERROR_SUCCESS )
1726 {
1727 PA_DEBUG(("Interface %d, Name: %s\n",device,friendlyName));
1728 RegCloseKey(hkey);
1729 }
1730 else
1731 {
1732 friendlyName[0] = 0;
1733 }
1734 }
1735 newFilter = FilterNew(devInterfaceDetails->DevicePath,friendlyName,&result);
1736 if( result == paNoError )
1737 {
1738 PA_DEBUG(("Filter created\n"));
1739 wdmHostApi->filters[slot] = newFilter;
1740 slot++;
1741 }
1742 else
1743 {
1744 PA_DEBUG(("Filter NOT created\n"));
1745 /* As there are now less filters than we initially thought
1746 * we must reduce the count by one */
1747 wdmHostApi->filterCount--;
1748 }
1749 }
1750 }
1751
1752 /* Clean up */
1753 if(handle != NULL)
1754 SetupDiDestroyDeviceInfoList(handle);
1755
1756 return paNoError;
1757 }
1758
PaWinWdm_Initialize(PaUtilHostApiRepresentation ** hostApi,PaHostApiIndex hostApiIndex)1759 PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
1760 {
1761 PaError result = paNoError;
1762 int i, deviceCount;
1763 PaWinWdmHostApiRepresentation *wdmHostApi;
1764 PaWinWdmDeviceInfo *deviceInfoArray;
1765 PaWinWdmFilter* pFilter;
1766 PaWinWdmDeviceInfo *wdmDeviceInfo;
1767 PaDeviceInfo *deviceInfo;
1768
1769 PA_LOGE_;
1770
1771 /*
1772 Attempt to load the KSUSER.DLL without which we cannot create pins
1773 We will unload this on termination
1774 */
1775 if(DllKsUser == NULL)
1776 {
1777 DllKsUser = LoadLibrary(TEXT("ksuser.dll"));
1778 if(DllKsUser == NULL)
1779 goto error;
1780 }
1781
1782 FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin");
1783 if(FunctionKsCreatePin == NULL)
1784 goto error;
1785
1786 wdmHostApi = (PaWinWdmHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWdmHostApiRepresentation) );
1787 if( !wdmHostApi )
1788 {
1789 result = paInsufficientMemory;
1790 goto error;
1791 }
1792
1793 wdmHostApi->allocations = PaUtil_CreateAllocationGroup();
1794 if( !wdmHostApi->allocations )
1795 {
1796 result = paInsufficientMemory;
1797 goto error;
1798 }
1799
1800 result = BuildFilterList( wdmHostApi );
1801 if( result != paNoError )
1802 {
1803 goto error;
1804 }
1805 deviceCount = wdmHostApi->filterCount;
1806
1807 *hostApi = &wdmHostApi->inheritedHostApiRep;
1808 (*hostApi)->info.structVersion = 1;
1809 (*hostApi)->info.type = paWDMKS;
1810 (*hostApi)->info.name = "Windows WDM-KS";
1811 (*hostApi)->info.defaultInputDevice = paNoDevice;
1812 (*hostApi)->info.defaultOutputDevice = paNoDevice;
1813
1814 if( deviceCount > 0 )
1815 {
1816 (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1817 wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo*) * deviceCount );
1818 if( !(*hostApi)->deviceInfos )
1819 {
1820 result = paInsufficientMemory;
1821 goto error;
1822 }
1823
1824 /* allocate all device info structs in a contiguous block */
1825 deviceInfoArray = (PaWinWdmDeviceInfo*)PaUtil_GroupAllocateMemory(
1826 wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo) * deviceCount );
1827 if( !deviceInfoArray )
1828 {
1829 result = paInsufficientMemory;
1830 goto error;
1831 }
1832
1833 for( i=0; i < deviceCount; ++i )
1834 {
1835 wdmDeviceInfo = &deviceInfoArray[i];
1836 deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo;
1837 pFilter = wdmHostApi->filters[i];
1838 if( pFilter == NULL )
1839 continue;
1840 wdmDeviceInfo->filter = pFilter;
1841 deviceInfo->structVersion = 2;
1842 deviceInfo->hostApi = hostApiIndex;
1843 deviceInfo->name = (char*)pFilter->friendlyName;
1844 PA_DEBUG(("Device found name: %s\n",(char*)pFilter->friendlyName));
1845 deviceInfo->maxInputChannels = pFilter->maxInputChannels;
1846 if(deviceInfo->maxInputChannels > 0)
1847 {
1848 /* Set the default input device to the first device we find with
1849 * more than zero input channels
1850 **/
1851 if((*hostApi)->info.defaultInputDevice == paNoDevice)
1852 {
1853 (*hostApi)->info.defaultInputDevice = i;
1854 }
1855 }
1856
1857 deviceInfo->maxOutputChannels = pFilter->maxOutputChannels;
1858 if(deviceInfo->maxOutputChannels > 0)
1859 {
1860 /* Set the default output device to the first device we find with
1861 * more than zero output channels
1862 **/
1863 if((*hostApi)->info.defaultOutputDevice == paNoDevice)
1864 {
1865 (*hostApi)->info.defaultOutputDevice = i;
1866 }
1867 }
1868
1869 /* These low values are not very useful because
1870 * a) The lowest latency we end up with can depend on many factors such
1871 * as the device buffer sizes/granularities, sample rate, channels and format
1872 * b) We cannot know the device buffer sizes until we try to open/use it at
1873 * a particular setting
1874 * So: we give 512x48000Hz frames as the default low input latency
1875 **/
1876 deviceInfo->defaultLowInputLatency = (512.0/48000.0);
1877 deviceInfo->defaultLowOutputLatency = (512.0/48000.0);
1878 deviceInfo->defaultHighInputLatency = (4096.0/48000.0);
1879 deviceInfo->defaultHighOutputLatency = (4096.0/48000.0);
1880 deviceInfo->defaultSampleRate = (double)(pFilter->bestSampleRate);
1881
1882 (*hostApi)->deviceInfos[i] = deviceInfo;
1883 }
1884 }
1885
1886 (*hostApi)->info.deviceCount = deviceCount;
1887
1888 (*hostApi)->Terminate = Terminate;
1889 (*hostApi)->OpenStream = OpenStream;
1890 (*hostApi)->IsFormatSupported = IsFormatSupported;
1891
1892 PaUtil_InitializeStreamInterface( &wdmHostApi->callbackStreamInterface, CloseStream, StartStream,
1893 StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1894 GetStreamTime, GetStreamCpuLoad,
1895 PaUtil_DummyRead, PaUtil_DummyWrite,
1896 PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1897
1898 PaUtil_InitializeStreamInterface( &wdmHostApi->blockingStreamInterface, CloseStream, StartStream,
1899 StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1900 GetStreamTime, PaUtil_DummyGetCpuLoad,
1901 ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1902
1903 PA_LOGL_;
1904 return result;
1905
1906 error:
1907 if( DllKsUser != NULL )
1908 {
1909 FreeLibrary( DllKsUser );
1910 DllKsUser = NULL;
1911 }
1912
1913 if( wdmHostApi )
1914 {
1915 PaUtil_FreeMemory( wdmHostApi->filters );
1916 if( wdmHostApi->allocations )
1917 {
1918 PaUtil_FreeAllAllocations( wdmHostApi->allocations );
1919 PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
1920 }
1921 PaUtil_FreeMemory( wdmHostApi );
1922 }
1923 PA_LOGL_;
1924 return result;
1925 }
1926
1927
Terminate(struct PaUtilHostApiRepresentation * hostApi)1928 static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1929 {
1930 PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
1931 int i;
1932 PA_LOGE_;
1933
1934 if( wdmHostApi->filters )
1935 {
1936 for( i=0; i<wdmHostApi->filterCount; i++)
1937 {
1938 if( wdmHostApi->filters[i] != NULL )
1939 {
1940 FilterFree( wdmHostApi->filters[i] );
1941 wdmHostApi->filters[i] = NULL;
1942 }
1943 }
1944 }
1945 PaUtil_FreeMemory( wdmHostApi->filters );
1946 if( wdmHostApi->allocations )
1947 {
1948 PaUtil_FreeAllAllocations( wdmHostApi->allocations );
1949 PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
1950 }
1951 PaUtil_FreeMemory( wdmHostApi );
1952 PA_LOGL_;
1953 }
1954
FillWFEXT(WAVEFORMATEXTENSIBLE * pwfext,PaSampleFormat sampleFormat,double sampleRate,int channelCount)1955 static void FillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
1956 {
1957 PA_LOGE_;
1958 PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
1959 PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
1960 PA_DEBUG(( "chanelCount = %d\n", channelCount ));
1961
1962 pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1963 pwfext->Format.nChannels = channelCount;
1964 pwfext->Format.nSamplesPerSec = (int)sampleRate;
1965 if(channelCount == 1)
1966 pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
1967 else
1968 pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
1969 if(sampleFormat == paFloat32)
1970 {
1971 pwfext->Format.nBlockAlign = channelCount * 4;
1972 pwfext->Format.wBitsPerSample = 32;
1973 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1974 pwfext->Samples.wValidBitsPerSample = 32;
1975 pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1976 }
1977 else if(sampleFormat == paInt32)
1978 {
1979 pwfext->Format.nBlockAlign = channelCount * 4;
1980 pwfext->Format.wBitsPerSample = 32;
1981 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1982 pwfext->Samples.wValidBitsPerSample = 32;
1983 pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1984 }
1985 else if(sampleFormat == paInt24)
1986 {
1987 pwfext->Format.nBlockAlign = channelCount * 3;
1988 pwfext->Format.wBitsPerSample = 24;
1989 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1990 pwfext->Samples.wValidBitsPerSample = 24;
1991 pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1992 }
1993 else if(sampleFormat == paInt16)
1994 {
1995 pwfext->Format.nBlockAlign = channelCount * 2;
1996 pwfext->Format.wBitsPerSample = 16;
1997 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1998 pwfext->Samples.wValidBitsPerSample = 16;
1999 pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
2000 }
2001 pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
2002
2003 PA_LOGL_;
2004 }
2005
IsFormatSupported(struct PaUtilHostApiRepresentation * hostApi,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate)2006 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
2007 const PaStreamParameters *inputParameters,
2008 const PaStreamParameters *outputParameters,
2009 double sampleRate )
2010 {
2011 int inputChannelCount, outputChannelCount;
2012 PaSampleFormat inputSampleFormat, outputSampleFormat;
2013 PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
2014 PaWinWdmFilter* pFilter;
2015 int result = paFormatIsSupported;
2016 WAVEFORMATEXTENSIBLE wfx;
2017
2018 PA_LOGE_;
2019
2020 if( inputParameters )
2021 {
2022 inputChannelCount = inputParameters->channelCount;
2023 inputSampleFormat = inputParameters->sampleFormat;
2024
2025 /* all standard sample formats are supported by the buffer adapter,
2026 this implementation doesn't support any custom sample formats */
2027 if( inputSampleFormat & paCustomFormat )
2028 return paSampleFormatNotSupported;
2029
2030 /* unless alternate device specification is supported, reject the use of
2031 paUseHostApiSpecificDeviceSpecification */
2032
2033 if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
2034 return paInvalidDevice;
2035
2036 /* check that input device can support inputChannelCount */
2037 if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
2038 return paInvalidChannelCount;
2039
2040 /* validate inputStreamInfo */
2041 if( inputParameters->hostApiSpecificStreamInfo )
2042 return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2043
2044 /* Check that the input format is supported */
2045 FillWFEXT(&wfx,paInt16,sampleRate,inputChannelCount);
2046
2047 pFilter = wdmHostApi->filters[inputParameters->device];
2048 result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
2049 if( result != paNoError )
2050 {
2051 /* Try a WAVE_FORMAT_PCM instead */
2052 wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2053 wfx.Format.cbSize = 0;
2054 wfx.Samples.wValidBitsPerSample = 0;
2055 wfx.dwChannelMask = 0;
2056 wfx.SubFormat = GUID_NULL;
2057 result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
2058 if( result != paNoError )
2059 return result;
2060 }
2061 }
2062 else
2063 {
2064 inputChannelCount = 0;
2065 }
2066
2067 if( outputParameters )
2068 {
2069 outputChannelCount = outputParameters->channelCount;
2070 outputSampleFormat = outputParameters->sampleFormat;
2071
2072 /* all standard sample formats are supported by the buffer adapter,
2073 this implementation doesn't support any custom sample formats */
2074 if( outputSampleFormat & paCustomFormat )
2075 return paSampleFormatNotSupported;
2076
2077 /* unless alternate device specification is supported, reject the use of
2078 paUseHostApiSpecificDeviceSpecification */
2079
2080 if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
2081 return paInvalidDevice;
2082
2083 /* check that output device can support outputChannelCount */
2084 if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
2085 return paInvalidChannelCount;
2086
2087 /* validate outputStreamInfo */
2088 if( outputParameters->hostApiSpecificStreamInfo )
2089 return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2090
2091 /* Check that the output format is supported */
2092 FillWFEXT(&wfx,paInt16,sampleRate,outputChannelCount);
2093
2094 pFilter = wdmHostApi->filters[outputParameters->device];
2095 result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
2096 if( result != paNoError )
2097 {
2098 /* Try a WAVE_FORMAT_PCM instead */
2099 wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2100 wfx.Format.cbSize = 0;
2101 wfx.Samples.wValidBitsPerSample = 0;
2102 wfx.dwChannelMask = 0;
2103 wfx.SubFormat = GUID_NULL;
2104 result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
2105 if( result != paNoError )
2106 return result;
2107 }
2108
2109 }
2110 else
2111 {
2112 outputChannelCount = 0;
2113 }
2114
2115 /*
2116 IMPLEMENT ME:
2117
2118 - if a full duplex stream is requested, check that the combination
2119 of input and output parameters is supported if necessary
2120
2121 - check that the device supports sampleRate
2122
2123 Because the buffer adapter handles conversion between all standard
2124 sample formats, the following checks are only required if paCustomFormat
2125 is implemented, or under some other unusual conditions.
2126
2127 - check that input device can support inputSampleFormat, or that
2128 we have the capability to convert from inputSampleFormat to
2129 a native format
2130
2131 - check that output device can support outputSampleFormat, or that
2132 we have the capability to convert from outputSampleFormat to
2133 a native format
2134 */
2135 if((inputChannelCount == 0)&&(outputChannelCount == 0))
2136 result = paSampleFormatNotSupported; /* Not right error */
2137
2138 PA_LOGL_;
2139 return result;
2140 }
2141
2142 /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
2143
OpenStream(struct PaUtilHostApiRepresentation * hostApi,PaStream ** s,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate,unsigned long framesPerBuffer,PaStreamFlags streamFlags,PaStreamCallback * streamCallback,void * userData)2144 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
2145 PaStream** s,
2146 const PaStreamParameters *inputParameters,
2147 const PaStreamParameters *outputParameters,
2148 double sampleRate,
2149 unsigned long framesPerBuffer,
2150 PaStreamFlags streamFlags,
2151 PaStreamCallback *streamCallback,
2152 void *userData )
2153 {
2154 PaError result = paNoError;
2155 PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
2156 PaWinWdmStream *stream = 0;
2157 /* unsigned long framesPerHostBuffer; these may not be equivalent for all implementations */
2158 PaSampleFormat inputSampleFormat, outputSampleFormat;
2159 PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
2160 int userInputChannels,userOutputChannels;
2161 int size;
2162 PaWinWdmFilter* pFilter;
2163 WAVEFORMATEXTENSIBLE wfx;
2164
2165 PA_LOGE_;
2166 PA_DEBUG(("OpenStream:sampleRate = %f\n",sampleRate));
2167 PA_DEBUG(("OpenStream:framesPerBuffer = %lu\n",framesPerBuffer));
2168
2169 if( inputParameters )
2170 {
2171 userInputChannels = inputParameters->channelCount;
2172 inputSampleFormat = inputParameters->sampleFormat;
2173
2174 /* unless alternate device specification is supported, reject the use of
2175 paUseHostApiSpecificDeviceSpecification */
2176
2177 if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
2178 return paInvalidDevice;
2179
2180 /* check that input device can support stream->userInputChannels */
2181 if( userInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
2182 return paInvalidChannelCount;
2183
2184 /* validate inputStreamInfo */
2185 if( inputParameters->hostApiSpecificStreamInfo )
2186 return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2187
2188 }
2189 else
2190 {
2191 userInputChannels = 0;
2192 inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
2193 }
2194
2195 if( outputParameters )
2196 {
2197 userOutputChannels = outputParameters->channelCount;
2198 outputSampleFormat = outputParameters->sampleFormat;
2199
2200 /* unless alternate device specification is supported, reject the use of
2201 paUseHostApiSpecificDeviceSpecification */
2202
2203 if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
2204 return paInvalidDevice;
2205
2206 /* check that output device can support stream->userInputChannels */
2207 if( userOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
2208 return paInvalidChannelCount;
2209
2210 /* validate outputStreamInfo */
2211 if( outputParameters->hostApiSpecificStreamInfo )
2212 return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2213
2214 }
2215 else
2216 {
2217 userOutputChannels = 0;
2218 outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
2219 }
2220
2221 /* validate platform specific flags */
2222 if( (streamFlags & paPlatformSpecificFlags) != 0 )
2223 return paInvalidFlag; /* unexpected platform specific flag */
2224
2225 stream = (PaWinWdmStream*)PaUtil_AllocateMemory( sizeof(PaWinWdmStream) );
2226 if( !stream )
2227 {
2228 result = paInsufficientMemory;
2229 goto error;
2230 }
2231 /* Zero the stream object */
2232 /* memset((void*)stream,0,sizeof(PaWinWdmStream)); */
2233
2234 if( streamCallback )
2235 {
2236 PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2237 &wdmHostApi->callbackStreamInterface, streamCallback, userData );
2238 }
2239 else
2240 {
2241 PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2242 &wdmHostApi->blockingStreamInterface, streamCallback, userData );
2243 }
2244
2245 PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
2246
2247 /* Instantiate the input pin if necessary */
2248 if(userInputChannels > 0)
2249 {
2250 result = paSampleFormatNotSupported;
2251 pFilter = wdmHostApi->filters[inputParameters->device];
2252 stream->userInputChannels = userInputChannels;
2253
2254 if(((inputSampleFormat & ~paNonInterleaved) & pFilter->formats) != 0)
2255 { /* inputSampleFormat is supported, so try to use it */
2256 hostInputSampleFormat = inputSampleFormat;
2257 FillWFEXT(&wfx, hostInputSampleFormat, sampleRate, stream->userInputChannels);
2258 stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
2259 stream->recordingPin = FilterCreateCapturePin(pFilter, (const WAVEFORMATEX*)&wfx, &result);
2260 stream->deviceInputChannels = stream->userInputChannels;
2261 }
2262
2263 if(result != paNoError)
2264 { /* Search through all PaSampleFormats to find one that works */
2265 hostInputSampleFormat = paFloat32;
2266
2267 do {
2268 FillWFEXT(&wfx, hostInputSampleFormat, sampleRate, stream->userInputChannels);
2269 stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
2270 stream->recordingPin = FilterCreateCapturePin(pFilter, (const WAVEFORMATEX*)&wfx, &result);
2271 stream->deviceInputChannels = stream->userInputChannels;
2272
2273 if(stream->recordingPin == NULL) result = paSampleFormatNotSupported;
2274 if(result != paNoError) hostInputSampleFormat <<= 1;
2275 }
2276 while(result != paNoError && hostInputSampleFormat <= paUInt8);
2277 }
2278
2279 if(result != paNoError)
2280 { /* None of the PaSampleFormats worked. Set the hostInputSampleFormat to the best fit
2281 * and try a PCM format.
2282 **/
2283 hostInputSampleFormat =
2284 PaUtil_SelectClosestAvailableFormat( pFilter->formats, inputSampleFormat );
2285
2286 /* Try a WAVE_FORMAT_PCM instead */
2287 wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2288 wfx.Format.cbSize = 0;
2289 wfx.Samples.wValidBitsPerSample = 0;
2290 wfx.dwChannelMask = 0;
2291 wfx.SubFormat = GUID_NULL;
2292 stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
2293 if(stream->recordingPin == NULL) result = paSampleFormatNotSupported;
2294 }
2295
2296 if( result != paNoError )
2297 {
2298 /* Some or all KS devices can only handle the exact number of channels
2299 * they specify. But PortAudio clients expect to be able to
2300 * at least specify mono I/O on a multi-channel device
2301 * If this is the case, then we will do the channel mapping internally
2302 **/
2303 if( stream->userInputChannels < pFilter->maxInputChannels )
2304 {
2305 FillWFEXT(&wfx,hostInputSampleFormat,sampleRate,pFilter->maxInputChannels);
2306 stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
2307 stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
2308 stream->deviceInputChannels = pFilter->maxInputChannels;
2309
2310 if( result != paNoError )
2311 {
2312 /* Try a WAVE_FORMAT_PCM instead */
2313 wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2314 wfx.Format.cbSize = 0;
2315 wfx.Samples.wValidBitsPerSample = 0;
2316 wfx.dwChannelMask = 0;
2317 wfx.SubFormat = GUID_NULL;
2318 stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
2319 }
2320 }
2321 }
2322
2323 if(stream->recordingPin == NULL)
2324 {
2325 goto error;
2326 }
2327
2328 switch(hostInputSampleFormat)
2329 {
2330 case paInt16: stream->inputSampleSize = 2; break;
2331 case paInt24: stream->inputSampleSize = 3; break;
2332 case paInt32:
2333 case paFloat32: stream->inputSampleSize = 4; break;
2334 }
2335
2336 stream->recordingPin->frameSize /= stream->bytesPerInputFrame;
2337 PA_DEBUG(("Pin output frames: %d\n",stream->recordingPin->frameSize));
2338 }
2339 else
2340 {
2341 stream->recordingPin = NULL;
2342 stream->bytesPerInputFrame = 0;
2343 }
2344
2345 /* Instantiate the output pin if necessary */
2346 if(userOutputChannels > 0)
2347 {
2348 result = paSampleFormatNotSupported;
2349 pFilter = wdmHostApi->filters[outputParameters->device];
2350 stream->userOutputChannels = userOutputChannels;
2351
2352 if(((outputSampleFormat & ~paNonInterleaved) & pFilter->formats) != 0)
2353 {
2354 hostOutputSampleFormat = outputSampleFormat;
2355 FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels);
2356 stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
2357 stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
2358 stream->deviceOutputChannels = stream->userOutputChannels;
2359 }
2360
2361 if(result != paNoError)
2362 {
2363 hostOutputSampleFormat = paFloat32;
2364
2365 do {
2366 FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels);
2367 stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
2368 stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
2369 stream->deviceOutputChannels = stream->userOutputChannels;
2370
2371 if(stream->playbackPin == NULL) result = paSampleFormatNotSupported;
2372 if(result != paNoError) hostOutputSampleFormat <<= 1;
2373 }
2374 while(result != paNoError && hostOutputSampleFormat <= paUInt8);
2375 }
2376
2377 if(result != paNoError)
2378 {
2379 hostOutputSampleFormat =
2380 PaUtil_SelectClosestAvailableFormat( pFilter->formats, outputSampleFormat );
2381
2382 /* Try a WAVE_FORMAT_PCM instead */
2383 wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2384 wfx.Format.cbSize = 0;
2385 wfx.Samples.wValidBitsPerSample = 0;
2386 wfx.dwChannelMask = 0;
2387 wfx.SubFormat = GUID_NULL;
2388 stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
2389 if(stream->playbackPin == NULL) result = paSampleFormatNotSupported;
2390 }
2391
2392 if( result != paNoError )
2393 {
2394 /* Some or all KS devices can only handle the exact number of channels
2395 * they specify. But PortAudio clients expect to be able to
2396 * at least specify mono I/O on a multi-channel device
2397 * If this is the case, then we will do the channel mapping internally
2398 **/
2399 if( stream->userOutputChannels < pFilter->maxOutputChannels )
2400 {
2401 FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,pFilter->maxOutputChannels);
2402 stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
2403 stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
2404 stream->deviceOutputChannels = pFilter->maxOutputChannels;
2405 if( result != paNoError )
2406 {
2407 /* Try a WAVE_FORMAT_PCM instead */
2408 wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2409 wfx.Format.cbSize = 0;
2410 wfx.Samples.wValidBitsPerSample = 0;
2411 wfx.dwChannelMask = 0;
2412 wfx.SubFormat = GUID_NULL;
2413 stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
2414 }
2415 }
2416 }
2417
2418 if(stream->playbackPin == NULL)
2419 {
2420 goto error;
2421 }
2422
2423 switch(hostOutputSampleFormat)
2424 {
2425 case paInt16: stream->outputSampleSize = 2; break;
2426 case paInt24: stream->outputSampleSize = 3; break;
2427 case paInt32:
2428 case paFloat32: stream->outputSampleSize = 4; break;
2429 }
2430
2431 stream->playbackPin->frameSize /= stream->bytesPerOutputFrame;
2432 PA_DEBUG(("Pin output frames: %d\n",stream->playbackPin->frameSize));
2433 }
2434 else
2435 {
2436 stream->playbackPin = NULL;
2437 stream->bytesPerOutputFrame = 0;
2438 }
2439
2440 /* Calculate the framesPerHostXxxxBuffer size based upon the suggested latency values */
2441
2442 /* Record the buffer length */
2443 if(inputParameters)
2444 {
2445 /* Calculate the frames from the user's value - add a bit to round up */
2446 stream->framesPerHostIBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001);
2447 if(stream->framesPerHostIBuffer > (unsigned long)sampleRate)
2448 { /* Upper limit is 1 second */
2449 stream->framesPerHostIBuffer = (unsigned long)sampleRate;
2450 }
2451 else if(stream->framesPerHostIBuffer < stream->recordingPin->frameSize)
2452 {
2453 stream->framesPerHostIBuffer = stream->recordingPin->frameSize;
2454 }
2455 PA_DEBUG(("Input frames chosen:%ld\n",stream->framesPerHostIBuffer));
2456 }
2457
2458 if(outputParameters)
2459 {
2460 /* Calculate the frames from the user's value - add a bit to round up */
2461 stream->framesPerHostOBuffer = (unsigned long)((outputParameters->suggestedLatency*sampleRate)+0.0001);
2462 if(stream->framesPerHostOBuffer > (unsigned long)sampleRate)
2463 { /* Upper limit is 1 second */
2464 stream->framesPerHostOBuffer = (unsigned long)sampleRate;
2465 }
2466 else if(stream->framesPerHostOBuffer < stream->playbackPin->frameSize)
2467 {
2468 stream->framesPerHostOBuffer = stream->playbackPin->frameSize;
2469 }
2470 PA_DEBUG(("Output frames chosen:%ld\n",stream->framesPerHostOBuffer));
2471 }
2472
2473 /* Host buffer size is bounded to the largest of the input and output
2474 frame sizes */
2475
2476 result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2477 stream->userInputChannels, inputSampleFormat, hostInputSampleFormat,
2478 stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat,
2479 sampleRate, streamFlags, framesPerBuffer,
2480 max(stream->framesPerHostOBuffer,stream->framesPerHostIBuffer),
2481 paUtilBoundedHostBufferSize,
2482 streamCallback, userData );
2483 if( result != paNoError )
2484 goto error;
2485
2486 stream->streamRepresentation.streamInfo.inputLatency =
2487 ((double)stream->framesPerHostIBuffer) / sampleRate;
2488 stream->streamRepresentation.streamInfo.outputLatency =
2489 ((double)stream->framesPerHostOBuffer) / sampleRate;
2490 stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2491
2492 PA_DEBUG(("BytesPerInputFrame = %d\n",stream->bytesPerInputFrame));
2493 PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->bytesPerOutputFrame));
2494
2495 /* Allocate all the buffers for host I/O */
2496 size = 2 * (stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame);
2497 PA_DEBUG(("Buffer size = %d\n",size));
2498 stream->hostBuffer = (char*)PaUtil_AllocateMemory(size);
2499 PA_DEBUG(("Buffer allocated\n"));
2500 if( !stream->hostBuffer )
2501 {
2502 PA_DEBUG(("Cannot allocate host buffer!\n"));
2503 result = paInsufficientMemory;
2504 goto error;
2505 }
2506 PA_DEBUG(("Buffer start = %p\n",stream->hostBuffer));
2507 /* memset(stream->hostBuffer,0,size); */
2508
2509 /* Set up the packets */
2510 stream->events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
2511 ResetEvent(stream->events[0]); /* Record buffer 1 */
2512 stream->events[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
2513 ResetEvent(stream->events[1]); /* Record buffer 2 */
2514 stream->events[2] = CreateEvent(NULL, FALSE, FALSE, NULL);
2515 ResetEvent(stream->events[2]); /* Play buffer 1 */
2516 stream->events[3] = CreateEvent(NULL, FALSE, FALSE, NULL);
2517 ResetEvent(stream->events[3]); /* Play buffer 2 */
2518 stream->events[4] = CreateEvent(NULL, FALSE, FALSE, NULL);
2519 ResetEvent(stream->events[4]); /* Abort event */
2520 if(stream->userInputChannels > 0)
2521 {
2522 DATAPACKET *p = &(stream->packets[0]);
2523 p->Signal.hEvent = stream->events[0];
2524 p->Header.Data = stream->hostBuffer;
2525 p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
2526 p->Header.DataUsed = 0;
2527 p->Header.Size = sizeof(p->Header);
2528 p->Header.PresentationTime.Numerator = 1;
2529 p->Header.PresentationTime.Denominator = 1;
2530
2531 p = &(stream->packets[1]);
2532 p->Signal.hEvent = stream->events[1];
2533 p->Header.Data = stream->hostBuffer + stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
2534 p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
2535 p->Header.DataUsed = 0;
2536 p->Header.Size = sizeof(p->Header);
2537 p->Header.PresentationTime.Numerator = 1;
2538 p->Header.PresentationTime.Denominator = 1;
2539 }
2540 if(stream->userOutputChannels > 0)
2541 {
2542 DATAPACKET *p = &(stream->packets[2]);
2543 p->Signal.hEvent = stream->events[2];
2544 p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
2545 p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
2546 p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
2547 p->Header.Size = sizeof(p->Header);
2548 p->Header.PresentationTime.Numerator = 1;
2549 p->Header.PresentationTime.Denominator = 1;
2550
2551 p = &(stream->packets[3]);
2552 p->Signal.hEvent = stream->events[3];
2553 p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
2554 p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
2555 p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
2556 p->Header.Size = sizeof(p->Header);
2557 p->Header.PresentationTime.Numerator = 1;
2558 p->Header.PresentationTime.Denominator = 1;
2559 }
2560
2561 stream->streamStarted = 0;
2562 stream->streamActive = 0;
2563 stream->streamStop = 0;
2564 stream->streamAbort = 0;
2565 stream->streamFlags = streamFlags;
2566 stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
2567
2568 *s = (PaStream*)stream;
2569
2570 PA_LOGL_;
2571 return result;
2572
2573 error:
2574 size = 5;
2575 while(size--)
2576 {
2577 if(stream->events[size] != NULL)
2578 {
2579 CloseHandle(stream->events[size]);
2580 stream->events[size] = NULL;
2581 }
2582 }
2583 if(stream->hostBuffer)
2584 PaUtil_FreeMemory( stream->hostBuffer );
2585
2586 if(stream->playbackPin)
2587 PinClose(stream->playbackPin);
2588 if(stream->recordingPin)
2589 PinClose(stream->recordingPin);
2590
2591 if( stream )
2592 PaUtil_FreeMemory( stream );
2593
2594 PA_LOGL_;
2595 return result;
2596 }
2597
2598 /*
2599 When CloseStream() is called, the multi-api layer ensures that
2600 the stream has already been stopped or aborted.
2601 */
CloseStream(PaStream * s)2602 static PaError CloseStream( PaStream* s )
2603 {
2604 PaError result = paNoError;
2605 PaWinWdmStream *stream = (PaWinWdmStream*)s;
2606 int size;
2607
2608 PA_LOGE_;
2609
2610 assert(!stream->streamStarted);
2611 assert(!stream->streamActive);
2612
2613 PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2614 PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2615 size = 5;
2616 while(size--)
2617 {
2618 if(stream->events[size] != NULL)
2619 {
2620 CloseHandle(stream->events[size]);
2621 stream->events[size] = NULL;
2622 }
2623 }
2624 if(stream->hostBuffer)
2625 PaUtil_FreeMemory( stream->hostBuffer );
2626
2627 if(stream->playbackPin)
2628 PinClose(stream->playbackPin);
2629 if(stream->recordingPin)
2630 PinClose(stream->recordingPin);
2631
2632 PaUtil_FreeMemory( stream );
2633
2634 PA_LOGL_;
2635 return result;
2636 }
2637
2638 /*
2639 Write the supplied packet to the pin
2640 Asynchronous
2641 Should return false on success
2642 */
PinWrite(HANDLE h,DATAPACKET * p)2643 static BOOL PinWrite(HANDLE h, DATAPACKET* p)
2644 {
2645 unsigned long cbReturned = 0;
2646 return DeviceIoControl(h,IOCTL_KS_WRITE_STREAM,NULL,0,
2647 &p->Header,p->Header.Size,&cbReturned,&p->Signal);
2648 }
2649
2650 /*
2651 Read to the supplied packet from the pin
2652 Asynchronous
2653 Should return false on success
2654 */
PinRead(HANDLE h,DATAPACKET * p)2655 static BOOL PinRead(HANDLE h, DATAPACKET* p)
2656 {
2657 unsigned long cbReturned = 0;
2658 return DeviceIoControl(h,IOCTL_KS_READ_STREAM,NULL,0,
2659 &p->Header,p->Header.Size,&cbReturned,&p->Signal);
2660 }
2661
2662 /*
2663 Copy the first interleaved channel of 16 bit data to the other channels
2664 */
DuplicateFirstChannelInt16(void * buffer,int channels,int samples)2665 static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples)
2666 {
2667 unsigned short* data = (unsigned short*)buffer;
2668 int channel;
2669 unsigned short sourceSample;
2670 while( samples-- )
2671 {
2672 sourceSample = *data++;
2673 channel = channels-1;
2674 while( channel-- )
2675 {
2676 *data++ = sourceSample;
2677 }
2678 }
2679 }
2680
2681 /*
2682 Copy the first interleaved channel of 24 bit data to the other channels
2683 */
DuplicateFirstChannelInt24(void * buffer,int channels,int samples)2684 static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples)
2685 {
2686 unsigned char* data = (unsigned char*)buffer;
2687 int channel;
2688 unsigned char sourceSample[3];
2689 while( samples-- )
2690 {
2691 sourceSample[0] = data[0];
2692 sourceSample[1] = data[1];
2693 sourceSample[2] = data[2];
2694 data += 3;
2695 channel = channels-1;
2696 while( channel-- )
2697 {
2698 data[0] = sourceSample[0];
2699 data[1] = sourceSample[1];
2700 data[2] = sourceSample[2];
2701 data += 3;
2702 }
2703 }
2704 }
2705
2706 /*
2707 Copy the first interleaved channel of 32 bit data to the other channels
2708 */
DuplicateFirstChannelInt32(void * buffer,int channels,int samples)2709 static void DuplicateFirstChannelInt32(void* buffer, int channels, int samples)
2710 {
2711 unsigned long* data = (unsigned long*)buffer;
2712 int channel;
2713 unsigned long sourceSample;
2714 while( samples-- )
2715 {
2716 sourceSample = *data++;
2717 channel = channels-1;
2718 while( channel-- )
2719 {
2720 *data++ = sourceSample;
2721 }
2722 }
2723 }
2724
ProcessingThread(LPVOID pParam)2725 static DWORD WINAPI ProcessingThread(LPVOID pParam)
2726 {
2727 PaWinWdmStream *stream = (PaWinWdmStream*)pParam;
2728 PaStreamCallbackTimeInfo ti;
2729 int cbResult = paContinue;
2730 int inbuf = 0;
2731 int outbuf = 0;
2732 int pending = 0;
2733 PaError result;
2734 unsigned long wait;
2735 unsigned long eventSignaled;
2736 int fillPlaybuf = 0;
2737 int emptyRecordbuf = 0;
2738 int framesProcessed;
2739 unsigned long timeout;
2740 int i;
2741 int doChannelCopy;
2742 int priming = 0;
2743 PaStreamCallbackFlags underover = 0;
2744
2745 PA_LOGE_;
2746
2747 ti.inputBufferAdcTime = 0.0;
2748 ti.currentTime = 0.0;
2749 ti.outputBufferDacTime = 0.0;
2750
2751 /* Get double buffering going */
2752
2753 /* Submit buffers */
2754 if(stream->playbackPin)
2755 {
2756 result = PinSetState(stream->playbackPin, KSSTATE_RUN);
2757
2758 PA_DEBUG(("play state run = %d;",(int)result));
2759 SetEvent(stream->events[outbuf+2]);
2760 outbuf = (outbuf+1)&1;
2761 SetEvent(stream->events[outbuf+2]);
2762 outbuf = (outbuf+1)&1;
2763 pending += 2;
2764 priming += 4;
2765 }
2766 if(stream->recordingPin)
2767 {
2768 result = PinSetState(stream->recordingPin, KSSTATE_RUN);
2769
2770 PA_DEBUG(("recording state run = %d;",(int)result));
2771 PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
2772 inbuf = (inbuf+1)&1; /* Increment and wrap */
2773 PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
2774 inbuf = (inbuf+1)&1; /* Increment and wrap */
2775 /* FIXME - do error checking */
2776 pending += 2;
2777 }
2778 PA_DEBUG(("Out buffer len:%f\n",(2000*stream->framesPerHostOBuffer) / stream->streamRepresentation.streamInfo.sampleRate));
2779 PA_DEBUG(("In buffer len:%f\n",(2000*stream->framesPerHostIBuffer) / stream->streamRepresentation.streamInfo.sampleRate));
2780 timeout = max(
2781 ((2000*(DWORD)stream->framesPerHostOBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate),
2782 ((2000*(DWORD)stream->framesPerHostIBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate));
2783 timeout = max(timeout,1);
2784 PA_DEBUG(("Timeout = %ld\n",timeout));
2785
2786 while(!stream->streamAbort)
2787 {
2788 fillPlaybuf = 0;
2789 emptyRecordbuf = 0;
2790
2791 /* Wait for next input or output buffer to be finished with*/
2792 assert(pending>0);
2793
2794 if(stream->streamStop)
2795 {
2796 PA_DEBUG(("ss1:pending=%d ",pending));
2797 }
2798 wait = WaitForMultipleObjects(5, stream->events, FALSE, 0);
2799 if( wait == WAIT_TIMEOUT )
2800 {
2801 /* No (under|over)flow has ocurred */
2802 wait = WaitForMultipleObjects(5, stream->events, FALSE, timeout);
2803 eventSignaled = wait - WAIT_OBJECT_0;
2804 }
2805 else
2806 {
2807 eventSignaled = wait - WAIT_OBJECT_0;
2808 if( eventSignaled < 2 )
2809 {
2810 underover |= paInputOverflow;
2811 PA_DEBUG(("Input overflow\n"));
2812 }
2813 else if(( eventSignaled < 4 )&&(!priming))
2814 {
2815 underover |= paOutputUnderflow;
2816 PA_DEBUG(("Output underflow\n"));
2817 }
2818 }
2819
2820 if(stream->streamStop)
2821 {
2822 PA_DEBUG(("ss2:wait=%ld",wait));
2823 }
2824 if(wait == WAIT_FAILED)
2825 {
2826 PA_DEBUG(("Wait fail = %ld! ",wait));
2827 break;
2828 }
2829 if(wait == WAIT_TIMEOUT)
2830 {
2831 continue;
2832 }
2833
2834 if(eventSignaled < 2)
2835 { /* Recording input buffer has been filled */
2836 if(stream->playbackPin)
2837 {
2838 /* First check if also the next playback buffer has been signaled */
2839 wait = WaitForSingleObject(stream->events[outbuf+2],0);
2840 if(wait == WAIT_OBJECT_0)
2841 {
2842 /* Yes, so do both buffers at same time */
2843 fillPlaybuf = 1;
2844 pending--;
2845 /* Was this an underflow situation? */
2846 if( underover )
2847 underover |= paOutputUnderflow; /* Yes! */
2848 }
2849 }
2850 emptyRecordbuf = 1;
2851 pending--;
2852 }
2853 else if(eventSignaled < 4)
2854 { /* Playback output buffer has been emptied */
2855 if(stream->recordingPin)
2856 {
2857 /* First check if also the next recording buffer has been signaled */
2858 wait = WaitForSingleObject(stream->events[inbuf],0);
2859 if(wait == WAIT_OBJECT_0)
2860 { /* Yes, so do both buffers at same time */
2861 emptyRecordbuf = 1;
2862 pending--;
2863 /* Was this an overflow situation? */
2864 if( underover )
2865 underover |= paInputOverflow; /* Yes! */
2866 }
2867 }
2868 fillPlaybuf = 1;
2869 pending--;
2870 }
2871 else
2872 {
2873 /* Abort event! */
2874 assert(stream->streamAbort); /* Should have been set */
2875 PA_DEBUG(("ABORTING "));
2876 break;
2877 }
2878 ResetEvent(stream->events[eventSignaled]);
2879
2880 if(stream->streamStop)
2881 {
2882 PA_DEBUG(("Stream stop! pending=%d",pending));
2883 cbResult = paComplete; /* Stop, but play remaining buffers */
2884 }
2885
2886 /* Do necessary buffer processing (which will invoke user callback if necessary */
2887 doChannelCopy = 0;
2888 if(cbResult==paContinue)
2889 {
2890 PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
2891 if((stream->bufferProcessor.hostInputFrameCount[0] + stream->bufferProcessor.hostInputFrameCount[1]) ==
2892 (stream->bufferProcessor.hostOutputFrameCount[0] + stream->bufferProcessor.hostOutputFrameCount[1]) )
2893 PaUtil_BeginBufferProcessing(&stream->bufferProcessor,&ti,underover);
2894 underover = 0; /* Reset the (under|over)flow status */
2895 if(fillPlaybuf)
2896 {
2897 PaUtil_SetOutputFrameCount(&stream->bufferProcessor,0);
2898 if( stream->userOutputChannels == 1 )
2899 {
2900 /* Write the single user channel to the first interleaved block */
2901 PaUtil_SetOutputChannel(&stream->bufferProcessor,0,stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels);
2902 /* We will do a copy to the other channels after the data has been written */
2903 doChannelCopy = 1;
2904 }
2905 else
2906 {
2907 for(i=0;i<stream->userOutputChannels;i++)
2908 {
2909 /* Only write the user output channels. Leave the rest blank */
2910 PaUtil_SetOutputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[outbuf+2].Header.Data))+(i*stream->outputSampleSize),stream->deviceOutputChannels);
2911 }
2912 }
2913 }
2914 if(emptyRecordbuf)
2915 {
2916 PaUtil_SetInputFrameCount(&stream->bufferProcessor,stream->packets[inbuf].Header.DataUsed/stream->bytesPerInputFrame);
2917 for(i=0;i<stream->userInputChannels;i++)
2918 {
2919 /* Only read as many channels as the user wants */
2920 PaUtil_SetInputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[inbuf].Header.Data))+(i*stream->inputSampleSize),stream->deviceInputChannels);
2921 }
2922 }
2923
2924 if (stream->recordingPin && stream->playbackPin) /* full duplex */
2925 {
2926 /* Only call the EndBufferProcessing function when the total input frames == total output frames */
2927
2928 if((stream->bufferProcessor.hostInputFrameCount[0] + stream->bufferProcessor.hostInputFrameCount[1]) ==
2929 (stream->bufferProcessor.hostOutputFrameCount[0] + stream->bufferProcessor.hostOutputFrameCount[1]) )
2930 {
2931 framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult);
2932 }
2933 else
2934 {
2935 framesProcessed = 0;
2936 }
2937 }
2938 else
2939 {
2940 framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult);
2941 }
2942
2943 if( doChannelCopy )
2944 {
2945 /* Copy the first output channel to the other channels */
2946 switch(stream->outputSampleSize)
2947 {
2948 case 2:
2949 DuplicateFirstChannelInt16(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
2950 break;
2951 case 3:
2952 DuplicateFirstChannelInt24(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
2953 break;
2954 case 4:
2955 DuplicateFirstChannelInt32(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
2956 break;
2957 default:
2958 assert(0); /* Unsupported format! */
2959 break;
2960 }
2961 }
2962 PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
2963 }
2964 else
2965 {
2966 fillPlaybuf = 0;
2967 emptyRecordbuf = 0;
2968 }
2969
2970 /*
2971 if(cbResult != paContinue)
2972 {
2973 PA_DEBUG(("cbResult=%d, pending=%d:",cbResult,pending));
2974 }
2975 */
2976 /* Submit buffers */
2977 if((fillPlaybuf)&&(cbResult!=paAbort))
2978 {
2979 if(!PinWrite(stream->playbackPin->handle,&stream->packets[outbuf+2]))
2980 outbuf = (outbuf+1)&1; /* Increment and wrap */
2981 pending++;
2982 if( priming )
2983 priming--; /* Have to prime twice */
2984 }
2985 if((emptyRecordbuf)&&(cbResult==paContinue))
2986 {
2987 stream->packets[inbuf].Header.DataUsed = 0; /* Reset for reuse */
2988 PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
2989 inbuf = (inbuf+1)&1; /* Increment and wrap */
2990 pending++;
2991 }
2992 if(pending==0)
2993 {
2994 PA_DEBUG(("pending==0 finished...;"));
2995 break;
2996 }
2997 if((!stream->playbackPin)&&(cbResult!=paContinue))
2998 {
2999 PA_DEBUG(("record only cbResult=%d...;",cbResult));
3000 break;
3001 }
3002 }
3003
3004 PA_DEBUG(("Finished thread"));
3005
3006 /* Finished, either normally or aborted */
3007 if(stream->playbackPin)
3008 {
3009 result = PinSetState(stream->playbackPin, KSSTATE_PAUSE);
3010 result = PinSetState(stream->playbackPin, KSSTATE_STOP);
3011 }
3012 if(stream->recordingPin)
3013 {
3014 result = PinSetState(stream->recordingPin, KSSTATE_PAUSE);
3015 result = PinSetState(stream->recordingPin, KSSTATE_STOP);
3016 }
3017
3018 stream->streamActive = 0;
3019
3020 if((!stream->streamStop)&&(!stream->streamAbort))
3021 {
3022 /* Invoke the user stream finished callback */
3023 /* Only do it from here if not being stopped/aborted by user */
3024 if( stream->streamRepresentation.streamFinishedCallback != 0 )
3025 stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3026 }
3027 stream->streamStop = 0;
3028 stream->streamAbort = 0;
3029
3030 /* Reset process priority if necessary */
3031 if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
3032 {
3033 SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
3034 stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
3035 }
3036
3037 PA_LOGL_;
3038 EXIT_THREAD;
3039 return 0;
3040 }
3041
StartStream(PaStream * s)3042 static PaError StartStream( PaStream *s )
3043 {
3044 PaError result = paNoError;
3045 PaWinWdmStream *stream = (PaWinWdmStream*)s;
3046 DWORD dwID;
3047 BOOL ret;
3048 int size;
3049
3050 PA_LOGE_;
3051
3052 stream->streamStop = 0;
3053 stream->streamAbort = 0;
3054 size = 5;
3055 while(size--)
3056 {
3057 if(stream->events[size] != NULL)
3058 {
3059 ResetEvent(stream->events[size]);
3060 }
3061 }
3062
3063 PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
3064
3065 stream->oldProcessPriority = GetPriorityClass(GetCurrentProcess());
3066 /* Uncomment the following line to enable dynamic boosting of the process
3067 * priority to real time for best low latency support
3068 * Disabled by default because RT processes can easily block the OS */
3069 /*ret = SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
3070 PA_DEBUG(("Class ret = %d;",ret));*/
3071
3072 stream->streamStarted = 1;
3073 stream->streamThread = (HANDLE)_beginthreadex(NULL, 0, ProcessingThread, stream, 0, &dwID);
3074 if(stream->streamThread == NULL)
3075 {
3076 stream->streamStarted = 0;
3077 result = paInsufficientMemory;
3078 goto end;
3079 }
3080 ret = SetThreadPriority(stream->streamThread,THREAD_PRIORITY_TIME_CRITICAL);
3081 PA_DEBUG(("Priority ret = %d;",ret));
3082 /* Make the stream active */
3083 stream->streamActive = 1;
3084
3085 end:
3086 PA_LOGL_;
3087 return result;
3088 }
3089
3090
StopStream(PaStream * s)3091 static PaError StopStream( PaStream *s )
3092 {
3093 PaError result = paNoError;
3094 PaWinWdmStream *stream = (PaWinWdmStream*)s;
3095 int doCb = 0;
3096
3097 PA_LOGE_;
3098
3099 if(stream->streamActive)
3100 {
3101 doCb = 1;
3102 stream->streamStop = 1;
3103 while(stream->streamActive)
3104 {
3105 PA_DEBUG(("W."));
3106 Sleep(10); /* Let thread sleep for 10 msec */
3107 }
3108 }
3109
3110 PA_DEBUG(("Terminating thread"));
3111 if(stream->streamStarted && stream->streamThread)
3112 {
3113 TerminateThread(stream->streamThread,0);
3114 stream->streamThread = NULL;
3115 }
3116
3117 stream->streamStarted = 0;
3118
3119 if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
3120 {
3121 SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
3122 stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
3123 }
3124
3125 if(doCb)
3126 {
3127 /* Do user callback now after all state has been reset */
3128 /* This means it should be safe for the called function */
3129 /* to invoke e.g. StartStream */
3130 if( stream->streamRepresentation.streamFinishedCallback != 0 )
3131 stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3132 }
3133
3134 PA_LOGL_;
3135 return result;
3136 }
3137
AbortStream(PaStream * s)3138 static PaError AbortStream( PaStream *s )
3139 {
3140 PaError result = paNoError;
3141 PaWinWdmStream *stream = (PaWinWdmStream*)s;
3142 int doCb = 0;
3143
3144 PA_LOGE_;
3145
3146 if(stream->streamActive)
3147 {
3148 doCb = 1;
3149 stream->streamAbort = 1;
3150 SetEvent(stream->events[4]); /* Signal immediately */
3151 while(stream->streamActive)
3152 {
3153 Sleep(10);
3154 }
3155 }
3156
3157 if(stream->streamStarted && stream->streamThread)
3158 {
3159 TerminateThread(stream->streamThread,0);
3160 stream->streamThread = NULL;
3161 }
3162
3163 stream->streamStarted = 0;
3164
3165 if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
3166 {
3167 SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
3168 stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
3169 }
3170
3171 if(doCb)
3172 {
3173 /* Do user callback now after all state has been reset */
3174 /* This means it should be safe for the called function */
3175 /* to invoke e.g. StartStream */
3176 if( stream->streamRepresentation.streamFinishedCallback != 0 )
3177 stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3178 }
3179
3180 stream->streamActive = 0;
3181 stream->streamStarted = 0;
3182
3183 PA_LOGL_;
3184 return result;
3185 }
3186
3187
IsStreamStopped(PaStream * s)3188 static PaError IsStreamStopped( PaStream *s )
3189 {
3190 PaWinWdmStream *stream = (PaWinWdmStream*)s;
3191 int result = 0;
3192
3193 PA_LOGE_;
3194
3195 if(!stream->streamStarted)
3196 result = 1;
3197
3198 PA_LOGL_;
3199 return result;
3200 }
3201
3202
IsStreamActive(PaStream * s)3203 static PaError IsStreamActive( PaStream *s )
3204 {
3205 PaWinWdmStream *stream = (PaWinWdmStream*)s;
3206 int result = 0;
3207
3208 PA_LOGE_;
3209
3210 if(stream->streamActive)
3211 result = 1;
3212
3213 PA_LOGL_;
3214 return result;
3215 }
3216
3217
GetStreamTime(PaStream * s)3218 static PaTime GetStreamTime( PaStream* s )
3219 {
3220 PA_LOGE_;
3221 PA_LOGL_;
3222 (void)s;
3223 return PaUtil_GetTime();
3224 }
3225
3226
GetStreamCpuLoad(PaStream * s)3227 static double GetStreamCpuLoad( PaStream* s )
3228 {
3229 PaWinWdmStream *stream = (PaWinWdmStream*)s;
3230 double result;
3231 PA_LOGE_;
3232 result = PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
3233 PA_LOGL_;
3234 return result;
3235 }
3236
3237
3238 /*
3239 As separate stream interfaces are used for blocking and callback
3240 streams, the following functions can be guaranteed to only be called
3241 for blocking streams.
3242 */
3243
ReadStream(PaStream * s,void * buffer,unsigned long frames)3244 static PaError ReadStream( PaStream* s,
3245 void *buffer,
3246 unsigned long frames )
3247 {
3248 PaWinWdmStream *stream = (PaWinWdmStream*)s;
3249
3250 PA_LOGE_;
3251
3252 /* suppress unused variable warnings */
3253 (void) buffer;
3254 (void) frames;
3255 (void) stream;
3256
3257 /* IMPLEMENT ME, see portaudio.h for required behavior*/
3258 PA_LOGL_;
3259 return paNoError;
3260 }
3261
3262
WriteStream(PaStream * s,const void * buffer,unsigned long frames)3263 static PaError WriteStream( PaStream* s,
3264 const void *buffer,
3265 unsigned long frames )
3266 {
3267 PaWinWdmStream *stream = (PaWinWdmStream*)s;
3268
3269 PA_LOGE_;
3270
3271 /* suppress unused variable warnings */
3272 (void) buffer;
3273 (void) frames;
3274 (void) stream;
3275
3276 /* IMPLEMENT ME, see portaudio.h for required behavior*/
3277 PA_LOGL_;
3278 return paNoError;
3279 }
3280
3281
GetStreamReadAvailable(PaStream * s)3282 static signed long GetStreamReadAvailable( PaStream* s )
3283 {
3284 PaWinWdmStream *stream = (PaWinWdmStream*)s;
3285
3286 PA_LOGE_;
3287
3288 /* suppress unused variable warnings */
3289 (void) stream;
3290
3291 /* IMPLEMENT ME, see portaudio.h for required behavior*/
3292 PA_LOGL_;
3293 return 0;
3294 }
3295
3296
GetStreamWriteAvailable(PaStream * s)3297 static signed long GetStreamWriteAvailable( PaStream* s )
3298 {
3299 PaWinWdmStream *stream = (PaWinWdmStream*)s;
3300
3301 PA_LOGE_;
3302 /* suppress unused variable warnings */
3303 (void) stream;
3304
3305 /* IMPLEMENT ME, see portaudio.h for required behavior*/
3306 PA_LOGL_;
3307 return 0;
3308 }