2
* $Id: pa_win_wdmks.c 1606 2011-02-17 15:56:04Z rob_bielik $
3
* PortAudio Windows WDM-KS interface
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
9
* Permission is hereby granted, free of charge, to any person obtaining
10
* a copy of this software and associated documentation files
11
* (the "Software"), to deal in the Software without restriction,
12
* including without limitation the rights to use, copy, modify, merge,
13
* publish, distribute, sublicense, and/or sell copies of the Software,
14
* and to permit persons to whom the Software is furnished to do so,
15
* subject to the following conditions:
17
* The above copyright notice and this permission notice shall be
18
* included in all copies or substantial portions of the Software.
20
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
24
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
* The text above constitutes the entire PortAudio license; however,
31
* the PortAudio community also makes the following non-binding requests:
33
* Any person wishing to distribute modifications to the Software is
34
* requested to send the modifications to the original developer so that
35
* they can be incorporated into the canonical version. It is also
36
* requested that these non-binding requests be included along with the
42
@brief Portaudio WDM-KS host API.
44
@note This is the implementation of the Portaudio host API using the
45
Windows WDM/Kernel Streaming API in order to enable very low latency
46
playback and recording on all modern Windows platforms (e.g. 2K, XP)
47
Note: This API accesses the device drivers below the usual KMIXER
48
component which is normally used to enable multi-client mixing and
49
format conversion. That means that it will lock out all other users
50
of a device for the duration of active stream using those devices
55
/* Debugging/tracing support */
62
#define _WIN32_WINNT 0x0501
66
#include <string.h> /* strlen() */
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"
84
#define PA_LOGE_ PA_DEBUG(("%s {\n",__FUNCTION__))
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 */
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
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)
109
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
110
#pragma comment( lib, "setupapi.lib" )
113
/* use CreateThread for CYGWIN, _beginthreadex for all others */
115
#define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
117
#define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
120
/* use ExitThread for CYGWIN, _endthreadex for all others */
122
#define EXIT_THREAD _endthreadex(0)
124
#define EXIT_THREAD ExitThread(0)
129
#define DYNAMIC_GUID(data) {data}
130
#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
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)
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;
149
/* Forward definition to break circular type reference between pin and filter */
150
struct __PaWinWdmFilter;
151
typedef struct __PaWinWdmFilter PaWinWdmFilter;
154
* A pin is an input or output node, e.g. for audio flow */
155
typedef struct __PaWinWdmPin
158
PaWinWdmFilter* parentFilter;
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;
170
unsigned long formats;
175
/* The Filter structure
176
* A filter has a number of pins and a "friendly name" */
177
struct __PaWinWdmFilter
182
TCHAR filterName[MAX_PATH];
183
TCHAR friendlyName[MAX_PATH];
184
int maxInputChannels;
185
int maxOutputChannels;
186
unsigned long formats;
191
/* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */
192
typedef struct __PaWinWdmHostApiRepresentation
194
PaUtilHostApiRepresentation inheritedHostApiRep;
195
PaUtilStreamInterface callbackStreamInterface;
196
PaUtilStreamInterface blockingStreamInterface;
198
PaUtilAllocationGroup* allocations;
199
PaWinWdmFilter** filters;
202
PaWinWdmHostApiRepresentation;
204
typedef struct __PaWinWdmDeviceInfo
206
PaDeviceInfo inheritedDeviceInfo;
207
PaWinWdmFilter* filter;
211
typedef struct __DATAPACKET
213
KSSTREAM_HEADER Header;
217
/* PaWinWdmStream - a stream data structure specifically for this implementation */
218
typedef struct __PaWinWdmStream
220
PaUtilStreamRepresentation streamRepresentation;
221
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
222
PaUtilBufferProcessor bufferProcessor;
224
PaWinWdmPin* recordingPin;
225
PaWinWdmPin* playbackPin;
227
unsigned long framesPerHostIBuffer;
228
unsigned long framesPerHostOBuffer;
229
int bytesPerInputFrame;
230
int bytesPerOutputFrame;
235
int oldProcessPriority;
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;
247
int outputSampleSize;
251
#include <setupapi.h>
253
HMODULE DllKsUser = NULL;
254
KSCREATEPIN* FunctionKsCreatePin = NULL;
256
/* prototypes for functions declared in this file */
261
#endif /* __cplusplus */
263
PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
267
#endif /* __cplusplus */
269
/* Low level I/O functions */
270
static PaError WdmSyncIoctl(HANDLE handle,
271
unsigned long ioctlNumber,
273
unsigned long inBufferCount,
275
unsigned long outBufferCount,
276
unsigned long* bytesReturned);
277
static PaError WdmGetPropertySimple(HANDLE handle,
278
const GUID* const guidPropertySet,
279
unsigned long property,
281
unsigned long valueCount,
283
unsigned long instanceCount);
284
static PaError WdmSetPropertySimple(HANDLE handle,
285
const GUID* const guidPropertySet,
286
unsigned long property,
288
unsigned long valueCount,
290
unsigned long instanceCount);
291
static PaError WdmGetPinPropertySimple(HANDLE handle,
293
const GUID* const guidPropertySet,
294
unsigned long property,
296
unsigned long valueCount);
297
static PaError WdmGetPinPropertyMulti(HANDLE handle,
299
const GUID* const guidPropertySet,
300
unsigned long property,
301
KSMULTIPLE_ITEM** ksMultipleItem);
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);
313
/* Filter management functions */
314
static PaWinWdmFilter* FilterNew(
318
static void FilterFree(PaWinWdmFilter* filter);
319
static PaWinWdmPin* FilterCreateRenderPin(
320
PaWinWdmFilter* filter,
321
const WAVEFORMATEX* wfex,
323
static PaWinWdmPin* FilterFindViableRenderPin(
324
PaWinWdmFilter* filter,
325
const WAVEFORMATEX* wfex,
327
static PaError FilterCanCreateRenderPin(
328
PaWinWdmFilter* filter,
329
const WAVEFORMATEX* wfex);
330
static PaWinWdmPin* FilterCreateCapturePin(
331
PaWinWdmFilter* filter,
332
const WAVEFORMATEX* wfex,
334
static PaWinWdmPin* FilterFindViableCapturePin(
335
PaWinWdmFilter* filter,
336
const WAVEFORMATEX* wfex,
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);
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,
353
static PaError OpenStream(
354
struct PaUtilHostApiRepresentation *hostApi,
356
const PaStreamParameters *inputParameters,
357
const PaStreamParameters *outputParameters,
359
unsigned long framesPerBuffer,
360
PaStreamFlags streamFlags,
361
PaStreamCallback *streamCallback,
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(
374
unsigned long frames );
375
static PaError WriteStream(
378
unsigned long frames );
379
static signed long GetStreamReadAvailable( PaStream* stream );
380
static signed long GetStreamWriteAvailable( PaStream* stream );
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);
391
/* Function bodies */
393
static unsigned long GetWfexSize(const WAVEFORMATEX* wfex)
395
if( wfex->wFormatTag == WAVE_FORMAT_PCM )
397
return sizeof( WAVEFORMATEX );
401
return (sizeof( WAVEFORMATEX ) + wfex->cbSize);
406
Low level pin/filter access functions
408
static PaError WdmSyncIoctl(
410
unsigned long ioctlNumber,
412
unsigned long inBufferCount,
414
unsigned long outBufferCount,
415
unsigned long* bytesReturned)
417
PaError result = paNoError;
418
OVERLAPPED overlapped;
420
unsigned long dummyBytesReturned;
425
/* User a dummy as the caller hasn't supplied one */
426
bytesReturned = &dummyBytesReturned;
429
FillMemory((void *)&overlapped,sizeof(overlapped),0);
430
overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
431
if( !overlapped.hEvent )
433
result = paInsufficientMemory;
436
overlapped.hEvent = (HANDLE)((DWORD_PTR)overlapped.hEvent | 0x1);
438
boolResult = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount,
439
outBuffer, outBufferCount, bytesReturned, &overlapped);
442
error = GetLastError();
443
if( error == ERROR_IO_PENDING )
445
error = WaitForSingleObject(overlapped.hEvent,INFINITE);
446
if( error != WAIT_OBJECT_0 )
448
result = paUnanticipatedHostError;
452
else if((( error == ERROR_INSUFFICIENT_BUFFER ) ||
453
( error == ERROR_MORE_DATA )) &&
454
( ioctlNumber == IOCTL_KS_PROPERTY ) &&
455
( outBufferCount == 0 ))
461
result = paUnanticipatedHostError;
468
if( overlapped.hEvent )
470
CloseHandle( overlapped.hEvent );
475
static PaError WdmGetPropertySimple(HANDLE handle,
476
const GUID* const guidPropertySet,
477
unsigned long property,
479
unsigned long valueCount,
481
unsigned long instanceCount)
484
KSPROPERTY* ksProperty;
485
unsigned long propertyCount;
487
propertyCount = sizeof(KSPROPERTY) + instanceCount;
488
ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
491
return paInsufficientMemory;
494
FillMemory((void*)ksProperty,sizeof(ksProperty),0);
495
ksProperty->Set = *guidPropertySet;
496
ksProperty->Id = property;
497
ksProperty->Flags = KSPROPERTY_TYPE_GET;
501
memcpy( (void*)(((char*)ksProperty)+sizeof(KSPROPERTY)), instance, instanceCount );
504
result = WdmSyncIoctl(
513
PaUtil_FreeMemory( ksProperty );
517
static PaError WdmSetPropertySimple(
519
const GUID* const guidPropertySet,
520
unsigned long property,
522
unsigned long valueCount,
524
unsigned long instanceCount)
527
KSPROPERTY* ksProperty;
528
unsigned long propertyCount = 0;
530
propertyCount = sizeof(KSPROPERTY) + instanceCount;
531
ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
534
return paInsufficientMemory;
537
ksProperty->Set = *guidPropertySet;
538
ksProperty->Id = property;
539
ksProperty->Flags = KSPROPERTY_TYPE_SET;
543
memcpy((void*)((char*)ksProperty + sizeof(KSPROPERTY)), instance, instanceCount);
546
result = WdmSyncIoctl(
555
PaUtil_FreeMemory( ksProperty );
559
static PaError WdmGetPinPropertySimple(
562
const GUID* const guidPropertySet,
563
unsigned long property,
565
unsigned long valueCount)
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;
576
result = WdmSyncIoctl(
588
static PaError WdmGetPinPropertyMulti(
591
const GUID* const guidPropertySet,
592
unsigned long property,
593
KSMULTIPLE_ITEM** ksMultipleItem)
596
unsigned long multipleItemSize = 0;
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;
605
result = WdmSyncIoctl(
613
if( result != paNoError )
618
*ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
619
if( !*ksMultipleItem )
621
return paInsufficientMemory;
624
result = WdmSyncIoctl(
629
(void*)*ksMultipleItem,
633
if( result != paNoError )
635
PaUtil_FreeMemory( ksMultipleItem );
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
647
static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error)
652
KSMULTIPLE_ITEM* item = NULL;
653
KSIDENTIFIER* identifier;
654
KSDATARANGE* dataRange;
657
PA_DEBUG(("Creating pin %d:\n",pinId));
659
/* Allocate the new PIN object */
660
pin = (PaWinWdmPin*)PaUtil_AllocateMemory( sizeof(PaWinWdmPin) );
663
result = paInsufficientMemory;
667
/* Zero the pin object */
668
/* memset( (void*)pin, 0, sizeof(PaWinWdmPin) ); */
670
pin->parentFilter = parentFilter;
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 )
678
result = paInsufficientMemory;
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;
701
pin->frameSize = 0; /* Unknown until we instantiate pin */
703
/* Get the COMMUNICATION property */
704
result = WdmGetPinPropertySimple(
705
parentFilter->handle,
708
KSPROPERTY_PIN_COMMUNICATION,
710
sizeof(KSPIN_COMMUNICATION));
711
if( result != paNoError )
714
if( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/
715
(pin->communication != KSPIN_COMMUNICATION_SINK) &&
716
(pin->communication != KSPIN_COMMUNICATION_BOTH) )
718
PA_DEBUG(("Not source/sink\n"));
719
result = paInvalidDevice;
723
/* Get dataflow information */
724
result = WdmGetPinPropertySimple(
725
parentFilter->handle,
728
KSPROPERTY_PIN_DATAFLOW,
730
sizeof(KSPIN_DATAFLOW));
732
if( result != paNoError )
735
/* Get the INTERFACE property list */
736
result = WdmGetPinPropertyMulti(
737
parentFilter->handle,
740
KSPROPERTY_PIN_INTERFACES,
743
if( result != paNoError )
746
identifier = (KSIDENTIFIER*)(item+1);
748
/* Check that at least one interface is STANDARD_STREAMING */
749
result = paUnanticipatedHostError;
750
for( i = 0; i < item->Count; i++ )
752
if( !memcmp( (void*)&identifier[i].Set, (void*)&KSINTERFACESETID_Standard, sizeof( GUID ) ) &&
753
( identifier[i].Id == KSINTERFACE_STANDARD_STREAMING ) )
760
if( result != paNoError )
762
PA_DEBUG(("No standard streaming\n"));
766
/* Don't need interfaces any more */
767
PaUtil_FreeMemory( item );
770
/* Get the MEDIUM properties list */
771
result = WdmGetPinPropertyMulti(
772
parentFilter->handle,
775
KSPROPERTY_PIN_MEDIUMS,
778
if( result != paNoError )
781
identifier = (KSIDENTIFIER*)(item+1); /* Not actually necessary... */
783
/* Check that at least one medium is STANDARD_DEVIO */
784
result = paUnanticipatedHostError;
785
for( i = 0; i < item->Count; i++ )
787
if( !memcmp( (void*)&identifier[i].Set, (void*)&KSMEDIUMSETID_Standard, sizeof( GUID ) ) &&
788
( identifier[i].Id == KSMEDIUM_STANDARD_DEVIO ) )
795
if( result != paNoError )
797
PA_DEBUG(("No standard devio\n"));
800
/* Don't need mediums any more */
801
PaUtil_FreeMemory( item );
805
result = WdmGetPinPropertyMulti(
806
parentFilter->handle,
809
KSPROPERTY_PIN_DATARANGES,
810
&pin->dataRangesItem);
812
if( result != paNoError )
815
pin->dataRanges = (KSDATARANGE*)(pin->dataRangesItem +1);
817
/* Check that at least one datarange supports audio */
818
result = paUnanticipatedHostError;
819
dataRange = pin->dataRanges;
820
pin->maxChannels = 0;
821
pin->bestSampleRate = 0;
823
for( i = 0; i <pin->dataRangesItem->Count; i++)
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 ) ) ) ) )
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 )
837
pin->maxChannels = ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels;
838
/*PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));*/
840
/* Record the formats (bit depths) that are supported */
841
if( ((KSDATARANGE_AUDIO*)dataRange)->MinimumBitsPerSample <= 16 )
843
pin->formats |= paInt16;
844
PA_DEBUG(("Format 16 bit supported\n"));
846
if( ((KSDATARANGE_AUDIO*)dataRange)->MaximumBitsPerSample >= 24 )
848
pin->formats |= paInt24;
849
PA_DEBUG(("Format 24 bit supported\n"));
851
if( ( pin->bestSampleRate != 48000) &&
852
(((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 48000) &&
853
(((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 48000) )
855
pin->bestSampleRate = 48000;
856
PA_DEBUG(("48kHz supported\n"));
858
else if(( pin->bestSampleRate != 48000) && ( pin->bestSampleRate != 44100 ) &&
859
(((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 44100) &&
860
(((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 44100) )
862
pin->bestSampleRate = 44100;
863
PA_DEBUG(("44.1kHz supported\n"));
867
pin->bestSampleRate = ((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency;
870
dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
873
if( result != paNoError )
876
/* Get instance information */
877
result = WdmGetPinPropertySimple(
878
parentFilter->handle,
881
KSPROPERTY_PIN_CINSTANCES,
883
sizeof(KSPIN_CINSTANCES));
885
if( result != paNoError )
890
PA_DEBUG(("Pin created successfully\n"));
898
PaUtil_FreeMemory( item );
901
PaUtil_FreeMemory( pin->pinConnect );
902
PaUtil_FreeMemory( pin->dataRangesItem );
903
PaUtil_FreeMemory( pin );
911
Safely free all resources associated with the pin
913
static void PinFree(PaWinWdmPin* pin)
919
if( pin->pinConnect )
921
PaUtil_FreeMemory( pin->pinConnect );
923
if( pin->dataRangesItem )
925
PaUtil_FreeMemory( pin->dataRangesItem );
927
PaUtil_FreeMemory( pin );
933
If the pin handle is open, close it
935
static void PinClose(PaWinWdmPin* pin)
940
PA_DEBUG(("Closing NULL pin!"));
944
if( pin->handle != NULL )
946
PinSetState( pin, KSSTATE_PAUSE );
947
PinSetState( pin, KSSTATE_STOP );
948
CloseHandle( pin->handle );
950
FilterRelease(pin->parentFilter);
956
Set the state of this (instantiated) pin
958
static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state)
964
return paInternalError;
965
if( pin->handle == NULL )
966
return paInternalError;
968
result = WdmSetPropertySimple(
970
&KSPROPSETID_Connection,
971
KSPROPERTY_CONNECTION_STATE,
980
static PaError PinInstantiate(PaWinWdmPin* pin)
983
unsigned long createResult;
984
KSALLOCATOR_FRAMING ksaf;
985
KSALLOCATOR_FRAMING_EX ksafex;
990
return paInternalError;
992
return paInternalError;
994
FilterUse(pin->parentFilter);
996
createResult = FunctionKsCreatePin(
997
pin->parentFilter->handle,
999
GENERIC_WRITE | GENERIC_READ,
1003
PA_DEBUG(("Pin create result = %x\n",createResult));
1004
if( createResult != ERROR_SUCCESS )
1006
FilterRelease(pin->parentFilter);
1008
return paInvalidDevice;
1011
result = WdmGetPropertySimple(
1013
&KSPROPSETID_Connection,
1014
KSPROPERTY_CONNECTION_ALLOCATORFRAMING,
1020
if( result != paNoError )
1022
result = WdmGetPropertySimple(
1024
&KSPROPSETID_Connection,
1025
KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX,
1030
if( result == paNoError )
1032
pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize;
1037
pin->frameSize = ksaf.FrameSize;
1046
static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state)
1051
return paInternalError;
1053
return paInternalError;
1054
if( pin->handle == NULL )
1055
return paInternalError;
1057
result = WdmGetPropertySimple(
1059
KSPROPSETID_Connection,
1060
KSPROPERTY_CONNECTION_STATE,
1069
static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format)
1077
return paInternalError;
1078
if( format == NULL )
1079
return paInternalError;
1081
size = GetWfexSize(format) + sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX) - sizeof(WAVEFORMATEX);
1083
if( pin->pinConnectSize != size )
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);
1096
memcpy( (void*)&(pin->ksDataFormatWfx->WaveFormatEx), format, GetWfexSize(format) );
1097
pin->ksDataFormatWfx->DataFormat.SampleSize = (unsigned short)(format->nChannels * (format->wBitsPerSample / 8));
1104
static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format)
1106
KSDATARANGE_AUDIO* dataRange;
1107
unsigned long count;
1108
GUID guid = DYNAMIC_GUID( DEFINE_WAVEFORMATEX_GUID(format->wFormatTag) );
1109
PaError result = paInvalidDevice;
1113
if( format->wFormatTag == WAVE_FORMAT_EXTENSIBLE )
1115
guid = ((WAVEFORMATEXTENSIBLE*)format)->SubFormat;
1117
dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges;
1118
for(count = 0; count<pin->dataRangesItem->Count; count++)
1120
if(( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_AUDIO,sizeof(GUID)) ) ||
1121
( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_WILDCARD,sizeof(GUID)) ))
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)) ))
1127
if(( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WILDCARD,sizeof(GUID)) ) ||
1128
( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX,sizeof(GUID) )))
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));
1137
if( dataRange->MaximumChannels < format->nChannels )
1139
result = paInvalidChannelCount;
1142
if( dataRange->MinimumBitsPerSample > format->wBitsPerSample )
1144
result = paSampleFormatNotSupported;
1147
if( dataRange->MaximumBitsPerSample < format->wBitsPerSample )
1149
result = paSampleFormatNotSupported;
1152
if( dataRange->MinimumSampleFrequency > format->nSamplesPerSec )
1154
result = paInvalidSampleRate;
1157
if( dataRange->MaximumSampleFrequency < format->nSamplesPerSec )
1159
result = paInvalidSampleRate;
1168
dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize);
1177
* Create a new filter object
1179
static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError* error)
1181
PaWinWdmFilter* filter;
1187
/* Allocate the new filter object */
1188
filter = (PaWinWdmFilter*)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter) );
1191
result = paInsufficientMemory;
1195
/* Zero the filter object - done by AllocateMemory */
1196
/* memset( (void*)filter, 0, sizeof(PaWinWdmFilter) ); */
1198
/* Copy the filter name */
1199
_tcsncpy(filter->filterName, filterName, MAX_PATH);
1201
/* Copy the friendly name */
1202
_tcsncpy(filter->friendlyName, friendlyName, MAX_PATH);
1204
/* Open the filter handle */
1205
result = FilterUse(filter);
1206
if( result != paNoError )
1212
result = WdmGetPinPropertySimple
1217
KSPROPERTY_PIN_CTYPES,
1219
sizeof(filter->pinCount)
1222
if( result != paNoError)
1227
/* Allocate pointer array to hold the pins */
1228
filter->pins = (PaWinWdmPin**)PaUtil_AllocateMemory( sizeof(PaWinWdmPin*) * filter->pinCount );
1231
result = paInsufficientMemory;
1235
/* Create all the pins we can */
1236
filter->maxInputChannels = 0;
1237
filter->maxOutputChannels = 0;
1238
filter->bestSampleRate = 0;
1241
for(pinId = 0; pinId < filter->pinCount; pinId++)
1243
/* Create the pin with this Id */
1244
PaWinWdmPin* newPin;
1245
newPin = PinNew(filter, pinId, &result);
1246
if( result == paInsufficientMemory )
1248
if( newPin != NULL )
1250
filter->pins[pinId] = newPin;
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)))
1258
if(newPin->maxChannels > filter->maxOutputChannels)
1259
filter->maxOutputChannels = newPin->maxChannels;
1260
filter->formats |= newPin->formats;
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)))
1267
if(newPin->maxChannels > filter->maxInputChannels)
1268
filter->maxInputChannels = newPin->maxChannels;
1269
filter->formats |= newPin->formats;
1272
if(newPin->bestSampleRate > filter->bestSampleRate)
1274
filter->bestSampleRate = newPin->bestSampleRate;
1279
if(( filter->maxInputChannels == 0) && ( filter->maxOutputChannels == 0))
1281
/* No input or output... not valid */
1287
/* No valid pin was found on this filter so we destroy it */
1288
result = paDeviceUnavailable;
1292
/* Close the filter handle for now
1293
* It will be opened later when needed */
1294
FilterRelease(filter);
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 );
1317
* Free a previously created filter
1319
static void FilterFree(PaWinWdmFilter* filter)
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 );
1336
* Reopen the filter handle if necessary so it can be used
1338
static PaError FilterUse(PaWinWdmFilter* filter)
1343
if( filter->handle == NULL )
1345
/* Open the filter */
1346
filter->handle = CreateFile(
1348
GENERIC_READ | GENERIC_WRITE,
1352
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
1355
if( filter->handle == NULL )
1357
return paDeviceUnavailable;
1360
filter->usageCount++;
1366
* Release the filter handle if nobody is using it
1368
static void FilterRelease(PaWinWdmFilter* filter)
1371
assert( filter->usageCount > 0 );
1374
filter->usageCount--;
1375
if( filter->usageCount == 0 )
1377
if( filter->handle != NULL )
1379
CloseHandle( filter->handle );
1380
filter->handle = NULL;
1387
* Create a render (playback) Pin using the supplied format
1389
static PaWinWdmPin* FilterCreateRenderPin(PaWinWdmFilter* filter,
1390
const WAVEFORMATEX* wfex,
1398
pin = FilterFindViableRenderPin(filter,wfex,&result);
1403
result = PinSetFormat(pin,wfex);
1404
if( result != paNoError )
1408
result = PinInstantiate(pin);
1409
if( result != paNoError )
1423
* Find a pin that supports the given format
1425
static PaWinWdmPin* FilterFindViableRenderPin(PaWinWdmFilter* filter,
1426
const WAVEFORMATEX* wfex,
1431
PaError result = paDeviceUnavailable;
1436
for( pinId = 0; pinId<filter->pinCount; pinId++ )
1438
pin = filter->pins[pinId];
1441
if(( pin->dataFlow == KSPIN_DATAFLOW_IN ) &&
1442
(( pin->communication == KSPIN_COMMUNICATION_SINK) ||
1443
( pin->communication == KSPIN_COMMUNICATION_BOTH)))
1445
result = PinIsFormatSupported( pin, wfex );
1446
if( result == paNoError )
1459
* Check if there is a pin that should playback
1460
* with the supplied format
1462
static PaError FilterCanCreateRenderPin(PaWinWdmFilter* filter,
1463
const WAVEFORMATEX* wfex)
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
1478
* Create a capture (record) Pin using the supplied format
1480
static PaWinWdmPin* FilterCreateCapturePin(PaWinWdmFilter* filter,
1481
const WAVEFORMATEX* wfex,
1489
pin = FilterFindViableCapturePin(filter,wfex,&result);
1495
result = PinSetFormat(pin,wfex);
1496
if( result != paNoError )
1501
result = PinInstantiate(pin);
1502
if( result != paNoError )
1516
* Find a capture pin that supports the given format
1518
static PaWinWdmPin* FilterFindViableCapturePin(PaWinWdmFilter* filter,
1519
const WAVEFORMATEX* wfex,
1524
PaError result = paDeviceUnavailable;
1529
for( pinId = 0; pinId<filter->pinCount; pinId++ )
1531
pin = filter->pins[pinId];
1534
if(( pin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
1535
(( pin->communication == KSPIN_COMMUNICATION_SINK) ||
1536
( pin->communication == KSPIN_COMMUNICATION_BOTH)))
1538
result = PinIsFormatSupported( pin, wfex );
1539
if( result == paNoError )
1552
* Check if there is a pin that should playback
1553
* with the supplied format
1555
static PaError FilterCanCreateCapturePin(PaWinWdmFilter* filter,
1556
const WAVEFORMATEX* wfex)
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
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.
1578
static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
1580
PaError result = paNoError;
1581
HDEVINFO handle = NULL;
1585
SP_DEVICE_INTERFACE_DATA interfaceData;
1586
SP_DEVICE_INTERFACE_DATA aliasData;
1587
SP_DEVINFO_DATA devInfoData;
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];
1594
DWORD sizeFriendlyName;
1596
PaWinWdmFilter* newFilter;
1597
GUID* category = (GUID*)&KSCATEGORY_AUDIO;
1598
GUID* alias_render = (GUID*)&KSCATEGORY_RENDER;
1599
GUID* alias_capture = (GUID*)&KSCATEGORY_CAPTURE;
1604
devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
1606
/* Open a handle to search for devices (filters) */
1607
handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
1608
if( handle == NULL )
1610
return paUnanticipatedHostError;
1612
PA_DEBUG(("Setup called\n"));
1614
/* First let's count the number of devices so we can allocate a list */
1616
for( device = 0;;device++ )
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"));
1625
break; /* No more devices */
1627
/* Check this one has the render or capture alias */
1629
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
1630
PA_DEBUG(("noError = %d\n",noError));
1633
if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1635
PA_DEBUG(("Device %d has render alias\n",device));
1636
hasAlias |= 1; /* Has render alias */
1640
PA_DEBUG(("Device %d has no render alias\n",device));
1643
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
1646
if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1648
PA_DEBUG(("Device %d has capture alias\n",device));
1649
hasAlias |= 2; /* Has capture alias */
1653
PA_DEBUG(("Device %d has no capture alias\n",device));
1657
invalidDevices++; /* This was not a valid capture or render audio device */
1660
/* Remember how many there are */
1661
wdmHostApi->filterCount = device-invalidDevices;
1663
PA_DEBUG(("Interfaces found: %d\n",device-invalidDevices));
1665
/* Now allocate the list of pointers to devices */
1666
wdmHostApi->filters = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * device );
1667
if( !wdmHostApi->filters )
1670
SetupDiDestroyDeviceInfoList(handle);
1671
return paInsufficientMemory;
1674
/* Now create filter objects for each interface found */
1676
for( device = 0;;device++ )
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;
1685
noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
1687
break; /* No more devices */
1689
/* Check this one has the render or capture alias */
1691
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
1694
if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1696
PA_DEBUG(("Device %d has render alias\n",device));
1697
hasAlias |= 1; /* Has render alias */
1700
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
1703
if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1705
PA_DEBUG(("Device %d has capture alias\n",device));
1706
hasAlias |= 2; /* Has capture alias */
1710
continue; /* This was not a valid capture or render audio device */
1712
noError = SetupDiGetDeviceInterfaceDetail(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
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)
1724
noError = RegQueryValueEx(hkey,TEXT("FriendlyName"),0,&type,(BYTE*)friendlyName,&sizeFriendlyName);
1725
if( noError == ERROR_SUCCESS )
1727
PA_DEBUG(("Interface %d, Name: %s\n",device,friendlyName));
1732
friendlyName[0] = 0;
1735
newFilter = FilterNew(devInterfaceDetails->DevicePath,friendlyName,&result);
1736
if( result == paNoError )
1738
PA_DEBUG(("Filter created\n"));
1739
wdmHostApi->filters[slot] = newFilter;
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--;
1754
SetupDiDestroyDeviceInfoList(handle);
1759
PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
1761
PaError result = paNoError;
1763
PaWinWdmHostApiRepresentation *wdmHostApi;
1764
PaWinWdmDeviceInfo *deviceInfoArray;
1765
PaWinWdmFilter* pFilter;
1766
PaWinWdmDeviceInfo *wdmDeviceInfo;
1767
PaDeviceInfo *deviceInfo;
1772
Attempt to load the KSUSER.DLL without which we cannot create pins
1773
We will unload this on termination
1775
if(DllKsUser == NULL)
1777
DllKsUser = LoadLibrary(TEXT("ksuser.dll"));
1778
if(DllKsUser == NULL)
1782
FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin");
1783
if(FunctionKsCreatePin == NULL)
1786
wdmHostApi = (PaWinWdmHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWdmHostApiRepresentation) );
1789
result = paInsufficientMemory;
1793
wdmHostApi->allocations = PaUtil_CreateAllocationGroup();
1794
if( !wdmHostApi->allocations )
1796
result = paInsufficientMemory;
1800
result = BuildFilterList( wdmHostApi );
1801
if( result != paNoError )
1805
deviceCount = wdmHostApi->filterCount;
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;
1814
if( deviceCount > 0 )
1816
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1817
wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo*) * deviceCount );
1818
if( !(*hostApi)->deviceInfos )
1820
result = paInsufficientMemory;
1824
/* allocate all device info structs in a contiguous block */
1825
deviceInfoArray = (PaWinWdmDeviceInfo*)PaUtil_GroupAllocateMemory(
1826
wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo) * deviceCount );
1827
if( !deviceInfoArray )
1829
result = paInsufficientMemory;
1833
for( i=0; i < deviceCount; ++i )
1835
wdmDeviceInfo = &deviceInfoArray[i];
1836
deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo;
1837
pFilter = wdmHostApi->filters[i];
1838
if( pFilter == NULL )
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)
1848
/* Set the default input device to the first device we find with
1849
* more than zero input channels
1851
if((*hostApi)->info.defaultInputDevice == paNoDevice)
1853
(*hostApi)->info.defaultInputDevice = i;
1857
deviceInfo->maxOutputChannels = pFilter->maxOutputChannels;
1858
if(deviceInfo->maxOutputChannels > 0)
1860
/* Set the default output device to the first device we find with
1861
* more than zero output channels
1863
if((*hostApi)->info.defaultOutputDevice == paNoDevice)
1865
(*hostApi)->info.defaultOutputDevice = i;
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
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);
1882
(*hostApi)->deviceInfos[i] = deviceInfo;
1886
(*hostApi)->info.deviceCount = deviceCount;
1888
(*hostApi)->Terminate = Terminate;
1889
(*hostApi)->OpenStream = OpenStream;
1890
(*hostApi)->IsFormatSupported = IsFormatSupported;
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 );
1898
PaUtil_InitializeStreamInterface( &wdmHostApi->blockingStreamInterface, CloseStream, StartStream,
1899
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1900
GetStreamTime, PaUtil_DummyGetCpuLoad,
1901
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1907
if( DllKsUser != NULL )
1909
FreeLibrary( DllKsUser );
1915
PaUtil_FreeMemory( wdmHostApi->filters );
1916
if( wdmHostApi->allocations )
1918
PaUtil_FreeAllAllocations( wdmHostApi->allocations );
1919
PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
1921
PaUtil_FreeMemory( wdmHostApi );
1928
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1930
PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
1934
if( wdmHostApi->filters )
1936
for( i=0; i<wdmHostApi->filterCount; i++)
1938
if( wdmHostApi->filters[i] != NULL )
1940
FilterFree( wdmHostApi->filters[i] );
1941
wdmHostApi->filters[i] = NULL;
1945
PaUtil_FreeMemory( wdmHostApi->filters );
1946
if( wdmHostApi->allocations )
1948
PaUtil_FreeAllAllocations( wdmHostApi->allocations );
1949
PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
1951
PaUtil_FreeMemory( wdmHostApi );
1955
static void FillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
1958
PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
1959
PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
1960
PA_DEBUG(( "chanelCount = %d\n", channelCount ));
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;
1968
pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
1969
if(sampleFormat == paFloat32)
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;
1977
else if(sampleFormat == paInt32)
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;
1985
else if(sampleFormat == paInt24)
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;
1993
else if(sampleFormat == paInt16)
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;
2001
pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
2006
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
2007
const PaStreamParameters *inputParameters,
2008
const PaStreamParameters *outputParameters,
2011
int inputChannelCount, outputChannelCount;
2012
PaSampleFormat inputSampleFormat, outputSampleFormat;
2013
PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
2014
PaWinWdmFilter* pFilter;
2015
int result = paFormatIsSupported;
2016
WAVEFORMATEXTENSIBLE wfx;
2020
if( inputParameters )
2022
inputChannelCount = inputParameters->channelCount;
2023
inputSampleFormat = inputParameters->sampleFormat;
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;
2030
/* unless alternate device specification is supported, reject the use of
2031
paUseHostApiSpecificDeviceSpecification */
2033
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
2034
return paInvalidDevice;
2036
/* check that input device can support inputChannelCount */
2037
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
2038
return paInvalidChannelCount;
2040
/* validate inputStreamInfo */
2041
if( inputParameters->hostApiSpecificStreamInfo )
2042
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2044
/* Check that the input format is supported */
2045
FillWFEXT(&wfx,paInt16,sampleRate,inputChannelCount);
2047
pFilter = wdmHostApi->filters[inputParameters->device];
2048
result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
2049
if( result != paNoError )
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 )
2064
inputChannelCount = 0;
2067
if( outputParameters )
2069
outputChannelCount = outputParameters->channelCount;
2070
outputSampleFormat = outputParameters->sampleFormat;
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;
2077
/* unless alternate device specification is supported, reject the use of
2078
paUseHostApiSpecificDeviceSpecification */
2080
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
2081
return paInvalidDevice;
2083
/* check that output device can support outputChannelCount */
2084
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
2085
return paInvalidChannelCount;
2087
/* validate outputStreamInfo */
2088
if( outputParameters->hostApiSpecificStreamInfo )
2089
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2091
/* Check that the output format is supported */
2092
FillWFEXT(&wfx,paInt16,sampleRate,outputChannelCount);
2094
pFilter = wdmHostApi->filters[outputParameters->device];
2095
result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
2096
if( result != paNoError )
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 )
2112
outputChannelCount = 0;
2118
- if a full duplex stream is requested, check that the combination
2119
of input and output parameters is supported if necessary
2121
- check that the device supports sampleRate
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.
2127
- check that input device can support inputSampleFormat, or that
2128
we have the capability to convert from inputSampleFormat to
2131
- check that output device can support outputSampleFormat, or that
2132
we have the capability to convert from outputSampleFormat to
2135
if((inputChannelCount == 0)&&(outputChannelCount == 0))
2136
result = paSampleFormatNotSupported; /* Not right error */
2142
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
2144
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
2146
const PaStreamParameters *inputParameters,
2147
const PaStreamParameters *outputParameters,
2149
unsigned long framesPerBuffer,
2150
PaStreamFlags streamFlags,
2151
PaStreamCallback *streamCallback,
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;
2162
PaWinWdmFilter* pFilter;
2163
WAVEFORMATEXTENSIBLE wfx;
2166
PA_DEBUG(("OpenStream:sampleRate = %f\n",sampleRate));
2167
PA_DEBUG(("OpenStream:framesPerBuffer = %lu\n",framesPerBuffer));
2169
if( inputParameters )
2171
userInputChannels = inputParameters->channelCount;
2172
inputSampleFormat = inputParameters->sampleFormat;
2174
/* unless alternate device specification is supported, reject the use of
2175
paUseHostApiSpecificDeviceSpecification */
2177
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
2178
return paInvalidDevice;
2180
/* check that input device can support stream->userInputChannels */
2181
if( userInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
2182
return paInvalidChannelCount;
2184
/* validate inputStreamInfo */
2185
if( inputParameters->hostApiSpecificStreamInfo )
2186
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2191
userInputChannels = 0;
2192
inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
2195
if( outputParameters )
2197
userOutputChannels = outputParameters->channelCount;
2198
outputSampleFormat = outputParameters->sampleFormat;
2200
/* unless alternate device specification is supported, reject the use of
2201
paUseHostApiSpecificDeviceSpecification */
2203
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
2204
return paInvalidDevice;
2206
/* check that output device can support stream->userInputChannels */
2207
if( userOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
2208
return paInvalidChannelCount;
2210
/* validate outputStreamInfo */
2211
if( outputParameters->hostApiSpecificStreamInfo )
2212
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2217
userOutputChannels = 0;
2218
outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
2221
/* validate platform specific flags */
2222
if( (streamFlags & paPlatformSpecificFlags) != 0 )
2223
return paInvalidFlag; /* unexpected platform specific flag */
2225
stream = (PaWinWdmStream*)PaUtil_AllocateMemory( sizeof(PaWinWdmStream) );
2228
result = paInsufficientMemory;
2231
/* Zero the stream object */
2232
/* memset((void*)stream,0,sizeof(PaWinWdmStream)); */
2234
if( streamCallback )
2236
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2237
&wdmHostApi->callbackStreamInterface, streamCallback, userData );
2241
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2242
&wdmHostApi->blockingStreamInterface, streamCallback, userData );
2245
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
2247
/* Instantiate the input pin if necessary */
2248
if(userInputChannels > 0)
2250
result = paSampleFormatNotSupported;
2251
pFilter = wdmHostApi->filters[inputParameters->device];
2252
stream->userInputChannels = userInputChannels;
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;
2263
if(result != paNoError)
2264
{ /* Search through all PaSampleFormats to find one that works */
2265
hostInputSampleFormat = paFloat32;
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;
2273
if(stream->recordingPin == NULL) result = paSampleFormatNotSupported;
2274
if(result != paNoError) hostInputSampleFormat <<= 1;
2276
while(result != paNoError && hostInputSampleFormat <= paUInt8);
2279
if(result != paNoError)
2280
{ /* None of the PaSampleFormats worked. Set the hostInputSampleFormat to the best fit
2281
* and try a PCM format.
2283
hostInputSampleFormat =
2284
PaUtil_SelectClosestAvailableFormat( pFilter->formats, inputSampleFormat );
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;
2296
if( result != paNoError )
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
2303
if( stream->userInputChannels < pFilter->maxInputChannels )
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;
2310
if( result != paNoError )
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);
2323
if(stream->recordingPin == NULL)
2328
switch(hostInputSampleFormat)
2330
case paInt16: stream->inputSampleSize = 2; break;
2331
case paInt24: stream->inputSampleSize = 3; break;
2333
case paFloat32: stream->inputSampleSize = 4; break;
2336
stream->recordingPin->frameSize /= stream->bytesPerInputFrame;
2337
PA_DEBUG(("Pin output frames: %d\n",stream->recordingPin->frameSize));
2341
stream->recordingPin = NULL;
2342
stream->bytesPerInputFrame = 0;
2345
/* Instantiate the output pin if necessary */
2346
if(userOutputChannels > 0)
2348
result = paSampleFormatNotSupported;
2349
pFilter = wdmHostApi->filters[outputParameters->device];
2350
stream->userOutputChannels = userOutputChannels;
2352
if(((outputSampleFormat & ~paNonInterleaved) & pFilter->formats) != 0)
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;
2361
if(result != paNoError)
2363
hostOutputSampleFormat = paFloat32;
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;
2371
if(stream->playbackPin == NULL) result = paSampleFormatNotSupported;
2372
if(result != paNoError) hostOutputSampleFormat <<= 1;
2374
while(result != paNoError && hostOutputSampleFormat <= paUInt8);
2377
if(result != paNoError)
2379
hostOutputSampleFormat =
2380
PaUtil_SelectClosestAvailableFormat( pFilter->formats, outputSampleFormat );
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;
2392
if( result != paNoError )
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
2399
if( stream->userOutputChannels < pFilter->maxOutputChannels )
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 )
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);
2418
if(stream->playbackPin == NULL)
2423
switch(hostOutputSampleFormat)
2425
case paInt16: stream->outputSampleSize = 2; break;
2426
case paInt24: stream->outputSampleSize = 3; break;
2428
case paFloat32: stream->outputSampleSize = 4; break;
2431
stream->playbackPin->frameSize /= stream->bytesPerOutputFrame;
2432
PA_DEBUG(("Pin output frames: %d\n",stream->playbackPin->frameSize));
2436
stream->playbackPin = NULL;
2437
stream->bytesPerOutputFrame = 0;
2440
/* Calculate the framesPerHostXxxxBuffer size based upon the suggested latency values */
2442
/* Record the buffer length */
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;
2451
else if(stream->framesPerHostIBuffer < stream->recordingPin->frameSize)
2453
stream->framesPerHostIBuffer = stream->recordingPin->frameSize;
2455
PA_DEBUG(("Input frames chosen:%ld\n",stream->framesPerHostIBuffer));
2458
if(outputParameters)
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;
2466
else if(stream->framesPerHostOBuffer < stream->playbackPin->frameSize)
2468
stream->framesPerHostOBuffer = stream->playbackPin->frameSize;
2470
PA_DEBUG(("Output frames chosen:%ld\n",stream->framesPerHostOBuffer));
2473
/* Host buffer size is bounded to the largest of the input and output
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 )
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;
2492
PA_DEBUG(("BytesPerInputFrame = %d\n",stream->bytesPerInputFrame));
2493
PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->bytesPerOutputFrame));
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 )
2502
PA_DEBUG(("Cannot allocate host buffer!\n"));
2503
result = paInsufficientMemory;
2506
PA_DEBUG(("Buffer start = %p\n",stream->hostBuffer));
2507
/* memset(stream->hostBuffer,0,size); */
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)
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;
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;
2540
if(stream->userOutputChannels > 0)
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;
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;
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;
2568
*s = (PaStream*)stream;
2577
if(stream->events[size] != NULL)
2579
CloseHandle(stream->events[size]);
2580
stream->events[size] = NULL;
2583
if(stream->hostBuffer)
2584
PaUtil_FreeMemory( stream->hostBuffer );
2586
if(stream->playbackPin)
2587
PinClose(stream->playbackPin);
2588
if(stream->recordingPin)
2589
PinClose(stream->recordingPin);
2592
PaUtil_FreeMemory( stream );
2599
When CloseStream() is called, the multi-api layer ensures that
2600
the stream has already been stopped or aborted.
2602
static PaError CloseStream( PaStream* s )
2604
PaError result = paNoError;
2605
PaWinWdmStream *stream = (PaWinWdmStream*)s;
2610
assert(!stream->streamStarted);
2611
assert(!stream->streamActive);
2613
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2614
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2618
if(stream->events[size] != NULL)
2620
CloseHandle(stream->events[size]);
2621
stream->events[size] = NULL;
2624
if(stream->hostBuffer)
2625
PaUtil_FreeMemory( stream->hostBuffer );
2627
if(stream->playbackPin)
2628
PinClose(stream->playbackPin);
2629
if(stream->recordingPin)
2630
PinClose(stream->recordingPin);
2632
PaUtil_FreeMemory( stream );
2639
Write the supplied packet to the pin
2641
Should return false on success
2643
static BOOL PinWrite(HANDLE h, DATAPACKET* p)
2645
unsigned long cbReturned = 0;
2646
return DeviceIoControl(h,IOCTL_KS_WRITE_STREAM,NULL,0,
2647
&p->Header,p->Header.Size,&cbReturned,&p->Signal);
2651
Read to the supplied packet from the pin
2653
Should return false on success
2655
static BOOL PinRead(HANDLE h, DATAPACKET* p)
2657
unsigned long cbReturned = 0;
2658
return DeviceIoControl(h,IOCTL_KS_READ_STREAM,NULL,0,
2659
&p->Header,p->Header.Size,&cbReturned,&p->Signal);
2663
Copy the first interleaved channel of 16 bit data to the other channels
2665
static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples)
2667
unsigned short* data = (unsigned short*)buffer;
2669
unsigned short sourceSample;
2672
sourceSample = *data++;
2673
channel = channels-1;
2676
*data++ = sourceSample;
2682
Copy the first interleaved channel of 24 bit data to the other channels
2684
static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples)
2686
unsigned char* data = (unsigned char*)buffer;
2688
unsigned char sourceSample[3];
2691
sourceSample[0] = data[0];
2692
sourceSample[1] = data[1];
2693
sourceSample[2] = data[2];
2695
channel = channels-1;
2698
data[0] = sourceSample[0];
2699
data[1] = sourceSample[1];
2700
data[2] = sourceSample[2];
2707
Copy the first interleaved channel of 32 bit data to the other channels
2709
static void DuplicateFirstChannelInt32(void* buffer, int channels, int samples)
2711
unsigned long* data = (unsigned long*)buffer;
2713
unsigned long sourceSample;
2716
sourceSample = *data++;
2717
channel = channels-1;
2720
*data++ = sourceSample;
2725
static DWORD WINAPI ProcessingThread(LPVOID pParam)
2727
PaWinWdmStream *stream = (PaWinWdmStream*)pParam;
2728
PaStreamCallbackTimeInfo ti;
2729
int cbResult = paContinue;
2735
unsigned long eventSignaled;
2736
int fillPlaybuf = 0;
2737
int emptyRecordbuf = 0;
2738
int framesProcessed;
2739
unsigned long timeout;
2743
PaStreamCallbackFlags underover = 0;
2747
ti.inputBufferAdcTime = 0.0;
2748
ti.currentTime = 0.0;
2749
ti.outputBufferDacTime = 0.0;
2751
/* Get double buffering going */
2753
/* Submit buffers */
2754
if(stream->playbackPin)
2756
result = PinSetState(stream->playbackPin, KSSTATE_RUN);
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;
2766
if(stream->recordingPin)
2768
result = PinSetState(stream->recordingPin, KSSTATE_RUN);
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 */
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));
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));
2786
while(!stream->streamAbort)
2791
/* Wait for next input or output buffer to be finished with*/
2794
if(stream->streamStop)
2796
PA_DEBUG(("ss1:pending=%d ",pending));
2798
wait = WaitForMultipleObjects(5, stream->events, FALSE, 0);
2799
if( wait == WAIT_TIMEOUT )
2801
/* No (under|over)flow has ocurred */
2802
wait = WaitForMultipleObjects(5, stream->events, FALSE, timeout);
2803
eventSignaled = wait - WAIT_OBJECT_0;
2807
eventSignaled = wait - WAIT_OBJECT_0;
2808
if( eventSignaled < 2 )
2810
underover |= paInputOverflow;
2811
PA_DEBUG(("Input overflow\n"));
2813
else if(( eventSignaled < 4 )&&(!priming))
2815
underover |= paOutputUnderflow;
2816
PA_DEBUG(("Output underflow\n"));
2820
if(stream->streamStop)
2822
PA_DEBUG(("ss2:wait=%ld",wait));
2824
if(wait == WAIT_FAILED)
2826
PA_DEBUG(("Wait fail = %ld! ",wait));
2829
if(wait == WAIT_TIMEOUT)
2834
if(eventSignaled < 2)
2835
{ /* Recording input buffer has been filled */
2836
if(stream->playbackPin)
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)
2842
/* Yes, so do both buffers at same time */
2845
/* Was this an underflow situation? */
2847
underover |= paOutputUnderflow; /* Yes! */
2853
else if(eventSignaled < 4)
2854
{ /* Playback output buffer has been emptied */
2855
if(stream->recordingPin)
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 */
2863
/* Was this an overflow situation? */
2865
underover |= paInputOverflow; /* Yes! */
2874
assert(stream->streamAbort); /* Should have been set */
2875
PA_DEBUG(("ABORTING "));
2878
ResetEvent(stream->events[eventSignaled]);
2880
if(stream->streamStop)
2882
PA_DEBUG(("Stream stop! pending=%d",pending));
2883
cbResult = paComplete; /* Stop, but play remaining buffers */
2886
/* Do necessary buffer processing (which will invoke user callback if necessary */
2888
if(cbResult==paContinue)
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 */
2897
PaUtil_SetOutputFrameCount(&stream->bufferProcessor,0);
2898
if( stream->userOutputChannels == 1 )
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 */
2907
for(i=0;i<stream->userOutputChannels;i++)
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);
2916
PaUtil_SetInputFrameCount(&stream->bufferProcessor,stream->packets[inbuf].Header.DataUsed/stream->bytesPerInputFrame);
2917
for(i=0;i<stream->userInputChannels;i++)
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);
2924
if (stream->recordingPin && stream->playbackPin) /* full duplex */
2926
/* Only call the EndBufferProcessing function when the total input frames == total output frames */
2928
if((stream->bufferProcessor.hostInputFrameCount[0] + stream->bufferProcessor.hostInputFrameCount[1]) ==
2929
(stream->bufferProcessor.hostOutputFrameCount[0] + stream->bufferProcessor.hostOutputFrameCount[1]) )
2931
framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult);
2935
framesProcessed = 0;
2940
framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult);
2945
/* Copy the first output channel to the other channels */
2946
switch(stream->outputSampleSize)
2949
DuplicateFirstChannelInt16(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
2952
DuplicateFirstChannelInt24(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
2955
DuplicateFirstChannelInt32(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
2958
assert(0); /* Unsupported format! */
2962
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
2971
if(cbResult != paContinue)
2973
PA_DEBUG(("cbResult=%d, pending=%d:",cbResult,pending));
2976
/* Submit buffers */
2977
if((fillPlaybuf)&&(cbResult!=paAbort))
2979
if(!PinWrite(stream->playbackPin->handle,&stream->packets[outbuf+2]))
2980
outbuf = (outbuf+1)&1; /* Increment and wrap */
2983
priming--; /* Have to prime twice */
2985
if((emptyRecordbuf)&&(cbResult==paContinue))
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 */
2994
PA_DEBUG(("pending==0 finished...;"));
2997
if((!stream->playbackPin)&&(cbResult!=paContinue))
2999
PA_DEBUG(("record only cbResult=%d...;",cbResult));
3004
PA_DEBUG(("Finished thread"));
3006
/* Finished, either normally or aborted */
3007
if(stream->playbackPin)
3009
result = PinSetState(stream->playbackPin, KSSTATE_PAUSE);
3010
result = PinSetState(stream->playbackPin, KSSTATE_STOP);
3012
if(stream->recordingPin)
3014
result = PinSetState(stream->recordingPin, KSSTATE_PAUSE);
3015
result = PinSetState(stream->recordingPin, KSSTATE_STOP);
3018
stream->streamActive = 0;
3020
if((!stream->streamStop)&&(!stream->streamAbort))
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 );
3027
stream->streamStop = 0;
3028
stream->streamAbort = 0;
3030
/* Reset process priority if necessary */
3031
if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
3033
SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
3034
stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
3042
static PaError StartStream( PaStream *s )
3044
PaError result = paNoError;
3045
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3052
stream->streamStop = 0;
3053
stream->streamAbort = 0;
3057
if(stream->events[size] != NULL)
3059
ResetEvent(stream->events[size]);
3063
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
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));*/
3072
stream->streamStarted = 1;
3073
stream->streamThread = (HANDLE)_beginthreadex(NULL, 0, ProcessingThread, stream, 0, &dwID);
3074
if(stream->streamThread == NULL)
3076
stream->streamStarted = 0;
3077
result = paInsufficientMemory;
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;
3091
static PaError StopStream( PaStream *s )
3093
PaError result = paNoError;
3094
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3099
if(stream->streamActive)
3102
stream->streamStop = 1;
3103
while(stream->streamActive)
3106
Sleep(10); /* Let thread sleep for 10 msec */
3110
PA_DEBUG(("Terminating thread"));
3111
if(stream->streamStarted && stream->streamThread)
3113
TerminateThread(stream->streamThread,0);
3114
stream->streamThread = NULL;
3117
stream->streamStarted = 0;
3119
if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
3121
SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
3122
stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
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 );
3138
static PaError AbortStream( PaStream *s )
3140
PaError result = paNoError;
3141
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3146
if(stream->streamActive)
3149
stream->streamAbort = 1;
3150
SetEvent(stream->events[4]); /* Signal immediately */
3151
while(stream->streamActive)
3157
if(stream->streamStarted && stream->streamThread)
3159
TerminateThread(stream->streamThread,0);
3160
stream->streamThread = NULL;
3163
stream->streamStarted = 0;
3165
if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
3167
SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
3168
stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
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 );
3180
stream->streamActive = 0;
3181
stream->streamStarted = 0;
3188
static PaError IsStreamStopped( PaStream *s )
3190
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3195
if(!stream->streamStarted)
3203
static PaError IsStreamActive( PaStream *s )
3205
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3210
if(stream->streamActive)
3218
static PaTime GetStreamTime( PaStream* s )
3223
return PaUtil_GetTime();
3227
static double GetStreamCpuLoad( PaStream* s )
3229
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3232
result = PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
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.
3244
static PaError ReadStream( PaStream* s,
3246
unsigned long frames )
3248
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3252
/* suppress unused variable warnings */
3257
/* IMPLEMENT ME, see portaudio.h for required behavior*/
3263
static PaError WriteStream( PaStream* s,
3265
unsigned long frames )
3267
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3271
/* suppress unused variable warnings */
3276
/* IMPLEMENT ME, see portaudio.h for required behavior*/
3282
static signed long GetStreamReadAvailable( PaStream* s )
3284
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3288
/* suppress unused variable warnings */
3291
/* IMPLEMENT ME, see portaudio.h for required behavior*/
3297
static signed long GetStreamWriteAvailable( PaStream* s )
3299
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3302
/* suppress unused variable warnings */
3305
/* IMPLEMENT ME, see portaudio.h for required behavior*/
b'\\ No newline at end of file'