2
* $Id: pa_win_wdmks.c 1411 2009-05-14 14:37:37Z rossb $
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
/* use CreateThread for CYGWIN, _beginthreadex for all others */
111
#define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
113
#define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
116
/* use ExitThread for CYGWIN, _endthreadex for all others */
118
#define EXIT_THREAD _endthreadex(0)
120
#define EXIT_THREAD ExitThread(0)
125
#define DYNAMIC_GUID(data) {data}
126
#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
128
#define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
129
#define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
130
#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
140
/* These next definitions allow the use of the KSUSER DLL */
141
typedef KSDDKAPI DWORD WINAPI KSCREATEPIN(HANDLE, PKSPIN_CONNECT, ACCESS_MASK, PHANDLE);
142
extern HMODULE DllKsUser;
143
extern KSCREATEPIN* FunctionKsCreatePin;
145
/* Forward definition to break circular type reference between pin and filter */
146
struct __PaWinWdmFilter;
147
typedef struct __PaWinWdmFilter PaWinWdmFilter;
150
* A pin is an input or output node, e.g. for audio flow */
151
typedef struct __PaWinWdmPin
154
PaWinWdmFilter* parentFilter;
156
KSPIN_CONNECT* pinConnect;
157
unsigned long pinConnectSize;
158
KSDATAFORMAT_WAVEFORMATEX* ksDataFormatWfx;
159
KSPIN_COMMUNICATION communication;
160
KSDATARANGE* dataRanges;
161
KSMULTIPLE_ITEM* dataRangesItem;
162
KSPIN_DATAFLOW dataFlow;
163
KSPIN_CINSTANCES instances;
164
unsigned long frameSize;
166
unsigned long formats;
171
/* The Filter structure
172
* A filter has a number of pins and a "friendly name" */
173
struct __PaWinWdmFilter
178
TCHAR filterName[MAX_PATH];
179
TCHAR friendlyName[MAX_PATH];
180
int maxInputChannels;
181
int maxOutputChannels;
182
unsigned long formats;
187
/* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */
188
typedef struct __PaWinWdmHostApiRepresentation
190
PaUtilHostApiRepresentation inheritedHostApiRep;
191
PaUtilStreamInterface callbackStreamInterface;
192
PaUtilStreamInterface blockingStreamInterface;
194
PaUtilAllocationGroup* allocations;
195
PaWinWdmFilter** filters;
198
PaWinWdmHostApiRepresentation;
200
typedef struct __PaWinWdmDeviceInfo
202
PaDeviceInfo inheritedDeviceInfo;
203
PaWinWdmFilter* filter;
207
typedef struct __DATAPACKET
209
KSSTREAM_HEADER Header;
213
/* PaWinWdmStream - a stream data structure specifically for this implementation */
214
typedef struct __PaWinWdmStream
216
PaUtilStreamRepresentation streamRepresentation;
217
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
218
PaUtilBufferProcessor bufferProcessor;
220
PaWinWdmPin* recordingPin;
221
PaWinWdmPin* playbackPin;
223
unsigned long framesPerHostIBuffer;
224
unsigned long framesPerHostOBuffer;
225
int bytesPerInputFrame;
226
int bytesPerOutputFrame;
231
int oldProcessPriority;
233
HANDLE events[5]; /* 2 play + 2 record packets + abort events */
234
DATAPACKET packets[4]; /* 2 play + 2 record */
235
PaStreamFlags streamFlags;
236
/* These values handle the case where the user wants to use fewer
237
* channels than the device has */
238
int userInputChannels;
239
int deviceInputChannels;
240
int userOutputChannels;
241
int deviceOutputChannels;
243
int outputSampleSize;
247
#include <setupapi.h>
249
HMODULE DllKsUser = NULL;
250
KSCREATEPIN* FunctionKsCreatePin = NULL;
252
/* prototypes for functions declared in this file */
257
#endif /* __cplusplus */
259
PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
263
#endif /* __cplusplus */
265
/* Low level I/O functions */
266
static PaError WdmSyncIoctl(HANDLE handle,
267
unsigned long ioctlNumber,
269
unsigned long inBufferCount,
271
unsigned long outBufferCount,
272
unsigned long* bytesReturned);
273
static PaError WdmGetPropertySimple(HANDLE handle,
274
const GUID* const guidPropertySet,
275
unsigned long property,
277
unsigned long valueCount,
279
unsigned long instanceCount);
280
static PaError WdmSetPropertySimple(HANDLE handle,
281
const GUID* const guidPropertySet,
282
unsigned long property,
284
unsigned long valueCount,
286
unsigned long instanceCount);
287
static PaError WdmGetPinPropertySimple(HANDLE handle,
289
const GUID* const guidPropertySet,
290
unsigned long property,
292
unsigned long valueCount);
293
static PaError WdmGetPinPropertyMulti(HANDLE handle,
295
const GUID* const guidPropertySet,
296
unsigned long property,
297
KSMULTIPLE_ITEM** ksMultipleItem);
299
/** Pin management functions */
300
static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error);
301
static void PinFree(PaWinWdmPin* pin);
302
static void PinClose(PaWinWdmPin* pin);
303
static PaError PinInstantiate(PaWinWdmPin* pin);
304
/*static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state); NOT USED */
305
static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state);
306
static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format);
307
static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format);
309
/* Filter management functions */
310
static PaWinWdmFilter* FilterNew(
314
static void FilterFree(PaWinWdmFilter* filter);
315
static PaWinWdmPin* FilterCreateRenderPin(
316
PaWinWdmFilter* filter,
317
const WAVEFORMATEX* wfex,
319
static PaWinWdmPin* FilterFindViableRenderPin(
320
PaWinWdmFilter* filter,
321
const WAVEFORMATEX* wfex,
323
static PaError FilterCanCreateRenderPin(
324
PaWinWdmFilter* filter,
325
const WAVEFORMATEX* wfex);
326
static PaWinWdmPin* FilterCreateCapturePin(
327
PaWinWdmFilter* filter,
328
const WAVEFORMATEX* wfex,
330
static PaWinWdmPin* FilterFindViableCapturePin(
331
PaWinWdmFilter* filter,
332
const WAVEFORMATEX* wfex,
334
static PaError FilterCanCreateCapturePin(
335
PaWinWdmFilter* filter,
336
const WAVEFORMATEX* pwfx);
337
static PaError FilterUse(
338
PaWinWdmFilter* filter);
339
static void FilterRelease(
340
PaWinWdmFilter* filter);
342
/* Interface functions */
343
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
344
static PaError IsFormatSupported(
345
struct PaUtilHostApiRepresentation *hostApi,
346
const PaStreamParameters *inputParameters,
347
const PaStreamParameters *outputParameters,
349
static PaError OpenStream(
350
struct PaUtilHostApiRepresentation *hostApi,
352
const PaStreamParameters *inputParameters,
353
const PaStreamParameters *outputParameters,
355
unsigned long framesPerBuffer,
356
PaStreamFlags streamFlags,
357
PaStreamCallback *streamCallback,
359
static PaError CloseStream( PaStream* stream );
360
static PaError StartStream( PaStream *stream );
361
static PaError StopStream( PaStream *stream );
362
static PaError AbortStream( PaStream *stream );
363
static PaError IsStreamStopped( PaStream *s );
364
static PaError IsStreamActive( PaStream *stream );
365
static PaTime GetStreamTime( PaStream *stream );
366
static double GetStreamCpuLoad( PaStream* stream );
367
static PaError ReadStream(
370
unsigned long frames );
371
static PaError WriteStream(
374
unsigned long frames );
375
static signed long GetStreamReadAvailable( PaStream* stream );
376
static signed long GetStreamWriteAvailable( PaStream* stream );
378
/* Utility functions */
379
static unsigned long GetWfexSize(const WAVEFORMATEX* wfex);
380
static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi);
381
static BOOL PinWrite(HANDLE h, DATAPACKET* p);
382
static BOOL PinRead(HANDLE h, DATAPACKET* p);
383
static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples);
384
static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples);
385
static DWORD WINAPI ProcessingThread(LPVOID pParam);
387
/* Function bodies */
389
static unsigned long GetWfexSize(const WAVEFORMATEX* wfex)
391
if( wfex->wFormatTag == WAVE_FORMAT_PCM )
393
return sizeof( WAVEFORMATEX );
397
return (sizeof( WAVEFORMATEX ) + wfex->cbSize);
402
Low level pin/filter access functions
404
static PaError WdmSyncIoctl(
406
unsigned long ioctlNumber,
408
unsigned long inBufferCount,
410
unsigned long outBufferCount,
411
unsigned long* bytesReturned)
413
PaError result = paNoError;
414
OVERLAPPED overlapped;
416
unsigned long dummyBytesReturned;
421
/* User a dummy as the caller hasn't supplied one */
422
bytesReturned = &dummyBytesReturned;
425
FillMemory((void *)&overlapped,sizeof(overlapped),0);
426
overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
427
if( !overlapped.hEvent )
429
result = paInsufficientMemory;
432
overlapped.hEvent = (HANDLE)((DWORD_PTR)overlapped.hEvent | 0x1);
434
boolResult = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount,
435
outBuffer, outBufferCount, bytesReturned, &overlapped);
438
error = GetLastError();
439
if( error == ERROR_IO_PENDING )
441
error = WaitForSingleObject(overlapped.hEvent,INFINITE);
442
if( error != WAIT_OBJECT_0 )
444
result = paUnanticipatedHostError;
448
else if((( error == ERROR_INSUFFICIENT_BUFFER ) ||
449
( error == ERROR_MORE_DATA )) &&
450
( ioctlNumber == IOCTL_KS_PROPERTY ) &&
451
( outBufferCount == 0 ))
457
result = paUnanticipatedHostError;
464
if( overlapped.hEvent )
466
CloseHandle( overlapped.hEvent );
471
static PaError WdmGetPropertySimple(HANDLE handle,
472
const GUID* const guidPropertySet,
473
unsigned long property,
475
unsigned long valueCount,
477
unsigned long instanceCount)
480
KSPROPERTY* ksProperty;
481
unsigned long propertyCount;
483
propertyCount = sizeof(KSPROPERTY) + instanceCount;
484
ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
487
return paInsufficientMemory;
490
FillMemory((void*)ksProperty,sizeof(ksProperty),0);
491
ksProperty->Set = *guidPropertySet;
492
ksProperty->Id = property;
493
ksProperty->Flags = KSPROPERTY_TYPE_GET;
497
memcpy( (void*)(((char*)ksProperty)+sizeof(KSPROPERTY)), instance, instanceCount );
500
result = WdmSyncIoctl(
509
PaUtil_FreeMemory( ksProperty );
513
static PaError WdmSetPropertySimple(
515
const GUID* const guidPropertySet,
516
unsigned long property,
518
unsigned long valueCount,
520
unsigned long instanceCount)
523
KSPROPERTY* ksProperty;
524
unsigned long propertyCount = 0;
526
propertyCount = sizeof(KSPROPERTY) + instanceCount;
527
ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
530
return paInsufficientMemory;
533
ksProperty->Set = *guidPropertySet;
534
ksProperty->Id = property;
535
ksProperty->Flags = KSPROPERTY_TYPE_SET;
539
memcpy((void*)((char*)ksProperty + sizeof(KSPROPERTY)), instance, instanceCount);
542
result = WdmSyncIoctl(
551
PaUtil_FreeMemory( ksProperty );
555
static PaError WdmGetPinPropertySimple(
558
const GUID* const guidPropertySet,
559
unsigned long property,
561
unsigned long valueCount)
566
ksPProp.Property.Set = *guidPropertySet;
567
ksPProp.Property.Id = property;
568
ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
569
ksPProp.PinId = pinId;
570
ksPProp.Reserved = 0;
572
result = WdmSyncIoctl(
584
static PaError WdmGetPinPropertyMulti(
587
const GUID* const guidPropertySet,
588
unsigned long property,
589
KSMULTIPLE_ITEM** ksMultipleItem)
592
unsigned long multipleItemSize = 0;
595
ksPProp.Property.Set = *guidPropertySet;
596
ksPProp.Property.Id = property;
597
ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
598
ksPProp.PinId = pinId;
599
ksPProp.Reserved = 0;
601
result = WdmSyncIoctl(
609
if( result != paNoError )
614
*ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
615
if( !*ksMultipleItem )
617
return paInsufficientMemory;
620
result = WdmSyncIoctl(
625
(void*)*ksMultipleItem,
629
if( result != paNoError )
631
PaUtil_FreeMemory( ksMultipleItem );
639
Create a new pin object belonging to a filter
640
The pin object holds all the configuration information about the pin
641
before it is opened, and then the handle of the pin after is opened
643
static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error)
648
KSMULTIPLE_ITEM* item = NULL;
649
KSIDENTIFIER* identifier;
650
KSDATARANGE* dataRange;
653
PA_DEBUG(("Creating pin %d:\n",pinId));
655
/* Allocate the new PIN object */
656
pin = (PaWinWdmPin*)PaUtil_AllocateMemory( sizeof(PaWinWdmPin) );
659
result = paInsufficientMemory;
663
/* Zero the pin object */
664
/* memset( (void*)pin, 0, sizeof(PaWinWdmPin) ); */
666
pin->parentFilter = parentFilter;
669
/* Allocate a connect structure */
670
pin->pinConnectSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX);
671
pin->pinConnect = (KSPIN_CONNECT*)PaUtil_AllocateMemory( pin->pinConnectSize );
672
if( !pin->pinConnect )
674
result = paInsufficientMemory;
678
/* Configure the connect structure with default values */
679
pin->pinConnect->Interface.Set = KSINTERFACESETID_Standard;
680
pin->pinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
681
pin->pinConnect->Interface.Flags = 0;
682
pin->pinConnect->Medium.Set = KSMEDIUMSETID_Standard;
683
pin->pinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
684
pin->pinConnect->Medium.Flags = 0;
685
pin->pinConnect->PinId = pinId;
686
pin->pinConnect->PinToHandle = NULL;
687
pin->pinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
688
pin->pinConnect->Priority.PrioritySubClass = 1;
689
pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)(pin->pinConnect + 1);
690
pin->ksDataFormatWfx->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEX);
691
pin->ksDataFormatWfx->DataFormat.Flags = 0;
692
pin->ksDataFormatWfx->DataFormat.Reserved = 0;
693
pin->ksDataFormatWfx->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
694
pin->ksDataFormatWfx->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
695
pin->ksDataFormatWfx->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
697
pin->frameSize = 0; /* Unknown until we instantiate pin */
699
/* Get the COMMUNICATION property */
700
result = WdmGetPinPropertySimple(
701
parentFilter->handle,
704
KSPROPERTY_PIN_COMMUNICATION,
706
sizeof(KSPIN_COMMUNICATION));
707
if( result != paNoError )
710
if( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/
711
(pin->communication != KSPIN_COMMUNICATION_SINK) &&
712
(pin->communication != KSPIN_COMMUNICATION_BOTH) )
714
PA_DEBUG(("Not source/sink\n"));
715
result = paInvalidDevice;
719
/* Get dataflow information */
720
result = WdmGetPinPropertySimple(
721
parentFilter->handle,
724
KSPROPERTY_PIN_DATAFLOW,
726
sizeof(KSPIN_DATAFLOW));
728
if( result != paNoError )
731
/* Get the INTERFACE property list */
732
result = WdmGetPinPropertyMulti(
733
parentFilter->handle,
736
KSPROPERTY_PIN_INTERFACES,
739
if( result != paNoError )
742
identifier = (KSIDENTIFIER*)(item+1);
744
/* Check that at least one interface is STANDARD_STREAMING */
745
result = paUnanticipatedHostError;
746
for( i = 0; i < item->Count; i++ )
748
if( !memcmp( (void*)&identifier[i].Set, (void*)&KSINTERFACESETID_Standard, sizeof( GUID ) ) &&
749
( identifier[i].Id == KSINTERFACE_STANDARD_STREAMING ) )
756
if( result != paNoError )
758
PA_DEBUG(("No standard streaming\n"));
762
/* Don't need interfaces any more */
763
PaUtil_FreeMemory( item );
766
/* Get the MEDIUM properties list */
767
result = WdmGetPinPropertyMulti(
768
parentFilter->handle,
771
KSPROPERTY_PIN_MEDIUMS,
774
if( result != paNoError )
777
identifier = (KSIDENTIFIER*)(item+1); /* Not actually necessary... */
779
/* Check that at least one medium is STANDARD_DEVIO */
780
result = paUnanticipatedHostError;
781
for( i = 0; i < item->Count; i++ )
783
if( !memcmp( (void*)&identifier[i].Set, (void*)&KSMEDIUMSETID_Standard, sizeof( GUID ) ) &&
784
( identifier[i].Id == KSMEDIUM_STANDARD_DEVIO ) )
791
if( result != paNoError )
793
PA_DEBUG(("No standard devio\n"));
796
/* Don't need mediums any more */
797
PaUtil_FreeMemory( item );
801
result = WdmGetPinPropertyMulti(
802
parentFilter->handle,
805
KSPROPERTY_PIN_DATARANGES,
806
&pin->dataRangesItem);
808
if( result != paNoError )
811
pin->dataRanges = (KSDATARANGE*)(pin->dataRangesItem +1);
813
/* Check that at least one datarange supports audio */
814
result = paUnanticipatedHostError;
815
dataRange = pin->dataRanges;
816
pin->maxChannels = 0;
817
pin->bestSampleRate = 0;
819
for( i = 0; i <pin->dataRangesItem->Count; i++)
821
PA_DEBUG(("DR major format %x\n",*(unsigned long*)(&(dataRange->MajorFormat))));
822
/* Check that subformat is WAVEFORMATEX, PCM or WILDCARD */
823
if( IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) ||
824
!memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_PCM, sizeof ( GUID ) ) ||
825
( !memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof ( GUID ) ) &&
826
( !memcmp((void*)&dataRange->MajorFormat, (void*)&KSDATAFORMAT_TYPE_AUDIO, sizeof ( GUID ) ) ) ) )
829
/* Record the maximum possible channels with this pin */
830
PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));
831
if( (int)((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels )
833
pin->maxChannels = ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels;
834
/*PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));*/
836
/* Record the formats (bit depths) that are supported */
837
if( ((KSDATARANGE_AUDIO*)dataRange)->MinimumBitsPerSample <= 16 )
839
pin->formats |= paInt16;
840
PA_DEBUG(("Format 16 bit supported\n"));
842
if( ((KSDATARANGE_AUDIO*)dataRange)->MaximumBitsPerSample >= 24 )
844
pin->formats |= paInt24;
845
PA_DEBUG(("Format 24 bit supported\n"));
847
if( ( pin->bestSampleRate != 48000) &&
848
(((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 48000) &&
849
(((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 48000) )
851
pin->bestSampleRate = 48000;
852
PA_DEBUG(("48kHz supported\n"));
854
else if(( pin->bestSampleRate != 48000) && ( pin->bestSampleRate != 44100 ) &&
855
(((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 44100) &&
856
(((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 44100) )
858
pin->bestSampleRate = 44100;
859
PA_DEBUG(("44.1kHz supported\n"));
863
pin->bestSampleRate = ((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency;
866
dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
869
if( result != paNoError )
872
/* Get instance information */
873
result = WdmGetPinPropertySimple(
874
parentFilter->handle,
877
KSPROPERTY_PIN_CINSTANCES,
879
sizeof(KSPIN_CINSTANCES));
881
if( result != paNoError )
886
PA_DEBUG(("Pin created successfully\n"));
894
PaUtil_FreeMemory( item );
897
PaUtil_FreeMemory( pin->pinConnect );
898
PaUtil_FreeMemory( pin->dataRangesItem );
899
PaUtil_FreeMemory( pin );
907
Safely free all resources associated with the pin
909
static void PinFree(PaWinWdmPin* pin)
915
if( pin->pinConnect )
917
PaUtil_FreeMemory( pin->pinConnect );
919
if( pin->dataRangesItem )
921
PaUtil_FreeMemory( pin->dataRangesItem );
923
PaUtil_FreeMemory( pin );
929
If the pin handle is open, close it
931
static void PinClose(PaWinWdmPin* pin)
936
PA_DEBUG(("Closing NULL pin!"));
940
if( pin->handle != NULL )
942
PinSetState( pin, KSSTATE_PAUSE );
943
PinSetState( pin, KSSTATE_STOP );
944
CloseHandle( pin->handle );
946
FilterRelease(pin->parentFilter);
952
Set the state of this (instantiated) pin
954
static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state)
960
return paInternalError;
961
if( pin->handle == NULL )
962
return paInternalError;
964
result = WdmSetPropertySimple(
966
&KSPROPSETID_Connection,
967
KSPROPERTY_CONNECTION_STATE,
976
static PaError PinInstantiate(PaWinWdmPin* pin)
979
unsigned long createResult;
980
KSALLOCATOR_FRAMING ksaf;
981
KSALLOCATOR_FRAMING_EX ksafex;
986
return paInternalError;
988
return paInternalError;
990
FilterUse(pin->parentFilter);
992
createResult = FunctionKsCreatePin(
993
pin->parentFilter->handle,
995
GENERIC_WRITE | GENERIC_READ,
999
PA_DEBUG(("Pin create result = %x\n",createResult));
1000
if( createResult != ERROR_SUCCESS )
1002
FilterRelease(pin->parentFilter);
1004
return paInvalidDevice;
1007
result = WdmGetPropertySimple(
1009
&KSPROPSETID_Connection,
1010
KSPROPERTY_CONNECTION_ALLOCATORFRAMING,
1016
if( result != paNoError )
1018
result = WdmGetPropertySimple(
1020
&KSPROPSETID_Connection,
1021
KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX,
1026
if( result == paNoError )
1028
pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize;
1033
pin->frameSize = ksaf.FrameSize;
1042
static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state)
1047
return paInternalError;
1049
return paInternalError;
1050
if( pin->handle == NULL )
1051
return paInternalError;
1053
result = WdmGetPropertySimple(
1055
KSPROPSETID_Connection,
1056
KSPROPERTY_CONNECTION_STATE,
1065
static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format)
1073
return paInternalError;
1074
if( format == NULL )
1075
return paInternalError;
1077
size = GetWfexSize(format) + sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX) - sizeof(WAVEFORMATEX);
1079
if( pin->pinConnectSize != size )
1081
newConnect = PaUtil_AllocateMemory( size );
1082
if( newConnect == NULL )
1083
return paInsufficientMemory;
1084
memcpy( newConnect, (void*)pin->pinConnect, min(pin->pinConnectSize,size) );
1085
PaUtil_FreeMemory( pin->pinConnect );
1086
pin->pinConnect = (KSPIN_CONNECT*)newConnect;
1087
pin->pinConnectSize = size;
1088
pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)((KSPIN_CONNECT*)newConnect + 1);
1089
pin->ksDataFormatWfx->DataFormat.FormatSize = size - sizeof(KSPIN_CONNECT);
1092
memcpy( (void*)&(pin->ksDataFormatWfx->WaveFormatEx), format, GetWfexSize(format) );
1093
pin->ksDataFormatWfx->DataFormat.SampleSize = (unsigned short)(format->nChannels * (format->wBitsPerSample / 8));
1100
static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format)
1102
KSDATARANGE_AUDIO* dataRange;
1103
unsigned long count;
1104
GUID guid = DYNAMIC_GUID( DEFINE_WAVEFORMATEX_GUID(format->wFormatTag) );
1105
PaError result = paInvalidDevice;
1109
if( format->wFormatTag == WAVE_FORMAT_EXTENSIBLE )
1111
guid = ((WAVEFORMATEXTENSIBLE*)format)->SubFormat;
1113
dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges;
1114
for(count = 0; count<pin->dataRangesItem->Count; count++)
1116
if(( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_AUDIO,sizeof(GUID)) ) ||
1117
( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_WILDCARD,sizeof(GUID)) ))
1119
/* This is an audio or wildcard datarange... */
1120
if(( !memcmp(&(dataRange->DataRange.SubFormat),&KSDATAFORMAT_SUBTYPE_WILDCARD,sizeof(GUID)) ) ||
1121
( !memcmp(&(dataRange->DataRange.SubFormat),&guid,sizeof(GUID)) ))
1123
if(( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WILDCARD,sizeof(GUID)) ) ||
1124
( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX,sizeof(GUID) )))
1127
PA_DEBUG(("Pin:%x, DataRange:%d\n",(void*)pin,count));
1128
PA_DEBUG(("\tFormatSize:%d, SampleSize:%d\n",dataRange->DataRange.FormatSize,dataRange->DataRange.SampleSize));
1129
PA_DEBUG(("\tMaxChannels:%d\n",dataRange->MaximumChannels));
1130
PA_DEBUG(("\tBits:%d-%d\n",dataRange->MinimumBitsPerSample,dataRange->MaximumBitsPerSample));
1131
PA_DEBUG(("\tSampleRate:%d-%d\n",dataRange->MinimumSampleFrequency,dataRange->MaximumSampleFrequency));
1133
if( dataRange->MaximumChannels < format->nChannels )
1135
result = paInvalidChannelCount;
1138
if( dataRange->MinimumBitsPerSample > format->wBitsPerSample )
1140
result = paSampleFormatNotSupported;
1143
if( dataRange->MaximumBitsPerSample < format->wBitsPerSample )
1145
result = paSampleFormatNotSupported;
1148
if( dataRange->MinimumSampleFrequency > format->nSamplesPerSec )
1150
result = paInvalidSampleRate;
1153
if( dataRange->MaximumSampleFrequency < format->nSamplesPerSec )
1155
result = paInvalidSampleRate;
1164
dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize);
1173
* Create a new filter object
1175
static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError* error)
1177
PaWinWdmFilter* filter;
1183
/* Allocate the new filter object */
1184
filter = (PaWinWdmFilter*)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter) );
1187
result = paInsufficientMemory;
1191
/* Zero the filter object - done by AllocateMemory */
1192
/* memset( (void*)filter, 0, sizeof(PaWinWdmFilter) ); */
1194
/* Copy the filter name */
1195
_tcsncpy(filter->filterName, filterName, MAX_PATH);
1197
/* Copy the friendly name */
1198
_tcsncpy(filter->friendlyName, friendlyName, MAX_PATH);
1200
/* Open the filter handle */
1201
result = FilterUse(filter);
1202
if( result != paNoError )
1208
result = WdmGetPinPropertySimple
1213
KSPROPERTY_PIN_CTYPES,
1215
sizeof(filter->pinCount)
1218
if( result != paNoError)
1223
/* Allocate pointer array to hold the pins */
1224
filter->pins = (PaWinWdmPin**)PaUtil_AllocateMemory( sizeof(PaWinWdmPin*) * filter->pinCount );
1227
result = paInsufficientMemory;
1231
/* Create all the pins we can */
1232
filter->maxInputChannels = 0;
1233
filter->maxOutputChannels = 0;
1234
filter->bestSampleRate = 0;
1237
for(pinId = 0; pinId < filter->pinCount; pinId++)
1239
/* Create the pin with this Id */
1240
PaWinWdmPin* newPin;
1241
newPin = PinNew(filter, pinId, &result);
1242
if( result == paInsufficientMemory )
1244
if( newPin != NULL )
1246
filter->pins[pinId] = newPin;
1249
/* Get the max output channel count */
1250
if(( newPin->dataFlow == KSPIN_DATAFLOW_IN ) &&
1251
(( newPin->communication == KSPIN_COMMUNICATION_SINK) ||
1252
( newPin->communication == KSPIN_COMMUNICATION_BOTH)))
1254
if(newPin->maxChannels > filter->maxOutputChannels)
1255
filter->maxOutputChannels = newPin->maxChannels;
1256
filter->formats |= newPin->formats;
1258
/* Get the max input channel count */
1259
if(( newPin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
1260
(( newPin->communication == KSPIN_COMMUNICATION_SINK) ||
1261
( newPin->communication == KSPIN_COMMUNICATION_BOTH)))
1263
if(newPin->maxChannels > filter->maxInputChannels)
1264
filter->maxInputChannels = newPin->maxChannels;
1265
filter->formats |= newPin->formats;
1268
if(newPin->bestSampleRate > filter->bestSampleRate)
1270
filter->bestSampleRate = newPin->bestSampleRate;
1275
if(( filter->maxInputChannels == 0) && ( filter->maxOutputChannels == 0))
1277
/* No input or output... not valid */
1283
/* No valid pin was found on this filter so we destroy it */
1284
result = paDeviceUnavailable;
1288
/* Close the filter handle for now
1289
* It will be opened later when needed */
1290
FilterRelease(filter);
1301
for( pinId = 0; pinId < filter->pinCount; pinId++ )
1302
PinFree(filter->pins[pinId]);
1303
PaUtil_FreeMemory( filter->pins );
1304
if( filter->handle )
1305
CloseHandle( filter->handle );
1306
PaUtil_FreeMemory( filter );
1313
* Free a previously created filter
1315
static void FilterFree(PaWinWdmFilter* filter)
1321
for( pinId = 0; pinId < filter->pinCount; pinId++ )
1322
PinFree(filter->pins[pinId]);
1323
PaUtil_FreeMemory( filter->pins );
1324
if( filter->handle )
1325
CloseHandle( filter->handle );
1326
PaUtil_FreeMemory( filter );
1332
* Reopen the filter handle if necessary so it can be used
1334
static PaError FilterUse(PaWinWdmFilter* filter)
1339
if( filter->handle == NULL )
1341
/* Open the filter */
1342
filter->handle = CreateFile(
1344
GENERIC_READ | GENERIC_WRITE,
1348
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
1351
if( filter->handle == NULL )
1353
return paDeviceUnavailable;
1356
filter->usageCount++;
1362
* Release the filter handle if nobody is using it
1364
static void FilterRelease(PaWinWdmFilter* filter)
1367
assert( filter->usageCount > 0 );
1370
filter->usageCount--;
1371
if( filter->usageCount == 0 )
1373
if( filter->handle != NULL )
1375
CloseHandle( filter->handle );
1376
filter->handle = NULL;
1383
* Create a render (playback) Pin using the supplied format
1385
static PaWinWdmPin* FilterCreateRenderPin(PaWinWdmFilter* filter,
1386
const WAVEFORMATEX* wfex,
1394
pin = FilterFindViableRenderPin(filter,wfex,&result);
1399
result = PinSetFormat(pin,wfex);
1400
if( result != paNoError )
1404
result = PinInstantiate(pin);
1405
if( result != paNoError )
1419
* Find a pin that supports the given format
1421
static PaWinWdmPin* FilterFindViableRenderPin(PaWinWdmFilter* filter,
1422
const WAVEFORMATEX* wfex,
1427
PaError result = paDeviceUnavailable;
1432
for( pinId = 0; pinId<filter->pinCount; pinId++ )
1434
pin = filter->pins[pinId];
1437
if(( pin->dataFlow == KSPIN_DATAFLOW_IN ) &&
1438
(( pin->communication == KSPIN_COMMUNICATION_SINK) ||
1439
( pin->communication == KSPIN_COMMUNICATION_BOTH)))
1441
result = PinIsFormatSupported( pin, wfex );
1442
if( result == paNoError )
1455
* Check if there is a pin that should playback
1456
* with the supplied format
1458
static PaError FilterCanCreateRenderPin(PaWinWdmFilter* filter,
1459
const WAVEFORMATEX* wfex)
1466
pin = FilterFindViableRenderPin(filter,wfex,&result);
1467
/* result will be paNoError if pin found
1468
* or else an error code indicating what is wrong with the format
1474
* Create a capture (record) Pin using the supplied format
1476
static PaWinWdmPin* FilterCreateCapturePin(PaWinWdmFilter* filter,
1477
const WAVEFORMATEX* wfex,
1485
pin = FilterFindViableCapturePin(filter,wfex,&result);
1491
result = PinSetFormat(pin,wfex);
1492
if( result != paNoError )
1497
result = PinInstantiate(pin);
1498
if( result != paNoError )
1512
* Find a capture pin that supports the given format
1514
static PaWinWdmPin* FilterFindViableCapturePin(PaWinWdmFilter* filter,
1515
const WAVEFORMATEX* wfex,
1520
PaError result = paDeviceUnavailable;
1525
for( pinId = 0; pinId<filter->pinCount; pinId++ )
1527
pin = filter->pins[pinId];
1530
if(( pin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
1531
(( pin->communication == KSPIN_COMMUNICATION_SINK) ||
1532
( pin->communication == KSPIN_COMMUNICATION_BOTH)))
1534
result = PinIsFormatSupported( pin, wfex );
1535
if( result == paNoError )
1548
* Check if there is a pin that should playback
1549
* with the supplied format
1551
static PaError FilterCanCreateCapturePin(PaWinWdmFilter* filter,
1552
const WAVEFORMATEX* wfex)
1559
pin = FilterFindViableCapturePin(filter,wfex,&result);
1560
/* result will be paNoError if pin found
1561
* or else an error code indicating what is wrong with the format
1567
* Build the list of available filters
1568
* Use the SetupDi API to enumerate all devices in the KSCATEGORY_AUDIO which
1569
* have a KSCATEGORY_RENDER or KSCATEGORY_CAPTURE alias. For each of these
1570
* devices initialise a PaWinWdmFilter structure by calling our NewFilter()
1571
* function. We enumerate devices twice, once to count how many there are,
1572
* and once to initialize the PaWinWdmFilter structures.
1574
static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
1576
PaError result = paNoError;
1577
HDEVINFO handle = NULL;
1581
SP_DEVICE_INTERFACE_DATA interfaceData;
1582
SP_DEVICE_INTERFACE_DATA aliasData;
1583
SP_DEVINFO_DATA devInfoData;
1585
const int sizeInterface = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR));
1586
unsigned char interfaceDetailsArray[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR))];
1587
SP_DEVICE_INTERFACE_DETAIL_DATA* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA*)interfaceDetailsArray;
1588
TCHAR friendlyName[MAX_PATH];
1590
DWORD sizeFriendlyName;
1592
PaWinWdmFilter* newFilter;
1593
GUID* category = (GUID*)&KSCATEGORY_AUDIO;
1594
GUID* alias_render = (GUID*)&KSCATEGORY_RENDER;
1595
GUID* alias_capture = (GUID*)&KSCATEGORY_CAPTURE;
1600
devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
1602
/* Open a handle to search for devices (filters) */
1603
handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
1604
if( handle == NULL )
1606
return paUnanticipatedHostError;
1608
PA_DEBUG(("Setup called\n"));
1610
/* First let's count the number of devices so we can allocate a list */
1612
for( device = 0;;device++ )
1614
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1615
interfaceData.Reserved = 0;
1616
aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1617
aliasData.Reserved = 0;
1618
noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
1619
PA_DEBUG(("Enum called\n"));
1621
break; /* No more devices */
1623
/* Check this one has the render or capture alias */
1625
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
1626
PA_DEBUG(("noError = %d\n",noError));
1629
if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1631
PA_DEBUG(("Device %d has render alias\n",device));
1632
hasAlias |= 1; /* Has render alias */
1636
PA_DEBUG(("Device %d has no render alias\n",device));
1639
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
1642
if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1644
PA_DEBUG(("Device %d has capture alias\n",device));
1645
hasAlias |= 2; /* Has capture alias */
1649
PA_DEBUG(("Device %d has no capture alias\n",device));
1653
invalidDevices++; /* This was not a valid capture or render audio device */
1656
/* Remember how many there are */
1657
wdmHostApi->filterCount = device-invalidDevices;
1659
PA_DEBUG(("Interfaces found: %d\n",device-invalidDevices));
1661
/* Now allocate the list of pointers to devices */
1662
wdmHostApi->filters = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * device );
1663
if( !wdmHostApi->filters )
1666
SetupDiDestroyDeviceInfoList(handle);
1667
return paInsufficientMemory;
1670
/* Now create filter objects for each interface found */
1672
for( device = 0;;device++ )
1674
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1675
interfaceData.Reserved = 0;
1676
aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1677
aliasData.Reserved = 0;
1678
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1679
devInfoData.Reserved = 0;
1681
noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
1683
break; /* No more devices */
1685
/* Check this one has the render or capture alias */
1687
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
1690
if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1692
PA_DEBUG(("Device %d has render alias\n",device));
1693
hasAlias |= 1; /* Has render alias */
1696
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
1699
if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
1701
PA_DEBUG(("Device %d has capture alias\n",device));
1702
hasAlias |= 2; /* Has capture alias */
1706
continue; /* This was not a valid capture or render audio device */
1708
noError = SetupDiGetDeviceInterfaceDetail(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
1711
/* Try to get the "friendly name" for this interface */
1712
sizeFriendlyName = sizeof(friendlyName);
1713
/* Fix contributed by Ben Allison
1714
* Removed KEY_SET_VALUE from flags on following call
1715
* as its causes failure when running without admin rights
1716
* and it was not required */
1717
hkey=SetupDiOpenDeviceInterfaceRegKey(handle,&interfaceData,0,KEY_QUERY_VALUE);
1718
if(hkey!=INVALID_HANDLE_VALUE)
1720
noError = RegQueryValueEx(hkey,TEXT("FriendlyName"),0,&type,(BYTE*)friendlyName,&sizeFriendlyName);
1721
if( noError == ERROR_SUCCESS )
1723
PA_DEBUG(("Interface %d, Name: %s\n",device,friendlyName));
1728
friendlyName[0] = 0;
1731
newFilter = FilterNew(devInterfaceDetails->DevicePath,friendlyName,&result);
1732
if( result == paNoError )
1734
PA_DEBUG(("Filter created\n"));
1735
wdmHostApi->filters[slot] = newFilter;
1740
PA_DEBUG(("Filter NOT created\n"));
1741
/* As there are now less filters than we initially thought
1742
* we must reduce the count by one */
1743
wdmHostApi->filterCount--;
1750
SetupDiDestroyDeviceInfoList(handle);
1755
PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
1757
PaError result = paNoError;
1759
PaWinWdmHostApiRepresentation *wdmHostApi;
1760
PaWinWdmDeviceInfo *deviceInfoArray;
1761
PaWinWdmFilter* pFilter;
1762
PaWinWdmDeviceInfo *wdmDeviceInfo;
1763
PaDeviceInfo *deviceInfo;
1768
Attempt to load the KSUSER.DLL without which we cannot create pins
1769
We will unload this on termination
1771
if(DllKsUser == NULL)
1773
DllKsUser = LoadLibrary(TEXT("ksuser.dll"));
1774
if(DllKsUser == NULL)
1778
FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin");
1779
if(FunctionKsCreatePin == NULL)
1782
wdmHostApi = (PaWinWdmHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWdmHostApiRepresentation) );
1785
result = paInsufficientMemory;
1789
wdmHostApi->allocations = PaUtil_CreateAllocationGroup();
1790
if( !wdmHostApi->allocations )
1792
result = paInsufficientMemory;
1796
result = BuildFilterList( wdmHostApi );
1797
if( result != paNoError )
1801
deviceCount = wdmHostApi->filterCount;
1803
*hostApi = &wdmHostApi->inheritedHostApiRep;
1804
(*hostApi)->info.structVersion = 1;
1805
(*hostApi)->info.type = paWDMKS;
1806
(*hostApi)->info.name = "Windows WDM-KS";
1807
(*hostApi)->info.defaultInputDevice = paNoDevice;
1808
(*hostApi)->info.defaultOutputDevice = paNoDevice;
1810
if( deviceCount > 0 )
1812
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1813
wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo*) * deviceCount );
1814
if( !(*hostApi)->deviceInfos )
1816
result = paInsufficientMemory;
1820
/* allocate all device info structs in a contiguous block */
1821
deviceInfoArray = (PaWinWdmDeviceInfo*)PaUtil_GroupAllocateMemory(
1822
wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo) * deviceCount );
1823
if( !deviceInfoArray )
1825
result = paInsufficientMemory;
1829
for( i=0; i < deviceCount; ++i )
1831
wdmDeviceInfo = &deviceInfoArray[i];
1832
deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo;
1833
pFilter = wdmHostApi->filters[i];
1834
if( pFilter == NULL )
1836
wdmDeviceInfo->filter = pFilter;
1837
deviceInfo->structVersion = 2;
1838
deviceInfo->hostApi = hostApiIndex;
1839
deviceInfo->name = (char*)pFilter->friendlyName;
1840
PA_DEBUG(("Device found name: %s\n",(char*)pFilter->friendlyName));
1841
deviceInfo->maxInputChannels = pFilter->maxInputChannels;
1842
if(deviceInfo->maxInputChannels > 0)
1844
/* Set the default input device to the first device we find with
1845
* more than zero input channels
1847
if((*hostApi)->info.defaultInputDevice == paNoDevice)
1849
(*hostApi)->info.defaultInputDevice = i;
1853
deviceInfo->maxOutputChannels = pFilter->maxOutputChannels;
1854
if(deviceInfo->maxOutputChannels > 0)
1856
/* Set the default output device to the first device we find with
1857
* more than zero output channels
1859
if((*hostApi)->info.defaultOutputDevice == paNoDevice)
1861
(*hostApi)->info.defaultOutputDevice = i;
1865
/* These low values are not very useful because
1866
* a) The lowest latency we end up with can depend on many factors such
1867
* as the device buffer sizes/granularities, sample rate, channels and format
1868
* b) We cannot know the device buffer sizes until we try to open/use it at
1869
* a particular setting
1870
* So: we give 512x48000Hz frames as the default low input latency
1872
deviceInfo->defaultLowInputLatency = (512.0/48000.0);
1873
deviceInfo->defaultLowOutputLatency = (512.0/48000.0);
1874
deviceInfo->defaultHighInputLatency = (4096.0/48000.0);
1875
deviceInfo->defaultHighOutputLatency = (4096.0/48000.0);
1876
deviceInfo->defaultSampleRate = (double)(pFilter->bestSampleRate);
1878
(*hostApi)->deviceInfos[i] = deviceInfo;
1882
(*hostApi)->info.deviceCount = deviceCount;
1884
(*hostApi)->Terminate = Terminate;
1885
(*hostApi)->OpenStream = OpenStream;
1886
(*hostApi)->IsFormatSupported = IsFormatSupported;
1888
PaUtil_InitializeStreamInterface( &wdmHostApi->callbackStreamInterface, CloseStream, StartStream,
1889
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1890
GetStreamTime, GetStreamCpuLoad,
1891
PaUtil_DummyRead, PaUtil_DummyWrite,
1892
PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1894
PaUtil_InitializeStreamInterface( &wdmHostApi->blockingStreamInterface, CloseStream, StartStream,
1895
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1896
GetStreamTime, PaUtil_DummyGetCpuLoad,
1897
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1903
if( DllKsUser != NULL )
1905
FreeLibrary( DllKsUser );
1911
PaUtil_FreeMemory( wdmHostApi->filters );
1912
if( wdmHostApi->allocations )
1914
PaUtil_FreeAllAllocations( wdmHostApi->allocations );
1915
PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
1917
PaUtil_FreeMemory( wdmHostApi );
1924
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1926
PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
1930
if( wdmHostApi->filters )
1932
for( i=0; i<wdmHostApi->filterCount; i++)
1934
if( wdmHostApi->filters[i] != NULL )
1936
FilterFree( wdmHostApi->filters[i] );
1937
wdmHostApi->filters[i] = NULL;
1941
PaUtil_FreeMemory( wdmHostApi->filters );
1942
if( wdmHostApi->allocations )
1944
PaUtil_FreeAllAllocations( wdmHostApi->allocations );
1945
PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
1947
PaUtil_FreeMemory( wdmHostApi );
1951
static void FillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
1954
PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
1955
PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
1956
PA_DEBUG(( "chanelCount = %d\n", channelCount ));
1958
pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1959
pwfext->Format.nChannels = channelCount;
1960
pwfext->Format.nSamplesPerSec = (int)sampleRate;
1961
if(channelCount == 1)
1962
pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
1964
pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
1965
if(sampleFormat == paFloat32)
1967
pwfext->Format.nBlockAlign = channelCount * 4;
1968
pwfext->Format.wBitsPerSample = 32;
1969
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1970
pwfext->Samples.wValidBitsPerSample = 32;
1971
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1973
else if(sampleFormat == paInt32)
1975
pwfext->Format.nBlockAlign = channelCount * 4;
1976
pwfext->Format.wBitsPerSample = 32;
1977
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1978
pwfext->Samples.wValidBitsPerSample = 32;
1979
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1981
else if(sampleFormat == paInt24)
1983
pwfext->Format.nBlockAlign = channelCount * 3;
1984
pwfext->Format.wBitsPerSample = 24;
1985
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1986
pwfext->Samples.wValidBitsPerSample = 24;
1987
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1989
else if(sampleFormat == paInt16)
1991
pwfext->Format.nBlockAlign = channelCount * 2;
1992
pwfext->Format.wBitsPerSample = 16;
1993
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1994
pwfext->Samples.wValidBitsPerSample = 16;
1995
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1997
pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
2002
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
2003
const PaStreamParameters *inputParameters,
2004
const PaStreamParameters *outputParameters,
2007
int inputChannelCount, outputChannelCount;
2008
PaSampleFormat inputSampleFormat, outputSampleFormat;
2009
PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
2010
PaWinWdmFilter* pFilter;
2011
int result = paFormatIsSupported;
2012
WAVEFORMATEXTENSIBLE wfx;
2016
if( inputParameters )
2018
inputChannelCount = inputParameters->channelCount;
2019
inputSampleFormat = inputParameters->sampleFormat;
2021
/* all standard sample formats are supported by the buffer adapter,
2022
this implementation doesn't support any custom sample formats */
2023
if( inputSampleFormat & paCustomFormat )
2024
return paSampleFormatNotSupported;
2026
/* unless alternate device specification is supported, reject the use of
2027
paUseHostApiSpecificDeviceSpecification */
2029
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
2030
return paInvalidDevice;
2032
/* check that input device can support inputChannelCount */
2033
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
2034
return paInvalidChannelCount;
2036
/* validate inputStreamInfo */
2037
if( inputParameters->hostApiSpecificStreamInfo )
2038
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2040
/* Check that the input format is supported */
2041
FillWFEXT(&wfx,paInt16,sampleRate,inputChannelCount);
2043
pFilter = wdmHostApi->filters[inputParameters->device];
2044
result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
2045
if( result != paNoError )
2047
/* Try a WAVE_FORMAT_PCM instead */
2048
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2049
wfx.Format.cbSize = 0;
2050
wfx.Samples.wValidBitsPerSample = 0;
2051
wfx.dwChannelMask = 0;
2052
wfx.SubFormat = GUID_NULL;
2053
result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
2054
if( result != paNoError )
2060
inputChannelCount = 0;
2063
if( outputParameters )
2065
outputChannelCount = outputParameters->channelCount;
2066
outputSampleFormat = outputParameters->sampleFormat;
2068
/* all standard sample formats are supported by the buffer adapter,
2069
this implementation doesn't support any custom sample formats */
2070
if( outputSampleFormat & paCustomFormat )
2071
return paSampleFormatNotSupported;
2073
/* unless alternate device specification is supported, reject the use of
2074
paUseHostApiSpecificDeviceSpecification */
2076
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
2077
return paInvalidDevice;
2079
/* check that output device can support outputChannelCount */
2080
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
2081
return paInvalidChannelCount;
2083
/* validate outputStreamInfo */
2084
if( outputParameters->hostApiSpecificStreamInfo )
2085
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2087
/* Check that the output format is supported */
2088
FillWFEXT(&wfx,paInt16,sampleRate,outputChannelCount);
2090
pFilter = wdmHostApi->filters[outputParameters->device];
2091
result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
2092
if( result != paNoError )
2094
/* Try a WAVE_FORMAT_PCM instead */
2095
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2096
wfx.Format.cbSize = 0;
2097
wfx.Samples.wValidBitsPerSample = 0;
2098
wfx.dwChannelMask = 0;
2099
wfx.SubFormat = GUID_NULL;
2100
result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
2101
if( result != paNoError )
2108
outputChannelCount = 0;
2114
- if a full duplex stream is requested, check that the combination
2115
of input and output parameters is supported if necessary
2117
- check that the device supports sampleRate
2119
Because the buffer adapter handles conversion between all standard
2120
sample formats, the following checks are only required if paCustomFormat
2121
is implemented, or under some other unusual conditions.
2123
- check that input device can support inputSampleFormat, or that
2124
we have the capability to convert from inputSampleFormat to
2127
- check that output device can support outputSampleFormat, or that
2128
we have the capability to convert from outputSampleFormat to
2131
if((inputChannelCount == 0)&&(outputChannelCount == 0))
2132
result = paSampleFormatNotSupported; /* Not right error */
2138
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
2140
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
2142
const PaStreamParameters *inputParameters,
2143
const PaStreamParameters *outputParameters,
2145
unsigned long framesPerBuffer,
2146
PaStreamFlags streamFlags,
2147
PaStreamCallback *streamCallback,
2150
PaError result = paNoError;
2151
PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
2152
PaWinWdmStream *stream = 0;
2153
/* unsigned long framesPerHostBuffer; these may not be equivalent for all implementations */
2154
PaSampleFormat inputSampleFormat, outputSampleFormat;
2155
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
2156
int userInputChannels,userOutputChannels;
2158
PaWinWdmFilter* pFilter;
2159
WAVEFORMATEXTENSIBLE wfx;
2162
PA_DEBUG(("OpenStream:sampleRate = %f\n",sampleRate));
2163
PA_DEBUG(("OpenStream:framesPerBuffer = %lu\n",framesPerBuffer));
2165
if( inputParameters )
2167
userInputChannels = inputParameters->channelCount;
2168
inputSampleFormat = inputParameters->sampleFormat;
2170
/* unless alternate device specification is supported, reject the use of
2171
paUseHostApiSpecificDeviceSpecification */
2173
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
2174
return paInvalidDevice;
2176
/* check that input device can support stream->userInputChannels */
2177
if( userInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
2178
return paInvalidChannelCount;
2180
/* validate inputStreamInfo */
2181
if( inputParameters->hostApiSpecificStreamInfo )
2182
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2187
userInputChannels = 0;
2188
inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
2191
if( outputParameters )
2193
userOutputChannels = outputParameters->channelCount;
2194
outputSampleFormat = outputParameters->sampleFormat;
2196
/* unless alternate device specification is supported, reject the use of
2197
paUseHostApiSpecificDeviceSpecification */
2199
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
2200
return paInvalidDevice;
2202
/* check that output device can support stream->userInputChannels */
2203
if( userOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
2204
return paInvalidChannelCount;
2206
/* validate outputStreamInfo */
2207
if( outputParameters->hostApiSpecificStreamInfo )
2208
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
2213
userOutputChannels = 0;
2214
outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
2217
/* validate platform specific flags */
2218
if( (streamFlags & paPlatformSpecificFlags) != 0 )
2219
return paInvalidFlag; /* unexpected platform specific flag */
2221
stream = (PaWinWdmStream*)PaUtil_AllocateMemory( sizeof(PaWinWdmStream) );
2224
result = paInsufficientMemory;
2227
/* Zero the stream object */
2228
/* memset((void*)stream,0,sizeof(PaWinWdmStream)); */
2230
if( streamCallback )
2232
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2233
&wdmHostApi->callbackStreamInterface, streamCallback, userData );
2237
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2238
&wdmHostApi->blockingStreamInterface, streamCallback, userData );
2241
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
2243
/* Instantiate the input pin if necessary */
2244
if(userInputChannels > 0)
2246
result = paSampleFormatNotSupported;
2247
pFilter = wdmHostApi->filters[inputParameters->device];
2248
stream->userInputChannels = userInputChannels;
2250
if(((inputSampleFormat & ~paNonInterleaved) & pFilter->formats) != 0)
2251
{ /* inputSampleFormat is supported, so try to use it */
2252
hostInputSampleFormat = inputSampleFormat;
2253
FillWFEXT(&wfx, hostInputSampleFormat, sampleRate, stream->userInputChannels);
2254
stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
2255
stream->recordingPin = FilterCreateCapturePin(pFilter, (const WAVEFORMATEX*)&wfx, &result);
2256
stream->deviceInputChannels = stream->userInputChannels;
2259
if(result != paNoError)
2260
{ /* Search through all PaSampleFormats to find one that works */
2261
hostInputSampleFormat = paFloat32;
2264
FillWFEXT(&wfx, hostInputSampleFormat, sampleRate, stream->userInputChannels);
2265
stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
2266
stream->recordingPin = FilterCreateCapturePin(pFilter, (const WAVEFORMATEX*)&wfx, &result);
2267
stream->deviceInputChannels = stream->userInputChannels;
2269
if(stream->recordingPin == NULL) result = paSampleFormatNotSupported;
2270
if(result != paNoError) hostInputSampleFormat <<= 1;
2272
while(result != paNoError && hostInputSampleFormat <= paUInt8);
2275
if(result != paNoError)
2276
{ /* None of the PaSampleFormats worked. Set the hostInputSampleFormat to the best fit
2277
* and try a PCM format.
2279
hostInputSampleFormat =
2280
PaUtil_SelectClosestAvailableFormat( pFilter->formats, inputSampleFormat );
2282
/* Try a WAVE_FORMAT_PCM instead */
2283
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2284
wfx.Format.cbSize = 0;
2285
wfx.Samples.wValidBitsPerSample = 0;
2286
wfx.dwChannelMask = 0;
2287
wfx.SubFormat = GUID_NULL;
2288
stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
2289
if(stream->recordingPin == NULL) result = paSampleFormatNotSupported;
2292
if( result != paNoError )
2294
/* Some or all KS devices can only handle the exact number of channels
2295
* they specify. But PortAudio clients expect to be able to
2296
* at least specify mono I/O on a multi-channel device
2297
* If this is the case, then we will do the channel mapping internally
2299
if( stream->userInputChannels < pFilter->maxInputChannels )
2301
FillWFEXT(&wfx,hostInputSampleFormat,sampleRate,pFilter->maxInputChannels);
2302
stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
2303
stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
2304
stream->deviceInputChannels = pFilter->maxInputChannels;
2306
if( result != paNoError )
2308
/* Try a WAVE_FORMAT_PCM instead */
2309
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2310
wfx.Format.cbSize = 0;
2311
wfx.Samples.wValidBitsPerSample = 0;
2312
wfx.dwChannelMask = 0;
2313
wfx.SubFormat = GUID_NULL;
2314
stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
2319
if(stream->recordingPin == NULL)
2324
switch(hostInputSampleFormat)
2326
case paInt16: stream->inputSampleSize = 2; break;
2327
case paInt24: stream->inputSampleSize = 3; break;
2329
case paFloat32: stream->inputSampleSize = 4; break;
2332
stream->recordingPin->frameSize /= stream->bytesPerInputFrame;
2333
PA_DEBUG(("Pin output frames: %d\n",stream->recordingPin->frameSize));
2337
stream->recordingPin = NULL;
2338
stream->bytesPerInputFrame = 0;
2341
/* Instantiate the output pin if necessary */
2342
if(userOutputChannels > 0)
2344
result = paSampleFormatNotSupported;
2345
pFilter = wdmHostApi->filters[outputParameters->device];
2346
stream->userOutputChannels = userOutputChannels;
2348
if(((outputSampleFormat & ~paNonInterleaved) & pFilter->formats) != 0)
2350
hostOutputSampleFormat = outputSampleFormat;
2351
FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels);
2352
stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
2353
stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
2354
stream->deviceOutputChannels = stream->userOutputChannels;
2357
if(result != paNoError)
2359
hostOutputSampleFormat = paFloat32;
2362
FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels);
2363
stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
2364
stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
2365
stream->deviceOutputChannels = stream->userOutputChannels;
2367
if(stream->playbackPin == NULL) result = paSampleFormatNotSupported;
2368
if(result != paNoError) hostOutputSampleFormat <<= 1;
2370
while(result != paNoError && hostOutputSampleFormat <= paUInt8);
2373
if(result != paNoError)
2375
hostOutputSampleFormat =
2376
PaUtil_SelectClosestAvailableFormat( pFilter->formats, outputSampleFormat );
2378
/* Try a WAVE_FORMAT_PCM instead */
2379
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2380
wfx.Format.cbSize = 0;
2381
wfx.Samples.wValidBitsPerSample = 0;
2382
wfx.dwChannelMask = 0;
2383
wfx.SubFormat = GUID_NULL;
2384
stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
2385
if(stream->playbackPin == NULL) result = paSampleFormatNotSupported;
2388
if( result != paNoError )
2390
/* Some or all KS devices can only handle the exact number of channels
2391
* they specify. But PortAudio clients expect to be able to
2392
* at least specify mono I/O on a multi-channel device
2393
* If this is the case, then we will do the channel mapping internally
2395
if( stream->userOutputChannels < pFilter->maxOutputChannels )
2397
FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,pFilter->maxOutputChannels);
2398
stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
2399
stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
2400
stream->deviceOutputChannels = pFilter->maxOutputChannels;
2401
if( result != paNoError )
2403
/* Try a WAVE_FORMAT_PCM instead */
2404
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
2405
wfx.Format.cbSize = 0;
2406
wfx.Samples.wValidBitsPerSample = 0;
2407
wfx.dwChannelMask = 0;
2408
wfx.SubFormat = GUID_NULL;
2409
stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
2414
if(stream->playbackPin == NULL)
2419
switch(hostOutputSampleFormat)
2421
case paInt16: stream->outputSampleSize = 2; break;
2422
case paInt24: stream->outputSampleSize = 3; break;
2424
case paFloat32: stream->outputSampleSize = 4; break;
2427
stream->playbackPin->frameSize /= stream->bytesPerOutputFrame;
2428
PA_DEBUG(("Pin output frames: %d\n",stream->playbackPin->frameSize));
2432
stream->playbackPin = NULL;
2433
stream->bytesPerOutputFrame = 0;
2436
/* Calculate the framesPerHostXxxxBuffer size based upon the suggested latency values */
2438
/* Record the buffer length */
2441
/* Calculate the frames from the user's value - add a bit to round up */
2442
stream->framesPerHostIBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001);
2443
if(stream->framesPerHostIBuffer > (unsigned long)sampleRate)
2444
{ /* Upper limit is 1 second */
2445
stream->framesPerHostIBuffer = (unsigned long)sampleRate;
2447
else if(stream->framesPerHostIBuffer < stream->recordingPin->frameSize)
2449
stream->framesPerHostIBuffer = stream->recordingPin->frameSize;
2451
PA_DEBUG(("Input frames chosen:%ld\n",stream->framesPerHostIBuffer));
2454
if(outputParameters)
2456
/* Calculate the frames from the user's value - add a bit to round up */
2457
stream->framesPerHostOBuffer = (unsigned long)((outputParameters->suggestedLatency*sampleRate)+0.0001);
2458
if(stream->framesPerHostOBuffer > (unsigned long)sampleRate)
2459
{ /* Upper limit is 1 second */
2460
stream->framesPerHostOBuffer = (unsigned long)sampleRate;
2462
else if(stream->framesPerHostOBuffer < stream->playbackPin->frameSize)
2464
stream->framesPerHostOBuffer = stream->playbackPin->frameSize;
2466
PA_DEBUG(("Output frames chosen:%ld\n",stream->framesPerHostOBuffer));
2469
/* Host buffer size is bounded to the largest of the input and output
2472
result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2473
stream->userInputChannels, inputSampleFormat, hostInputSampleFormat,
2474
stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat,
2475
sampleRate, streamFlags, framesPerBuffer,
2476
max(stream->framesPerHostOBuffer,stream->framesPerHostIBuffer),
2477
paUtilBoundedHostBufferSize,
2478
streamCallback, userData );
2479
if( result != paNoError )
2482
stream->streamRepresentation.streamInfo.inputLatency =
2483
((double)stream->framesPerHostIBuffer) / sampleRate;
2484
stream->streamRepresentation.streamInfo.outputLatency =
2485
((double)stream->framesPerHostOBuffer) / sampleRate;
2486
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2488
PA_DEBUG(("BytesPerInputFrame = %d\n",stream->bytesPerInputFrame));
2489
PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->bytesPerOutputFrame));
2491
/* Allocate all the buffers for host I/O */
2492
size = 2 * (stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame);
2493
PA_DEBUG(("Buffer size = %d\n",size));
2494
stream->hostBuffer = (char*)PaUtil_AllocateMemory(size);
2495
PA_DEBUG(("Buffer allocated\n"));
2496
if( !stream->hostBuffer )
2498
PA_DEBUG(("Cannot allocate host buffer!\n"));
2499
result = paInsufficientMemory;
2502
PA_DEBUG(("Buffer start = %p\n",stream->hostBuffer));
2503
/* memset(stream->hostBuffer,0,size); */
2505
/* Set up the packets */
2506
stream->events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
2507
ResetEvent(stream->events[0]); /* Record buffer 1 */
2508
stream->events[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
2509
ResetEvent(stream->events[1]); /* Record buffer 2 */
2510
stream->events[2] = CreateEvent(NULL, FALSE, FALSE, NULL);
2511
ResetEvent(stream->events[2]); /* Play buffer 1 */
2512
stream->events[3] = CreateEvent(NULL, FALSE, FALSE, NULL);
2513
ResetEvent(stream->events[3]); /* Play buffer 2 */
2514
stream->events[4] = CreateEvent(NULL, FALSE, FALSE, NULL);
2515
ResetEvent(stream->events[4]); /* Abort event */
2516
if(stream->userInputChannels > 0)
2518
DATAPACKET *p = &(stream->packets[0]);
2519
p->Signal.hEvent = stream->events[0];
2520
p->Header.Data = stream->hostBuffer;
2521
p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
2522
p->Header.DataUsed = 0;
2523
p->Header.Size = sizeof(p->Header);
2524
p->Header.PresentationTime.Numerator = 1;
2525
p->Header.PresentationTime.Denominator = 1;
2527
p = &(stream->packets[1]);
2528
p->Signal.hEvent = stream->events[1];
2529
p->Header.Data = stream->hostBuffer + stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
2530
p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
2531
p->Header.DataUsed = 0;
2532
p->Header.Size = sizeof(p->Header);
2533
p->Header.PresentationTime.Numerator = 1;
2534
p->Header.PresentationTime.Denominator = 1;
2536
if(stream->userOutputChannels > 0)
2538
DATAPACKET *p = &(stream->packets[2]);
2539
p->Signal.hEvent = stream->events[2];
2540
p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
2541
p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
2542
p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
2543
p->Header.Size = sizeof(p->Header);
2544
p->Header.PresentationTime.Numerator = 1;
2545
p->Header.PresentationTime.Denominator = 1;
2547
p = &(stream->packets[3]);
2548
p->Signal.hEvent = stream->events[3];
2549
p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
2550
p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
2551
p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
2552
p->Header.Size = sizeof(p->Header);
2553
p->Header.PresentationTime.Numerator = 1;
2554
p->Header.PresentationTime.Denominator = 1;
2557
stream->streamStarted = 0;
2558
stream->streamActive = 0;
2559
stream->streamStop = 0;
2560
stream->streamAbort = 0;
2561
stream->streamFlags = streamFlags;
2562
stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
2564
*s = (PaStream*)stream;
2573
if(stream->events[size] != NULL)
2575
CloseHandle(stream->events[size]);
2576
stream->events[size] = NULL;
2579
if(stream->hostBuffer)
2580
PaUtil_FreeMemory( stream->hostBuffer );
2582
if(stream->playbackPin)
2583
PinClose(stream->playbackPin);
2584
if(stream->recordingPin)
2585
PinClose(stream->recordingPin);
2588
PaUtil_FreeMemory( stream );
2595
When CloseStream() is called, the multi-api layer ensures that
2596
the stream has already been stopped or aborted.
2598
static PaError CloseStream( PaStream* s )
2600
PaError result = paNoError;
2601
PaWinWdmStream *stream = (PaWinWdmStream*)s;
2606
assert(!stream->streamStarted);
2607
assert(!stream->streamActive);
2609
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2610
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2614
if(stream->events[size] != NULL)
2616
CloseHandle(stream->events[size]);
2617
stream->events[size] = NULL;
2620
if(stream->hostBuffer)
2621
PaUtil_FreeMemory( stream->hostBuffer );
2623
if(stream->playbackPin)
2624
PinClose(stream->playbackPin);
2625
if(stream->recordingPin)
2626
PinClose(stream->recordingPin);
2628
PaUtil_FreeMemory( stream );
2635
Write the supplied packet to the pin
2637
Should return false on success
2639
static BOOL PinWrite(HANDLE h, DATAPACKET* p)
2641
unsigned long cbReturned = 0;
2642
return DeviceIoControl(h,IOCTL_KS_WRITE_STREAM,NULL,0,
2643
&p->Header,p->Header.Size,&cbReturned,&p->Signal);
2647
Read to the supplied packet from the pin
2649
Should return false on success
2651
static BOOL PinRead(HANDLE h, DATAPACKET* p)
2653
unsigned long cbReturned = 0;
2654
return DeviceIoControl(h,IOCTL_KS_READ_STREAM,NULL,0,
2655
&p->Header,p->Header.Size,&cbReturned,&p->Signal);
2659
Copy the first interleaved channel of 16 bit data to the other channels
2661
static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples)
2663
unsigned short* data = (unsigned short*)buffer;
2665
unsigned short sourceSample;
2668
sourceSample = *data++;
2669
channel = channels-1;
2672
*data++ = sourceSample;
2678
Copy the first interleaved channel of 24 bit data to the other channels
2680
static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples)
2682
unsigned char* data = (unsigned char*)buffer;
2684
unsigned char sourceSample[3];
2687
sourceSample[0] = data[0];
2688
sourceSample[1] = data[1];
2689
sourceSample[2] = data[2];
2691
channel = channels-1;
2694
data[0] = sourceSample[0];
2695
data[1] = sourceSample[1];
2696
data[2] = sourceSample[2];
2703
Copy the first interleaved channel of 32 bit data to the other channels
2705
static void DuplicateFirstChannelInt32(void* buffer, int channels, int samples)
2707
unsigned long* data = (unsigned long*)buffer;
2709
unsigned long sourceSample;
2712
sourceSample = *data++;
2713
channel = channels-1;
2716
*data++ = sourceSample;
2721
static DWORD WINAPI ProcessingThread(LPVOID pParam)
2723
PaWinWdmStream *stream = (PaWinWdmStream*)pParam;
2724
PaStreamCallbackTimeInfo ti;
2725
int cbResult = paContinue;
2731
unsigned long eventSignaled;
2732
int fillPlaybuf = 0;
2733
int emptyRecordbuf = 0;
2734
int framesProcessed;
2735
unsigned long timeout;
2739
PaStreamCallbackFlags underover = 0;
2743
ti.inputBufferAdcTime = 0.0;
2744
ti.currentTime = 0.0;
2745
ti.outputBufferDacTime = 0.0;
2747
/* Get double buffering going */
2749
/* Submit buffers */
2750
if(stream->playbackPin)
2752
result = PinSetState(stream->playbackPin, KSSTATE_RUN);
2754
PA_DEBUG(("play state run = %d;",(int)result));
2755
SetEvent(stream->events[outbuf+2]);
2756
outbuf = (outbuf+1)&1;
2757
SetEvent(stream->events[outbuf+2]);
2758
outbuf = (outbuf+1)&1;
2762
if(stream->recordingPin)
2764
result = PinSetState(stream->recordingPin, KSSTATE_RUN);
2766
PA_DEBUG(("recording state run = %d;",(int)result));
2767
PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
2768
inbuf = (inbuf+1)&1; /* Increment and wrap */
2769
PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
2770
inbuf = (inbuf+1)&1; /* Increment and wrap */
2771
/* FIXME - do error checking */
2774
PA_DEBUG(("Out buffer len:%f\n",(2000*stream->framesPerHostOBuffer) / stream->streamRepresentation.streamInfo.sampleRate));
2775
PA_DEBUG(("In buffer len:%f\n",(2000*stream->framesPerHostIBuffer) / stream->streamRepresentation.streamInfo.sampleRate));
2777
((2000*(DWORD)stream->framesPerHostOBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate),
2778
((2000*(DWORD)stream->framesPerHostIBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate));
2779
timeout = max(timeout,1);
2780
PA_DEBUG(("Timeout = %ld\n",timeout));
2782
while(!stream->streamAbort)
2787
/* Wait for next input or output buffer to be finished with*/
2790
if(stream->streamStop)
2792
PA_DEBUG(("ss1:pending=%d ",pending));
2794
wait = WaitForMultipleObjects(5, stream->events, FALSE, 0);
2795
if( wait == WAIT_TIMEOUT )
2797
/* No (under|over)flow has ocurred */
2798
wait = WaitForMultipleObjects(5, stream->events, FALSE, timeout);
2799
eventSignaled = wait - WAIT_OBJECT_0;
2803
eventSignaled = wait - WAIT_OBJECT_0;
2804
if( eventSignaled < 2 )
2806
underover |= paInputOverflow;
2807
PA_DEBUG(("Input overflow\n"));
2809
else if(( eventSignaled < 4 )&&(!priming))
2811
underover |= paOutputUnderflow;
2812
PA_DEBUG(("Output underflow\n"));
2816
if(stream->streamStop)
2818
PA_DEBUG(("ss2:wait=%ld",wait));
2820
if(wait == WAIT_FAILED)
2822
PA_DEBUG(("Wait fail = %ld! ",wait));
2825
if(wait == WAIT_TIMEOUT)
2830
if(eventSignaled < 2)
2831
{ /* Recording input buffer has been filled */
2832
if(stream->playbackPin)
2834
/* First check if also the next playback buffer has been signaled */
2835
wait = WaitForSingleObject(stream->events[outbuf+2],0);
2836
if(wait == WAIT_OBJECT_0)
2838
/* Yes, so do both buffers at same time */
2841
/* Was this an underflow situation? */
2843
underover |= paOutputUnderflow; /* Yes! */
2849
else if(eventSignaled < 4)
2850
{ /* Playback output buffer has been emptied */
2851
if(stream->recordingPin)
2853
/* First check if also the next recording buffer has been signaled */
2854
wait = WaitForSingleObject(stream->events[inbuf],0);
2855
if(wait == WAIT_OBJECT_0)
2856
{ /* Yes, so do both buffers at same time */
2859
/* Was this an overflow situation? */
2861
underover |= paInputOverflow; /* Yes! */
2870
assert(stream->streamAbort); /* Should have been set */
2871
PA_DEBUG(("ABORTING "));
2874
ResetEvent(stream->events[eventSignaled]);
2876
if(stream->streamStop)
2878
PA_DEBUG(("Stream stop! pending=%d",pending));
2879
cbResult = paComplete; /* Stop, but play remaining buffers */
2882
/* Do necessary buffer processing (which will invoke user callback if necessary */
2884
if(cbResult==paContinue)
2886
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
2887
if((stream->bufferProcessor.hostInputFrameCount[0] + stream->bufferProcessor.hostInputFrameCount[1]) ==
2888
(stream->bufferProcessor.hostOutputFrameCount[0] + stream->bufferProcessor.hostOutputFrameCount[1]) )
2889
PaUtil_BeginBufferProcessing(&stream->bufferProcessor,&ti,underover);
2890
underover = 0; /* Reset the (under|over)flow status */
2893
PaUtil_SetOutputFrameCount(&stream->bufferProcessor,0);
2894
if( stream->userOutputChannels == 1 )
2896
/* Write the single user channel to the first interleaved block */
2897
PaUtil_SetOutputChannel(&stream->bufferProcessor,0,stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels);
2898
/* We will do a copy to the other channels after the data has been written */
2903
for(i=0;i<stream->userOutputChannels;i++)
2905
/* Only write the user output channels. Leave the rest blank */
2906
PaUtil_SetOutputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[outbuf+2].Header.Data))+(i*stream->outputSampleSize),stream->deviceOutputChannels);
2912
PaUtil_SetInputFrameCount(&stream->bufferProcessor,stream->packets[inbuf].Header.DataUsed/stream->bytesPerInputFrame);
2913
for(i=0;i<stream->userInputChannels;i++)
2915
/* Only read as many channels as the user wants */
2916
PaUtil_SetInputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[inbuf].Header.Data))+(i*stream->inputSampleSize),stream->deviceInputChannels);
2920
if (stream->recordingPin && stream->playbackPin) /* full duplex */
2922
/* Only call the EndBufferProcessing function when the total input frames == total output frames */
2924
if((stream->bufferProcessor.hostInputFrameCount[0] + stream->bufferProcessor.hostInputFrameCount[1]) ==
2925
(stream->bufferProcessor.hostOutputFrameCount[0] + stream->bufferProcessor.hostOutputFrameCount[1]) )
2927
framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult);
2931
framesProcessed = 0;
2936
framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult);
2941
/* Copy the first output channel to the other channels */
2942
switch(stream->outputSampleSize)
2945
DuplicateFirstChannelInt16(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
2948
DuplicateFirstChannelInt24(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
2951
DuplicateFirstChannelInt32(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
2954
assert(0); /* Unsupported format! */
2958
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
2967
if(cbResult != paContinue)
2969
PA_DEBUG(("cbResult=%d, pending=%d:",cbResult,pending));
2972
/* Submit buffers */
2973
if((fillPlaybuf)&&(cbResult!=paAbort))
2975
if(!PinWrite(stream->playbackPin->handle,&stream->packets[outbuf+2]))
2976
outbuf = (outbuf+1)&1; /* Increment and wrap */
2979
priming--; /* Have to prime twice */
2981
if((emptyRecordbuf)&&(cbResult==paContinue))
2983
stream->packets[inbuf].Header.DataUsed = 0; /* Reset for reuse */
2984
PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
2985
inbuf = (inbuf+1)&1; /* Increment and wrap */
2990
PA_DEBUG(("pending==0 finished...;"));
2993
if((!stream->playbackPin)&&(cbResult!=paContinue))
2995
PA_DEBUG(("record only cbResult=%d...;",cbResult));
3000
PA_DEBUG(("Finished thread"));
3002
/* Finished, either normally or aborted */
3003
if(stream->playbackPin)
3005
result = PinSetState(stream->playbackPin, KSSTATE_PAUSE);
3006
result = PinSetState(stream->playbackPin, KSSTATE_STOP);
3008
if(stream->recordingPin)
3010
result = PinSetState(stream->recordingPin, KSSTATE_PAUSE);
3011
result = PinSetState(stream->recordingPin, KSSTATE_STOP);
3014
stream->streamActive = 0;
3016
if((!stream->streamStop)&&(!stream->streamAbort))
3018
/* Invoke the user stream finished callback */
3019
/* Only do it from here if not being stopped/aborted by user */
3020
if( stream->streamRepresentation.streamFinishedCallback != 0 )
3021
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3023
stream->streamStop = 0;
3024
stream->streamAbort = 0;
3026
/* Reset process priority if necessary */
3027
if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
3029
SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
3030
stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
3038
static PaError StartStream( PaStream *s )
3040
PaError result = paNoError;
3041
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3048
stream->streamStop = 0;
3049
stream->streamAbort = 0;
3053
if(stream->events[size] != NULL)
3055
ResetEvent(stream->events[size]);
3059
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
3061
stream->oldProcessPriority = GetPriorityClass(GetCurrentProcess());
3062
/* Uncomment the following line to enable dynamic boosting of the process
3063
* priority to real time for best low latency support
3064
* Disabled by default because RT processes can easily block the OS */
3065
/*ret = SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
3066
PA_DEBUG(("Class ret = %d;",ret));*/
3068
stream->streamStarted = 1;
3069
stream->streamThread = (HANDLE)_beginthreadex(NULL, 0, ProcessingThread, stream, 0, &dwID);
3070
if(stream->streamThread == NULL)
3072
stream->streamStarted = 0;
3073
result = paInsufficientMemory;
3076
ret = SetThreadPriority(stream->streamThread,THREAD_PRIORITY_TIME_CRITICAL);
3077
PA_DEBUG(("Priority ret = %d;",ret));
3078
/* Make the stream active */
3079
stream->streamActive = 1;
3087
static PaError StopStream( PaStream *s )
3089
PaError result = paNoError;
3090
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3095
if(stream->streamActive)
3098
stream->streamStop = 1;
3099
while(stream->streamActive)
3102
Sleep(10); /* Let thread sleep for 10 msec */
3106
PA_DEBUG(("Terminating thread"));
3107
if(stream->streamStarted && stream->streamThread)
3109
TerminateThread(stream->streamThread,0);
3110
stream->streamThread = NULL;
3113
stream->streamStarted = 0;
3115
if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
3117
SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
3118
stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
3123
/* Do user callback now after all state has been reset */
3124
/* This means it should be safe for the called function */
3125
/* to invoke e.g. StartStream */
3126
if( stream->streamRepresentation.streamFinishedCallback != 0 )
3127
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3134
static PaError AbortStream( PaStream *s )
3136
PaError result = paNoError;
3137
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3142
if(stream->streamActive)
3145
stream->streamAbort = 1;
3146
SetEvent(stream->events[4]); /* Signal immediately */
3147
while(stream->streamActive)
3153
if(stream->streamStarted && stream->streamThread)
3155
TerminateThread(stream->streamThread,0);
3156
stream->streamThread = NULL;
3159
stream->streamStarted = 0;
3161
if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
3163
SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
3164
stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
3169
/* Do user callback now after all state has been reset */
3170
/* This means it should be safe for the called function */
3171
/* to invoke e.g. StartStream */
3172
if( stream->streamRepresentation.streamFinishedCallback != 0 )
3173
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3176
stream->streamActive = 0;
3177
stream->streamStarted = 0;
3184
static PaError IsStreamStopped( PaStream *s )
3186
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3191
if(!stream->streamStarted)
3199
static PaError IsStreamActive( PaStream *s )
3201
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3206
if(stream->streamActive)
3214
static PaTime GetStreamTime( PaStream* s )
3219
return PaUtil_GetTime();
3223
static double GetStreamCpuLoad( PaStream* s )
3225
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3228
result = PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
3235
As separate stream interfaces are used for blocking and callback
3236
streams, the following functions can be guaranteed to only be called
3237
for blocking streams.
3240
static PaError ReadStream( PaStream* s,
3242
unsigned long frames )
3244
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3248
/* suppress unused variable warnings */
3253
/* IMPLEMENT ME, see portaudio.h for required behavior*/
3259
static PaError WriteStream( PaStream* s,
3261
unsigned long frames )
3263
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3267
/* suppress unused variable warnings */
3272
/* IMPLEMENT ME, see portaudio.h for required behavior*/
3278
static signed long GetStreamReadAvailable( PaStream* s )
3280
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3284
/* suppress unused variable warnings */
3287
/* IMPLEMENT ME, see portaudio.h for required behavior*/
3293
static signed long GetStreamWriteAvailable( PaStream* s )
3295
PaWinWdmStream *stream = (PaWinWdmStream*)s;
3298
/* suppress unused variable warnings */
3301
/* IMPLEMENT ME, see portaudio.h for required behavior*/
b'\\ No newline at end of file'