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