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

  ViewVC Help
Powered by ViewVC 1.1.22