/[pcsx2_0.9.7]/trunk/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c
ViewVC logotype

Annotation of /trunk/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 62 - (hide annotations) (download)
Tue Sep 7 11:08:22 2010 UTC (9 years, 11 months ago) by william
File MIME type: text/plain
File size: 126769 byte(s)
Auto Commited Import of: pcsx2-0.9.7-r3738-debug in ./trunk
1 william 31 /*
2     * Portable Audio I/O Library WASAPI implementation
3     * Copyright (c) 2006-2010 David Viens, Dmitry Kostjuchenko
4     *
5     * Based on the Open Source API proposed by Ross Bencina
6     * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
7     *
8     * Permission is hereby granted, free of charge, to any person obtaining
9     * a copy of this software and associated documentation files
10     * (the "Software"), to deal in the Software without restriction,
11     * including without limitation the rights to use, copy, modify, merge,
12     * publish, distribute, sublicense, and/or sell copies of the Software,
13     * and to permit persons to whom the Software is furnished to do so,
14     * subject to the following conditions:
15     *
16     * The above copyright notice and this permission notice shall be
17     * included in all copies or substantial portions of the Software.
18     *
19     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20     * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21     * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22     * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
23     * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24     * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25     * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26     */
27    
28     /*
29     * The text above constitutes the entire PortAudio license; however,
30     * the PortAudio community also makes the following non-binding requests:
31     *
32     * Any person wishing to distribute modifications to the Software is
33     * requested to send the modifications to the original developer so that
34     * they can be incorporated into the canonical version. It is also
35     * requested that these non-binding requests be included along with the
36     * license above.
37     */
38    
39     /** @file
40     @ingroup hostapi_src
41     @brief WASAPI implementation of support for a host API.
42     @note pa_wasapi currently requires minimum VC 2005, and the latest Vista SDK
43     */
44    
45     #define WIN32_LEAN_AND_MEAN // exclude rare headers
46     #include <windows.h>
47     #include <stdio.h>
48     #include <process.h>
49     #include <mmsystem.h>
50     #include <mmreg.h> // must be before other Wasapi headers
51     #if defined(_MSC_VER) && (_MSC_VER >= 1400)
52     #include <Avrt.h>
53     #define COBJMACROS
54     #include <Audioclient.h>
55     #include <endpointvolume.h>
56     #define INITGUID //<< avoid additional linkage of static libs, uneeded code will be optimized out
57     #include <mmdeviceapi.h>
58     #include <functiondiscoverykeys.h>
59     #undef INITGUID
60     #endif
61 william 62 #ifndef __MWERKS__
62     #include <malloc.h>
63     #include <memory.h>
64     #endif /* __MWERKS__ */
65 william 31
66     #include "pa_util.h"
67     #include "pa_allocation.h"
68     #include "pa_hostapi.h"
69     #include "pa_stream.h"
70     #include "pa_cpuload.h"
71     #include "pa_process.h"
72     #include "pa_debugprint.h"
73     #include "pa_win_wasapi.h"
74    
75     #ifndef NTDDI_VERSION
76    
77     #ifndef _AVRT_ //<< fix MinGW dummy compile by defining missing type: AVRT_PRIORITY
78     typedef enum _AVRT_PRIORITY
79     {
80     AVRT_PRIORITY_LOW = -1,
81     AVRT_PRIORITY_NORMAL,
82     AVRT_PRIORITY_HIGH,
83     AVRT_PRIORITY_CRITICAL
84     } AVRT_PRIORITY, *PAVRT_PRIORITY;
85     #endif
86    
87     #include <basetyps.h> // << for IID/CLSID
88     #include <rpcsal.h>
89     #include <sal.h>
90    
91     #ifndef __LPCGUID_DEFINED__
92     #define __LPCGUID_DEFINED__
93     typedef const GUID *LPCGUID;
94     #endif
95    
96     #ifndef PROPERTYKEY_DEFINED
97     #define PROPERTYKEY_DEFINED
98     typedef struct _tagpropertykey
99     {
100     GUID fmtid;
101     DWORD pid;
102     } PROPERTYKEY;
103     #endif
104    
105     #ifdef __midl_proxy
106     #define __MIDL_CONST
107     #else
108     #define __MIDL_CONST const
109     #endif
110    
111     #ifndef WAVE_FORMAT_IEEE_FLOAT
112     #define WAVE_FORMAT_IEEE_FLOAT 0x0003 // 32-bit floating-point
113     #endif
114    
115     // Speaker Positions:
116     #define SPEAKER_FRONT_LEFT 0x1
117     #define SPEAKER_FRONT_RIGHT 0x2
118     #define SPEAKER_FRONT_CENTER 0x4
119     #define SPEAKER_LOW_FREQUENCY 0x8
120     #define SPEAKER_BACK_LEFT 0x10
121     #define SPEAKER_BACK_RIGHT 0x20
122     #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
123     #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
124     #define SPEAKER_BACK_CENTER 0x100
125     #define SPEAKER_SIDE_LEFT 0x200
126     #define SPEAKER_SIDE_RIGHT 0x400
127     #define SPEAKER_TOP_CENTER 0x800
128     #define SPEAKER_TOP_FRONT_LEFT 0x1000
129     #define SPEAKER_TOP_FRONT_CENTER 0x2000
130     #define SPEAKER_TOP_FRONT_RIGHT 0x4000
131     #define SPEAKER_TOP_BACK_LEFT 0x8000
132     #define SPEAKER_TOP_BACK_CENTER 0x10000
133     #define SPEAKER_TOP_BACK_RIGHT 0x20000
134    
135     // Bit mask locations reserved for future use
136     #define SPEAKER_RESERVED 0x7FFC0000
137    
138     // Used to specify that any possible permutation of speaker configurations
139     #define SPEAKER_ALL 0x80000000
140    
141     // DirectSound Speaker Config
142     #if (NTDDI_VERSION >= NTDDI_WINXP)
143     #define KSAUDIO_SPEAKER_DIRECTOUT 0
144     #endif
145     #define KSAUDIO_SPEAKER_MONO (SPEAKER_FRONT_CENTER)
146     #define KSAUDIO_SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
147     #define KSAUDIO_SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
148     SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
149     #define KSAUDIO_SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
150     SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER)
151     #define KSAUDIO_SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
152     SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \
153     SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
154     #define KSAUDIO_SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
155     SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \
156     SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \
157     SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER)
158    
159     #if ( (NTDDI_VERSION >= NTDDI_WINXPSP2) && (NTDDI_VERSION < NTDDI_WS03) ) || (NTDDI_VERSION >= NTDDI_WS03SP1)
160    
161     #define KSAUDIO_SPEAKER_5POINT1_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
162     SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \
163     SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
164     #define KSAUDIO_SPEAKER_7POINT1_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
165     SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \
166     SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \
167     SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
168     // The following are obsolete 5.1 and 7.1 settings (they lack side speakers). Note this means
169     // that the default 5.1 and 7.1 settings (KSAUDIO_SPEAKER_5POINT1 and KSAUDIO_SPEAKER_7POINT1 are
170     // similarly obsolete but are unchanged for compatibility reasons).
171     #define KSAUDIO_SPEAKER_5POINT1_BACK KSAUDIO_SPEAKER_5POINT1
172     #define KSAUDIO_SPEAKER_7POINT1_WIDE KSAUDIO_SPEAKER_7POINT1
173    
174     #endif // XP SP2 and later (chronologically)
175    
176     // DVD Speaker Positions
177     #define KSAUDIO_SPEAKER_GROUND_FRONT_LEFT SPEAKER_FRONT_LEFT
178     #define KSAUDIO_SPEAKER_GROUND_FRONT_CENTER SPEAKER_FRONT_CENTER
179     #define KSAUDIO_SPEAKER_GROUND_FRONT_RIGHT SPEAKER_FRONT_RIGHT
180     #define KSAUDIO_SPEAKER_GROUND_REAR_LEFT SPEAKER_BACK_LEFT
181     #define KSAUDIO_SPEAKER_GROUND_REAR_RIGHT SPEAKER_BACK_RIGHT
182     #define KSAUDIO_SPEAKER_TOP_MIDDLE SPEAKER_TOP_CENTER
183     #define KSAUDIO_SPEAKER_SUPER_WOOFER SPEAKER_LOW_FREQUENCY
184    
185     #ifdef WIN64
186     #include <wtypes.h>
187     typedef LONG NTSTATUS;
188     typedef void *PKSEVENT_ENTRY;
189     typedef void *PKSDEVICE;
190     typedef void *PKSFILTERFACTORY;
191     typedef void *PKSFILTER;
192     typedef void *PIRP;
193     typedef void *PCM_RESOURCE_LIST;
194     typedef void *PDEVICE_CAPABILITIES;
195     typedef void *PKSPIN;
196     typedef void *PKSPROCESSPIN_INDEXENTRY;
197     typedef void KSATTRIBUTE_LIST;
198     typedef void *PKSHANDSHAKE;
199     typedef void *PKTIMER;
200     typedef void *PKDPC;
201     typedef void *PKSSTREAM_POINTER;
202     typedef void KSPROPERTY_SET;
203     typedef void KSCLOCK_DISPATCH;
204     typedef void KSMETHOD_SET;
205     typedef void KSEVENT_SET;
206     typedef void KSALLOCATOR_DISPATCH;
207     typedef void KSDEVICE_DISPATCH;
208     typedef void KSFILTER_DISPATCH;
209     typedef void KSFILTER_DESCRIPTOR;
210     typedef void KSPIN_DESCRIPTOR_EX;
211     typedef void KSPIN_DISPATCH;
212     typedef void KSDEVICE_DESCRIPTOR;
213     typedef void *PFNKSDELETEALLOCATOR;
214     typedef void *PFNKSDEFAULTALLOCATE;
215     typedef void *PFNKSDEFAULTFREE;
216     typedef void KSNODE_DESCRIPTOR;
217     typedef char KSPIN_DESCRIPTOR;
218     typedef void *PFNKSINTERSECTHANDLEREX;
219     typedef void *PDEVICE_OBJECT;
220     typedef void *PHYSICAL_ADDRESS;
221     typedef void *PKSSTREAM_POINTER_OFFSET;
222     typedef void *PKSPROCESSPIN;
223     typedef void *PMDL;
224     typedef ULONG KSSTREAM_POINTER_OFFSET;
225     typedef void KSJACK_DESCRIPTION;
226     #define FASTCALL
227     #include <oleidl.h>
228     #include <objidl.h>
229     #else
230     typedef struct _BYTE_BLOB
231     {
232     unsigned long clSize;
233     unsigned char abData[ 1 ];
234     } BYTE_BLOB;
235     typedef /* [unique] */ __RPC_unique_pointer BYTE_BLOB *UP_BYTE_BLOB;
236     typedef LONGLONG REFERENCE_TIME;
237     #define NONAMELESSUNION
238     #endif
239    
240     #undef WINVER
241     #undef _WIN32_WINNT
242     #include <sdkddkver.h>
243     #include <propkeydef.h>
244     #define COBJMACROS
245     #define INITGUID
246     #include <audioclient.h>
247     #include <mmdeviceapi.h>
248     #include <endpointvolume.h>
249     #include <functiondiscoverykeys.h>
250     #undef INITGUID
251    
252     #endif // NTDDI_VERSION
253    
254     #ifndef GUID_SECT
255     #define GUID_SECT
256     #endif
257    
258     #define __DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
259     #define __DEFINE_IID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const IID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
260     #define __DEFINE_CLSID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const CLSID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
261     #define PA_DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
262     __DEFINE_CLSID(pa_CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
263     #define PA_DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
264     __DEFINE_IID(pa_IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
265    
266     // "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2"
267     PA_DEFINE_IID(IAudioClient, 1cb9ad4c, dbfa, 4c32, b1, 78, c2, f5, 68, a7, 03, b2);
268     // "1BE09788-6894-4089-8586-9A2A6C265AC5"
269     PA_DEFINE_IID(IMMEndpoint, 1be09788, 6894, 4089, 85, 86, 9a, 2a, 6c, 26, 5a, c5);
270     // "A95664D2-9614-4F35-A746-DE8DB63617E6"
271     PA_DEFINE_IID(IMMDeviceEnumerator, a95664d2, 9614, 4f35, a7, 46, de, 8d, b6, 36, 17, e6);
272     // "BCDE0395-E52F-467C-8E3D-C4579291692E"
273     PA_DEFINE_CLSID(IMMDeviceEnumerator,bcde0395, e52f, 467c, 8e, 3d, c4, 57, 92, 91, 69, 2e);
274     // "F294ACFC-3146-4483-A7BF-ADDCA7C260E2"
275     PA_DEFINE_IID(IAudioRenderClient, f294acfc, 3146, 4483, a7, bf, ad, dc, a7, c2, 60, e2);
276     // "C8ADBD64-E71E-48a0-A4DE-185C395CD317"
277     PA_DEFINE_IID(IAudioCaptureClient, c8adbd64, e71e, 48a0, a4, de, 18, 5c, 39, 5c, d3, 17);
278     // Media formats:
279     __DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
280     __DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_ADPCM, 0x00000002, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
281     __DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
282    
283     /* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */
284     #if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
285     #define CREATE_THREAD(PROC) (HANDLE)_beginthreadex( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )
286     #define PA_THREAD_FUNC static unsigned WINAPI
287     #define PA_THREAD_ID unsigned
288     #else
289     #define CREATE_THREAD(PROC) CreateThread( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )
290     #define PA_THREAD_FUNC static DWORD WINAPI
291     #define PA_THREAD_ID DWORD
292     #endif
293    
294     // Thread function forward decl.
295     PA_THREAD_FUNC ProcThreadEvent(void *param);
296     PA_THREAD_FUNC ProcThreadPoll(void *param);
297    
298     // Availabe from Windows 7
299     #ifndef AUDCLNT_E_BUFFER_ERROR
300     #define AUDCLNT_E_BUFFER_ERROR AUDCLNT_ERR(0x018)
301     #endif
302     #ifndef AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
303     #define AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED AUDCLNT_ERR(0x019)
304     #endif
305     #ifndef AUDCLNT_E_INVALID_DEVICE_PERIOD
306     #define AUDCLNT_E_INVALID_DEVICE_PERIOD AUDCLNT_ERR(0x020)
307     #endif
308    
309     #define MAX_STR_LEN 512
310    
311     enum { S_INPUT = 0, S_OUTPUT, S_COUNT };
312    
313     #define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
314    
315     #define PRINT(x) PA_DEBUG(x);
316    
317     #define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
318     PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText )
319    
320     #ifndef IF_FAILED_JUMP
321     #define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;
322     #endif
323    
324     #define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; }
325     #define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->lpVtbl->Release((punk)); (punk) = NULL; }
326    
327     // Mixer function
328     typedef void (*MixMonoToStereoF) (void *__to, void *__from, UINT32 count);
329    
330     // AVRT is the new "multimedia schedulling stuff"
331     typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);
332     typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);
333     typedef BOOL (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE);
334     typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics) (LPCTSTR,LPDWORD);
335     typedef BOOL (WINAPI *FAvRevertMmThreadCharacteristics)(HANDLE);
336     typedef BOOL (WINAPI *FAvSetMmThreadPriority) (HANDLE,AVRT_PRIORITY);
337    
338     static HMODULE hDInputDLL = 0;
339     FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup = NULL;
340     FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup = NULL;
341     FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup = NULL;
342     FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics = NULL;
343     FAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL;
344     FAvSetMmThreadPriority pAvSetMmThreadPriority = NULL;
345    
346     #define _GetProc(fun, type, name) { \
347     fun = (type) GetProcAddress(hDInputDLL,name); \
348     if (fun == NULL) { \
349     PRINT(("GetProcAddr failed for %s" ,name)); \
350     return FALSE; \
351     } \
352     } \
353    
354     // ------------------------------------------------------------------------------------------
355     /* prototypes for functions declared in this file */
356     #ifdef __cplusplus
357     extern "C"
358     {
359     #endif /* __cplusplus */
360     PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
361     #ifdef __cplusplus
362     }
363     #endif /* __cplusplus */
364     // dummy entry point for other compilers and sdks
365     // currently built using RC1 SDK (5600)
366     //#if _MSC_VER < 1400
367     //PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
368     //{
369     //return paNoError;
370     //}
371     //#else
372    
373     // ------------------------------------------------------------------------------------------
374     static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
375     static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
376     const PaStreamParameters *inputParameters,
377     const PaStreamParameters *outputParameters,
378     double sampleRate );
379     static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
380     PaStream** s,
381     const PaStreamParameters *inputParameters,
382     const PaStreamParameters *outputParameters,
383     double sampleRate,
384     unsigned long framesPerBuffer,
385     PaStreamFlags streamFlags,
386     PaStreamCallback *streamCallback,
387     void *userData );
388     static PaError CloseStream( PaStream* stream );
389     static PaError StartStream( PaStream *stream );
390     static PaError StopStream( PaStream *stream );
391     static PaError AbortStream( PaStream *stream );
392     static PaError IsStreamStopped( PaStream *s );
393     static PaError IsStreamActive( PaStream *stream );
394     static PaTime GetStreamTime( PaStream *stream );
395     static double GetStreamCpuLoad( PaStream* stream );
396     static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
397     static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
398     static signed long GetStreamReadAvailable( PaStream* stream );
399     static signed long GetStreamWriteAvailable( PaStream* stream );
400    
401     // ------------------------------------------------------------------------------------------
402     /*
403     These are fields that can be gathered from IDevice and IAudioDevice PRIOR to Initialize, and
404     done in first pass i assume that neither of these will cause the Driver to "load", but again,
405     who knows how they implement their stuff
406     */
407     typedef struct PaWasapiDeviceInfo
408     {
409     // Device
410     IMMDevice *device;
411    
412     // from GetId
413     WCHAR szDeviceID[MAX_STR_LEN];
414    
415     // from GetState
416     DWORD state;
417    
418     // Fields filled from IMMEndpoint'sGetDataFlow
419     EDataFlow flow;
420    
421     // Fields filled from IAudioDevice (_prior_ to Initialize)
422     // from GetDevicePeriod(
423     REFERENCE_TIME DefaultDevicePeriod;
424     REFERENCE_TIME MinimumDevicePeriod;
425    
426     // from GetMixFormat
427     // WAVEFORMATEX *MixFormat;//needs to be CoTaskMemFree'd after use!
428    
429     // Default format (setup through Control Panel by user)
430     WAVEFORMATEXTENSIBLE DefaultFormat;
431    
432     // Formfactor
433     EndpointFormFactor formFactor;
434     }
435     PaWasapiDeviceInfo;
436    
437     // ------------------------------------------------------------------------------------------
438     /* PaWasapiHostApiRepresentation - host api datastructure specific to this implementation */
439     typedef struct
440     {
441     PaUtilHostApiRepresentation inheritedHostApiRep;
442     PaUtilStreamInterface callbackStreamInterface;
443     PaUtilStreamInterface blockingStreamInterface;
444    
445     PaUtilAllocationGroup *allocations;
446    
447     /* implementation specific data goes here */
448    
449     //in case we later need the synch
450     IMMDeviceEnumerator *enumerator;
451    
452     //this is the REAL number of devices, whether they are usefull to PA or not!
453     UINT32 deviceCount;
454    
455     WCHAR defaultRenderer [MAX_STR_LEN];
456     WCHAR defaultCapturer [MAX_STR_LEN];
457    
458     PaWasapiDeviceInfo *devInfo;
459    
460     // Is true when WOW64 Vista/7 Workaround is needed
461     BOOL useWOW64Workaround;
462     }
463     PaWasapiHostApiRepresentation;
464    
465     // ------------------------------------------------------------------------------------------
466     /* PaWasapiStream - a stream data structure specifically for this implementation */
467     typedef struct PaWasapiSubStream
468     {
469     IAudioClient *client;
470     WAVEFORMATEXTENSIBLE wavex;
471     UINT32 bufferSize;
472     REFERENCE_TIME device_latency;
473     REFERENCE_TIME period;
474     double latency_seconds;
475     UINT32 framesPerHostCallback;
476     AUDCLNT_SHAREMODE shareMode;
477     UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ...
478     UINT32 flags;
479    
480     // Used by blocking interface:
481     UINT32 prevTime; // time ms between calls of WriteStream
482     UINT32 prevSleep; // time ms to sleep from frames written in previous call
483    
484     // Used for Mono >> Stereo workaround, if driver does not support it
485     // (in Exclusive mode WASAPI usually refuses to operate with Mono (1-ch)
486     void *monoBuffer; //!< pointer to buffer
487     UINT32 monoBufferSize; //!< buffer size in bytes
488     MixMonoToStereoF monoMixer; //!< pointer to mixer function
489     }
490     PaWasapiSubStream;
491    
492     // ------------------------------------------------------------------------------------------
493     /* PaWasapiHostProcessor - redirects processing data */
494     typedef struct PaWasapiHostProcessor
495     {
496     PaWasapiHostProcessorCallback processor;
497     void *userData;
498     }
499     PaWasapiHostProcessor;
500    
501     // ------------------------------------------------------------------------------------------
502     typedef struct PaWasapiStream
503     {
504     /* IMPLEMENT ME: rename this */
505     PaUtilStreamRepresentation streamRepresentation;
506     PaUtilCpuLoadMeasurer cpuLoadMeasurer;
507     PaUtilBufferProcessor bufferProcessor;
508    
509     // input
510     PaWasapiSubStream in;
511     IAudioCaptureClient *cclient;
512     IAudioEndpointVolume *inVol;
513    
514     // output
515     PaWasapiSubStream out;
516     IAudioRenderClient *rclient;
517     IAudioEndpointVolume *outVol;
518    
519     // event handles for event-driven processing mode
520     HANDLE event[S_COUNT];
521    
522     // must be volatile to avoid race condition on user query while
523     // thread is being started
524     volatile BOOL running;
525    
526     PA_THREAD_ID dwThreadId;
527     HANDLE hThread;
528     HANDLE hCloseRequest;
529     HANDLE hThreadStart; //!< signalled by thread on start
530     HANDLE hThreadExit; //!< signalled by thread on exit
531     HANDLE hBlockingOpStreamRD;
532     HANDLE hBlockingOpStreamWR;
533    
534     // Host callback Output overrider
535     PaWasapiHostProcessor hostProcessOverrideOutput;
536    
537     // Host callback Input overrider
538     PaWasapiHostProcessor hostProcessOverrideInput;
539    
540     // Defines blocking/callback interface used
541     BOOL bBlocking;
542    
543     // Av Task (MM thread management)
544     HANDLE hAvTask;
545    
546     // Thread priority level
547     PaWasapiThreadPriority nThreadPriority;
548     }
549     PaWasapiStream;
550    
551     // Local stream methods
552     static void _OnStreamStop(PaWasapiStream *stream);
553     static void _FinishStream(PaWasapiStream *stream);
554    
555     // Local statics
556     static volatile BOOL g_WasapiCOMInit = FALSE;
557     static volatile DWORD g_WasapiInitThread = 0;
558    
559     // ------------------------------------------------------------------------------------------
560     #define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__)
561     static HRESULT __LogHostError(HRESULT res, const char *func, const char *file, int line)
562     {
563     const char *text = NULL;
564     switch (res)
565     {
566     case S_OK: return res;
567     case E_POINTER :text ="E_POINTER"; break;
568     case E_INVALIDARG :text ="E_INVALIDARG"; break;
569    
570     case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break;
571     case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;
572     case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;
573     case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;
574     case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break;
575     case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;
576     case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break;
577     case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
578     case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break;
579     case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break;
580     case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;
581     case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;
582     case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;
583     case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;
584     case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
585     case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;
586     case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;
587     case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;
588     case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
589     case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
590     case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
591     case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;
592     case AUDCLNT_E_BUFFER_ERROR :text ="AUDCLNT_E_BUFFER_ERROR"; break;
593     case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED :text ="AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break;
594     case AUDCLNT_E_INVALID_DEVICE_PERIOD :text ="AUDCLNT_E_INVALID_DEVICE_PERIOD"; break;
595    
596     case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break;
597     case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
598     case AUDCLNT_S_POSITION_STALLED :text ="AUDCLNT_S_POSITION_STALLED"; break;
599    
600     // other windows common errors:
601     case CO_E_NOTINITIALIZED :text ="CO_E_NOTINITIALIZED: you must call CoInitialize() before Pa_OpenStream()"; break;
602    
603     default:
604     text = "UNKNOWN ERROR";
605     }
606     PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", res, text, func, file, line));
607     PA_SKELETON_SET_LAST_HOST_ERROR(res, text);
608     return res;
609     }
610    
611     // ------------------------------------------------------------------------------------------
612     #define LogPaError(PAERR) __LogPaError(PAERR, __FUNCTION__, __FILE__, __LINE__)
613     static PaError __LogPaError(PaError err, const char *func, const char *file, int line)
614     {
615     if (err == paNoError)
616     return err;
617     PRINT(("WASAPI ERROR PAERROR: %i : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", err, Pa_GetErrorText(err), func, file, line));
618     return err;
619     }
620    
621     // ------------------------------------------------------------------------------------------
622     static double nano100ToMillis(REFERENCE_TIME ref)
623     {
624     // 1 nano = 0.000000001 seconds
625     //100 nano = 0.0000001 seconds
626     //100 nano = 0.0001 milliseconds
627     return ((double)ref)*0.0001;
628     }
629    
630     // ------------------------------------------------------------------------------------------
631     static double nano100ToSeconds(REFERENCE_TIME ref)
632     {
633     // 1 nano = 0.000000001 seconds
634     //100 nano = 0.0000001 seconds
635     //100 nano = 0.0001 milliseconds
636     return ((double)ref)*0.0000001;
637     }
638    
639     // ------------------------------------------------------------------------------------------
640     static REFERENCE_TIME MillisTonano100(double ref)
641     {
642     // 1 nano = 0.000000001 seconds
643     //100 nano = 0.0000001 seconds
644     //100 nano = 0.0001 milliseconds
645     return (REFERENCE_TIME)(ref/0.0001);
646     }
647    
648     // ------------------------------------------------------------------------------------------
649     static REFERENCE_TIME SecondsTonano100(double ref)
650     {
651     // 1 nano = 0.000000001 seconds
652     //100 nano = 0.0000001 seconds
653     //100 nano = 0.0001 milliseconds
654     return (REFERENCE_TIME)(ref/0.0000001);
655     }
656    
657     // ------------------------------------------------------------------------------------------
658     // Makes Hns period from frames and sample rate
659     static REFERENCE_TIME MakeHnsPeriod(UINT32 nFrames, DWORD nSamplesPerSec)
660     {
661     return (REFERENCE_TIME)((10000.0 * 1000 / nSamplesPerSec * nFrames) + 0.5);
662     }
663    
664     // ------------------------------------------------------------------------------------------
665     // Converts PaSampleFormat to bits per sample value
666     static WORD PaSampleFormatToBitsPerSample(PaSampleFormat format_id)
667     {
668     switch (format_id & ~paNonInterleaved)
669     {
670     case paFloat32:
671     case paInt32: return 32;
672     case paInt24: return 24;
673     case paInt16: return 16;
674     case paInt8:
675     case paUInt8: return 8;
676     }
677     return 0;
678     }
679    
680     // ------------------------------------------------------------------------------------------
681     // Converts PaSampleFormat to bits per sample value
682     static WORD PaSampleFormatToBytesPerSample(PaSampleFormat format_id)
683     {
684     return PaSampleFormatToBitsPerSample(format_id) >> 3; // 'bits/8'
685     }
686    
687     // ------------------------------------------------------------------------------------------
688     // Converts Hns period into number of frames
689     static UINT32 MakeFramesFromHns(REFERENCE_TIME hnsPeriod, UINT32 nSamplesPerSec)
690     {
691     UINT32 nFrames = (UINT32)( // frames =
692     1.0 * hnsPeriod * // hns *
693     nSamplesPerSec / // (frames / s) /
694     1000 / // (ms / s) /
695     10000 // (hns / s) /
696     + 0.5 // rounding
697     );
698     return nFrames;
699     }
700    
701     // ------------------------------------------------------------------------------------------
702     // Aligns v backwards
703     static UINT32 ALIGN_BWD(UINT32 v, UINT32 align)
704     {
705     return ((v - (align ? v % align : 0)));
706     }
707    
708     // ------------------------------------------------------------------------------------------
709     // Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer
710     // is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
711     // is thrown although we must align for Vista anyway.
712     static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign)
713     {
714     #define HDA_PACKET_SIZE 128
715    
716     //long packets_total = 10000 * (nSamplesPerSec * nBlockAlign / HDA_PACKET_SIZE);
717     long frame_bytes = nFrames * nBlockAlign;
718     long packets;
719    
720     // align to packet size
721     frame_bytes = ALIGN_BWD(frame_bytes, HDA_PACKET_SIZE);
722     nFrames = frame_bytes / nBlockAlign;
723     packets = frame_bytes / HDA_PACKET_SIZE;
724    
725     // align to packets count
726     /*while (packets && ((packets_total % packets) != 0))
727     {
728     --packets;
729     }*/
730    
731     frame_bytes = packets * HDA_PACKET_SIZE;
732     nFrames = frame_bytes / nBlockAlign;
733    
734     return nFrames;
735     }
736    
737     // ------------------------------------------------------------------------------------------
738     static UINT32 GetFramesSleepTime(UINT32 nFrames, UINT32 nSamplesPerSec)
739     {
740     REFERENCE_TIME nDuration;
741     if (nSamplesPerSec == 0)
742     return 0;
743     #define REFTIMES_PER_SEC 10000000
744     #define REFTIMES_PER_MILLISEC 10000
745     // Calculate the actual duration of the allocated buffer.
746     nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);
747     return (UINT32)(nDuration/REFTIMES_PER_MILLISEC/2);
748     }
749    
750     // ------------------------------------------------------------------------------------------
751     static BOOL SetupAVRT()
752     {
753     hDInputDLL = LoadLibraryA("avrt.dll");
754     if (hDInputDLL == NULL)
755     return FALSE;
756    
757     _GetProc(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup");
758     _GetProc(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup");
759     _GetProc(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup");
760     _GetProc(pAvSetMmThreadCharacteristics, FAvSetMmThreadCharacteristics, "AvSetMmThreadCharacteristicsA");
761     _GetProc(pAvRevertMmThreadCharacteristics,FAvRevertMmThreadCharacteristics,"AvRevertMmThreadCharacteristics");
762     _GetProc(pAvSetMmThreadPriority, FAvSetMmThreadPriority, "AvSetMmThreadPriority");
763    
764     return pAvRtCreateThreadOrderingGroup &&
765     pAvRtDeleteThreadOrderingGroup &&
766     pAvRtWaitOnThreadOrderingGroup &&
767     pAvSetMmThreadCharacteristics &&
768     pAvRevertMmThreadCharacteristics &&
769     pAvSetMmThreadPriority;
770     }
771    
772     // ------------------------------------------------------------------------------------------
773     static void CloseAVRT()
774     {
775     if (hDInputDLL != NULL)
776     FreeLibrary(hDInputDLL);
777     hDInputDLL = NULL;
778     }
779    
780     // ------------------------------------------------------------------------------------------
781     static BOOL IsWow64()
782     {
783     // http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx
784    
785     typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
786     LPFN_ISWOW64PROCESS fnIsWow64Process;
787    
788     BOOL bIsWow64 = FALSE;
789    
790     // IsWow64Process is not available on all supported versions of Windows.
791     // Use GetModuleHandle to get a handle to the DLL that contains the function
792     // and GetProcAddress to get a pointer to the function if available.
793    
794     fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
795     GetModuleHandle(TEXT("kernel32")), TEXT("IsWow64Process"));
796    
797     if (fnIsWow64Process == NULL)
798     return FALSE;
799    
800     if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
801     return FALSE;
802    
803     return bIsWow64;
804     }
805    
806     // ------------------------------------------------------------------------------------------
807     typedef enum EWindowsVersion
808     {
809     WINDOWS_UNKNOWN = 0,
810     WINDOWS_VISTA_SERVER2008 = (1 << 0),
811     WINDOWS_7_SERVER2008R2 = (1 << 1),
812     WINDOWS_FUTURE = (1 << 2)
813     }
814     EWindowsVersion;
815     // Defines Windows 7/Windows Server 2008 R2 and up (future versions)
816     #define WINDOWS_7_SERVER2008R2_AND_UP (WINDOWS_7_SERVER2008R2|WINDOWS_FUTURE)
817     // The function is limited to Vista/7 mostly as we need just to find out Vista/WOW64 combination
818     // in order to use WASAPI WOW64 workarounds.
819     static UINT32 GetWindowsVersion()
820     {
821     static UINT32 version = WINDOWS_UNKNOWN;
822    
823     if (version == WINDOWS_UNKNOWN)
824     {
825     DWORD dwVersion = 0;
826     DWORD dwMajorVersion = 0;
827     DWORD dwMinorVersion = 0;
828     DWORD dwBuild = 0;
829    
830     typedef DWORD (WINAPI *LPFN_GETVERSION)(VOID);
831     LPFN_GETVERSION fnGetVersion;
832    
833     fnGetVersion = (LPFN_GETVERSION) GetProcAddress(GetModuleHandle(TEXT("kernel32")), TEXT("GetVersion"));
834     if (fnGetVersion == NULL)
835     return WINDOWS_UNKNOWN;
836    
837     dwVersion = fnGetVersion();
838    
839     // Get the Windows version
840     dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
841     dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
842    
843     // Get the build number
844     if (dwVersion < 0x80000000)
845     dwBuild = (DWORD)(HIWORD(dwVersion));
846    
847     switch (dwMajorVersion)
848     {
849     case 0:
850     case 1:
851     case 2:
852     case 3:
853     case 4:
854     case 5:
855     break; // skip lower
856     case 6:
857     switch (dwMinorVersion)
858     {
859     case 0:
860     version |= WINDOWS_VISTA_SERVER2008;
861     break;
862     case 1:
863     version |= WINDOWS_7_SERVER2008R2;
864     break;
865     default:
866     version |= WINDOWS_FUTURE;
867     }
868     break;
869     default:
870     version |= WINDOWS_FUTURE;
871     }
872     }
873    
874     return version;
875     }
876    
877     // ------------------------------------------------------------------------------------------
878     static BOOL UseWOW64Workaround()
879     {
880     // note: WOW64 bug is common to Windows Vista x64, thus we fall back to safe Poll-driven
881     // method. Windows 7 x64 seems has WOW64 bug fixed.
882    
883     return (IsWow64() && (GetWindowsVersion() & WINDOWS_VISTA_SERVER2008));
884     }
885    
886     // ------------------------------------------------------------------------------------------
887     #define _WASAPI_MONO_TO_STEREO_MIXER(TYPE)\
888     TYPE * __restrict to = __to;\
889     TYPE * __restrict from = __from;\
890     TYPE * __restrict end = from + count;\
891     while (from != end)\
892     {\
893     *to ++ = *from;\
894     *to ++ = *from;\
895     ++ from;\
896     }
897    
898     // ------------------------------------------------------------------------------------------
899     static void _MixMonoToStereo_8(void *__to, void *__from, UINT32 count)
900     {
901     _WASAPI_MONO_TO_STEREO_MIXER(BYTE);
902     }
903    
904     // ------------------------------------------------------------------------------------------
905     static void _MixMonoToStereo_16(void *__to, void *__from, UINT32 count)
906     {
907     _WASAPI_MONO_TO_STEREO_MIXER(short);
908     }
909    
910     // ------------------------------------------------------------------------------------------
911     static void _MixMonoToStereo_24(void *__to, void *__from, UINT32 count)
912     {
913     _WASAPI_MONO_TO_STEREO_MIXER(int); // !!! int24 data is contained in 32-bit containers
914     }
915    
916     // ------------------------------------------------------------------------------------------
917     static void _MixMonoToStereo_32(void *__to, void *__from, UINT32 count)
918     {
919     _WASAPI_MONO_TO_STEREO_MIXER(int);
920     }
921    
922     // ------------------------------------------------------------------------------------------
923     static void _MixMonoToStereo_32f(void *__to, void *__from, UINT32 count)
924     {
925     _WASAPI_MONO_TO_STEREO_MIXER(float);
926     }
927    
928     // ------------------------------------------------------------------------------------------
929     static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format)
930     {
931     switch (format)
932     {
933     case paUInt8: return _MixMonoToStereo_8;
934     case paInt16: return _MixMonoToStereo_16;
935     case paInt24: return _MixMonoToStereo_24;
936     case paInt32: return _MixMonoToStereo_32;
937     case paFloat32: return _MixMonoToStereo_32f;
938     }
939     return NULL;
940     }
941    
942     // ------------------------------------------------------------------------------------------
943     PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
944     {
945     PaError result = paNoError;
946     PaWasapiHostApiRepresentation *paWasapi;
947     PaDeviceInfo *deviceInfoArray;
948     HRESULT hr = S_OK;
949     IMMDeviceCollection* pEndPoints = NULL;
950     UINT i;
951    
952     if (!SetupAVRT())
953     {
954     PRINT(("WASAPI: No AVRT! (not VISTA?)"));
955     return paNoError;
956     }
957    
958     /*
959     If COM is already initialized CoInitialize will either return
960     FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
961     threading mode. In either case we shouldn't consider it an error
962     but we need to be careful to not call CoUninitialize() if
963     RPC_E_CHANGED_MODE was returned.
964     */
965     hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
966     if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))
967     {
968     PRINT(("WASAPI: failed CoInitialize"));
969     return paUnanticipatedHostError;
970     }
971     if (hr != RPC_E_CHANGED_MODE)
972     g_WasapiCOMInit = TRUE;
973    
974     // Memorize calling thread id and report warning on Uninitialize if calling thread is different
975     // as CoInitialize must match CoUninitialize in the same thread.
976     g_WasapiInitThread = GetCurrentThreadId();
977    
978     paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) );
979     if (paWasapi == NULL)
980     {
981     result = paInsufficientMemory;
982     goto error;
983     }
984    
985     paWasapi->allocations = PaUtil_CreateAllocationGroup();
986     if (paWasapi->allocations == NULL)
987     {
988     result = paInsufficientMemory;
989     goto error;
990     }
991    
992     *hostApi = &paWasapi->inheritedHostApiRep;
993     (*hostApi)->info.structVersion = 1;
994     (*hostApi)->info.type = paWASAPI;
995     (*hostApi)->info.name = "Windows WASAPI";
996     (*hostApi)->info.deviceCount = 0;
997     (*hostApi)->info.defaultInputDevice = paNoDevice;
998     (*hostApi)->info.defaultOutputDevice = paNoDevice;
999    
1000     paWasapi->enumerator = NULL;
1001     hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER,
1002     &pa_IID_IMMDeviceEnumerator, (void **)&paWasapi->enumerator);
1003     IF_FAILED_JUMP(hr, error);
1004    
1005     // getting default device ids in the eMultimedia "role"
1006     {
1007     {
1008     IMMDevice *defaultRenderer = NULL;
1009     hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eRender, eMultimedia, &defaultRenderer);
1010     if (hr != S_OK)
1011     {
1012     if (hr != E_NOTFOUND)
1013     IF_FAILED_JUMP(hr, error);
1014     }
1015     else
1016     {
1017     WCHAR *pszDeviceId = NULL;
1018     hr = IMMDevice_GetId(defaultRenderer, &pszDeviceId);
1019     IF_FAILED_JUMP(hr, error);
1020     wcsncpy(paWasapi->defaultRenderer, pszDeviceId, MAX_STR_LEN-1);
1021     CoTaskMemFree(pszDeviceId);
1022     IMMDevice_Release(defaultRenderer);
1023     }
1024     }
1025    
1026     {
1027     IMMDevice *defaultCapturer = NULL;
1028     hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eCapture, eMultimedia, &defaultCapturer);
1029     if (hr != S_OK)
1030     {
1031     if (hr != E_NOTFOUND)
1032     IF_FAILED_JUMP(hr, error);
1033     }
1034     else
1035     {
1036     WCHAR *pszDeviceId = NULL;
1037     hr = IMMDevice_GetId(defaultCapturer, &pszDeviceId);
1038     IF_FAILED_JUMP(hr, error);
1039     wcsncpy(paWasapi->defaultCapturer, pszDeviceId, MAX_STR_LEN-1);
1040     CoTaskMemFree(pszDeviceId);
1041     IMMDevice_Release(defaultCapturer);
1042     }
1043     }
1044     }
1045    
1046     hr = IMMDeviceEnumerator_EnumAudioEndpoints(paWasapi->enumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints);
1047     IF_FAILED_JUMP(hr, error);
1048    
1049     hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount);
1050     IF_FAILED_JUMP(hr, error);
1051    
1052     paWasapi->devInfo = (PaWasapiDeviceInfo *)malloc(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);
1053     for (i = 0; i < paWasapi->deviceCount; ++i)
1054     memset(&paWasapi->devInfo[i], 0, sizeof(PaWasapiDeviceInfo));
1055    
1056     if (paWasapi->deviceCount > 0)
1057     {
1058     (*hostApi)->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory(
1059     paWasapi->allocations, sizeof(PaDeviceInfo *) * paWasapi->deviceCount);
1060     if ((*hostApi)->deviceInfos == NULL)
1061     {
1062     result = paInsufficientMemory;
1063     goto error;
1064     }
1065    
1066     /* allocate all device info structs in a contiguous block */
1067     deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory(
1068     paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount);
1069     if (deviceInfoArray == NULL)
1070     {
1071     result = paInsufficientMemory;
1072     goto error;
1073     }
1074    
1075     for (i = 0; i < paWasapi->deviceCount; ++i)
1076     {
1077     DWORD state = 0;
1078     PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
1079     deviceInfo->structVersion = 2;
1080     deviceInfo->hostApi = hostApiIndex;
1081    
1082     PA_DEBUG(("WASAPI: device idx: %02d\n", i));
1083     PA_DEBUG(("WASAPI: ---------------\n"));
1084    
1085     hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device);
1086     IF_FAILED_JUMP(hr, error);
1087    
1088     // getting ID
1089     {
1090     WCHAR *pszDeviceId = NULL;
1091     hr = IMMDevice_GetId(paWasapi->devInfo[i].device, &pszDeviceId);
1092     IF_FAILED_JUMP(hr, error);
1093     wcsncpy(paWasapi->devInfo[i].szDeviceID, pszDeviceId, MAX_STR_LEN-1);
1094     CoTaskMemFree(pszDeviceId);
1095    
1096     if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer) == 0)
1097     {// we found the default input!
1098     (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
1099     }
1100     if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer) == 0)
1101     {// we found the default output!
1102     (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
1103     }
1104     }
1105    
1106     hr = IMMDevice_GetState(paWasapi->devInfo[i].device, &paWasapi->devInfo[i].state);
1107     IF_FAILED_JUMP(hr, error);
1108    
1109     if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE)
1110     {
1111     PRINT(("WASAPI device: %d is not currently available (state:%d)\n",i,state));
1112     }
1113    
1114     {
1115     IPropertyStore *pProperty;
1116     hr = IMMDevice_OpenPropertyStore(paWasapi->devInfo[i].device, STGM_READ, &pProperty);
1117     IF_FAILED_JUMP(hr, error);
1118    
1119     // "Friendly" Name
1120     {
1121     char *deviceName;
1122     PROPVARIANT value;
1123     PropVariantInit(&value);
1124     hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value);
1125     IF_FAILED_JUMP(hr, error);
1126     deviceInfo->name = NULL;
1127     deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1);
1128     if (deviceName == NULL)
1129     {
1130     result = paInsufficientMemory;
1131     goto error;
1132     }
1133     if (value.pwszVal)
1134     wcstombs(deviceName, value.pwszVal, MAX_STR_LEN-1);
1135     else
1136     _snprintf(deviceName, MAX_STR_LEN-1, "baddev%d", i);
1137     deviceInfo->name = deviceName;
1138     PropVariantClear(&value);
1139     PA_DEBUG(("WASAPI:%d| name[%s]\n", i, deviceInfo->name));
1140     }
1141    
1142     // Default format
1143     {
1144     PROPVARIANT value;
1145     PropVariantInit(&value);
1146     hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value);
1147     IF_FAILED_JUMP(hr, error);
1148     memcpy(&paWasapi->devInfo[i].DefaultFormat, value.blob.pBlobData, min(sizeof(paWasapi->devInfo[i].DefaultFormat), value.blob.cbSize));
1149     // cleanup
1150     PropVariantClear(&value);
1151     }
1152    
1153     // Formfactor
1154     {
1155     PROPVARIANT value;
1156     PropVariantInit(&value);
1157     hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value);
1158     IF_FAILED_JUMP(hr, error);
1159     // set
1160     #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION)
1161     // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1)));
1162     UINT v;
1163     memcpy(&v, (((WORD *)&value.wReserved3)+1), sizeof(v));
1164     paWasapi->devInfo[i].formFactor = (EndpointFormFactor)v;
1165     #else
1166     paWasapi->devInfo[i].formFactor = (EndpointFormFactor)value.uintVal;
1167     #endif
1168     PA_DEBUG(("WASAPI:%d| form-factor[%d]\n", i, paWasapi->devInfo[i].formFactor));
1169     // cleanup
1170     PropVariantClear(&value);
1171     }
1172    
1173     SAFE_RELEASE(pProperty);
1174     }
1175    
1176    
1177     // Endpoint data
1178     {
1179     IMMEndpoint *endpoint = NULL;
1180     hr = IMMDevice_QueryInterface(paWasapi->devInfo[i].device, &pa_IID_IMMEndpoint, (void **)&endpoint);
1181     if (SUCCEEDED(hr))
1182     {
1183     hr = IMMEndpoint_GetDataFlow(endpoint, &paWasapi->devInfo[i].flow);
1184     SAFE_RELEASE(endpoint);
1185     }
1186     }
1187    
1188     // Getting a temporary IAudioClient for more fields
1189     // we make sure NOT to call Initialize yet!
1190     {
1191     IAudioClient *tmpClient = NULL;
1192    
1193     hr = IMMDevice_Activate(paWasapi->devInfo[i].device, &pa_IID_IAudioClient,
1194     CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient);
1195     IF_FAILED_JUMP(hr, error);
1196    
1197     hr = IAudioClient_GetDevicePeriod(tmpClient,
1198     &paWasapi->devInfo[i].DefaultDevicePeriod,
1199     &paWasapi->devInfo[i].MinimumDevicePeriod);
1200     IF_FAILED_JUMP(hr, error);
1201    
1202     //hr = tmpClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);
1203    
1204     // Release client
1205     SAFE_RELEASE(tmpClient);
1206    
1207     if (hr != S_OK)
1208     {
1209     //davidv: this happened with my hardware, previously for that same device in DirectSound:
1210     //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
1211     //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat
1212     LogHostError(hr);
1213     goto error;
1214     }
1215     }
1216    
1217     // we can now fill in portaudio device data
1218     deviceInfo->maxInputChannels = 0;
1219     deviceInfo->maxOutputChannels = 0;
1220     deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec;
1221     switch (paWasapi->devInfo[i].flow)
1222     {
1223     case eRender: {
1224     deviceInfo->maxOutputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;
1225     deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
1226     deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
1227     PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
1228     deviceInfo->maxOutputChannels, (float)deviceInfo->defaultHighOutputLatency, (float)deviceInfo->defaultLowOutputLatency));
1229     break;}
1230     case eCapture: {
1231     deviceInfo->maxInputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;
1232     deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
1233     deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
1234     PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
1235     deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency));
1236     break; }
1237     default:
1238     PRINT(("WASAPI:%d| bad Data Flow!\n", i));
1239     //continue; // do not skip from list, allow to initialize
1240     break;
1241     }
1242    
1243     (*hostApi)->deviceInfos[i] = deviceInfo;
1244     ++(*hostApi)->info.deviceCount;
1245     }
1246     }
1247    
1248     (*hostApi)->Terminate = Terminate;
1249     (*hostApi)->OpenStream = OpenStream;
1250     (*hostApi)->IsFormatSupported = IsFormatSupported;
1251    
1252     PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream,
1253     StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1254     GetStreamTime, GetStreamCpuLoad,
1255     PaUtil_DummyRead, PaUtil_DummyWrite,
1256     PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1257    
1258     PaUtil_InitializeStreamInterface( &paWasapi->blockingStreamInterface, CloseStream, StartStream,
1259     StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1260     GetStreamTime, PaUtil_DummyGetCpuLoad,
1261     ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1262    
1263    
1264     // findout if platform workaround is required
1265     paWasapi->useWOW64Workaround = UseWOW64Workaround();
1266    
1267     SAFE_RELEASE(pEndPoints);
1268    
1269     PRINT(("WASAPI: initialized ok\n"));
1270    
1271     return paNoError;
1272    
1273     error:
1274    
1275     PRINT(("WASAPI: failed %s error[%d|%s]\n", __FUNCTION__, result, Pa_GetErrorText(result)));
1276    
1277     SAFE_RELEASE(pEndPoints);
1278    
1279     Terminate((PaUtilHostApiRepresentation *)paWasapi);
1280    
1281     return result;
1282     }
1283    
1284     // ------------------------------------------------------------------------------------------
1285     static void Terminate( PaUtilHostApiRepresentation *hostApi )
1286     {
1287     UINT i;
1288     PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
1289     if (paWasapi == NULL)
1290     return;
1291    
1292     // Release IMMDeviceEnumerator
1293     SAFE_RELEASE(paWasapi->enumerator);
1294    
1295     for (i = 0; i < paWasapi->deviceCount; ++i)
1296     {
1297     PaWasapiDeviceInfo *info = &paWasapi->devInfo[i];
1298     SAFE_RELEASE(info->device);
1299    
1300     //if (info->MixFormat)
1301     // CoTaskMemFree(info->MixFormat);
1302     }
1303     free(paWasapi->devInfo);
1304    
1305     if (paWasapi->allocations)
1306     {
1307     PaUtil_FreeAllAllocations(paWasapi->allocations);
1308     PaUtil_DestroyAllocationGroup(paWasapi->allocations);
1309     }
1310    
1311     PaUtil_FreeMemory(paWasapi);
1312    
1313     // Close AVRT
1314     CloseAVRT();
1315    
1316     // Uninit COM (checking calling thread we won't unitialize user's COM if one is calling
1317     // Pa_Unitialize by mistake from not initializing thread)
1318     if (g_WasapiCOMInit)
1319     {
1320     DWORD calling_thread_id = GetCurrentThreadId();
1321     if (g_WasapiInitThread != calling_thread_id)
1322     {
1323     PRINT(("WASAPI: failed CoUninitializes calling thread[%d] does not match initializing thread[%d]\n",
1324     calling_thread_id, g_WasapiInitThread));
1325     }
1326     else
1327     {
1328     CoUninitialize();
1329     }
1330     }
1331     }
1332    
1333     // ------------------------------------------------------------------------------------------
1334     static PaWasapiHostApiRepresentation *_GetHostApi(PaError *_error)
1335     {
1336     PaError error;
1337    
1338     PaUtilHostApiRepresentation *pApi;
1339     if ((error = PaUtil_GetHostApiRepresentation(&pApi, paWASAPI)) != paNoError)
1340     {
1341     if (_error != NULL)
1342     (*_error) = error;
1343    
1344     return NULL;
1345     }
1346     return (PaWasapiHostApiRepresentation *)pApi;
1347     }
1348    
1349     // ------------------------------------------------------------------------------------------
1350     int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice )
1351     {
1352     PaError ret;
1353     PaWasapiHostApiRepresentation *paWasapi;
1354     UINT32 size;
1355     PaDeviceIndex index;
1356    
1357     if (pFormat == NULL)
1358     return paBadBufferPtr;
1359     if (nFormatSize <= 0)
1360     return paBufferTooSmall;
1361    
1362     // Get API
1363     paWasapi = _GetHostApi(&ret);
1364     if (paWasapi == NULL)
1365     return ret;
1366    
1367     // Get device index
1368     ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
1369     if (ret != paNoError)
1370     return ret;
1371    
1372     // Validate index
1373     if ((UINT32)index >= paWasapi->deviceCount)
1374     return paInvalidDevice;
1375    
1376     size = min(nFormatSize, (UINT32)sizeof(paWasapi->devInfo[ index ].DefaultFormat));
1377     memcpy(pFormat, &paWasapi->devInfo[ index ].DefaultFormat, size);
1378    
1379     return size;
1380     }
1381    
1382     // ------------------------------------------------------------------------------------------
1383     int PaWasapi_GetDeviceRole( PaDeviceIndex nDevice )
1384     {
1385     PaError ret;
1386     PaDeviceIndex index;
1387    
1388     // Get API
1389     PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
1390     if (paWasapi == NULL)
1391     return ret;
1392    
1393     // Get device index
1394     ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
1395     if (ret != paNoError)
1396     return ret;
1397    
1398     // Validate index
1399     if ((UINT32)index >= paWasapi->deviceCount)
1400     return paInvalidDevice;
1401    
1402     return paWasapi->devInfo[ index ].formFactor;
1403     }
1404    
1405     // ------------------------------------------------------------------------------------------
1406     PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput, unsigned int *nOutput )
1407     {
1408     PaWasapiStream *stream = (PaWasapiStream *)pStream;
1409     if (stream == NULL)
1410     return paBadStreamPtr;
1411    
1412     if (nInput != NULL)
1413     (*nInput) = stream->in.framesPerHostCallback;
1414    
1415     if (nOutput != NULL)
1416     (*nOutput) = stream->out.framesPerHostCallback;
1417    
1418     return paNoError;
1419     }
1420    
1421     // ------------------------------------------------------------------------------------------
1422     static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in)
1423     {
1424     const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
1425     switch (old->wFormatTag)
1426     {
1427     case WAVE_FORMAT_EXTENSIBLE: {
1428    
1429     PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));
1430    
1431     if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1432     {
1433     PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));
1434     }
1435     else
1436     if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
1437     {
1438     PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));
1439     }
1440     else
1441     {
1442     PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",
1443     in->SubFormat.Data1,
1444     in->SubFormat.Data2,
1445     in->SubFormat.Data3,
1446     (int)in->SubFormat.Data4[0],
1447     (int)in->SubFormat.Data4[1],
1448     (int)in->SubFormat.Data4[2],
1449     (int)in->SubFormat.Data4[3],
1450     (int)in->SubFormat.Data4[4],
1451     (int)in->SubFormat.Data4[5],
1452     (int)in->SubFormat.Data4[6],
1453     (int)in->SubFormat.Data4[7]));
1454     }
1455     PRINT(("Samples.wValidBitsPerSample=%d\n", in->Samples.wValidBitsPerSample));
1456     PRINT(("dwChannelMask =0x%X\n",in->dwChannelMask));
1457    
1458     break; }
1459    
1460     case WAVE_FORMAT_PCM: PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;
1461     case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break;
1462     default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break;
1463     }
1464    
1465     PRINT(("nChannels =%d\n",old->nChannels));
1466     PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));
1467     PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));
1468     PRINT(("nBlockAlign =%d\n",old->nBlockAlign));
1469     PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));
1470     PRINT(("cbSize =%d\n",old->cbSize));
1471     }
1472    
1473     // ------------------------------------------------------------------------------------------
1474     static PaSampleFormat waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in)
1475     {
1476     const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
1477    
1478     switch (old->wFormatTag)
1479     {
1480     case WAVE_FORMAT_EXTENSIBLE: {
1481     if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1482     {
1483     if (in->Samples.wValidBitsPerSample == 32)
1484     return paFloat32;
1485     }
1486     else
1487     if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
1488     {
1489     switch (old->wBitsPerSample)
1490     {
1491     case 32: return paInt32;
1492     case 24: return paInt24;
1493     case 8: return paUInt8;
1494     case 16: return paInt16;
1495     }
1496     }
1497     break; }
1498    
1499     case WAVE_FORMAT_IEEE_FLOAT:
1500     return paFloat32;
1501    
1502     case WAVE_FORMAT_PCM: {
1503     switch (old->wBitsPerSample)
1504     {
1505     case 32: return paInt32;
1506     case 24: return paInt24;
1507     case 8: return paUInt8;
1508     case 16: return paInt16;
1509     }
1510     break; }
1511     }
1512    
1513     return paCustomFormat;
1514     }
1515    
1516     // ------------------------------------------------------------------------------------------
1517     static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStreamParameters *params,
1518     double sampleRate)
1519     {
1520     WORD bitsPerSample;
1521     WAVEFORMATEX *old;
1522     DWORD channelMask = 0;
1523     PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo;
1524    
1525     // Get user assigned channel mask
1526     if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask))
1527     channelMask = streamInfo->channelMask;
1528    
1529     // Convert PaSampleFormat to bits per sample
1530     if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)
1531     return paSampleFormatNotSupported;
1532    
1533     memset(wavex, 0, sizeof(*wavex));
1534    
1535     old = (WAVEFORMATEX *)wavex;
1536     old->nChannels = (WORD)params->channelCount;
1537     old->nSamplesPerSec = (DWORD)sampleRate;
1538     if ((old->wBitsPerSample = bitsPerSample) > 16)
1539     {
1540     old->wBitsPerSample = 32; // 20 or 24 bits must go in 32 bit containers (ints)
1541     }
1542     old->nBlockAlign = (old->nChannels * (old->wBitsPerSample/8));
1543     old->nAvgBytesPerSec = (old->nSamplesPerSec * old->nBlockAlign);
1544    
1545     //WAVEFORMATEX
1546     /*if ((params->channelCount <= 2) && ((bitsPerSample == 16) || (bitsPerSample == 8)))
1547     {
1548     old->cbSize = 0;
1549     old->wFormatTag = WAVE_FORMAT_PCM;
1550     }
1551     //WAVEFORMATEXTENSIBLE
1552     else*/
1553     {
1554     old->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1555     old->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1556    
1557     if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
1558     wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1559     else
1560     wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
1561    
1562     wavex->Samples.wValidBitsPerSample = bitsPerSample; //no extra padding!
1563    
1564     // Set channel mask
1565     if (channelMask != 0)
1566     {
1567     wavex->dwChannelMask = channelMask;
1568     }
1569     else
1570     {
1571     switch (params->channelCount)
1572     {
1573     case 1: wavex->dwChannelMask = KSAUDIO_SPEAKER_MONO; break;
1574     case 2: wavex->dwChannelMask = KSAUDIO_SPEAKER_STEREO; break;
1575     case 4: wavex->dwChannelMask = KSAUDIO_SPEAKER_QUAD; break;
1576     case 6: wavex->dwChannelMask = KSAUDIO_SPEAKER_5POINT1; break;
1577     case 8: wavex->dwChannelMask = KSAUDIO_SPEAKER_7POINT1; break;
1578     default: wavex->dwChannelMask = 0; break;
1579     }
1580     }
1581     }
1582     return paNoError;
1583     }
1584    
1585     // ------------------------------------------------------------------------------------------
1586     static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
1587     {
1588     PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
1589     PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
1590     PA_DEBUG(( "chanelCount = %d\n", channelCount ));
1591    
1592     pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1593     pwfext->Format.nChannels = (WORD)channelCount;
1594     pwfext->Format.nSamplesPerSec = (DWORD)sampleRate;
1595     if(channelCount == 1)
1596     pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
1597     else
1598     pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
1599     if(sampleFormat == paFloat32)
1600     {
1601     pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
1602     pwfext->Format.wBitsPerSample = 32;
1603     pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1604     pwfext->Samples.wValidBitsPerSample = 32;
1605     pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1606     }
1607     else if(sampleFormat == paInt32)
1608     {
1609     pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
1610     pwfext->Format.wBitsPerSample = 32;
1611     pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1612     pwfext->Samples.wValidBitsPerSample = 32;
1613     pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
1614     }
1615     else if(sampleFormat == paInt24)
1616     {
1617     pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
1618     pwfext->Format.wBitsPerSample = 32; // 24-bit in 32-bit int container
1619     pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1620     pwfext->Samples.wValidBitsPerSample = 24;
1621     pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
1622     }
1623     else if(sampleFormat == paInt16)
1624     {
1625     pwfext->Format.nBlockAlign = (WORD)(channelCount * 2);
1626     pwfext->Format.wBitsPerSample = 16;
1627     pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1628     pwfext->Samples.wValidBitsPerSample = 16;
1629     pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
1630     }
1631     pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
1632     }
1633    
1634     // ------------------------------------------------------------------------------------------
1635     static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
1636     const PaStreamParameters *params, AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex)
1637     {
1638     PaError answer = paInvalidSampleRate;
1639     WAVEFORMATEX *sharedClosestMatch = NULL;
1640     HRESULT hr = !S_OK;
1641    
1642     MakeWaveFormatFromParams(outWavex, params, sampleRate);
1643    
1644     hr = IAudioClient_IsFormatSupported(myClient, shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
1645     if (hr == S_OK)
1646     answer = paFormatIsSupported;
1647     else
1648     if (sharedClosestMatch)
1649     {
1650     WORD bitsPerSample;
1651     WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;
1652    
1653     GUID subf_guid = GUID_NULL;
1654     if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1655     {
1656     memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEXTENSIBLE));
1657     subf_guid = ext->SubFormat;
1658     }
1659     else
1660     memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEX));
1661    
1662     CoTaskMemFree(sharedClosestMatch);
1663    
1664     // Make supported by default
1665     answer = paFormatIsSupported;
1666    
1667     // Validate SampleRate
1668     if ((DWORD)sampleRate != outWavex->Format.nSamplesPerSec)
1669     return paInvalidSampleRate;
1670    
1671     // Validate Channel count
1672     if ((WORD)params->channelCount != outWavex->Format.nChannels)
1673     return paInvalidChannelCount;
1674    
1675     // Validate Sample format
1676     if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)
1677     return paSampleFormatNotSupported;
1678    
1679     // Validate Sample format: bit size (WASAPI does not limit 'bit size')
1680     //if (bitsPerSample != outWavex->Format.wBitsPerSample)
1681     // return paSampleFormatNotSupported;
1682    
1683     // Validate Sample format: paFloat32 (WASAPI does not limit 'bit type')
1684     //if ((params->sampleFormat == paFloat32) && (subf_guid != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1685     // return paSampleFormatNotSupported;
1686    
1687     // Validate Sample format: paInt32 (WASAPI does not limit 'bit type')
1688     //if ((params->sampleFormat == paInt32) && (subf_guid != KSDATAFORMAT_SUBTYPE_PCM))
1689     // return paSampleFormatNotSupported;
1690     }
1691     else
1692     {
1693     #define FORMATTESTS 3
1694     static const int BestToWorst[FORMATTESTS] = { paFloat32, paInt24, paInt16 };
1695    
1696     // try selecting suitable sample type
1697     int i;
1698     for (i = 0; i < FORMATTESTS; ++i)
1699     {
1700     WAVEFORMATEXTENSIBLE ext = { 0 };
1701     wasapiFillWFEXT(&ext, BestToWorst[i], sampleRate, params->channelCount);
1702    
1703     hr = IAudioClient_IsFormatSupported(myClient, shareMode, &ext.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
1704     if (hr == S_OK)
1705     {
1706     memcpy(outWavex, &ext, sizeof(WAVEFORMATEXTENSIBLE));
1707     answer = paFormatIsSupported;
1708     break;
1709     }
1710     }
1711    
1712     if (answer != paFormatIsSupported)
1713     {
1714     // If mono, then driver does not support 1 channel, we use internal workaround
1715     // of tiny software mixing functionality, e.g. we provide to user buffer 1 channel
1716     // but then mix into 2 for device buffer
1717     if (params->channelCount == 1)
1718     {
1719     WAVEFORMATEXTENSIBLE stereo = { 0 };
1720     wasapiFillWFEXT(&stereo, params->sampleFormat, sampleRate, 2);
1721    
1722     hr = IAudioClient_IsFormatSupported(myClient, shareMode, &stereo.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
1723     if (hr == S_OK)
1724     {
1725     memcpy(outWavex, &stereo, sizeof(WAVEFORMATEXTENSIBLE));
1726     answer = paFormatIsSupported;
1727     }
1728     }
1729     }
1730    
1731     LogHostError(hr);
1732     }
1733    
1734     return answer;
1735     }
1736    
1737     // ------------------------------------------------------------------------------------------
1738     static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,
1739     const PaStreamParameters *inputParameters,
1740     const PaStreamParameters *outputParameters,
1741     double sampleRate)
1742     {
1743     if (hostApi == NULL)
1744     return paHostApiNotFound;
1745     if ((UINT32)sampleRate == 0)
1746     return paInvalidSampleRate;
1747    
1748     if (inputParameters != NULL)
1749     {
1750     /* all standard sample formats are supported by the buffer adapter,
1751     this implementation doesn't support any custom sample formats */
1752     if (inputParameters->sampleFormat & paCustomFormat)
1753     return paSampleFormatNotSupported;
1754    
1755     /* unless alternate device specification is supported, reject the use of
1756     paUseHostApiSpecificDeviceSpecification */
1757     if (inputParameters->device == paUseHostApiSpecificDeviceSpecification)
1758     return paInvalidDevice;
1759    
1760     /* check that input device can support inputChannelCount */
1761     if (inputParameters->channelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels)
1762     return paInvalidChannelCount;
1763    
1764     /* validate inputStreamInfo */
1765     if (inputParameters->hostApiSpecificStreamInfo)
1766     {
1767     PaWasapiStreamInfo *inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
1768     if ((inputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||
1769     (inputStreamInfo->version != 1) ||
1770     (inputStreamInfo->hostApiType != paWASAPI))
1771     {
1772     return paIncompatibleHostApiSpecificStreamInfo;
1773     }
1774     }
1775    
1776     return paNoError;
1777     }
1778    
1779     if (outputParameters != NULL)
1780     {
1781     /* all standard sample formats are supported by the buffer adapter,
1782     this implementation doesn't support any custom sample formats */
1783     if (outputParameters->sampleFormat & paCustomFormat)
1784     return paSampleFormatNotSupported;
1785    
1786     /* unless alternate device specification is supported, reject the use of
1787     paUseHostApiSpecificDeviceSpecification */
1788     if (outputParameters->device == paUseHostApiSpecificDeviceSpecification)
1789     return paInvalidDevice;
1790    
1791     /* check that output device can support outputChannelCount */
1792     if (outputParameters->channelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels)
1793     return paInvalidChannelCount;
1794    
1795     /* validate outputStreamInfo */
1796     if(outputParameters->hostApiSpecificStreamInfo)
1797     {
1798     PaWasapiStreamInfo *outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
1799     if ((outputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||
1800     (outputStreamInfo->version != 1) ||
1801     (outputStreamInfo->hostApiType != paWASAPI))
1802     {
1803     return paIncompatibleHostApiSpecificStreamInfo;
1804     }
1805     }
1806    
1807     return paNoError;
1808     }
1809    
1810     return (inputParameters || outputParameters ? paNoError : paInternalError);
1811     }
1812    
1813     // ------------------------------------------------------------------------------------------
1814     static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1815     const PaStreamParameters *inputParameters,
1816     const PaStreamParameters *outputParameters,
1817     double sampleRate )
1818     {
1819     IAudioClient *tmpClient = NULL;
1820     PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
1821     PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
1822    
1823     // Validate PaStreamParameters
1824     PaError error;
1825     if ((error = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
1826     return error;
1827    
1828     if (inputParameters != NULL)
1829     {
1830     WAVEFORMATEXTENSIBLE wavex;
1831     HRESULT hr;
1832     PaError answer;
1833     AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
1834     inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
1835    
1836     if (inputStreamInfo && (inputStreamInfo->flags & paWinWasapiExclusive))
1837     shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
1838    
1839     hr = IMMDevice_Activate(paWasapi->devInfo[inputParameters->device].device,
1840     &pa_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient);
1841     if (hr != S_OK)
1842     {
1843     LogHostError(hr);
1844     return paInvalidDevice;
1845     }
1846    
1847     answer = GetClosestFormat(tmpClient, sampleRate, inputParameters, shareMode, &wavex);
1848     SAFE_RELEASE(tmpClient);
1849    
1850     if (answer != paFormatIsSupported)
1851     return answer;
1852     }
1853    
1854     if (outputParameters != NULL)
1855     {
1856     HRESULT hr;
1857     WAVEFORMATEXTENSIBLE wavex;
1858     PaError answer;
1859     AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
1860     outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
1861    
1862     if (outputStreamInfo && (outputStreamInfo->flags & paWinWasapiExclusive))
1863     shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
1864    
1865     hr = IMMDevice_Activate(paWasapi->devInfo[outputParameters->device].device,
1866     &pa_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient);
1867     if (hr != S_OK)
1868     {
1869     LogHostError(hr);
1870     return paInvalidDevice;
1871     }
1872    
1873     answer = GetClosestFormat(tmpClient, sampleRate, outputParameters, shareMode, &wavex);
1874     SAFE_RELEASE(tmpClient);
1875    
1876     if (answer != paFormatIsSupported)
1877     return answer;
1878     }
1879    
1880     return paFormatIsSupported;
1881     }
1882    
1883     // ------------------------------------------------------------------------------------------
1884     static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceInfo *info,
1885     const PaStreamParameters *params, UINT32 framesPerLatency, double sampleRate, UINT32 streamFlags,
1886     BOOL blocking, PaError *pa_error)
1887     {
1888     PaError error;
1889     HRESULT hr = S_OK;
1890     UINT32 nFrames = 0;
1891     IAudioClient *pAudioClient = NULL;
1892     double suggestedLatency = 0.0;
1893    
1894     if (!pSubStream || !info || !params)
1895     return E_POINTER;
1896     if ((UINT32)sampleRate == 0)
1897     return E_INVALIDARG;
1898    
1899     // Get the audio client
1900     hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&pAudioClient);
1901     if (hr != S_OK)
1902     {
1903     LogHostError(hr);
1904     goto done;
1905     }
1906    
1907     // Get closest format
1908     if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex)) != paFormatIsSupported)
1909     {
1910     if (pa_error)
1911     (*pa_error) = error;
1912    
1913     LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
1914     goto done; // fail, format not supported
1915     }
1916    
1917     // Check for Mono >> Stereo workaround
1918     if ((params->channelCount == 1) && (pSubStream->wavex.Format.nChannels == 2))
1919     {
1920     if (blocking)
1921     {
1922     LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
1923     goto done; // fail, blocking mode not supported
1924     }
1925    
1926     // select mixer
1927     pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat);
1928     if (pSubStream->monoMixer == NULL)
1929     {
1930     LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
1931     goto done; // fail, no mixer for format
1932     }
1933     }
1934    
1935     // Correct latency to default device period (in Exclusive mode this is 10ms) if user selected 0
1936     suggestedLatency = (params->suggestedLatency < 0.001 ? nano100ToSeconds(info->DefaultDevicePeriod) : params->suggestedLatency);
1937    
1938     // Add latency frames
1939     framesPerLatency += MakeFramesFromHns(SecondsTonano100(suggestedLatency), pSubStream->wavex.Format.nSamplesPerSec);
1940    
1941     // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only.
1942     // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will
1943     // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary
1944     // for Exclusive mode only! when audio data is feeded directly to hardware.
1945     if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1946     {
1947     framesPerLatency = AlignFramesPerBuffer(framesPerLatency,
1948     pSubStream->wavex.Format.nSamplesPerSec, pSubStream->wavex.Format.nBlockAlign);
1949     }
1950    
1951     // Calculate period
1952     pSubStream->period = MakeHnsPeriod(framesPerLatency, pSubStream->wavex.Format.nSamplesPerSec);
1953    
1954     // Enforce min/max period for device in Shared mode to avoid distorted sound.
1955     // Avoid doing so for Exclusive mode as alignment will suffer. Exclusive mode processes
1956     // big buffers without problem. Push Exclusive beyond limits if possible.
1957     if (pSubStream->shareMode == AUDCLNT_SHAREMODE_SHARED)
1958     {
1959     if (pSubStream->period < info->DefaultDevicePeriod)
1960     pSubStream->period = info->DefaultDevicePeriod;
1961     }
1962    
1963     // Windows 7 does not allow to set latency lower than minimal device period and will
1964     // return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista though this error is not implemented
1965     // allowing to hack WASAPI device with lower period resulting in possibly lower latency.
1966     if (GetWindowsVersion() & WINDOWS_7_SERVER2008R2_AND_UP)
1967     {
1968     if (pSubStream->period < info->MinimumDevicePeriod)
1969     pSubStream->period = info->MinimumDevicePeriod;
1970    
1971     /*
1972     AUDCLNT_E_BUFFER_SIZE_ERROR: Applies to Windows 7 and later.
1973     Indicates that the buffer duration value requested by an exclusive-mode client is
1974     out of range. The requested duration value for pull mode must not be greater than
1975     500 milliseconds; for push mode the duration value must not be greater than 2 seconds.
1976     */
1977     if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1978     {
1979     static const REFERENCE_TIME MAX_BUFFER_EVENT_DURATION = 500 * 10000;
1980     static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION = 2000 * 10000;
1981    
1982     if (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) // pull mode, max 500ms
1983     {
1984     if (pSubStream->period > MAX_BUFFER_EVENT_DURATION)
1985     pSubStream->period = MAX_BUFFER_EVENT_DURATION;
1986     }
1987     else // push mode, max 2000ms
1988     {
1989     if (pSubStream->period > MAX_BUFFER_POLL_DURATION)
1990     pSubStream->period = MAX_BUFFER_POLL_DURATION;
1991     }
1992     }
1993     }
1994    
1995     // Open the stream and associate it with an audio session
1996     hr = IAudioClient_Initialize(pAudioClient,
1997     pSubStream->shareMode,
1998     streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,
1999     pSubStream->period,
2000     (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),
2001     &pSubStream->wavex.Format,
2002     NULL);
2003    
2004     // This would mean too low latency
2005     if (hr == AUDCLNT_E_BUFFER_SIZE_ERROR)
2006     {
2007     PRINT(("WASAPI: CreateAudioClient: correcting buffer size to device minimum\n"));
2008    
2009     // User was trying to set lowest possible, so set lowest device possible
2010     pSubStream->period = info->MinimumDevicePeriod;
2011    
2012     // Release the previous allocations
2013     SAFE_RELEASE(pAudioClient);
2014    
2015     // Create a new audio client
2016     hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
2017     if (hr != S_OK)
2018     {
2019     LogHostError(hr);
2020     goto done;
2021     }
2022    
2023     // Open the stream and associate it with an audio session
2024     hr = IAudioClient_Initialize(pAudioClient,
2025     pSubStream->shareMode,
2026     streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,
2027     pSubStream->period,
2028     (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),
2029     &pSubStream->wavex.Format,
2030     NULL);
2031     }
2032    
2033     // If the requested buffer size is not aligned...
2034     if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
2035     {
2036     PRINT(("WASAPI: CreateAudioClient: aligning buffer size\n"));
2037    
2038     // Get the next aligned frame
2039     hr = IAudioClient_GetBufferSize(pAudioClient, &nFrames);
2040     if (hr != S_OK)
2041     {
2042     LogHostError(hr);
2043     goto done;
2044     }
2045    
2046     // Release the previous allocations
2047     SAFE_RELEASE(pAudioClient);
2048    
2049     // Create a new audio client
2050     hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
2051     if (hr != S_OK)
2052     {
2053     LogHostError(hr);
2054     goto done;
2055     }
2056    
2057     // Get closest format
2058     if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex)) != paFormatIsSupported)
2059     {
2060     if (pa_error)
2061     (*pa_error) = error;
2062    
2063     LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); // fail, format not supported
2064     goto done;
2065     }
2066    
2067     // Check for Mono >> Stereo workaround
2068     if ((params->channelCount == 1) && (pSubStream->wavex.Format.nChannels == 2))
2069     {
2070     if (blocking)
2071     {
2072     LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2073     goto done; // fail, blocking mode not supported
2074     }
2075    
2076     // select mixer
2077     pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat);
2078     if (pSubStream->monoMixer == NULL)
2079     {
2080     LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2081     goto done; // fail, no mixer for format
2082     }
2083     }
2084    
2085     // Calculate period
2086     pSubStream->period = MakeHnsPeriod(nFrames, pSubStream->wavex.Format.nSamplesPerSec);
2087    
2088     // Windows 7 does not allow to set latency lower than minimal device period and will
2089     // return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista though this error is not implemented
2090     // allowing to hack WASAPI device with lower period resulting in possibly lower latency.
2091     if (GetWindowsVersion() & WINDOWS_7_SERVER2008R2_AND_UP)
2092     {
2093     if (pSubStream->period < info->MinimumDevicePeriod)
2094     pSubStream->period = info->MinimumDevicePeriod;
2095     }
2096    
2097     // Open the stream and associate it with an audio session
2098     hr = IAudioClient_Initialize(pAudioClient,
2099     pSubStream->shareMode,
2100     streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,
2101     pSubStream->period,
2102     (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),
2103     &pSubStream->wavex.Format,
2104     NULL);
2105     if (hr != S_OK)
2106     {
2107     LogHostError(hr);
2108     goto done;
2109     }
2110     }
2111     else
2112     if (hr != S_OK)
2113     {
2114     LogHostError(hr);
2115     goto done;
2116     }
2117    
2118     // Set result
2119     pSubStream->client = pAudioClient;
2120     IAudioClient_AddRef(pSubStream->client);
2121    
2122     done:
2123    
2124     // Clean up
2125     SAFE_RELEASE(pAudioClient);
2126     return hr;
2127     }
2128    
2129     // ------------------------------------------------------------------------------------------
2130     static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
2131     PaStream** s,
2132     const PaStreamParameters *inputParameters,
2133     const PaStreamParameters *outputParameters,
2134     double sampleRate,
2135     unsigned long framesPerBuffer,
2136     PaStreamFlags streamFlags,
2137     PaStreamCallback *streamCallback,
2138     void *userData )
2139     {
2140     PaError result = paNoError;
2141     HRESULT hr;
2142     PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
2143     PaWasapiStream *stream = NULL;
2144     int inputChannelCount, outputChannelCount;
2145     PaSampleFormat inputSampleFormat, outputSampleFormat;
2146     PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
2147     PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
2148     PaWasapiDeviceInfo *info = NULL;
2149     UINT32 maxBufferSize;
2150     PaTime buffer_latency;
2151     ULONG framesPerHostCallback;
2152     PaUtilHostBufferSizeMode bufferMode;
2153    
2154     // validate PaStreamParameters
2155     if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
2156     return LogPaError(result);
2157    
2158     // Validate platform specific flags
2159     if ((streamFlags & paPlatformSpecificFlags) != 0)
2160     {
2161     LogPaError(result = paInvalidFlag); /* unexpected platform specific flag */
2162     goto error;
2163     }
2164    
2165     // Allocate memory for PaWasapiStream
2166     if ((stream = (PaWasapiStream *)PaUtil_AllocateMemory(sizeof(PaWasapiStream))) == NULL)
2167     {
2168     LogPaError(result = paInsufficientMemory);
2169     goto error;
2170     }
2171    
2172     // Default thread priority is Audio: for exclusive mode we will use Pro Audio.
2173     stream->nThreadPriority = eThreadPriorityAudio;
2174    
2175     // Set default number of frames: paFramesPerBufferUnspecified
2176     if (framesPerBuffer == paFramesPerBufferUnspecified)
2177     {
2178     UINT32 framesPerBufferIn = 0, framesPerBufferOut = 0;
2179     if (inputParameters != NULL)
2180     {
2181     info = &paWasapi->devInfo[inputParameters->device];
2182     framesPerBufferIn = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate);
2183     }
2184     if (outputParameters != NULL)
2185     {
2186     info = &paWasapi->devInfo[outputParameters->device];
2187     framesPerBufferOut = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate);
2188     }
2189     // choosing maximum default size
2190     framesPerBuffer = max(framesPerBufferIn, framesPerBufferOut);
2191     }
2192     if (framesPerBuffer == 0)
2193     framesPerBuffer = ((UINT32)sampleRate / 100) * 2;
2194    
2195     // Try create device: Input
2196     if (inputParameters != NULL)
2197     {
2198     inputChannelCount = inputParameters->channelCount;
2199     inputSampleFormat = inputParameters->sampleFormat;
2200     inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
2201     info = &paWasapi->devInfo[inputParameters->device];
2202     stream->in.flags = (inputStreamInfo ? inputStreamInfo->flags : 0);
2203    
2204     // Select Exclusive/Shared mode
2205     stream->in.shareMode = AUDCLNT_SHAREMODE_SHARED;
2206     if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiExclusive))
2207     {
2208     // Boost thread priority
2209     stream->nThreadPriority = eThreadPriorityProAudio;
2210     // Make Exclusive
2211     stream->in.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
2212     }
2213    
2214     // If user provided explicit thread priority level, use it
2215     if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiThreadPriority))
2216     {
2217     if ((inputStreamInfo->threadPriority > eThreadPriorityNone) &&
2218     (inputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
2219     stream->nThreadPriority = inputStreamInfo->threadPriority;
2220     }
2221    
2222     // Choose processing mode
2223     stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
2224     if (paWasapi->useWOW64Workaround)
2225     stream->in.streamFlags = 0; // polling interface
2226     if (streamCallback == NULL)
2227     stream->in.streamFlags = 0; // polling interface
2228     if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling))
2229     stream->in.streamFlags = 0; // polling interface
2230    
2231     // Create Audio client
2232     hr = CreateAudioClient(&stream->in, info, inputParameters, 0/*framesPerLatency*/,
2233     sampleRate, stream->in.streamFlags, (streamCallback == NULL), &result);
2234     if (hr != S_OK)
2235     {
2236     LogHostError(hr);
2237     if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
2238     result = paInvalidDevice;
2239    
2240     LogPaError(result);
2241     goto error;
2242     }
2243     LogWAVEFORMATEXTENSIBLE(&stream->in.wavex);
2244    
2245     // Get closest format
2246     hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );
2247    
2248     // Create volume mgr
2249     stream->inVol = NULL;
2250     /*hr = info->device->Activate(
2251     __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
2252     (void**)&stream->inVol);
2253     if (hr != S_OK)
2254     return paInvalidDevice;*/
2255    
2256     // Set user-side custom host processor
2257     if ((inputStreamInfo != NULL) &&
2258     (inputStreamInfo->flags & paWinWasapiRedirectHostProcessor))
2259     {
2260     stream->hostProcessOverrideInput.processor = inputStreamInfo->hostProcessorInput;
2261     stream->hostProcessOverrideInput.userData = userData;
2262     }
2263    
2264     // Get max possible buffer size to check if it is not less than that we request
2265     hr = IAudioClient_GetBufferSize(stream->in.client, &maxBufferSize);
2266     if (hr != S_OK)
2267     {
2268     LogHostError(hr);
2269     LogPaError(result = paInvalidDevice);
2270     goto error;
2271     }
2272    
2273     // Correct buffer to max size if it maxed out result of GetBufferSize
2274     stream->in.bufferSize = maxBufferSize;
2275    
2276     // Get interface latency (actually uneeded as we calculate latency from the size
2277     // of maxBufferSize).
2278     hr = IAudioClient_GetStreamLatency(stream->in.client, &stream->in.device_latency);
2279     if (hr != S_OK)
2280     {
2281     LogHostError(hr);
2282     LogPaError(result = paInvalidDevice);
2283     goto error;
2284     }
2285     //stream->in.latency_seconds = nano100ToSeconds(stream->in.device_latency);
2286    
2287     // Number of frames that are required at each period
2288     stream->in.framesPerHostCallback = maxBufferSize;
2289    
2290     // Calculate buffer latency
2291     buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec;
2292    
2293     // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
2294     stream->in.latency_seconds += buffer_latency;
2295    
2296     PRINT(("PaWASAPIOpenStream(input): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO")));
2297     }
2298     else
2299     {
2300     inputChannelCount = 0;
2301     inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
2302     }
2303    
2304     // Try create device: Output
2305     if (outputParameters != NULL)
2306     {
2307     outputChannelCount = outputParameters->channelCount;
2308     outputSampleFormat = outputParameters->sampleFormat;
2309     outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
2310     info = &paWasapi->devInfo[outputParameters->device];
2311     stream->out.flags = (outputStreamInfo ? outputStreamInfo->flags : 0);
2312    
2313     // Select Exclusive/Shared mode
2314     stream->out.shareMode = AUDCLNT_SHAREMODE_SHARED;
2315     if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiExclusive))
2316     {
2317     // Boost thread priority
2318     stream->nThreadPriority = eThreadPriorityProAudio;
2319     // Make Exclusive
2320     stream->out.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
2321     }
2322    
2323     // If user provided explicit thread priority level, use it
2324     if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiThreadPriority))
2325     {
2326     if ((outputStreamInfo->threadPriority > eThreadPriorityNone) &&
2327     (outputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
2328     stream->nThreadPriority = outputStreamInfo->threadPriority;
2329     }
2330    
2331     // Choose processing mode
2332     stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
2333     if (paWasapi->useWOW64Workaround)
2334     stream->out.streamFlags = 0; // polling interface
2335     if (streamCallback == NULL)
2336     stream->out.streamFlags = 0; // polling interface
2337     if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling))
2338     stream->out.streamFlags = 0; // polling interface
2339    
2340     // Create Audio client
2341     hr = CreateAudioClient(&stream->out, info, outputParameters, 0/*framesPerLatency*/,
2342     sampleRate, stream->out.streamFlags, (streamCallback == NULL), &result);
2343     if (hr != S_OK)
2344     {
2345     LogHostError(hr);
2346     if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
2347     result = paInvalidDevice;
2348    
2349     LogPaError(result);
2350     goto error;
2351     }
2352     LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
2353    
2354     // Get closest format
2355     hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat );
2356    
2357     // Activate volume
2358     stream->outVol = NULL;
2359     /*hr = info->device->Activate(
2360     __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
2361     (void**)&stream->outVol);
2362     if (hr != S_OK)
2363     return paInvalidDevice;*/
2364    
2365     // Set user-side custom host processor
2366     if ((outputStreamInfo != NULL) &&
2367     (outputStreamInfo->flags & paWinWasapiRedirectHostProcessor))
2368     {
2369     stream->hostProcessOverrideOutput.processor = outputStreamInfo->hostProcessorOutput;
2370     stream->hostProcessOverrideOutput.userData = userData;
2371     }
2372    
2373     // Get max possible buffer size to check if it is not less than that we request
2374     hr = IAudioClient_GetBufferSize(stream->out.client, &maxBufferSize);
2375     if (hr != S_OK)
2376     {
2377     LogHostError(hr);
2378     LogPaError(result = paInvalidDevice);
2379     goto error;
2380     }
2381    
2382     // Correct buffer to max size if it maxed out result of GetBufferSize
2383     stream->out.bufferSize = maxBufferSize;
2384    
2385     // Get interface latency (actually uneeded as we calculate latency from the size
2386     // of maxBufferSize).
2387     hr = IAudioClient_GetStreamLatency(stream->out.client, &stream->out.device_latency);
2388     if (hr != S_OK)
2389     {
2390     LogHostError(hr);
2391     LogPaError(result = paInvalidDevice);
2392     goto error;
2393     }
2394     //stream->out.latency_seconds = nano100ToSeconds(stream->out.device_latency);
2395    
2396     // Number of frames that are required at each period
2397     stream->out.framesPerHostCallback = maxBufferSize;
2398    
2399     // Calculate buffer latency
2400     buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec;
2401    
2402     // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
2403     stream->out.latency_seconds += buffer_latency;
2404    
2405     PRINT(("PaWASAPIOpenStream(output): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO")));
2406     }
2407     else
2408     {
2409     outputChannelCount = 0;
2410     outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
2411     }
2412    
2413     // paWinWasapiPolling must be on/or not on both streams
2414     if ((inputParameters != NULL) && (outputParameters != NULL))
2415     {
2416     if ((inputStreamInfo != NULL) && (outputStreamInfo != NULL))
2417     {
2418     if (((inputStreamInfo->flags & paWinWasapiPolling) &&
2419     !(outputStreamInfo->flags & paWinWasapiPolling))
2420     ||
2421     (!(inputStreamInfo->flags & paWinWasapiPolling) &&
2422     (outputStreamInfo->flags & paWinWasapiPolling)))
2423     {
2424     LogPaError(result = paInvalidFlag);
2425     goto error;
2426     }
2427     }
2428     }
2429    
2430     // Initialize stream representation
2431     if (streamCallback)
2432     {
2433     stream->bBlocking = FALSE;
2434     PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,
2435     &paWasapi->callbackStreamInterface,
2436     streamCallback, userData);
2437     }
2438     else
2439     {
2440     stream->bBlocking = TRUE;
2441     PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,
2442     &paWasapi->blockingStreamInterface,
2443     streamCallback, userData);
2444     }
2445    
2446     // Initialize CPU measurer
2447     PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate);
2448    
2449     if (outputParameters && inputParameters)
2450     {
2451     // serious problem #1
2452     if (stream->in.period != stream->out.period)
2453     {
2454     PRINT(("WASAPI: OpenStream: period discrepancy\n"));
2455     goto error;
2456     }
2457    
2458     // serious problem #2
2459     if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback)
2460     {
2461     PRINT(("WASAPI: OpenStream: framesPerHostCallback discrepancy\n"));
2462     goto error;
2463     }
2464     }
2465    
2466     framesPerHostCallback = (outputParameters) ? stream->out.framesPerHostCallback:
2467     stream->in.framesPerHostCallback;
2468    
2469     // Choose correct mode of buffer processing:
2470     // Exclusive/Shared non paWinWasapiPolling mode: paUtilFixedHostBufferSize - always fixed
2471     // Exclusive/Shared paWinWasapiPolling mode: paUtilBoundedHostBufferSize - may vary
2472     bufferMode = paUtilFixedHostBufferSize;
2473     if (inputParameters &&
2474     (!stream->in.streamFlags || ((stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
2475     bufferMode = paUtilBoundedHostBufferSize;
2476     else
2477     if (outputParameters &&
2478     (!stream->out.streamFlags || ((stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
2479     bufferMode = paUtilBoundedHostBufferSize;
2480    
2481     // Initialize buffer processor
2482     result = PaUtil_InitializeBufferProcessor(
2483     &stream->bufferProcessor,
2484     inputChannelCount,
2485     inputSampleFormat,
2486     hostInputSampleFormat,
2487     outputChannelCount,
2488     outputSampleFormat,
2489     hostOutputSampleFormat,
2490     sampleRate,
2491     streamFlags,
2492     framesPerBuffer,
2493     framesPerHostCallback,
2494     bufferMode,
2495     streamCallback,
2496     userData);
2497     if (result != paNoError)
2498     {
2499     LogPaError(result);
2500     goto error;
2501     }
2502    
2503     // Set Input latency
2504     stream->streamRepresentation.streamInfo.inputLatency =
2505     PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
2506     + ((inputParameters)?stream->in.latency_seconds :0);
2507    
2508     // Set Output latency
2509     stream->streamRepresentation.streamInfo.outputLatency =
2510     PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
2511     + ((outputParameters)?stream->out.latency_seconds :0);
2512    
2513     // Set SR
2514     stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2515    
2516     (*s) = (PaStream *)stream;
2517     return result;
2518    
2519     error:
2520    
2521     if (stream != NULL)
2522     CloseStream(stream);
2523    
2524     return result;
2525     }
2526    
2527     // ------------------------------------------------------------------------------------------
2528     static PaError CloseStream( PaStream* s )
2529     {
2530     PaError result = paNoError;
2531     PaWasapiStream *stream = (PaWasapiStream*)s;
2532    
2533     // abort active stream
2534     if (IsStreamActive(s))
2535     {
2536     if ((result = AbortStream(s)) != paNoError)
2537     return result;
2538     }
2539    
2540     SAFE_RELEASE(stream->cclient);
2541     SAFE_RELEASE(stream->rclient);
2542     SAFE_RELEASE(stream->out.client);
2543     SAFE_RELEASE(stream->in.client);
2544     SAFE_RELEASE(stream->inVol);
2545     SAFE_RELEASE(stream->outVol);
2546    
2547     CloseHandle(stream->event[S_INPUT]);
2548     CloseHandle(stream->event[S_OUTPUT]);
2549    
2550     SAFE_CLOSE(stream->hThread);
2551     SAFE_CLOSE(stream->hThreadStart);
2552     SAFE_CLOSE(stream->hThreadExit);
2553     SAFE_CLOSE(stream->hCloseRequest);
2554     SAFE_CLOSE(stream->hBlockingOpStreamRD);
2555     SAFE_CLOSE(stream->hBlockingOpStreamWR);
2556    
2557     free(stream->in.monoBuffer);
2558     free(stream->out.monoBuffer);
2559    
2560     PaUtil_TerminateBufferProcessor(&stream->bufferProcessor);
2561     PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation);
2562     PaUtil_FreeMemory(stream);
2563    
2564     return result;
2565     }
2566    
2567     // ------------------------------------------------------------------------------------------
2568     static PaError StartStream( PaStream *s )
2569     {
2570     HRESULT hr;
2571     PaWasapiStream *stream = (PaWasapiStream*)s;
2572    
2573     // check if stream is active already
2574     if (IsStreamActive(s))
2575     return paStreamIsNotStopped;
2576    
2577     PaUtil_ResetBufferProcessor(&stream->bufferProcessor);
2578    
2579     // Create close event
2580     stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL);
2581    
2582     // Create thread
2583     if (!stream->bBlocking)
2584     {
2585     // Create thread events
2586     stream->hThreadStart = CreateEvent(NULL, TRUE, FALSE, NULL);
2587     stream->hThreadExit = CreateEvent(NULL, TRUE, FALSE, NULL);
2588    
2589     if ((stream->in.client && (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) ||
2590     (stream->out.client && (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)))
2591     {
2592     if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL)
2593     return paUnanticipatedHostError;
2594     }
2595     else
2596     {
2597     if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL)
2598     return paUnanticipatedHostError;
2599     }
2600    
2601     // Wait for thread to start
2602     if (WaitForSingleObject(stream->hThreadStart, 60*1000) == WAIT_TIMEOUT)
2603     return paUnanticipatedHostError;
2604     }
2605     else
2606     {
2607     // Create blocking operation events (non-signaled event means - blocking operation is pending)
2608     if (stream->out.client)
2609     stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL);
2610     if (stream->in.client)
2611     stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL);
2612    
2613     // Initialize event & start INPUT stream
2614     if (stream->in.client)
2615     {
2616     if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)
2617     {
2618     LogHostError(hr);
2619     return paUnanticipatedHostError;
2620     }
2621    
2622     if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)
2623     {
2624     LogHostError(hr);
2625     return paUnanticipatedHostError;
2626     }
2627     }
2628    
2629    
2630     // Initialize event & start OUTPUT stream
2631     if (stream->out.client)
2632     {
2633     if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)
2634     {
2635     LogHostError(hr);
2636     return paUnanticipatedHostError;
2637     }
2638    
2639     // Start
2640     if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)
2641     {
2642     LogHostError(hr);
2643     return paUnanticipatedHostError;
2644     }
2645     }
2646    
2647     // Signal: stream running
2648     stream->running = TRUE;
2649    
2650     // Set current time
2651     stream->out.prevTime = timeGetTime();
2652     stream->out.prevSleep = 0;
2653     }
2654    
2655     return paNoError;
2656     }
2657    
2658     // ------------------------------------------------------------------------------------------
2659     static void _FinishStream(PaWasapiStream *stream)
2660     {
2661     // Issue command to thread to stop processing and wait for thread exit
2662     if (!stream->bBlocking)
2663     {
2664     SignalObjectAndWait(stream->hCloseRequest, stream->hThreadExit, INFINITE, FALSE);
2665     }
2666     else
2667     // Blocking mode does not own thread
2668     {
2669     // Signal close event and wait for each of 2 blocking operations to complete
2670     if (stream->out.client)
2671     SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE);
2672     if (stream->out.client)
2673     SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE);
2674    
2675     // Process stop
2676     _OnStreamStop(stream);
2677     }
2678    
2679     // Close thread handles to allow restart
2680     SAFE_CLOSE(stream->hThread);
2681     SAFE_CLOSE(stream->hThreadStart);
2682     SAFE_CLOSE(stream->hThreadExit);
2683     SAFE_CLOSE(stream->hCloseRequest);
2684     SAFE_CLOSE(stream->hBlockingOpStreamRD);
2685     SAFE_CLOSE(stream->hBlockingOpStreamWR);
2686    
2687     stream->running = FALSE;
2688     }
2689    
2690     // ------------------------------------------------------------------------------------------
2691     static PaError StopStream( PaStream *s )
2692     {
2693     // Finish stream
2694     _FinishStream((PaWasapiStream *)s);
2695     return paNoError;
2696     }
2697    
2698     // ------------------------------------------------------------------------------------------
2699     static PaError AbortStream( PaStream *s )
2700     {
2701     // Finish stream
2702     _FinishStream((PaWasapiStream *)s);
2703     return paNoError;
2704     }
2705    
2706     // ------------------------------------------------------------------------------------------
2707     static PaError IsStreamStopped( PaStream *s )
2708     {
2709     return !((PaWasapiStream *)s)->running;
2710     }
2711    
2712     // ------------------------------------------------------------------------------------------
2713     static PaError IsStreamActive( PaStream *s )
2714     {
2715     return ((PaWasapiStream *)s)->running;
2716     }
2717    
2718     // ------------------------------------------------------------------------------------------
2719     static PaTime GetStreamTime( PaStream *s )
2720     {
2721     PaWasapiStream *stream = (PaWasapiStream*)s;
2722    
2723     /* suppress unused variable warnings */
2724     (void) stream;
2725    
2726     /* IMPLEMENT ME, see portaudio.h for required behavior*/
2727    
2728     //this is lame ds and mme does the same thing, quite useless method imho
2729     //why dont we fetch the time in the pa callbacks?
2730     //at least its doing to be clocked to something
2731     return PaUtil_GetTime();
2732     }
2733    
2734     // ------------------------------------------------------------------------------------------
2735     static double GetStreamCpuLoad( PaStream* s )
2736     {
2737     return PaUtil_GetCpuLoad(&((PaWasapiStream *)s)->cpuLoadMeasurer);
2738     }
2739    
2740     // ------------------------------------------------------------------------------------------
2741     /* NOT TESTED */
2742     static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames )
2743     {
2744     PaWasapiStream *stream = (PaWasapiStream*)s;
2745    
2746     HRESULT hr = S_OK;
2747     UINT32 frames;
2748 william 62 BYTE *user_buffer = (BYTE *)_buffer;
2749     BYTE *wasapi_buffer = NULL;
2750 william 31 DWORD flags = 0;
2751 william 62 UINT32 i;
2752 william 31
2753     // validate
2754     if (!stream->running)
2755     return paStreamIsStopped;
2756     if (stream->cclient == NULL)
2757     return paBadStreamPtr;
2758    
2759     // Notify blocking op has begun
2760     ResetEvent(stream->hBlockingOpStreamRD);
2761    
2762 william 62 // make a local copy of the user buffer pointer(s), this is necessary
2763     // because PaUtil_CopyOutput() advances these pointers every time it is called
2764     if (!stream->bufferProcessor.userInputIsInterleaved)
2765     {
2766     user_buffer = (BYTE *)alloca(sizeof(BYTE *) * stream->bufferProcessor.inputChannelCount);
2767     if (user_buffer == NULL)
2768     return paInsufficientMemory;
2769    
2770     for (i = 0; i < stream->bufferProcessor.inputChannelCount; ++i)
2771     ((BYTE **)user_buffer)[i] = ((BYTE **)_buffer)[i];
2772     }
2773    
2774 william 31 while (_frames != 0)
2775     {
2776 william 62 UINT32 processed, processed_size;
2777    
2778 william 31 // Get the available data in the shared buffer.
2779 william 62 if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &wasapi_buffer, &frames, &flags, NULL, NULL)) != S_OK)
2780 william 31 {
2781     if (hr == AUDCLNT_S_BUFFER_EMPTY)
2782     {
2783     // Check if blocking call must be interrupted
2784     if (WaitForSingleObject(stream->hCloseRequest, 1) != WAIT_TIMEOUT)
2785     break;
2786     }
2787    
2788     return LogHostError(hr);
2789     goto stream_rd_end;
2790     }
2791    
2792     // Detect silence
2793     // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
2794     // data = NULL;
2795    
2796     // Check if frames <= _frames
2797     if (frames > _frames)
2798     frames = _frames;
2799    
2800 william 62 // Register available frames to processor
2801     PaUtil_SetInputFrameCount(&stream->bufferProcessor, frames);
2802    
2803     // Register host buffer pointer to processor
2804     PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.inputChannelCount);
2805    
2806     // Copy user data to host buffer (with conversion if applicable)
2807     processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, frames);
2808 william 31
2809 william 62 // Advance user buffer to consumed portion
2810     processed_size = processed * stream->in.wavex.Format.nBlockAlign;
2811     if (stream->bufferProcessor.userInputIsInterleaved)
2812 william 31 {
2813 william 62 user_buffer += processed_size;
2814     }
2815     else
2816     {
2817     for (i = 0; i < stream->bufferProcessor.inputChannelCount; ++i)
2818     ((BYTE **)user_buffer)[i] = ((BYTE **)user_buffer)[i] + processed_size;
2819     }
2820    
2821     // Release host buffer
2822     if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, processed)) != S_OK)
2823     {
2824 william 31 LogHostError(hr);
2825     goto stream_rd_end;
2826     }
2827    
2828 william 62 _frames -= processed;
2829 william 31 }
2830    
2831     stream_rd_end:
2832    
2833     // Notify blocking op has ended
2834     SetEvent(stream->hBlockingOpStreamRD);
2835    
2836     return (hr != S_OK ? paUnanticipatedHostError : paNoError);
2837     }
2838    
2839     // ------------------------------------------------------------------------------------------
2840     static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _frames )
2841     {
2842     PaWasapiStream *stream = (PaWasapiStream*)s;
2843    
2844     UINT32 frames;
2845 william 62 const BYTE *user_buffer = (const BYTE *)_buffer;
2846     BYTE *wasapi_buffer;
2847 william 31 HRESULT hr = S_OK;
2848     UINT32 next_rev_sleep, blocks, block_sleep_ms;
2849     UINT32 i;
2850    
2851     // validate
2852     if (!stream->running)
2853     return paStreamIsStopped;
2854     if (stream->rclient == NULL)
2855     return paBadStreamPtr;
2856    
2857     // Notify blocking op has begun
2858     ResetEvent(stream->hBlockingOpStreamWR);
2859    
2860     // Calculate sleep time for next call
2861     {
2862     UINT32 remainder = 0;
2863     UINT32 sleep_ms = 0;
2864     DWORD elapsed_ms;
2865     blocks = _frames / stream->out.framesPerHostCallback;
2866     block_sleep_ms = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec);
2867     if (blocks == 0)
2868     {
2869     blocks = 1;
2870     sleep_ms = GetFramesSleepTime(_frames, stream->out.wavex.Format.nSamplesPerSec); // partial
2871     }
2872     else
2873     {
2874     remainder = _frames - blocks * stream->out.framesPerHostCallback;
2875     sleep_ms = block_sleep_ms; // full
2876     }
2877    
2878     // Sleep for remainder
2879     elapsed_ms = timeGetTime() - stream->out.prevTime;
2880     if (sleep_ms >= elapsed_ms)
2881     sleep_ms -= elapsed_ms;
2882    
2883     next_rev_sleep = sleep_ms;
2884     }
2885    
2886     // Sleep diff from last call
2887     if (stream->out.prevSleep)
2888     Sleep(stream->out.prevSleep);
2889     stream->out.prevSleep = next_rev_sleep;
2890    
2891 william 62 // make a local copy of the user buffer pointer(s), this is necessary
2892     // because PaUtil_CopyOutput() advances these pointers every time it is called
2893     if (!stream->bufferProcessor.userOutputIsInterleaved)
2894     {
2895     user_buffer = (const BYTE *)alloca(sizeof(const BYTE *) * stream->bufferProcessor.outputChannelCount);
2896     if (user_buffer == NULL)
2897     return paInsufficientMemory;
2898    
2899     for (i = 0; i < stream->bufferProcessor.outputChannelCount; ++i)
2900     ((const BYTE **)user_buffer)[i] = ((const BYTE **)_buffer)[i];
2901     }
2902    
2903 william 31 // Feed engine
2904     for (i = 0; i < blocks; ++i)
2905     {
2906 william 62 UINT32 available, processed;
2907 william 31
2908     // Get block frames
2909     frames = stream->out.framesPerHostCallback;
2910     if (frames > _frames)
2911     frames = _frames;
2912    
2913     if (i)
2914     Sleep(block_sleep_ms);
2915    
2916     while (frames != 0)
2917     {
2918     UINT32 padding = 0;
2919 william 62 UINT32 processed_size;
2920 william 31
2921     // Check if blocking call must be interrupted
2922     if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)
2923     break;
2924    
2925     // Get Read position
2926     hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding);
2927     if (hr != S_OK)
2928     {
2929     LogHostError(hr);
2930     goto stream_wr_end;
2931     }
2932    
2933 william 62 // Calculate frames available
2934 william 31 if (frames >= padding)
2935     available = frames - padding;
2936     else
2937     available = frames;
2938    
2939 william 62 // Get pointer to host buffer
2940     if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, available, &wasapi_buffer)) != S_OK)
2941 william 31 {
2942     // Buffer size is too big, waiting
2943     if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
2944     continue;
2945     LogHostError(hr);
2946     goto stream_wr_end;
2947     }
2948    
2949 william 62 // Register available frames to processor
2950     PaUtil_SetOutputFrameCount(&stream->bufferProcessor, available);
2951    
2952     // Register host buffer pointer to processor
2953     PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.outputChannelCount);
2954    
2955     // Copy user data to host buffer (with conversion if applicable)
2956     processed = PaUtil_CopyOutput(&stream->bufferProcessor, (const void **)&user_buffer, available);
2957 william 31
2958 william 62 // Advance user buffer to consumed portion
2959     processed_size = processed * stream->out.wavex.Format.nBlockAlign;
2960     if (stream->bufferProcessor.userOutputIsInterleaved)
2961 william 31 {
2962 william 62 user_buffer += processed_size;
2963     }
2964     else
2965     {
2966     for (i = 0; i < stream->bufferProcessor.outputChannelCount; ++i)
2967     ((const BYTE **)user_buffer)[i] = ((const BYTE **)user_buffer)[i] + processed_size;
2968     }
2969    
2970     // Release host buffer
2971     if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, processed, 0)) != S_OK)
2972     {
2973 william 31 LogHostError(hr);
2974     goto stream_wr_end;
2975     }
2976    
2977 william 62 // Deduct frames
2978     frames -= processed;
2979 william 31 }
2980    
2981     _frames -= frames;
2982     }
2983    
2984     stream_wr_end:
2985    
2986     // Set prev time
2987     stream->out.prevTime = timeGetTime();
2988    
2989     // Notify blocking op has ended
2990     SetEvent(stream->hBlockingOpStreamWR);
2991    
2992     return (hr != S_OK ? paUnanticipatedHostError : paNoError);
2993     }
2994    
2995     // ------------------------------------------------------------------------------------------
2996     /* NOT TESTED */
2997     static signed long GetStreamReadAvailable( PaStream* s )
2998     {
2999     PaWasapiStream *stream = (PaWasapiStream*)s;
3000     HRESULT hr;
3001     UINT32 pending = 0;
3002    
3003     // validate
3004     if (!stream->running)
3005     return paStreamIsStopped;
3006     if (stream->cclient == NULL)
3007     return paBadStreamPtr;
3008    
3009     hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending);
3010     if (hr != S_OK)
3011     {
3012     LogHostError(hr);
3013     return paUnanticipatedHostError;
3014     }
3015    
3016     return (long)pending;
3017     }
3018    
3019     // ------------------------------------------------------------------------------------------
3020     static signed long GetStreamWriteAvailable( PaStream* s )
3021     {
3022     PaWasapiStream *stream = (PaWasapiStream*)s;
3023    
3024     UINT32 frames = stream->out.framesPerHostCallback;
3025     HRESULT hr;
3026     UINT32 padding = 0;
3027    
3028     // validate
3029     if (!stream->running)
3030     return paStreamIsStopped;
3031     if (stream->rclient == NULL)
3032     return paBadStreamPtr;
3033    
3034     hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding);
3035     if (hr != S_OK)
3036     {
3037     LogHostError(hr);
3038     return paUnanticipatedHostError;
3039     }
3040    
3041     // Calculate
3042     frames -= padding;
3043    
3044     return frames;
3045     }
3046    
3047     // ------------------------------------------------------------------------------------------
3048     static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
3049     void *outputBuffer, long outputFrames,
3050     void *userData )
3051     {
3052     PaWasapiStream *stream = (PaWasapiStream*)userData;
3053     PaStreamCallbackTimeInfo timeInfo = {0,0,0};
3054     PaStreamCallbackFlags flags = 0;
3055     int callbackResult;
3056     unsigned long framesProcessed;
3057     HRESULT hr;
3058     UINT32 pending;
3059    
3060     PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
3061    
3062     /*
3063     Pa_GetStreamTime:
3064     - generate timing information
3065     - handle buffer slips
3066     */
3067     timeInfo.currentTime = PaUtil_GetTime();
3068     // Query input latency
3069     if (stream->in.client != NULL)
3070     {
3071     PaTime pending_time;
3072     if ((hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending)) == S_OK)
3073     pending_time = (PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec;
3074     else
3075     pending_time = (PaTime)stream->in.latency_seconds;
3076    
3077     timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time;
3078     }
3079     // Query output current latency
3080     if (stream->out.client != NULL)
3081     {
3082     PaTime pending_time;
3083     if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &pending)) == S_OK)
3084     pending_time = (PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec;
3085     else
3086     pending_time = (PaTime)stream->out.latency_seconds;
3087    
3088     timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time;
3089     }
3090    
3091     /*
3092     If you need to byte swap or shift inputBuffer to convert it into a
3093     portaudio format, do it here.
3094     */
3095    
3096     PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, flags );
3097    
3098     /*
3099     depending on whether the host buffers are interleaved, non-interleaved
3100     or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
3101     PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
3102     */
3103    
3104     if (stream->bufferProcessor.inputChannelCount > 0)
3105     {
3106     PaUtil_SetInputFrameCount( &stream->bufferProcessor, inputFrames );
3107     PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
3108     0, /* first channel of inputBuffer is channel 0 */
3109     inputBuffer,
3110     0 ); /* 0 - use inputChannelCount passed to init buffer processor */
3111     }
3112    
3113     if (stream->bufferProcessor.outputChannelCount > 0)
3114     {
3115     PaUtil_SetOutputFrameCount( &stream->bufferProcessor, outputFrames);
3116     PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
3117     0, /* first channel of outputBuffer is channel 0 */
3118     outputBuffer,
3119     0 ); /* 0 - use outputChannelCount passed to init buffer processor */
3120     }
3121    
3122     /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
3123     in general you would pass paContinue for normal operation, and
3124     paComplete to drain the buffer processor's internal output buffer.
3125     You can check whether the buffer processor's output buffer is empty
3126     using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
3127     */
3128     callbackResult = paContinue;
3129     framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
3130    
3131     /*
3132     If you need to byte swap or shift outputBuffer to convert it to
3133     host format, do it here.
3134     */
3135    
3136     PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
3137    
3138     if (callbackResult == paContinue)
3139     {
3140     /* nothing special to do */
3141     }
3142     else
3143     if (callbackResult == paAbort)
3144     {
3145     // stop stream
3146     SetEvent(stream->hCloseRequest);
3147     }
3148     else
3149     {
3150     // stop stream
3151     SetEvent(stream->hCloseRequest);
3152     }
3153     }
3154    
3155     // ------------------------------------------------------------------------------------------
3156     HANDLE MMCSS_activate(const char *name)
3157     {
3158     DWORD task_idx = 0;
3159     HANDLE hTask = pAvSetMmThreadCharacteristics(name, &task_idx);
3160     if (hTask == NULL)
3161     {
3162     PRINT(("WASAPI: AvSetMmThreadCharacteristics failed!\n"));
3163     }
3164    
3165     /*BOOL priority_ok = pAvSetMmThreadPriority(hTask, AVRT_PRIORITY_NORMAL);
3166     if (priority_ok == FALSE)
3167     {
3168     PRINT(("WASAPI: AvSetMmThreadPriority failed!\n"));
3169     }*/
3170    
3171     // debug
3172     {
3173     int cur_priority = GetThreadPriority(GetCurrentThread());
3174     DWORD cur_priority_class = GetPriorityClass(GetCurrentProcess());
3175     PRINT(("WASAPI: thread[ priority-0x%X class-0x%X ]\n", cur_priority, cur_priority_class));
3176     }
3177    
3178     return hTask;
3179     }
3180    
3181     // ------------------------------------------------------------------------------------------
3182     void MMCSS_deactivate(HANDLE hTask)
3183     {
3184     if (!hTask)
3185     return;
3186    
3187     if (pAvRevertMmThreadCharacteristics(hTask) == FALSE)
3188     {
3189     PRINT(("WASAPI: AvRevertMmThreadCharacteristics failed!\n"));
3190     }
3191     }
3192    
3193     // ------------------------------------------------------------------------------------------
3194     PaError PaWasapi_ThreadPriorityBoost(void **hTask, PaWasapiThreadPriority nPriorityClass)
3195     {
3196     static const char *mmcs_name[] =
3197     {
3198     NULL,
3199     "Audio",
3200     "Capture",
3201     "Distribution",
3202     "Games",
3203     "Playback",
3204     "Pro Audio",
3205     "Window Manager"
3206     };
3207     HANDLE task;
3208    
3209     if (hTask == NULL)
3210     return paUnanticipatedHostError;
3211    
3212     if ((UINT32)nPriorityClass >= STATIC_ARRAY_SIZE(mmcs_name))
3213     return paUnanticipatedHostError;
3214    
3215     task = MMCSS_activate(mmcs_name[nPriorityClass]);
3216     if (task == NULL)
3217     return paUnanticipatedHostError;
3218    
3219     (*hTask) = task;
3220     return paNoError;
3221     }
3222    
3223     // ------------------------------------------------------------------------------------------
3224     PaError PaWasapi_ThreadPriorityRevert(void *hTask)
3225     {
3226     if (hTask == NULL)
3227     return paUnanticipatedHostError;
3228    
3229     MMCSS_deactivate((HANDLE)hTask);
3230    
3231     return paNoError;
3232     }
3233    
3234     // ------------------------------------------------------------------------------------------
3235     static HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)
3236     {
3237     HRESULT hr;
3238     BYTE *data = NULL;
3239    
3240     // Get buffer
3241     if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, frames, &data)) != S_OK)
3242     {
3243     if (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED)
3244     {
3245     // Using GetCurrentPadding to overcome AUDCLNT_E_BUFFER_TOO_LARGE in
3246     // shared mode results in no sound in Event-driven mode (MSDN does not
3247     // document this, or is it WASAPI bug?), thus we better
3248     // try to acquire buffer next time when GetBuffer allows to do so.
3249     #if 0
3250     // Get Read position
3251     UINT32 padding = 0;
3252     hr = stream->out.client->GetCurrentPadding(&padding);
3253     if (hr != S_OK)
3254     return LogHostError(hr);
3255    
3256     // Get frames to write
3257     frames -= padding;
3258     if (frames == 0)
3259     return S_OK;
3260    
3261     if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK)
3262     return LogHostError(hr);
3263     #else
3264     if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
3265     return S_OK; // be silent in shared mode, try again next time
3266     #endif
3267     }
3268     else
3269     return LogHostError(hr);
3270     }
3271    
3272     // Process data
3273     if (stream->out.monoMixer != NULL)
3274     {
3275     #define __DIV_8(v) ((v) >> 3) //!< (v / 8)
3276    
3277     // expand buffer (one way only for better performancedue to no calls to realloc)
3278     UINT32 mono_frames_size = frames * __DIV_8(stream->out.wavex.Format.wBitsPerSample);
3279     if (mono_frames_size > stream->out.monoBufferSize)
3280     {
3281     stream->out.monoBuffer = realloc(stream->out.monoBuffer, mono_frames_size);
3282     }
3283    
3284     // feed to user
3285     processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);
3286    
3287     // mix 1 to 2 channels
3288     stream->out.monoMixer(data, stream->out.monoBuffer, frames);
3289    
3290     #undef __DIV_8
3291     }
3292     else
3293     {
3294     processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData);
3295     }
3296    
3297     // Release buffer
3298     if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, frames, 0)) != S_OK)
3299     LogHostError(hr);
3300    
3301     return hr;
3302     }
3303    
3304     // ------------------------------------------------------------------------------------------
3305     static HRESULT PollInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)
3306     {
3307     HRESULT hr;
3308     UINT32 frames;
3309     BYTE *data = NULL;
3310     DWORD flags = 0;
3311    
3312     for (;;)
3313     {
3314     // Get the available data in the shared buffer.
3315     if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &data, &frames, &flags, NULL, NULL)) != S_OK)
3316     {
3317     if (hr == AUDCLNT_S_BUFFER_EMPTY)
3318     break; // capture buffer exhausted
3319    
3320     return LogHostError(hr);
3321     break;
3322     }
3323    
3324     // Detect silence
3325     // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
3326     // data = NULL;
3327    
3328     // Process data
3329     if (stream->in.monoMixer != NULL)
3330     {
3331     #define __DIV_8(v) ((v) >> 3) //!< (v / 8)
3332    
3333     // expand buffer (one way only for better performancedue to no calls to realloc)
3334     UINT32 mono_frames_size = frames * __DIV_8(stream->in.wavex.Format.wBitsPerSample);
3335     if (mono_frames_size > stream->in.monoBufferSize)
3336     {
3337     stream->in.monoBuffer = realloc(stream->in.monoBuffer, mono_frames_size);
3338     }
3339    
3340     // feed to user
3341     processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData);
3342    
3343     // mix 1 to 2 channels
3344     stream->in.monoMixer(data, stream->in.monoBuffer, frames);
3345    
3346     #undef __DIV_8
3347     }
3348     else
3349     {
3350     processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData);
3351     }
3352    
3353     // Release buffer
3354     if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, frames)) != S_OK)
3355     return LogHostError(hr);
3356     }
3357    
3358     return hr;
3359     }
3360    
3361     // ------------------------------------------------------------------------------------------
3362     void _OnStreamStop(PaWasapiStream *stream)
3363     {
3364     // Stop INPUT client
3365     if (stream->in.client != NULL)
3366     IAudioClient_Stop(stream->in.client);
3367    
3368     // Stop OUTPUT client
3369     if (stream->out.client != NULL)
3370     IAudioClient_Stop(stream->out.client);
3371    
3372     // Restore thread priority
3373     if (stream->hAvTask != NULL)
3374     {
3375     PaWasapi_ThreadPriorityRevert(stream->hAvTask);
3376     stream->hAvTask = NULL;
3377     }
3378    
3379     // Notify
3380     if (stream->streamRepresentation.streamFinishedCallback != NULL)
3381     stream->streamRepresentation.streamFinishedCallback(stream->streamRepresentation.userData);
3382     }
3383    
3384     // ------------------------------------------------------------------------------------------
3385     PA_THREAD_FUNC ProcThreadEvent(void *param)
3386     {
3387     PaWasapiHostProcessor processor[S_COUNT];
3388     HRESULT hr;
3389     DWORD dwResult;
3390     PaWasapiStream *stream = (PaWasapiStream *)param;
3391     PaWasapiHostProcessor defaultProcessor;
3392     BOOL set_event[S_COUNT] = { FALSE, FALSE };
3393    
3394     // Waiting on all events in case of Full-Duplex/Exclusive mode.
3395     BOOL bWaitAllEvents = FALSE;
3396     if ((stream->in.client != NULL) && (stream->out.client != NULL))
3397     {
3398     bWaitAllEvents = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) &&
3399     (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE);
3400     }
3401    
3402     // Setup data processors
3403     defaultProcessor.processor = WaspiHostProcessingLoop;
3404     defaultProcessor.userData = stream;
3405     processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);
3406     processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);
3407    
3408     // Boost thread priority
3409     PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
3410    
3411     // Create events
3412     if (stream->event[S_OUTPUT] == NULL)
3413     {
3414     stream->event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
3415     set_event[S_OUTPUT] = TRUE;
3416     }
3417     if (stream->event[S_INPUT] == NULL)
3418     {
3419     stream->event[S_INPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
3420     set_event[S_INPUT] = TRUE;
3421     }
3422     if ((stream->event[S_OUTPUT] == NULL) || (stream->event[S_INPUT] == NULL))
3423     {
3424     PRINT(("WASAPI Thread: failed creating Input/Output event handle\n"));
3425     goto thread_error;
3426     }
3427    
3428     // Initialize event & start INPUT stream
3429     if (stream->in.client)
3430     {
3431     // Create & set handle
3432     if (set_event[S_INPUT])
3433     {
3434     if ((hr = IAudioClient_SetEventHandle(stream->in.client, stream->event[S_INPUT])) != S_OK)
3435     {
3436     LogHostError(hr);
3437     goto thread_error;
3438     }
3439     }
3440    
3441     // Create Capture client
3442     if (stream->cclient == NULL)
3443     {
3444     if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)
3445     {
3446     LogHostError(hr);
3447     goto thread_error;
3448     }
3449     }
3450    
3451     // Start
3452     if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)
3453     {
3454     LogHostError(hr);
3455     goto thread_error;
3456     }
3457     }
3458    
3459     // Initialize event & start OUTPUT stream
3460     if (stream->out.client)
3461     {
3462     // Create & set handle
3463     if (set_event[S_OUTPUT])
3464     {
3465     if ((hr = IAudioClient_SetEventHandle(stream->out.client, stream->event[S_OUTPUT])) != S_OK)
3466     {
3467     LogHostError(hr);
3468     goto thread_error;
3469     }
3470     }
3471    
3472     // Create Render client
3473     if (stream->rclient == NULL)
3474     {
3475     if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)
3476     {
3477     LogHostError(hr);
3478     goto thread_error;
3479     }
3480     }
3481    
3482     // Preload buffer before start
3483     if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)
3484     {
3485     LogHostError(hr);
3486     goto thread_error;
3487     }
3488    
3489     // Start
3490     if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)
3491     {
3492     LogHostError(hr);
3493     goto thread_error;
3494     }
3495    
3496     }
3497    
3498     // Signal: stream running
3499     stream->running = TRUE;
3500    
3501     // Notify: thread started
3502     SetEvent(stream->hThreadStart);
3503    
3504     // Processing Loop
3505     for (;;)
3506     {
3507     // 2 sec timeout
3508     dwResult = WaitForMultipleObjects(S_COUNT, stream->event, bWaitAllEvents, 10000);
3509    
3510     // Check for close event (after wait for buffers to avoid any calls to user
3511     // callback when hCloseRequest was set)
3512     if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)
3513     break;
3514    
3515     // Process S_INPUT/S_OUTPUT
3516     switch (dwResult)
3517     {
3518     case WAIT_TIMEOUT: {
3519     PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n"));
3520     goto thread_end;
3521     break; }
3522    
3523     // Input stream
3524     case WAIT_OBJECT_0 + S_INPUT: {
3525     if (stream->cclient == NULL)
3526     break;
3527     PollInputBuffer(stream, processor);
3528     break; }
3529    
3530     // Output stream
3531     case WAIT_OBJECT_0 + S_OUTPUT: {
3532     if (stream->rclient == NULL)
3533     break;
3534     FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback);
3535     break; }
3536     }
3537     }
3538    
3539     thread_end:
3540    
3541     // Process stop
3542     _OnStreamStop(stream);
3543    
3544     // Notify: thread exited
3545     SetEvent(stream->hThreadExit);
3546    
3547     // Notify: not running
3548     stream->running = FALSE;
3549    
3550     return 0;
3551    
3552     thread_error:
3553    
3554     // Prevent deadlocking in Pa_StreamStart
3555     SetEvent(stream->hThreadStart);
3556    
3557     // Exit
3558     goto thread_end;
3559     }
3560    
3561     // ------------------------------------------------------------------------------------------
3562     PA_THREAD_FUNC ProcThreadPoll(void *param)
3563     {
3564     PaWasapiHostProcessor processor[S_COUNT];
3565     HRESULT hr;
3566     PaWasapiStream *stream = (PaWasapiStream *)param;
3567     PaWasapiHostProcessor defaultProcessor;
3568     INT32 i;
3569    
3570     // Calculate the actual duration of the allocated buffer.
3571     DWORD sleep_ms = 0;
3572     DWORD sleep_ms_in = GetFramesSleepTime(stream->in.framesPerHostCallback, stream->in.wavex.Format.nSamplesPerSec);
3573     DWORD sleep_ms_out = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec);
3574     if ((sleep_ms_in != 0) && (sleep_ms_out != 0))
3575     sleep_ms = min(sleep_ms_in, sleep_ms_out);
3576     else
3577     {
3578     sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);
3579     }
3580    
3581     // Setup data processors
3582     defaultProcessor.processor = WaspiHostProcessingLoop;
3583     defaultProcessor.userData = stream;
3584     processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);
3585     processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);
3586    
3587     // Boost thread priority
3588     PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
3589    
3590     // Initialize event & start INPUT stream
3591     if (stream->in.client)
3592     {
3593     if (stream->cclient == NULL)
3594     {
3595     if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)
3596     {
3597     LogHostError(hr);
3598     goto thread_error;
3599     }
3600     }
3601    
3602     if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)
3603     {
3604     LogHostError(hr);
3605     goto thread_error;
3606     }
3607     }
3608    
3609    
3610     // Initialize event & start OUTPUT stream
3611     if (stream->out.client)
3612     {
3613     if (stream->rclient == NULL)
3614     {
3615     if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)
3616     {
3617     LogHostError(hr);
3618     goto thread_error;
3619     }
3620     }
3621    
3622     // Preload buffer (obligatory, othervise ->Start() will fail)
3623     if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)
3624     {
3625     LogHostError(hr);
3626     goto thread_error;
3627     }
3628    
3629     // Start
3630     if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)
3631     {
3632     LogHostError(hr);
3633     goto thread_error;
3634     }
3635     }
3636    
3637     // Signal: stream running
3638     stream->running = TRUE;
3639    
3640     // Notify: thread started
3641     SetEvent(stream->hThreadStart);
3642    
3643     // Processing Loop
3644     while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)
3645     {
3646     for (i = 0; i < S_COUNT; ++i)
3647     {
3648     // Process S_INPUT/S_OUTPUT
3649     switch (i)
3650     {
3651     // Input stream
3652     case S_INPUT: {
3653     if (stream->cclient == NULL)
3654     break;
3655     PollInputBuffer(stream, processor);
3656     break; }
3657    
3658     // Output stream
3659     case S_OUTPUT: {
3660    
3661     UINT32 frames, padding;
3662    
3663     if (stream->rclient == NULL)
3664     break;
3665    
3666     frames = stream->out.framesPerHostCallback;
3667    
3668     // Get Read position
3669     padding = 0;
3670     hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding);
3671     if (hr != S_OK)
3672     {
3673     LogHostError(hr);
3674     break;
3675     }
3676    
3677     // Fill buffer
3678     frames -= padding;
3679     if (frames != 0)
3680     FillOutputBuffer(stream, processor, frames);
3681    
3682     break; }
3683     }
3684     }
3685     }
3686    
3687     thread_end:
3688    
3689     // Process stop
3690     _OnStreamStop(stream);
3691    
3692     // Notify: thread exited
3693     SetEvent(stream->hThreadExit);
3694    
3695     // Notify: not running
3696     stream->running = FALSE;
3697    
3698     return 0;
3699    
3700     thread_error:
3701    
3702     // Prevent deadlocking in Pa_StreamStart
3703     SetEvent(stream->hThreadStart);
3704    
3705     // Exit
3706     goto thread_end;
3707     }
3708    
3709     //#endif //VC 2005
3710    
3711    
3712    
3713    
3714     #if 0
3715     if(bFirst) {
3716     float masteur;
3717     hr = stream->outVol->GetMasterVolumeLevelScalar(&masteur);
3718     if (hr != S_OK)
3719     LogHostError(hr);
3720     float chan1, chan2;
3721     hr = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);
3722     if (hr != S_OK)
3723     LogHostError(hr);
3724     hr = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);
3725     if (hr != S_OK)
3726     LogHostError(hr);
3727    
3728     BOOL bMute;
3729     hr = stream->outVol->GetMute(&bMute);
3730     if (hr != S_OK)
3731     LogHostError(hr);
3732    
3733     stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);
3734     stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);
3735     stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL);
3736     stream->outVol->SetMute(FALSE, NULL);
3737     bFirst = FALSE;
3738     }
3739     #endif

  ViewVC Help
Powered by ViewVC 1.1.22