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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.22