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

  ViewVC Help
Powered by ViewVC 1.1.22