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 #include <windows.h>
46 #include <stdio.h>
47 #include <process.h>
48 #include <assert.h>
49
50 // WinRT
51 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
52 #define PA_WINRT
53 #define INITGUID
54 #endif
55
56 // WASAPI
57 #include <mmreg.h> // must be before other Wasapi headers
58 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
59 #include <Avrt.h>
60 #define COBJMACROS
61 #include <Audioclient.h>
62 #include <endpointvolume.h>
63 #define INITGUID // Avoid additional linkage of static libs, excessive code will be optimized out by the compiler
64 #include <mmdeviceapi.h>
65 #include <functiondiscoverykeys.h>
66 #include <devicetopology.h> // Used to get IKsJackDescription interface
67 #undef INITGUID
68 #endif
69 #ifndef __MWERKS__
70 #include <malloc.h>
71 #include <memory.h>
72 #endif
73
74 #include "pa_util.h"
75 #include "pa_allocation.h"
76 #include "pa_hostapi.h"
77 #include "pa_stream.h"
78 #include "pa_cpuload.h"
79 #include "pa_process.h"
80 #include "pa_win_wasapi.h"
81 #include "pa_debugprint.h"
82 #include "pa_ringbuffer.h"
83 #include "pa_win_coinitialize.h"
84
85 #if !defined(NTDDI_VERSION)
86
87 #undef WINVER
88 #undef _WIN32_WINNT
89 #define WINVER 0x0600 // VISTA
90 #define _WIN32_WINNT WINVER
91
92 #ifndef _AVRT_ //<< fix MinGW dummy compile by defining missing type: AVRT_PRIORITY
93 typedef enum _AVRT_PRIORITY
94 {
95 AVRT_PRIORITY_LOW = -1,
96 AVRT_PRIORITY_NORMAL,
97 AVRT_PRIORITY_HIGH,
98 AVRT_PRIORITY_CRITICAL
99 } AVRT_PRIORITY, *PAVRT_PRIORITY;
100 #endif
101
102 #include <basetyps.h> // << for IID/CLSID
103 #include <rpcsal.h>
104 #include <sal.h>
105
106 #ifndef __LPCGUID_DEFINED__
107 #define __LPCGUID_DEFINED__
108 typedef const GUID *LPCGUID;
109 #endif
110
111 #ifndef PROPERTYKEY_DEFINED
112 #define PROPERTYKEY_DEFINED
113 typedef struct _tagpropertykey
114 {
115 GUID fmtid;
116 DWORD pid;
117 } PROPERTYKEY;
118 #endif
119
120 #ifdef __midl_proxy
121 #define __MIDL_CONST
122 #else
123 #define __MIDL_CONST const
124 #endif
125
126 #ifdef WIN64
127 #include <wtypes.h>
128 typedef LONG NTSTATUS;
129 #define FASTCALL
130 #include <oleidl.h>
131 #include <objidl.h>
132 #else
133 typedef struct _BYTE_BLOB
134 {
135 unsigned long clSize;
136 unsigned char abData[ 1 ];
137 } BYTE_BLOB;
138 typedef /* [unique] */ __RPC_unique_pointer BYTE_BLOB *UP_BYTE_BLOB;
139 typedef LONGLONG REFERENCE_TIME;
140 #define NONAMELESSUNION
141 #endif
142
143 #ifndef WAVE_FORMAT_IEEE_FLOAT
144 #define WAVE_FORMAT_IEEE_FLOAT 0x0003 // 32-bit floating-point
145 #endif
146
147 #ifndef __MINGW_EXTENSION
148 #if defined(__GNUC__) || defined(__GNUG__)
149 #define __MINGW_EXTENSION __extension__
150 #else
151 #define __MINGW_EXTENSION
152 #endif
153 #endif
154
155 #include <sdkddkver.h>
156 #include <propkeydef.h>
157 #define COBJMACROS
158 #define INITGUID // Avoid additional linkage of static libs, excessive code will be optimized out by the compiler
159 #include <audioclient.h>
160 #include <mmdeviceapi.h>
161 #include <endpointvolume.h>
162 #include <functiondiscoverykeys.h>
163 #include <devicetopology.h> // Used to get IKsJackDescription interface
164 #undef INITGUID
165
166 #endif // NTDDI_VERSION
167
168 // Missing declarations for WinRT
169 #ifdef PA_WINRT
170
171 #define DEVICE_STATE_ACTIVE 0x00000001
172
173 typedef enum _EDataFlow
174 {
175 eRender = 0,
176 eCapture = ( eRender + 1 ) ,
177 eAll = ( eCapture + 1 ) ,
178 EDataFlow_enum_count = ( eAll + 1 )
179 }
180 EDataFlow;
181
182 typedef enum _EndpointFormFactor
183 {
184 RemoteNetworkDevice = 0,
185 Speakers = ( RemoteNetworkDevice + 1 ) ,
186 LineLevel = ( Speakers + 1 ) ,
187 Headphones = ( LineLevel + 1 ) ,
188 Microphone = ( Headphones + 1 ) ,
189 Headset = ( Microphone + 1 ) ,
190 Handset = ( Headset + 1 ) ,
191 UnknownDigitalPassthrough = ( Handset + 1 ) ,
192 SPDIF = ( UnknownDigitalPassthrough + 1 ) ,
193 HDMI = ( SPDIF + 1 ) ,
194 UnknownFormFactor = ( HDMI + 1 )
195 }
196 EndpointFormFactor;
197
198 #endif
199
200 #ifndef GUID_SECT
201 #define GUID_SECT
202 #endif
203
204 #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}}
205 #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}}
206 #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}}
207 #define PA_DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
208 __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)
209 #define PA_DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
210 __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)
211
212 // "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2"
213 PA_DEFINE_IID(IAudioClient, 1cb9ad4c, dbfa, 4c32, b1, 78, c2, f5, 68, a7, 03, b2);
214 // "726778CD-F60A-4EDA-82DE-E47610CD78AA"
215 PA_DEFINE_IID(IAudioClient2, 726778cd, f60a, 4eda, 82, de, e4, 76, 10, cd, 78, aa);
216 // "1BE09788-6894-4089-8586-9A2A6C265AC5"
217 PA_DEFINE_IID(IMMEndpoint, 1be09788, 6894, 4089, 85, 86, 9a, 2a, 6c, 26, 5a, c5);
218 // "A95664D2-9614-4F35-A746-DE8DB63617E6"
219 PA_DEFINE_IID(IMMDeviceEnumerator, a95664d2, 9614, 4f35, a7, 46, de, 8d, b6, 36, 17, e6);
220 // "BCDE0395-E52F-467C-8E3D-C4579291692E"
221 PA_DEFINE_CLSID(IMMDeviceEnumerator,bcde0395, e52f, 467c, 8e, 3d, c4, 57, 92, 91, 69, 2e);
222 // "F294ACFC-3146-4483-A7BF-ADDCA7C260E2"
223 PA_DEFINE_IID(IAudioRenderClient, f294acfc, 3146, 4483, a7, bf, ad, dc, a7, c2, 60, e2);
224 // "C8ADBD64-E71E-48a0-A4DE-185C395CD317"
225 PA_DEFINE_IID(IAudioCaptureClient, c8adbd64, e71e, 48a0, a4, de, 18, 5c, 39, 5c, d3, 17);
226 // *2A07407E-6497-4A18-9787-32F79BD0D98F* Or this??
227 PA_DEFINE_IID(IDeviceTopology, 2A07407E, 6497, 4A18, 97, 87, 32, f7, 9b, d0, d9, 8f);
228 // *AE2DE0E4-5BCA-4F2D-AA46-5D13F8FDB3A9*
229 PA_DEFINE_IID(IPart, AE2DE0E4, 5BCA, 4F2D, aa, 46, 5d, 13, f8, fd, b3, a9);
230 // *4509F757-2D46-4637-8E62-CE7DB944F57B*
231 PA_DEFINE_IID(IKsJackDescription, 4509F757, 2D46, 4637, 8e, 62, ce, 7d, b9, 44, f5, 7b);
232
233 // Media formats:
234 __DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
235 __DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_ADPCM, 0x00000002, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
236 __DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
237
238 #ifdef __IAudioClient2_INTERFACE_DEFINED__
239 typedef enum _pa_AUDCLNT_STREAMOPTIONS {
240 pa_AUDCLNT_STREAMOPTIONS_NONE = 0x00,
241 pa_AUDCLNT_STREAMOPTIONS_RAW = 0x01,
242 pa_AUDCLNT_STREAMOPTIONS_MATCH_FORMAT = 0x02
243 } pa_AUDCLNT_STREAMOPTIONS;
244 typedef struct _pa_AudioClientProperties {
245 UINT32 cbSize;
246 BOOL bIsOffload;
247 AUDIO_STREAM_CATEGORY eCategory;
248 pa_AUDCLNT_STREAMOPTIONS Options;
249 } pa_AudioClientProperties;
250 #define PA_AUDIOCLIENTPROPERTIES_SIZE_CATEGORY (sizeof(pa_AudioClientProperties) - sizeof(pa_AUDCLNT_STREAMOPTIONS))
251 #define PA_AUDIOCLIENTPROPERTIES_SIZE_OPTIONS sizeof(pa_AudioClientProperties)
252 #endif // __IAudioClient2_INTERFACE_DEFINED__
253
254 /* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */
255 #if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
256 #define CREATE_THREAD(PROC) (HANDLE)_beginthreadex( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )
257 #define PA_THREAD_FUNC static unsigned WINAPI
258 #define PA_THREAD_ID unsigned
259 #else
260 #define CREATE_THREAD(PROC) CreateThread( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )
261 #define PA_THREAD_FUNC static DWORD WINAPI
262 #define PA_THREAD_ID DWORD
263 #endif
264
265 // Thread function forward decl.
266 PA_THREAD_FUNC ProcThreadEvent(void *param);
267 PA_THREAD_FUNC ProcThreadPoll(void *param);
268
269 // Availabe from Windows 7
270 #ifndef AUDCLNT_E_BUFFER_ERROR
271 #define AUDCLNT_E_BUFFER_ERROR AUDCLNT_ERR(0x018)
272 #endif
273 #ifndef AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
274 #define AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED AUDCLNT_ERR(0x019)
275 #endif
276 #ifndef AUDCLNT_E_INVALID_DEVICE_PERIOD
277 #define AUDCLNT_E_INVALID_DEVICE_PERIOD AUDCLNT_ERR(0x020)
278 #endif
279
280 #define MAX_STR_LEN 512
281
282 enum { S_INPUT = 0, S_OUTPUT = 1, S_COUNT = 2, S_FULLDUPLEX = 0 };
283
284 // Number of packets which compose single contignous buffer. With trial and error it was calculated
285 // that WASAPI Input sub-system uses 6 packets per whole buffer. Please provide more information
286 // or corrections if available.
287 enum { WASAPI_PACKETS_PER_INPUT_BUFFER = 6 };
288
289 #define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
290
291 #define PRINT(x) PA_DEBUG(x);
292
293 #define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
294 PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText )
295
296 #define PA_WASAPI__IS_FULLDUPLEX(STREAM) ((STREAM)->in.clientProc && (STREAM)->out.clientProc)
297
298 #ifndef IF_FAILED_JUMP
299 #define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;
300 #endif
301
302 #ifndef IF_FAILED_INTERNAL_ERROR_JUMP
303 #define IF_FAILED_INTERNAL_ERROR_JUMP(hr, error, label) if(FAILED(hr)) { error = paInternalError; goto label; }
304 #endif
305
306 #define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; }
307 #define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->lpVtbl->Release((punk)); (punk) = NULL; }
308
309 // Mixer function
310 typedef void (*MixMonoToStereoF) (void *__to, void *__from, UINT32 count);
311
312 // AVRT is the new "multimedia schedulling stuff"
313 #ifndef PA_WINRT
314 typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);
315 typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);
316 typedef BOOL (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE);
317 typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics) (LPCSTR,LPDWORD);
318 typedef BOOL (WINAPI *FAvRevertMmThreadCharacteristics)(HANDLE);
319 typedef BOOL (WINAPI *FAvSetMmThreadPriority) (HANDLE,AVRT_PRIORITY);
320 static HMODULE hDInputDLL = 0;
321 FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup = NULL;
322 FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup = NULL;
323 FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup = NULL;
324 FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics = NULL;
325 FAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL;
326 FAvSetMmThreadPriority pAvSetMmThreadPriority = NULL;
327 #endif
328
329 #define _GetProc(fun, type, name) { \
330 fun = (type) GetProcAddress(hDInputDLL,name); \
331 if (fun == NULL) { \
332 PRINT(("GetProcAddr failed for %s" ,name)); \
333 return FALSE; \
334 } \
335 } \
336
337 // ------------------------------------------------------------------------------------------
338 /* prototypes for functions declared in this file */
339 #ifdef __cplusplus
340 extern "C"
341 {
342 #endif /* __cplusplus */
343 PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
344 #ifdef __cplusplus
345 }
346 #endif /* __cplusplus */
347 // dummy entry point for other compilers and sdks
348 // currently built using RC1 SDK (5600)
349 //#if _MSC_VER < 1400
350 //PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
351 //{
352 //return paNoError;
353 //}
354 //#else
355
356 // ------------------------------------------------------------------------------------------
357 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
358 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
359 const PaStreamParameters *inputParameters,
360 const PaStreamParameters *outputParameters,
361 double sampleRate );
362 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
363 PaStream** s,
364 const PaStreamParameters *inputParameters,
365 const PaStreamParameters *outputParameters,
366 double sampleRate,
367 unsigned long framesPerBuffer,
368 PaStreamFlags streamFlags,
369 PaStreamCallback *streamCallback,
370 void *userData );
371 static PaError CloseStream( PaStream* stream );
372 static PaError StartStream( PaStream *stream );
373 static PaError StopStream( PaStream *stream );
374 static PaError AbortStream( PaStream *stream );
375 static PaError IsStreamStopped( PaStream *s );
376 static PaError IsStreamActive( PaStream *stream );
377 static PaTime GetStreamTime( PaStream *stream );
378 static double GetStreamCpuLoad( PaStream* stream );
379 static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
380 static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
381 static signed long GetStreamReadAvailable( PaStream* stream );
382 static signed long GetStreamWriteAvailable( PaStream* stream );
383
384 // ------------------------------------------------------------------------------------------
385 /*
386 These are fields that can be gathered from IDevice and IAudioDevice PRIOR to Initialize, and
387 done in first pass i assume that neither of these will cause the Driver to "load", but again,
388 who knows how they implement their stuff
389 */
390 typedef struct PaWasapiDeviceInfo
391 {
392 // Device
393 #ifndef PA_WINRT
394 IMMDevice *device;
395 #endif
396
397 // from GetId
398 WCHAR szDeviceID[MAX_STR_LEN];
399
400 // from GetState
401 DWORD state;
402
403 // Fields filled from IAudioDevice (_prior_ to Initialize)
404 // from GetDevicePeriod(
405 REFERENCE_TIME DefaultDevicePeriod;
406 REFERENCE_TIME MinimumDevicePeriod;
407
408 // Default format (setup through Control Panel by user)
409 WAVEFORMATEXTENSIBLE DefaultFormat;
410
411 // Fields filled from IMMEndpoint'sGetDataFlow
412 EDataFlow flow;
413
414 // Formfactor
415 EndpointFormFactor formFactor;
416 }
417 PaWasapiDeviceInfo;
418
419 // ------------------------------------------------------------------------------------------
420 /* PaWasapiHostApiRepresentation - host api datastructure specific to this implementation */
421 typedef struct
422 {
423 PaUtilHostApiRepresentation inheritedHostApiRep;
424 PaUtilStreamInterface callbackStreamInterface;
425 PaUtilStreamInterface blockingStreamInterface;
426
427 PaUtilAllocationGroup *allocations;
428
429 /* implementation specific data goes here */
430
431 PaWinUtilComInitializationResult comInitializationResult;
432
433 //in case we later need the synch
434 #ifndef PA_WINRT
435 IMMDeviceEnumerator *enumerator;
436 #endif
437
438 //this is the REAL number of devices, whether they are usefull to PA or not!
439 UINT32 deviceCount;
440
441 WCHAR defaultRenderer [MAX_STR_LEN];
442 WCHAR defaultCapturer [MAX_STR_LEN];
443
444 PaWasapiDeviceInfo *devInfo;
445
446 // Is true when WOW64 Vista/7 Workaround is needed
447 BOOL useWOW64Workaround;
448 }
449 PaWasapiHostApiRepresentation;
450
451 // ------------------------------------------------------------------------------------------
452 /* PaWasapiAudioClientParams - audio client parameters */
453 typedef struct PaWasapiAudioClientParams
454 {
455 PaWasapiDeviceInfo *device_info;
456 PaStreamParameters stream_params;
457 PaWasapiStreamInfo wasapi_params;
458 UINT32 frames_per_buffer;
459 double sample_rate;
460 BOOL blocking;
461 BOOL full_duplex;
462 BOOL wow64_workaround;
463 }
464 PaWasapiAudioClientParams;
465
466 // ------------------------------------------------------------------------------------------
467 /* PaWasapiStream - a stream data structure specifically for this implementation */
468 typedef struct PaWasapiSubStream
469 {
470 IAudioClient *clientParent;
471 #ifndef PA_WINRT
472 IStream *clientStream;
473 #endif
474 IAudioClient *clientProc;
475
476 WAVEFORMATEXTENSIBLE wavex;
477 UINT32 bufferSize;
478 REFERENCE_TIME deviceLatency;
479 REFERENCE_TIME period;
480 double latencySeconds;
481 UINT32 framesPerHostCallback;
482 AUDCLNT_SHAREMODE shareMode;
483 UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ...
484 UINT32 flags;
485 PaWasapiAudioClientParams params; //!< parameters
486
487 // Buffers
488 UINT32 buffers; //!< number of buffers used (from host side)
489 UINT32 framesPerBuffer; //!< number of frames per 1 buffer
490 BOOL userBufferAndHostMatch;
491
492 // Used for Mono >> Stereo workaround, if driver does not support it
493 // (in Exclusive mode WASAPI usually refuses to operate with Mono (1-ch)
494 void *monoBuffer; //!< pointer to buffer
495 UINT32 monoBufferSize; //!< buffer size in bytes
496 MixMonoToStereoF monoMixer; //!< pointer to mixer function
497
498 PaUtilRingBuffer *tailBuffer; //!< buffer with trailing sample for blocking mode operations (only for Input)
499 void *tailBufferMemory; //!< tail buffer memory region
500 }
501 PaWasapiSubStream;
502
503 // ------------------------------------------------------------------------------------------
504 /* PaWasapiHostProcessor - redirects processing data */
505 typedef struct PaWasapiHostProcessor
506 {
507 PaWasapiHostProcessorCallback processor;
508 void *userData;
509 }
510 PaWasapiHostProcessor;
511
512 // ------------------------------------------------------------------------------------------
513 typedef struct PaWasapiStream
514 {
515 /* IMPLEMENT ME: rename this */
516 PaUtilStreamRepresentation streamRepresentation;
517 PaUtilCpuLoadMeasurer cpuLoadMeasurer;
518 PaUtilBufferProcessor bufferProcessor;
519
520 // input
521 PaWasapiSubStream in;
522 IAudioCaptureClient *captureClientParent;
523 #ifndef PA_WINRT
524 IStream *captureClientStream;
525 #endif
526 IAudioCaptureClient *captureClient;
527 IAudioEndpointVolume *inVol;
528
529 // output
530 PaWasapiSubStream out;
531 IAudioRenderClient *renderClientParent;
532 #ifndef PA_WINRT
533 IStream *renderClientStream;
534 #endif
535 IAudioRenderClient *renderClient;
536 IAudioEndpointVolume *outVol;
537
538 // event handles for event-driven processing mode
539 HANDLE event[S_COUNT];
540
541 // buffer mode
542 PaUtilHostBufferSizeMode bufferMode;
543
544 // must be volatile to avoid race condition on user query while
545 // thread is being started
546 volatile BOOL running;
547
548 PA_THREAD_ID dwThreadId;
549 HANDLE hThread;
550 HANDLE hCloseRequest;
551 HANDLE hThreadStart; //!< signalled by thread on start
552 HANDLE hThreadExit; //!< signalled by thread on exit
553 HANDLE hBlockingOpStreamRD;
554 HANDLE hBlockingOpStreamWR;
555
556 // Host callback Output overrider
557 PaWasapiHostProcessor hostProcessOverrideOutput;
558
559 // Host callback Input overrider
560 PaWasapiHostProcessor hostProcessOverrideInput;
561
562 // Defines blocking/callback interface used
563 BOOL bBlocking;
564
565 // Av Task (MM thread management)
566 HANDLE hAvTask;
567
568 // Thread priority level
569 PaWasapiThreadPriority nThreadPriority;
570 }
571 PaWasapiStream;
572
573 // COM marshaling
574 static HRESULT MarshalSubStreamComPointers(PaWasapiSubStream *substream);
575 static HRESULT MarshalStreamComPointers(PaWasapiStream *stream);
576 static HRESULT UnmarshalSubStreamComPointers(PaWasapiSubStream *substream);
577 static HRESULT UnmarshalStreamComPointers(PaWasapiStream *stream);
578 static void ReleaseUnmarshaledSubComPointers(PaWasapiSubStream *substream);
579 static void ReleaseUnmarshaledComPointers(PaWasapiStream *stream);
580
581 // Local stream methods
582 static void _StreamOnStop(PaWasapiStream *stream);
583 static void _StreamFinish(PaWasapiStream *stream);
584 static void _StreamCleanup(PaWasapiStream *stream);
585 static HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available);
586 static HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available);
587 static void *PaWasapi_ReallocateMemory(void *ptr, size_t size);
588 static void PaWasapi_FreeMemory(void *ptr);
589
590 // Local statics
591
592 // ------------------------------------------------------------------------------------------
593 #define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__)
__LogHostError(HRESULT res,const char * func,const char * file,int line)594 static HRESULT __LogHostError(HRESULT res, const char *func, const char *file, int line)
595 {
596 const char *text = NULL;
597 switch (res)
598 {
599 case S_OK: return res;
600 case E_POINTER :text ="E_POINTER"; break;
601 case E_INVALIDARG :text ="E_INVALIDARG"; break;
602
603 case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break;
604 case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;
605 case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;
606 case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;
607 case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break;
608 case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;
609 case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break;
610 case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
611 case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break;
612 case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break;
613 case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;
614 case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;
615 case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;
616 case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;
617 case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
618 case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;
619 case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;
620 case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;
621 case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
622 case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
623 case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
624 case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;
625 case AUDCLNT_E_BUFFER_ERROR :text ="AUDCLNT_E_BUFFER_ERROR"; break;
626 case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED :text ="AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break;
627 case AUDCLNT_E_INVALID_DEVICE_PERIOD :text ="AUDCLNT_E_INVALID_DEVICE_PERIOD"; break;
628
629 case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break;
630 case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
631 case AUDCLNT_S_POSITION_STALLED :text ="AUDCLNT_S_POSITION_STALLED"; break;
632
633 // other windows common errors:
634 case CO_E_NOTINITIALIZED :text ="CO_E_NOTINITIALIZED: you must call CoInitialize() before Pa_OpenStream()"; break;
635
636 default:
637 text = "UNKNOWN ERROR";
638 }
639 PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", res, text, func, file, line));
640 PA_SKELETON_SET_LAST_HOST_ERROR(res, text);
641 return res;
642 }
643
644 // ------------------------------------------------------------------------------------------
645 #define LogPaError(PAERR) __LogPaError(PAERR, __FUNCTION__, __FILE__, __LINE__)
__LogPaError(PaError err,const char * func,const char * file,int line)646 static PaError __LogPaError(PaError err, const char *func, const char *file, int line)
647 {
648 if (err == paNoError)
649 return err;
650 PRINT(("WASAPI ERROR PAERROR: %i : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", err, Pa_GetErrorText(err), func, file, line));
651 return err;
652 }
653
654 // ------------------------------------------------------------------------------------------
655 /*! \class ThreadSleepScheduler
656 Allows to emulate thread sleep of less than 1 millisecond under Windows. Scheduler
657 calculates number of times the thread must run untill next sleep of 1 millisecond.
658 It does not make thread sleeping for real number of microseconds but rather controls
659 how many of imaginary microseconds the thread task can allow thread to sleep.
660 */
661 typedef struct ThreadIdleScheduler
662 {
663 UINT32 m_idle_microseconds; //!< number of microseconds to sleep
664 UINT32 m_next_sleep; //!< next sleep round
665 UINT32 m_i; //!< current round iterator position
666 UINT32 m_resolution; //!< resolution in number of milliseconds
667 }
668 ThreadIdleScheduler;
669 //! Setup scheduler.
ThreadIdleScheduler_Setup(ThreadIdleScheduler * sched,UINT32 resolution,UINT32 microseconds)670 static void ThreadIdleScheduler_Setup(ThreadIdleScheduler *sched, UINT32 resolution, UINT32 microseconds)
671 {
672 assert(microseconds != 0);
673 assert(resolution != 0);
674 assert((resolution * 1000) >= microseconds);
675
676 memset(sched, 0, sizeof(*sched));
677
678 sched->m_idle_microseconds = microseconds;
679 sched->m_resolution = resolution;
680 sched->m_next_sleep = (resolution * 1000) / microseconds;
681 }
682 //! Iterate and check if can sleep.
ThreadIdleScheduler_NextSleep(ThreadIdleScheduler * sched)683 static UINT32 ThreadIdleScheduler_NextSleep(ThreadIdleScheduler *sched)
684 {
685 // advance and check if thread can sleep
686 if (++ sched->m_i == sched->m_next_sleep)
687 {
688 sched->m_i = 0;
689 return sched->m_resolution;
690 }
691 return 0;
692 }
693
694 // ------------------------------------------------------------------------------------------
695 /*static double nano100ToMillis(REFERENCE_TIME ref)
696 {
697 // 1 nano = 0.000000001 seconds
698 //100 nano = 0.0000001 seconds
699 //100 nano = 0.0001 milliseconds
700 return ((double)ref)*0.0001;
701 }*/
702
703 // ------------------------------------------------------------------------------------------
nano100ToSeconds(REFERENCE_TIME ref)704 static double nano100ToSeconds(REFERENCE_TIME ref)
705 {
706 // 1 nano = 0.000000001 seconds
707 //100 nano = 0.0000001 seconds
708 //100 nano = 0.0001 milliseconds
709 return ((double)ref)*0.0000001;
710 }
711
712 // ------------------------------------------------------------------------------------------
713 /*static REFERENCE_TIME MillisTonano100(double ref)
714 {
715 // 1 nano = 0.000000001 seconds
716 //100 nano = 0.0000001 seconds
717 //100 nano = 0.0001 milliseconds
718 return (REFERENCE_TIME)(ref/0.0001);
719 }*/
720
721 // ------------------------------------------------------------------------------------------
SecondsTonano100(double ref)722 static REFERENCE_TIME SecondsTonano100(double ref)
723 {
724 // 1 nano = 0.000000001 seconds
725 //100 nano = 0.0000001 seconds
726 //100 nano = 0.0001 milliseconds
727 return (REFERENCE_TIME)(ref/0.0000001);
728 }
729
730 // ------------------------------------------------------------------------------------------
731 // Makes Hns period from frames and sample rate
MakeHnsPeriod(UINT32 nFrames,DWORD nSamplesPerSec)732 static REFERENCE_TIME MakeHnsPeriod(UINT32 nFrames, DWORD nSamplesPerSec)
733 {
734 return (REFERENCE_TIME)((10000.0 * 1000 / nSamplesPerSec * nFrames) + 0.5);
735 }
736
737 // ------------------------------------------------------------------------------------------
738 // Converts PaSampleFormat to bits per sample value
PaSampleFormatToBitsPerSample(PaSampleFormat format_id)739 static WORD PaSampleFormatToBitsPerSample(PaSampleFormat format_id)
740 {
741 switch (format_id & ~paNonInterleaved)
742 {
743 case paFloat32:
744 case paInt32: return 32;
745 case paInt24: return 24;
746 case paInt16: return 16;
747 case paInt8:
748 case paUInt8: return 8;
749 }
750 return 0;
751 }
752
753 // ------------------------------------------------------------------------------------------
754 // Converts PaSampleFormat to bits per sample value
755 /*static WORD PaSampleFormatToBytesPerSample(PaSampleFormat format_id)
756 {
757 return PaSampleFormatToBitsPerSample(format_id) >> 3; // 'bits/8'
758 }*/
759
760 // ------------------------------------------------------------------------------------------
761 // Converts Hns period into number of frames
MakeFramesFromHns(REFERENCE_TIME hnsPeriod,UINT32 nSamplesPerSec)762 static UINT32 MakeFramesFromHns(REFERENCE_TIME hnsPeriod, UINT32 nSamplesPerSec)
763 {
764 UINT32 nFrames = (UINT32)( // frames =
765 1.0 * hnsPeriod * // hns *
766 nSamplesPerSec / // (frames / s) /
767 1000 / // (ms / s) /
768 10000 // (hns / s) /
769 + 0.5 // rounding
770 );
771 return nFrames;
772 }
773
774 // Aligning function type
775 typedef UINT32 (*ALIGN_FUNC) (UINT32 v, UINT32 align);
776
777 // ------------------------------------------------------------------------------------------
778 // Aligns 'v' backwards
ALIGN_BWD(UINT32 v,UINT32 align)779 static UINT32 ALIGN_BWD(UINT32 v, UINT32 align)
780 {
781 return ((v - (align ? v % align : 0)));
782 }
783
784 // ------------------------------------------------------------------------------------------
785 // Aligns 'v' forward
ALIGN_FWD(UINT32 v,UINT32 align)786 static UINT32 ALIGN_FWD(UINT32 v, UINT32 align)
787 {
788 UINT32 remainder = (align ? (v % align) : 0);
789 if (remainder == 0)
790 return v;
791 return v + (align - remainder);
792 }
793
794 // ------------------------------------------------------------------------------------------
795 // Get next value power of 2
ALIGN_NEXT_POW2(UINT32 v)796 UINT32 ALIGN_NEXT_POW2(UINT32 v)
797 {
798 UINT32 v2 = 1;
799 while (v > (v2 <<= 1)) { }
800 v = v2;
801 return v;
802 }
803
804 // ------------------------------------------------------------------------------------------
805 // Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer
806 // is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
807 // is thrown although we must align for Vista anyway.
AlignFramesPerBuffer(UINT32 nFrames,UINT32 nSamplesPerSec,UINT32 nBlockAlign,ALIGN_FUNC pAlignFunc)808 static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign,
809 ALIGN_FUNC pAlignFunc)
810 {
811 #define HDA_PACKET_SIZE (128)
812
813 long frame_bytes = nFrames * nBlockAlign;
814 long packets;
815 (void)nSamplesPerSec;
816
817 // align to packet size
818 frame_bytes = pAlignFunc(frame_bytes, HDA_PACKET_SIZE); // use ALIGN_FWD if bigger but safer period is more desired
819
820 // atlest 1 frame must be available
821 if (frame_bytes < HDA_PACKET_SIZE)
822 frame_bytes = HDA_PACKET_SIZE;
823
824 nFrames = frame_bytes / nBlockAlign;
825 packets = frame_bytes / HDA_PACKET_SIZE;
826
827 frame_bytes = packets * HDA_PACKET_SIZE;
828 nFrames = frame_bytes / nBlockAlign;
829
830 return nFrames;
831
832 #undef HDA_PACKET_SIZE
833 }
834
835 // ------------------------------------------------------------------------------------------
GetFramesSleepTime(UINT32 nFrames,UINT32 nSamplesPerSec)836 static UINT32 GetFramesSleepTime(UINT32 nFrames, UINT32 nSamplesPerSec)
837 {
838 REFERENCE_TIME nDuration;
839 if (nSamplesPerSec == 0)
840 return 0;
841 #define REFTIMES_PER_SEC 10000000
842 #define REFTIMES_PER_MILLISEC 10000
843 // Calculate the actual duration of the allocated buffer.
844 nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);
845 return (UINT32)(nDuration/REFTIMES_PER_MILLISEC/2);
846 }
847
848 // ------------------------------------------------------------------------------------------
GetFramesSleepTimeMicroseconds(UINT32 nFrames,UINT32 nSamplesPerSec)849 static UINT32 GetFramesSleepTimeMicroseconds(UINT32 nFrames, UINT32 nSamplesPerSec)
850 {
851 REFERENCE_TIME nDuration;
852 if (nSamplesPerSec == 0)
853 return 0;
854 #define REFTIMES_PER_SEC 10000000
855 #define REFTIMES_PER_MILLISEC 10000
856 // Calculate the actual duration of the allocated buffer.
857 nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);
858 return (UINT32)(nDuration/10/2);
859 }
860
861 // ------------------------------------------------------------------------------------------
862 #ifndef PA_WINRT
SetupAVRT()863 static BOOL SetupAVRT()
864 {
865 hDInputDLL = LoadLibraryA("avrt.dll");
866 if (hDInputDLL == NULL)
867 return FALSE;
868
869 _GetProc(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup");
870 _GetProc(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup");
871 _GetProc(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup");
872 _GetProc(pAvSetMmThreadCharacteristics, FAvSetMmThreadCharacteristics, "AvSetMmThreadCharacteristicsA");
873 _GetProc(pAvRevertMmThreadCharacteristics,FAvRevertMmThreadCharacteristics,"AvRevertMmThreadCharacteristics");
874 _GetProc(pAvSetMmThreadPriority, FAvSetMmThreadPriority, "AvSetMmThreadPriority");
875
876 return pAvRtCreateThreadOrderingGroup &&
877 pAvRtDeleteThreadOrderingGroup &&
878 pAvRtWaitOnThreadOrderingGroup &&
879 pAvSetMmThreadCharacteristics &&
880 pAvRevertMmThreadCharacteristics &&
881 pAvSetMmThreadPriority;
882 }
883 #endif
884
885 // ------------------------------------------------------------------------------------------
CloseAVRT()886 static void CloseAVRT()
887 {
888 #ifndef PA_WINRT
889 if (hDInputDLL != NULL)
890 FreeLibrary(hDInputDLL);
891 hDInputDLL = NULL;
892 #endif
893 }
894
895 // ------------------------------------------------------------------------------------------
IsWow64()896 static BOOL IsWow64()
897 {
898 #ifndef PA_WINRT
899
900 // http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx
901
902 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
903 LPFN_ISWOW64PROCESS fnIsWow64Process;
904
905 BOOL bIsWow64 = FALSE;
906
907 // IsWow64Process is not available on all supported versions of Windows.
908 // Use GetModuleHandle to get a handle to the DLL that contains the function
909 // and GetProcAddress to get a pointer to the function if available.
910
911 fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
912 GetModuleHandleA("kernel32"), "IsWow64Process");
913
914 if (fnIsWow64Process == NULL)
915 return FALSE;
916
917 if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
918 return FALSE;
919
920 return bIsWow64;
921
922 #else
923
924 return FALSE;
925
926 #endif
927 }
928
929 // ------------------------------------------------------------------------------------------
930 typedef enum EWindowsVersion
931 {
932 WINDOWS_UNKNOWN = 0,
933 WINDOWS_VISTA_SERVER2008,
934 WINDOWS_7_SERVER2008R2,
935 WINDOWS_8_SERVER2012,
936 WINDOWS_8_1_SERVER2012R2,
937 WINDOWS_10_SERVER2016,
938 WINDOWS_FUTURE
939 }
940 EWindowsVersion;
941 // Alternative way for checking Windows version (allows to check version on Windows 8.1 and up)
942 #ifndef PA_WINRT
IsWindowsVersionOrGreater(WORD wMajorVersion,WORD wMinorVersion,WORD wServicePackMajor)943 static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
944 {
945 typedef ULONGLONG (NTAPI *LPFN_VERSETCONDITIONMASK)(ULONGLONG ConditionMask, DWORD TypeMask, BYTE Condition);
946 typedef BOOL (WINAPI *LPFN_VERIFYVERSIONINFO)(LPOSVERSIONINFOEXA lpVersionInformation, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
947
948 LPFN_VERSETCONDITIONMASK fnVerSetConditionMask;
949 LPFN_VERIFYVERSIONINFO fnVerifyVersionInfo;
950 OSVERSIONINFOEXA osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
951 DWORDLONG dwlConditionMask;
952
953 fnVerSetConditionMask = (LPFN_VERSETCONDITIONMASK)GetProcAddress(GetModuleHandleA("kernel32"), "VerSetConditionMask");
954 fnVerifyVersionInfo = (LPFN_VERIFYVERSIONINFO)GetProcAddress(GetModuleHandleA("kernel32"), "VerifyVersionInfoA");
955
956 if ((fnVerSetConditionMask == NULL) || (fnVerifyVersionInfo == NULL))
957 return FALSE;
958
959 dwlConditionMask = fnVerSetConditionMask(
960 fnVerSetConditionMask(
961 fnVerSetConditionMask(
962 0, VER_MAJORVERSION, VER_GREATER_EQUAL),
963 VER_MINORVERSION, VER_GREATER_EQUAL),
964 VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
965
966 osvi.dwMajorVersion = wMajorVersion;
967 osvi.dwMinorVersion = wMinorVersion;
968 osvi.wServicePackMajor = wServicePackMajor;
969
970 return (fnVerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE);
971 }
972 #endif
973 // Get Windows version
GetWindowsVersion()974 static EWindowsVersion GetWindowsVersion()
975 {
976 #ifndef PA_WINRT
977 static EWindowsVersion version = WINDOWS_UNKNOWN;
978
979 if (version == WINDOWS_UNKNOWN)
980 {
981 DWORD dwVersion = 0;
982 DWORD dwMajorVersion = 0;
983 DWORD dwMinorVersion = 0;
984 DWORD dwBuild = 0;
985
986 typedef DWORD (WINAPI *LPFN_GETVERSION)(VOID);
987 LPFN_GETVERSION fnGetVersion;
988
989 fnGetVersion = (LPFN_GETVERSION)GetProcAddress(GetModuleHandleA("kernel32"), "GetVersion");
990 if (fnGetVersion != NULL)
991 {
992 PRINT(("WASAPI: getting Windows version with GetVersion()\n"));
993
994 dwVersion = fnGetVersion();
995
996 // Get the Windows version
997 dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
998 dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
999
1000 // Get the build number
1001 if (dwVersion < 0x80000000)
1002 dwBuild = (DWORD)(HIWORD(dwVersion));
1003
1004 switch (dwMajorVersion)
1005 {
1006 case 0:
1007 case 1:
1008 case 2:
1009 case 3:
1010 case 4:
1011 case 5:
1012 break; // skip lower
1013 case 6:
1014 switch (dwMinorVersion)
1015 {
1016 case 0: version = WINDOWS_VISTA_SERVER2008; break;
1017 case 1: version = WINDOWS_7_SERVER2008R2; break;
1018 case 2: version = WINDOWS_8_SERVER2012; break;
1019 case 3: version = WINDOWS_8_1_SERVER2012R2; break;
1020 default: version = WINDOWS_FUTURE; break;
1021 }
1022 break;
1023 case 10:
1024 switch (dwMinorVersion)
1025 {
1026 case 0: version = WINDOWS_10_SERVER2016; break;
1027 default: version = WINDOWS_FUTURE; break;
1028 }
1029 break;
1030 default:
1031 version = WINDOWS_FUTURE;
1032 break;
1033 }
1034 }
1035 else
1036 {
1037 PRINT(("WASAPI: getting Windows version with VerifyVersionInfo()\n"));
1038
1039 if (IsWindowsVersionOrGreater(10, 0, 0))
1040 version = WINDOWS_10_SERVER2016;
1041 else
1042 if (IsWindowsVersionOrGreater(6, 3, 0))
1043 version = WINDOWS_8_1_SERVER2012R2;
1044 else
1045 if (IsWindowsVersionOrGreater(6, 2, 0))
1046 version = WINDOWS_8_SERVER2012;
1047 else
1048 if (IsWindowsVersionOrGreater(6, 1, 0))
1049 version = WINDOWS_7_SERVER2008R2;
1050 else
1051 if (IsWindowsVersionOrGreater(6, 0, 0))
1052 version = WINDOWS_VISTA_SERVER2008;
1053 else
1054 version = WINDOWS_FUTURE;
1055 }
1056
1057 PRINT(("WASAPI: Windows version = %d\n", version));
1058 }
1059
1060 return version;
1061 #else
1062 return WINDOWS_8_SERVER2012;
1063 #endif
1064 }
1065
1066 // ------------------------------------------------------------------------------------------
UseWOW64Workaround()1067 static BOOL UseWOW64Workaround()
1068 {
1069 // note: WOW64 bug is common to Windows Vista x64, thus we fall back to safe Poll-driven
1070 // method. Windows 7 x64 seems has WOW64 bug fixed.
1071
1072 return (IsWow64() && (GetWindowsVersion() == WINDOWS_VISTA_SERVER2008));
1073 }
1074
1075 // ------------------------------------------------------------------------------------------
GetAudioClientVersion()1076 static UINT32 GetAudioClientVersion()
1077 {
1078 if (GetWindowsVersion() >= WINDOWS_10_SERVER2016)
1079 return 3;
1080 else
1081 if (GetWindowsVersion() >= WINDOWS_8_SERVER2012)
1082 return 2;
1083
1084 return 1;
1085 }
1086
1087 // ------------------------------------------------------------------------------------------
GetAudioClientIID()1088 static const IID *GetAudioClientIID()
1089 {
1090 static const IID *cli_iid = NULL;
1091 if (cli_iid == NULL)
1092 {
1093 UINT32 cli_version = GetAudioClientVersion();
1094 if (cli_version <= 1)
1095 {
1096 cli_iid = &pa_IID_IAudioClient;
1097 }
1098 else
1099 {
1100 switch (cli_version)
1101 {
1102 case 3: cli_iid = &pa_IID_IAudioClient2; cli_version = 2; break; // use IAudioClient2 for Windows 10+ until IAudioClient3 functions are required
1103 default: cli_iid = &pa_IID_IAudioClient2; cli_version = 2; break;
1104 }
1105 }
1106
1107 PRINT(("WASAPI: IAudioClient version = %d\n", cli_version));
1108 }
1109
1110 return cli_iid;
1111 }
1112
1113 // ------------------------------------------------------------------------------------------
1114 typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerDir;
1115
1116 // ------------------------------------------------------------------------------------------
1117 #define _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(TYPE)\
1118 TYPE * __restrict to = __to;\
1119 TYPE * __restrict from = __from;\
1120 TYPE * __restrict end = from + count;\
1121 while (from != end)\
1122 {\
1123 *to ++ = *from;\
1124 *to ++ = *from;\
1125 ++ from;\
1126 }
1127
1128 // ------------------------------------------------------------------------------------------
1129 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(TYPE)\
1130 TYPE * __restrict to = (TYPE *)__to;\
1131 TYPE * __restrict from = (TYPE *)__from;\
1132 TYPE * __restrict end = to + count;\
1133 while (to != end)\
1134 {\
1135 *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\
1136 from += 2;\
1137 }
1138
1139 // ------------------------------------------------------------------------------------------
1140 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(TYPE)\
1141 TYPE * __restrict to = (TYPE *)__to;\
1142 TYPE * __restrict from = (TYPE *)__from;\
1143 TYPE * __restrict end = to + count;\
1144 while (to != end)\
1145 {\
1146 *to ++ = (TYPE)(((INT32)from[0] + (INT32)from[1]) >> 1);\
1147 from += 2;\
1148 }
1149
1150 // ------------------------------------------------------------------------------------------
1151 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(TYPE)\
1152 TYPE * __restrict to = (TYPE *)__to;\
1153 TYPE * __restrict from = (TYPE *)__from;\
1154 TYPE * __restrict end = to + count;\
1155 while (to != end)\
1156 {\
1157 *to ++ = (TYPE)(((INT64)from[0] + (INT64)from[1]) >> 1);\
1158 from += 2;\
1159 }
1160
1161 // ------------------------------------------------------------------------------------------
1162 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(TYPE)\
1163 TYPE * __restrict to = (TYPE *)__to;\
1164 TYPE * __restrict from = (TYPE *)__from;\
1165 TYPE * __restrict end = to + count;\
1166 while (to != end)\
1167 {\
1168 *to ++ = from[0];\
1169 from += 2;\
1170 }
1171
1172 // ------------------------------------------------------------------------------------------
_MixMonoToStereo_1TO2_8(void * __to,void * __from,UINT32 count)1173 static void _MixMonoToStereo_1TO2_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(BYTE); }
_MixMonoToStereo_1TO2_16(void * __to,void * __from,UINT32 count)1174 static void _MixMonoToStereo_1TO2_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(short); }
_MixMonoToStereo_1TO2_24(void * __to,void * __from,UINT32 count)1175 static void _MixMonoToStereo_1TO2_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); /* !!! int24 data is contained in 32-bit containers*/ }
_MixMonoToStereo_1TO2_32(void * __to,void * __from,UINT32 count)1176 static void _MixMonoToStereo_1TO2_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); }
_MixMonoToStereo_1TO2_32f(void * __to,void * __from,UINT32 count)1177 static void _MixMonoToStereo_1TO2_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(float); }
1178
1179 // ------------------------------------------------------------------------------------------
_MixMonoToStereo_2TO1_8(void * __to,void * __from,UINT32 count)1180 static void _MixMonoToStereo_2TO1_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(BYTE); }
_MixMonoToStereo_2TO1_16(void * __to,void * __from,UINT32 count)1181 static void _MixMonoToStereo_2TO1_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(short); }
_MixMonoToStereo_2TO1_24(void * __to,void * __from,UINT32 count)1182 static void _MixMonoToStereo_2TO1_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(int); /* !!! int24 data is contained in 32-bit containers*/ }
_MixMonoToStereo_2TO1_32(void * __to,void * __from,UINT32 count)1183 static void _MixMonoToStereo_2TO1_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(int); }
_MixMonoToStereo_2TO1_32f(void * __to,void * __from,UINT32 count)1184 static void _MixMonoToStereo_2TO1_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(float); }
1185
1186 // ------------------------------------------------------------------------------------------
_MixMonoToStereo_2TO1_8_L(void * __to,void * __from,UINT32 count)1187 static void _MixMonoToStereo_2TO1_8_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(BYTE); }
_MixMonoToStereo_2TO1_16_L(void * __to,void * __from,UINT32 count)1188 static void _MixMonoToStereo_2TO1_16_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(short); }
_MixMonoToStereo_2TO1_24_L(void * __to,void * __from,UINT32 count)1189 static void _MixMonoToStereo_2TO1_24_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); /* !!! int24 data is contained in 32-bit containers*/ }
_MixMonoToStereo_2TO1_32_L(void * __to,void * __from,UINT32 count)1190 static void _MixMonoToStereo_2TO1_32_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); }
_MixMonoToStereo_2TO1_32f_L(void * __to,void * __from,UINT32 count)1191 static void _MixMonoToStereo_2TO1_32f_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(float); }
1192
1193 // ------------------------------------------------------------------------------------------
_GetMonoToStereoMixer(PaSampleFormat format,EMixerDir dir)1194 static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir dir)
1195 {
1196 switch (dir)
1197 {
1198 case MIX_DIR__1TO2:
1199 switch (format & ~paNonInterleaved)
1200 {
1201 case paUInt8: return _MixMonoToStereo_1TO2_8;
1202 case paInt16: return _MixMonoToStereo_1TO2_16;
1203 case paInt24: return _MixMonoToStereo_1TO2_24;
1204 case paInt32: return _MixMonoToStereo_1TO2_32;
1205 case paFloat32: return _MixMonoToStereo_1TO2_32f;
1206 }
1207 break;
1208
1209 case MIX_DIR__2TO1:
1210 switch (format & ~paNonInterleaved)
1211 {
1212 case paUInt8: return _MixMonoToStereo_2TO1_8;
1213 case paInt16: return _MixMonoToStereo_2TO1_16;
1214 case paInt24: return _MixMonoToStereo_2TO1_24;
1215 case paInt32: return _MixMonoToStereo_2TO1_32;
1216 case paFloat32: return _MixMonoToStereo_2TO1_32f;
1217 }
1218 break;
1219
1220 case MIX_DIR__2TO1_L:
1221 switch (format & ~paNonInterleaved)
1222 {
1223 case paUInt8: return _MixMonoToStereo_2TO1_8_L;
1224 case paInt16: return _MixMonoToStereo_2TO1_16_L;
1225 case paInt24: return _MixMonoToStereo_2TO1_24_L;
1226 case paInt32: return _MixMonoToStereo_2TO1_32_L;
1227 case paFloat32: return _MixMonoToStereo_2TO1_32f_L;
1228 }
1229 break;
1230 }
1231
1232 return NULL;
1233 }
1234
1235 // ------------------------------------------------------------------------------------------
1236 #ifdef PA_WINRT
1237 typedef struct PaActivateAudioInterfaceCompletionHandler
1238 {
1239 IActivateAudioInterfaceCompletionHandler parent;
1240 volatile LONG refs;
1241 volatile LONG done;
1242 struct
1243 {
1244 const IID *iid;
1245 void **obj;
1246 }
1247 in;
1248 struct
1249 {
1250 HRESULT hr;
1251 }
1252 out;
1253 }
1254 PaActivateAudioInterfaceCompletionHandler;
1255
HRESULT(STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_QueryInterface)1256 static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_QueryInterface)(
1257 IActivateAudioInterfaceCompletionHandler *This, REFIID riid, void **ppvObject)
1258 {
1259 PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
1260
1261 // From MSDN:
1262 // "The IAgileObject interface is a marker interface that indicates that an object
1263 // is free threaded and can be called from any apartment."
1264 if (IsEqualIID(riid, &IID_IUnknown) ||
1265 IsEqualIID(riid, &IID_IAgileObject))
1266 {
1267 IActivateAudioInterfaceCompletionHandler_AddRef((IActivateAudioInterfaceCompletionHandler *)handler);
1268 (*ppvObject) = handler;
1269 return S_OK;
1270 }
1271
1272 return E_NOINTERFACE;
1273 }
1274
ULONG(STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_AddRef)1275 static ULONG (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_AddRef)(
1276 IActivateAudioInterfaceCompletionHandler *This)
1277 {
1278 PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
1279
1280 return InterlockedIncrement(&handler->refs);
1281 }
1282
ULONG(STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_Release)1283 static ULONG (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_Release)(
1284 IActivateAudioInterfaceCompletionHandler *This)
1285 {
1286 PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
1287 ULONG refs;
1288
1289 if ((refs = InterlockedDecrement(&handler->refs)) == 0)
1290 {
1291 PaUtil_FreeMemory(handler->parent.lpVtbl);
1292 PaUtil_FreeMemory(handler);
1293 }
1294
1295 return refs;
1296 }
1297
HRESULT(STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_ActivateCompleted)1298 static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_ActivateCompleted)(
1299 IActivateAudioInterfaceCompletionHandler *This, IActivateAudioInterfaceAsyncOperation *activateOperation)
1300 {
1301 PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
1302
1303 HRESULT hr = S_OK;
1304 HRESULT hrActivateResult = S_OK;
1305 IUnknown *punkAudioInterface = NULL;
1306
1307 // Check for a successful activation result
1308 hr = IActivateAudioInterfaceAsyncOperation_GetActivateResult(activateOperation, &hrActivateResult, &punkAudioInterface);
1309 if (SUCCEEDED(hr) && SUCCEEDED(hrActivateResult))
1310 {
1311 // Get pointer to the requested audio interface
1312 IUnknown_QueryInterface(punkAudioInterface, handler->in.iid, handler->in.obj);
1313 if ((*handler->in.obj) == NULL)
1314 hrActivateResult = E_FAIL;
1315 }
1316 SAFE_RELEASE(punkAudioInterface);
1317
1318 if (SUCCEEDED(hr))
1319 handler->out.hr = hrActivateResult;
1320 else
1321 handler->out.hr = hr;
1322
1323 // Got client object, stop busy waiting in ActivateAudioInterface
1324 InterlockedExchange(&handler->done, TRUE);
1325
1326 return hr;
1327 }
1328
CreateActivateAudioInterfaceCompletionHandler(const IID * iid,void ** obj)1329 static IActivateAudioInterfaceCompletionHandler *CreateActivateAudioInterfaceCompletionHandler(const IID *iid, void **obj)
1330 {
1331 PaActivateAudioInterfaceCompletionHandler *handler = PaUtil_AllocateMemory(sizeof(PaActivateAudioInterfaceCompletionHandler));
1332 ZeroMemory(handler, sizeof(*handler));
1333 handler->parent.lpVtbl = PaUtil_AllocateMemory(sizeof(*handler->parent.lpVtbl));
1334 handler->parent.lpVtbl->QueryInterface = &PaActivateAudioInterfaceCompletionHandler_QueryInterface;
1335 handler->parent.lpVtbl->AddRef = &PaActivateAudioInterfaceCompletionHandler_AddRef;
1336 handler->parent.lpVtbl->Release = &PaActivateAudioInterfaceCompletionHandler_Release;
1337 handler->parent.lpVtbl->ActivateCompleted = &PaActivateAudioInterfaceCompletionHandler_ActivateCompleted;
1338 handler->refs = 1;
1339 handler->in.iid = iid;
1340 handler->in.obj = obj;
1341 return (IActivateAudioInterfaceCompletionHandler *)handler;
1342 }
1343 #endif
1344
1345 // ------------------------------------------------------------------------------------------
1346 #ifdef PA_WINRT
ActivateAudioInterface_WINRT(const PaWasapiDeviceInfo * deviceInfo,const IID * iid,void ** obj)1347 static HRESULT ActivateAudioInterface_WINRT(const PaWasapiDeviceInfo *deviceInfo, const IID *iid, void **obj)
1348 {
1349 #define PA_WASAPI_DEVICE_PATH_LEN 64
1350
1351 PaError result = paNoError;
1352 HRESULT hr = S_OK;
1353 IActivateAudioInterfaceAsyncOperation *asyncOp = NULL;
1354 IActivateAudioInterfaceCompletionHandler *handler = CreateActivateAudioInterfaceCompletionHandler(iid, obj);
1355 PaActivateAudioInterfaceCompletionHandler *handlerImpl = (PaActivateAudioInterfaceCompletionHandler *)handler;
1356 OLECHAR devicePath[PA_WASAPI_DEVICE_PATH_LEN] = { 0 };
1357
1358 // Get device path in form L"{DEVICE_GUID}"
1359 switch (deviceInfo->flow)
1360 {
1361 case eRender:
1362 StringFromGUID2(&DEVINTERFACE_AUDIO_RENDER, devicePath, PA_WASAPI_DEVICE_PATH_LEN - 1);
1363 break;
1364 case eCapture:
1365 StringFromGUID2(&DEVINTERFACE_AUDIO_CAPTURE, devicePath, PA_WASAPI_DEVICE_PATH_LEN - 1);
1366 break;
1367 default:
1368 return S_FALSE;
1369 }
1370
1371 // Async operation will call back to IActivateAudioInterfaceCompletionHandler::ActivateCompleted
1372 // which must be an agile interface implementation
1373 hr = ActivateAudioInterfaceAsync(devicePath, iid, NULL, handler, &asyncOp);
1374 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1375
1376 // Wait in busy loop for async operation to complete
1377 // Use Interlocked API here to ensure that ->done variable is read every time through the loop
1378 while (SUCCEEDED(hr) && !InterlockedOr(&handlerImpl->done, 0))
1379 {
1380 Sleep(1);
1381 }
1382
1383 hr = handlerImpl->out.hr;
1384
1385 error:
1386
1387 SAFE_RELEASE(asyncOp);
1388 SAFE_RELEASE(handler);
1389
1390 return hr;
1391
1392 #undef PA_WASAPI_DEVICE_PATH_LEN
1393 }
1394 #endif
1395
1396 // ------------------------------------------------------------------------------------------
ActivateAudioInterface(const PaWasapiDeviceInfo * deviceInfo,IAudioClient ** client)1397 static HRESULT ActivateAudioInterface(const PaWasapiDeviceInfo *deviceInfo, IAudioClient **client)
1398 {
1399 #ifndef PA_WINRT
1400 return IMMDevice_Activate(deviceInfo->device, GetAudioClientIID(), CLSCTX_ALL, NULL, (void **)client);
1401 #else
1402 return ActivateAudioInterface_WINRT(deviceInfo, GetAudioClientIID(), (void **)client);
1403 #endif
1404 }
1405
1406 // ------------------------------------------------------------------------------------------
1407 #ifdef PA_WINRT
SignalObjectAndWait(HANDLE hObjectToSignal,HANDLE hObjectToWaitOn,DWORD dwMilliseconds,BOOL bAlertable)1408 static DWORD SignalObjectAndWait(HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds, BOOL bAlertable)
1409 {
1410 SetEvent(hObjectToSignal);
1411 return WaitForSingleObjectEx(hObjectToWaitOn, dwMilliseconds, bAlertable);
1412 }
1413 #endif
1414
1415 // ------------------------------------------------------------------------------------------
PaWasapi_Initialize(PaUtilHostApiRepresentation ** hostApi,PaHostApiIndex hostApiIndex)1416 PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
1417 {
1418 PaError result = paNoError;
1419 PaWasapiHostApiRepresentation *paWasapi;
1420 PaDeviceInfo *deviceInfoArray;
1421 HRESULT hr = S_OK;
1422 UINT i;
1423 #ifndef PA_WINRT
1424 IMMDeviceCollection* pEndPoints = NULL;
1425 #else
1426 WAVEFORMATEX *mixFormat;
1427 #endif
1428
1429 #ifndef PA_WINRT
1430 if (!SetupAVRT())
1431 {
1432 PRINT(("WASAPI: No AVRT! (not VISTA?)"));
1433 return paNoError;
1434 }
1435 #endif
1436
1437 paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) );
1438 if (paWasapi == NULL)
1439 {
1440 result = paInsufficientMemory;
1441 goto error;
1442 }
1443
1444 memset( paWasapi, 0, sizeof(PaWasapiHostApiRepresentation) ); /* ensure all fields are zeroed. especially paWasapi->allocations */
1445
1446 result = PaWinUtil_CoInitialize( paWASAPI, &paWasapi->comInitializationResult );
1447 if( result != paNoError )
1448 {
1449 goto error;
1450 }
1451
1452 paWasapi->allocations = PaUtil_CreateAllocationGroup();
1453 if (paWasapi->allocations == NULL)
1454 {
1455 result = paInsufficientMemory;
1456 goto error;
1457 }
1458
1459 *hostApi = &paWasapi->inheritedHostApiRep;
1460 (*hostApi)->info.structVersion = 1;
1461 (*hostApi)->info.type = paWASAPI;
1462 (*hostApi)->info.name = "Windows WASAPI";
1463 (*hostApi)->info.deviceCount = 0;
1464 (*hostApi)->info.defaultInputDevice = paNoDevice;
1465 (*hostApi)->info.defaultOutputDevice = paNoDevice;
1466
1467 #ifndef PA_WINRT
1468 paWasapi->enumerator = NULL;
1469 hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER,
1470 &pa_IID_IMMDeviceEnumerator, (void **)&paWasapi->enumerator);
1471
1472 // We need to set the result to a value otherwise we will return paNoError
1473 // [IF_FAILED_JUMP(hResult, error);]
1474 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1475
1476 // getting default device ids in the eMultimedia "role"
1477 {
1478 {
1479 IMMDevice *defaultRenderer = NULL;
1480 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eRender, eMultimedia, &defaultRenderer);
1481 if (hr != S_OK)
1482 {
1483 if (hr != E_NOTFOUND) {
1484 // We need to set the result to a value otherwise we will return paNoError
1485 // [IF_FAILED_JUMP(hResult, error);]
1486 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1487 }
1488 }
1489 else
1490 {
1491 WCHAR *pszDeviceId = NULL;
1492 hr = IMMDevice_GetId(defaultRenderer, &pszDeviceId);
1493 // We need to set the result to a value otherwise we will return paNoError
1494 // [IF_FAILED_JUMP(hResult, error);]
1495 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1496 wcsncpy(paWasapi->defaultRenderer, pszDeviceId, MAX_STR_LEN-1);
1497 CoTaskMemFree(pszDeviceId);
1498 IMMDevice_Release(defaultRenderer);
1499 }
1500 }
1501
1502 {
1503 IMMDevice *defaultCapturer = NULL;
1504 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eCapture, eMultimedia, &defaultCapturer);
1505 if (hr != S_OK)
1506 {
1507 if (hr != E_NOTFOUND) {
1508 // We need to set the result to a value otherwise we will return paNoError
1509 // [IF_FAILED_JUMP(hResult, error);]
1510 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1511 }
1512 }
1513 else
1514 {
1515 WCHAR *pszDeviceId = NULL;
1516 hr = IMMDevice_GetId(defaultCapturer, &pszDeviceId);
1517 // We need to set the result to a value otherwise we will return paNoError
1518 // [IF_FAILED_JUMP(hResult, error);]
1519 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1520 wcsncpy(paWasapi->defaultCapturer, pszDeviceId, MAX_STR_LEN-1);
1521 CoTaskMemFree(pszDeviceId);
1522 IMMDevice_Release(defaultCapturer);
1523 }
1524 }
1525 }
1526
1527 hr = IMMDeviceEnumerator_EnumAudioEndpoints(paWasapi->enumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints);
1528 // We need to set the result to a value otherwise we will return paNoError
1529 // [IF_FAILED_JUMP(hResult, error);]
1530 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1531
1532 hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount);
1533 // We need to set the result to a value otherwise we will return paNoError
1534 // [IF_FAILED_JUMP(hResult, error);]
1535 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1536
1537 #else
1538 paWasapi->deviceCount = 2;
1539 #endif
1540
1541 paWasapi->devInfo = (PaWasapiDeviceInfo *)PaUtil_AllocateMemory(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);
1542 if (paWasapi->devInfo == NULL)
1543 {
1544 result = paInsufficientMemory;
1545 goto error;
1546 }
1547 for (i = 0; i < paWasapi->deviceCount; ++i)
1548 memset(&paWasapi->devInfo[i], 0, sizeof(PaWasapiDeviceInfo));
1549
1550 if (paWasapi->deviceCount > 0)
1551 {
1552 (*hostApi)->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory(
1553 paWasapi->allocations, sizeof(PaDeviceInfo *) * paWasapi->deviceCount);
1554 if ((*hostApi)->deviceInfos == NULL)
1555 {
1556 result = paInsufficientMemory;
1557 goto error;
1558 }
1559
1560 /* allocate all device info structs in a contiguous block */
1561 deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory(
1562 paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount);
1563 if (deviceInfoArray == NULL)
1564 {
1565 result = paInsufficientMemory;
1566 goto error;
1567 }
1568
1569 for (i = 0; i < paWasapi->deviceCount; ++i)
1570 {
1571 PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
1572 deviceInfo->structVersion = 2;
1573 deviceInfo->hostApi = hostApiIndex;
1574
1575 PA_DEBUG(("WASAPI: device idx: %02d\n", i));
1576 PA_DEBUG(("WASAPI: ---------------\n"));
1577
1578 #ifndef PA_WINRT
1579 hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device);
1580 // We need to set the result to a value otherwise we will return paNoError
1581 // [IF_FAILED_JUMP(hResult, error);]
1582 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1583
1584 // getting ID
1585 {
1586 WCHAR *pszDeviceId = NULL;
1587 hr = IMMDevice_GetId(paWasapi->devInfo[i].device, &pszDeviceId);
1588 // We need to set the result to a value otherwise we will return paNoError
1589 // [IF_FAILED_JUMP(hr, error);]
1590 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1591 wcsncpy(paWasapi->devInfo[i].szDeviceID, pszDeviceId, MAX_STR_LEN-1);
1592 CoTaskMemFree(pszDeviceId);
1593
1594 if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer) == 0)
1595 {// we found the default input!
1596 (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
1597 }
1598 if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer) == 0)
1599 {// we found the default output!
1600 (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
1601 }
1602 }
1603
1604 hr = IMMDevice_GetState(paWasapi->devInfo[i].device, &paWasapi->devInfo[i].state);
1605 // We need to set the result to a value otherwise we will return paNoError
1606 // [IF_FAILED_JUMP(hResult, error);]
1607 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1608
1609 if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE)
1610 {
1611 PRINT(("WASAPI device: %d is not currently available (state:%d)\n", i, paWasapi->devInfo[i].state));
1612 }
1613
1614 {
1615 IPropertyStore *pProperty;
1616 hr = IMMDevice_OpenPropertyStore(paWasapi->devInfo[i].device, STGM_READ, &pProperty);
1617 // We need to set the result to a value otherwise we will return paNoError
1618 // [IF_FAILED_JUMP(hResult, error);]
1619 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1620
1621 // "Friendly" Name
1622 {
1623 char *deviceName;
1624 PROPVARIANT value;
1625 PropVariantInit(&value);
1626 hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value);
1627 // We need to set the result to a value otherwise we will return paNoError
1628 // [IF_FAILED_JUMP(hResult, error);]
1629 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1630 deviceInfo->name = NULL;
1631 deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1);
1632 if (deviceName == NULL)
1633 {
1634 result = paInsufficientMemory;
1635 goto error;
1636 }
1637 if (value.pwszVal)
1638 WideCharToMultiByte(CP_UTF8, 0, value.pwszVal, (int)wcslen(value.pwszVal), deviceName, MAX_STR_LEN - 1, 0, 0);
1639 else
1640 _snprintf(deviceName, MAX_STR_LEN - 1, "baddev%d", i);
1641 deviceInfo->name = deviceName;
1642 PropVariantClear(&value);
1643 PA_DEBUG(("WASAPI:%d| name[%s]\n", i, deviceInfo->name));
1644 }
1645
1646 // Default format
1647 {
1648 PROPVARIANT value;
1649 PropVariantInit(&value);
1650 hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value);
1651 // We need to set the result to a value otherwise we will return paNoError
1652 // [IF_FAILED_JUMP(hResult, error);]
1653 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1654 memcpy(&paWasapi->devInfo[i].DefaultFormat, value.blob.pBlobData, min(sizeof(paWasapi->devInfo[i].DefaultFormat), value.blob.cbSize));
1655 // cleanup
1656 PropVariantClear(&value);
1657 }
1658
1659 // Formfactor
1660 {
1661 PROPVARIANT value;
1662 PropVariantInit(&value);
1663 hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value);
1664 // We need to set the result to a value otherwise we will return paNoError
1665 // [IF_FAILED_JUMP(hResult, error);]
1666 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1667 // set
1668 #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION)
1669 // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1)));
1670 UINT v;
1671 memcpy(&v, (((WORD *)&value.wReserved3)+1), sizeof(v));
1672 paWasapi->devInfo[i].formFactor = (EndpointFormFactor)v;
1673 #else
1674 paWasapi->devInfo[i].formFactor = (EndpointFormFactor)value.uintVal;
1675 #endif
1676 PA_DEBUG(("WASAPI:%d| form-factor[%d]\n", i, paWasapi->devInfo[i].formFactor));
1677 // cleanup
1678 PropVariantClear(&value);
1679 }
1680
1681 SAFE_RELEASE(pProperty);
1682 }
1683
1684 // Endpoint data
1685 {
1686 IMMEndpoint *endpoint = NULL;
1687 hr = IMMDevice_QueryInterface(paWasapi->devInfo[i].device, &pa_IID_IMMEndpoint, (void **)&endpoint);
1688 if (SUCCEEDED(hr))
1689 {
1690 hr = IMMEndpoint_GetDataFlow(endpoint, &paWasapi->devInfo[i].flow);
1691 SAFE_RELEASE(endpoint);
1692 }
1693 }
1694 #endif
1695
1696 // Getting a temporary IAudioClient for more fields
1697 // we make sure NOT to call Initialize yet!
1698 {
1699 #ifdef PA_WINRT
1700 // Set flow as ActivateAudioInterface depends on it and selects corresponding
1701 // direction for the Audio Client
1702 paWasapi->devInfo[i].flow = (i == 0 ? eRender : eCapture);
1703 #endif
1704
1705 // Create temp Audio Client instance to query additional details
1706 IAudioClient *tmpClient = NULL;
1707 hr = ActivateAudioInterface(&paWasapi->devInfo[i], &tmpClient);
1708 // We need to set the result to a value otherwise we will return paNoError
1709 // [IF_FAILED_JUMP(hResult, error);]
1710 IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
1711
1712 // Get latency
1713 hr = IAudioClient_GetDevicePeriod(tmpClient,
1714 &paWasapi->devInfo[i].DefaultDevicePeriod,
1715 &paWasapi->devInfo[i].MinimumDevicePeriod);
1716 if (FAILED(hr))
1717 {
1718 PA_DEBUG(("WASAPI:%d| failed getting min/default periods by IAudioClient::GetDevicePeriod() with error[%08X], will use 30000/100000 hns\n", i, (UINT32)hr));
1719
1720 // assign WASAPI common values
1721 paWasapi->devInfo[i].DefaultDevicePeriod = 100000;
1722 paWasapi->devInfo[i].MinimumDevicePeriod = 30000;
1723
1724 // ignore error, let continue further without failing with paInternalError
1725 hr = S_OK;
1726 }
1727
1728 #ifdef PA_WINRT
1729 // Get mix format which will treat as default device format
1730 hr = IAudioClient_GetMixFormat(tmpClient, &mixFormat);
1731 if (SUCCEEDED(hr))
1732 {
1733 // Default device
1734 if (i == 0)
1735 (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
1736 else
1737 (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
1738
1739 // State
1740 paWasapi->devInfo[i].state = DEVICE_STATE_ACTIVE;
1741
1742 // Default format
1743 memcpy(&paWasapi->devInfo[i].DefaultFormat, mixFormat, min(sizeof(paWasapi->devInfo[i].DefaultFormat), sizeof(*mixFormat)));
1744 CoTaskMemFree(mixFormat);
1745
1746 // Form-factor
1747 paWasapi->devInfo[i].formFactor = UnknownFormFactor;
1748
1749 // Name
1750 deviceInfo->name = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1);
1751 if (deviceInfo->name == NULL)
1752 {
1753 SAFE_RELEASE(tmpClient);
1754 result = paInsufficientMemory;
1755 goto error;
1756 }
1757 _snprintf((char *)deviceInfo->name, MAX_STR_LEN - 1, "WASAPI_%s:%d", (i == 0 ? "Output" : "Input"), i);
1758 PA_DEBUG(("WASAPI:%d| name[%s]\n", i, deviceInfo->name));
1759 }
1760 #endif
1761
1762 // Release tmp client
1763 SAFE_RELEASE(tmpClient);
1764
1765 if (hr != S_OK)
1766 {
1767 //davidv: this happened with my hardware, previously for that same device in DirectSound:
1768 //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
1769 //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat
1770 LogHostError(hr);
1771 // We need to set the result to a value otherwise we will return paNoError
1772 result = paInternalError;
1773 goto error;
1774 }
1775 }
1776
1777 // we can now fill in portaudio device data
1778 deviceInfo->maxInputChannels = 0;
1779 deviceInfo->maxOutputChannels = 0;
1780 deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec;
1781 switch (paWasapi->devInfo[i].flow)
1782 {
1783 case eRender: {
1784 deviceInfo->maxOutputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;
1785 deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
1786 deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
1787 PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
1788 deviceInfo->maxOutputChannels, (float)deviceInfo->defaultHighOutputLatency, (float)deviceInfo->defaultLowOutputLatency));
1789 break;}
1790 case eCapture: {
1791 deviceInfo->maxInputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;
1792 deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
1793 deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
1794 PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
1795 deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency));
1796 break; }
1797 default:
1798 PRINT(("WASAPI:%d| bad Data Flow!\n", i));
1799 // We need to set the result to a value otherwise we will return paNoError
1800 result = paInternalError;
1801 //continue; // do not skip from list, allow to initialize
1802 break;
1803 }
1804
1805 (*hostApi)->deviceInfos[i] = deviceInfo;
1806 ++(*hostApi)->info.deviceCount;
1807 }
1808 }
1809
1810 (*hostApi)->Terminate = Terminate;
1811 (*hostApi)->OpenStream = OpenStream;
1812 (*hostApi)->IsFormatSupported = IsFormatSupported;
1813
1814 PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream,
1815 StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1816 GetStreamTime, GetStreamCpuLoad,
1817 PaUtil_DummyRead, PaUtil_DummyWrite,
1818 PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1819
1820 PaUtil_InitializeStreamInterface( &paWasapi->blockingStreamInterface, CloseStream, StartStream,
1821 StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1822 GetStreamTime, PaUtil_DummyGetCpuLoad,
1823 ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1824
1825
1826 // findout if platform workaround is required
1827 paWasapi->useWOW64Workaround = UseWOW64Workaround();
1828
1829 #ifndef PA_WINRT
1830 SAFE_RELEASE(pEndPoints);
1831 #endif
1832
1833 PRINT(("WASAPI: initialized ok\n"));
1834
1835 return paNoError;
1836
1837 error:
1838
1839 PRINT(("WASAPI: failed %s error[%d|%s]\n", __FUNCTION__, result, Pa_GetErrorText(result)));
1840
1841 #ifndef PA_WINRT
1842 SAFE_RELEASE(pEndPoints);
1843 #endif
1844
1845 Terminate((PaUtilHostApiRepresentation *)paWasapi);
1846
1847 // Safety if error was not set so that we do not think initialize was a success
1848 if (result == paNoError) {
1849 result = paInternalError;
1850 }
1851
1852 return result;
1853 }
1854
1855 // ------------------------------------------------------------------------------------------
Terminate(PaUtilHostApiRepresentation * hostApi)1856 static void Terminate( PaUtilHostApiRepresentation *hostApi )
1857 {
1858 UINT i;
1859 PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
1860 if (paWasapi == NULL)
1861 return;
1862
1863 // Release IMMDeviceEnumerator
1864 #ifndef PA_WINRT
1865 SAFE_RELEASE(paWasapi->enumerator);
1866 #endif
1867
1868 // Release device info bound objects and device info itself
1869 for (i = 0; i < paWasapi->deviceCount; ++i)
1870 {
1871 PaWasapiDeviceInfo *info = &paWasapi->devInfo[i];
1872 #ifndef PA_WINRT
1873 SAFE_RELEASE(info->device);
1874 #else
1875 (void)info;
1876 #endif
1877 }
1878 PaUtil_FreeMemory(paWasapi->devInfo);
1879
1880 if (paWasapi->allocations)
1881 {
1882 PaUtil_FreeAllAllocations(paWasapi->allocations);
1883 PaUtil_DestroyAllocationGroup(paWasapi->allocations);
1884 }
1885
1886 PaWinUtil_CoUninitialize( paWASAPI, &paWasapi->comInitializationResult );
1887
1888 PaUtil_FreeMemory(paWasapi);
1889
1890 // Close AVRT
1891 CloseAVRT();
1892 }
1893
1894 // ------------------------------------------------------------------------------------------
_GetHostApi(PaError * _error)1895 static PaWasapiHostApiRepresentation *_GetHostApi(PaError *_error)
1896 {
1897 PaError error;
1898
1899 PaUtilHostApiRepresentation *pApi;
1900 if ((error = PaUtil_GetHostApiRepresentation(&pApi, paWASAPI)) != paNoError)
1901 {
1902 if (_error != NULL)
1903 (*_error) = error;
1904
1905 return NULL;
1906 }
1907 return (PaWasapiHostApiRepresentation *)pApi;
1908 }
1909
1910 // ------------------------------------------------------------------------------------------
PaWasapi_GetDeviceDefaultFormat(void * pFormat,unsigned int nFormatSize,PaDeviceIndex nDevice)1911 int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice )
1912 {
1913 PaError ret;
1914 PaWasapiHostApiRepresentation *paWasapi;
1915 UINT32 size;
1916 PaDeviceIndex index;
1917
1918 if (pFormat == NULL)
1919 return paBadBufferPtr;
1920 if (nFormatSize <= 0)
1921 return paBufferTooSmall;
1922
1923 // Get API
1924 paWasapi = _GetHostApi(&ret);
1925 if (paWasapi == NULL)
1926 return ret;
1927
1928 // Get device index
1929 ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
1930 if (ret != paNoError)
1931 return ret;
1932
1933 // Validate index
1934 if ((UINT32)index >= paWasapi->deviceCount)
1935 return paInvalidDevice;
1936
1937 size = min(nFormatSize, (UINT32)sizeof(paWasapi->devInfo[ index ].DefaultFormat));
1938 memcpy(pFormat, &paWasapi->devInfo[ index ].DefaultFormat, size);
1939
1940 return size;
1941 }
1942
1943 // ------------------------------------------------------------------------------------------
PaWasapi_GetDeviceRole(PaDeviceIndex nDevice)1944 int PaWasapi_GetDeviceRole( PaDeviceIndex nDevice )
1945 {
1946 PaError ret;
1947 PaDeviceIndex index;
1948
1949 // Get API
1950 PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
1951 if (paWasapi == NULL)
1952 return paNotInitialized;
1953
1954 // Get device index
1955 ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
1956 if (ret != paNoError)
1957 return ret;
1958
1959 // Validate index
1960 if ((UINT32)index >= paWasapi->deviceCount)
1961 return paInvalidDevice;
1962
1963 return paWasapi->devInfo[ index ].formFactor;
1964 }
1965
1966 // ------------------------------------------------------------------------------------------
PaWasapi_GetFramesPerHostBuffer(PaStream * pStream,unsigned int * nInput,unsigned int * nOutput)1967 PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput, unsigned int *nOutput )
1968 {
1969 PaWasapiStream *stream = (PaWasapiStream *)pStream;
1970 if (stream == NULL)
1971 return paBadStreamPtr;
1972
1973 if (nInput != NULL)
1974 (*nInput) = stream->in.framesPerHostCallback;
1975
1976 if (nOutput != NULL)
1977 (*nOutput) = stream->out.framesPerHostCallback;
1978
1979 return paNoError;
1980 }
1981
1982 // ------------------------------------------------------------------------------------------
LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE * in)1983 static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in)
1984 {
1985 const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
1986 switch (old->wFormatTag)
1987 {
1988 case WAVE_FORMAT_EXTENSIBLE: {
1989
1990 PRINT(("wFormatTag =WAVE_FORMAT_EXTENSIBLE\n"));
1991
1992 if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1993 {
1994 PRINT(("SubFormat =KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));
1995 }
1996 else
1997 if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
1998 {
1999 PRINT(("SubFormat =KSDATAFORMAT_SUBTYPE_PCM\n"));
2000 }
2001 else
2002 {
2003 PRINT(("SubFormat =CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",
2004 in->SubFormat.Data1,
2005 in->SubFormat.Data2,
2006 in->SubFormat.Data3,
2007 (int)in->SubFormat.Data4[0],
2008 (int)in->SubFormat.Data4[1],
2009 (int)in->SubFormat.Data4[2],
2010 (int)in->SubFormat.Data4[3],
2011 (int)in->SubFormat.Data4[4],
2012 (int)in->SubFormat.Data4[5],
2013 (int)in->SubFormat.Data4[6],
2014 (int)in->SubFormat.Data4[7]));
2015 }
2016 PRINT(("Samples.wValidBitsPerSample =%d\n", in->Samples.wValidBitsPerSample));
2017 PRINT(("dwChannelMask =0x%X\n",in->dwChannelMask));
2018
2019 break; }
2020
2021 case WAVE_FORMAT_PCM: PRINT(("wFormatTag =WAVE_FORMAT_PCM\n")); break;
2022 case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag =WAVE_FORMAT_IEEE_FLOAT\n")); break;
2023 default:
2024 PRINT(("wFormatTag =UNKNOWN(%d)\n",old->wFormatTag)); break;
2025 }
2026
2027 PRINT(("nChannels =%d\n",old->nChannels));
2028 PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));
2029 PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));
2030 PRINT(("nBlockAlign =%d\n",old->nBlockAlign));
2031 PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));
2032 PRINT(("cbSize =%d\n",old->cbSize));
2033 }
2034
2035 // ------------------------------------------------------------------------------------------
WaveToPaFormat(const WAVEFORMATEXTENSIBLE * in)2036 static PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *in)
2037 {
2038 const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
2039
2040 switch (old->wFormatTag)
2041 {
2042 case WAVE_FORMAT_EXTENSIBLE: {
2043 if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
2044 {
2045 if (in->Samples.wValidBitsPerSample == 32)
2046 return paFloat32;
2047 }
2048 else
2049 if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
2050 {
2051 switch (old->wBitsPerSample)
2052 {
2053 case 32: return paInt32;
2054 case 24: return paInt24;
2055 case 8: return paUInt8;
2056 case 16: return paInt16;
2057 }
2058 }
2059 break; }
2060
2061 case WAVE_FORMAT_IEEE_FLOAT:
2062 return paFloat32;
2063
2064 case WAVE_FORMAT_PCM: {
2065 switch (old->wBitsPerSample)
2066 {
2067 case 32: return paInt32;
2068 case 24: return paInt24;
2069 case 8: return paUInt8;
2070 case 16: return paInt16;
2071 }
2072 break; }
2073 }
2074
2075 return paCustomFormat;
2076 }
2077
2078 // ------------------------------------------------------------------------------------------
MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE * wavex,const PaStreamParameters * params,double sampleRate)2079 static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStreamParameters *params,
2080 double sampleRate)
2081 {
2082 WORD bitsPerSample;
2083 WAVEFORMATEX *old;
2084 DWORD channelMask = 0;
2085 PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo;
2086
2087 // Get user assigned channel mask
2088 if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask))
2089 channelMask = streamInfo->channelMask;
2090
2091 // Convert PaSampleFormat to bits per sample
2092 if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)
2093 return paSampleFormatNotSupported;
2094
2095 memset(wavex, 0, sizeof(*wavex));
2096
2097 old = (WAVEFORMATEX *)wavex;
2098 old->nChannels = (WORD)params->channelCount;
2099 old->nSamplesPerSec = (DWORD)sampleRate;
2100 if ((old->wBitsPerSample = bitsPerSample) > 16)
2101 {
2102 old->wBitsPerSample = 32; // 20 or 24 bits must go in 32 bit containers (ints)
2103 }
2104 old->nBlockAlign = (old->nChannels * (old->wBitsPerSample/8));
2105 old->nAvgBytesPerSec = (old->nSamplesPerSec * old->nBlockAlign);
2106
2107 // WAVEFORMATEX
2108 if ((params->channelCount <= 2) && ((bitsPerSample == 16) || (bitsPerSample == 8)))
2109 {
2110 old->cbSize = 0;
2111 old->wFormatTag = WAVE_FORMAT_PCM;
2112 }
2113 // WAVEFORMATEXTENSIBLE
2114 else
2115 {
2116 old->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
2117 old->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
2118
2119 if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
2120 wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
2121 else
2122 wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
2123
2124 wavex->Samples.wValidBitsPerSample = bitsPerSample; //no extra padding!
2125
2126 // Set channel mask
2127 if (channelMask != 0)
2128 {
2129 wavex->dwChannelMask = channelMask;
2130 }
2131 else
2132 {
2133 switch (params->channelCount)
2134 {
2135 case 1: wavex->dwChannelMask = PAWIN_SPEAKER_MONO; break;
2136 case 2: wavex->dwChannelMask = PAWIN_SPEAKER_STEREO; break;
2137 case 3: wavex->dwChannelMask = PAWIN_SPEAKER_STEREO|SPEAKER_LOW_FREQUENCY; break;
2138 case 4: wavex->dwChannelMask = PAWIN_SPEAKER_QUAD; break;
2139 case 5: wavex->dwChannelMask = PAWIN_SPEAKER_QUAD|SPEAKER_LOW_FREQUENCY; break;
2140 #ifdef PAWIN_SPEAKER_5POINT1_SURROUND
2141 case 6: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1_SURROUND; break;
2142 #else
2143 case 6: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1; break;
2144 #endif
2145 #ifdef PAWIN_SPEAKER_5POINT1_SURROUND
2146 case 7: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1_SURROUND|SPEAKER_BACK_CENTER; break;
2147 #else
2148 case 7: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1|SPEAKER_BACK_CENTER; break;
2149 #endif
2150 #ifdef PAWIN_SPEAKER_7POINT1_SURROUND
2151 case 8: wavex->dwChannelMask = PAWIN_SPEAKER_7POINT1_SURROUND; break;
2152 #else
2153 case 8: wavex->dwChannelMask = PAWIN_SPEAKER_7POINT1; break;
2154 #endif
2155
2156 default: wavex->dwChannelMask = 0;
2157 }
2158 }
2159 }
2160 return paNoError;
2161 }
2162
2163 // ------------------------------------------------------------------------------------------
2164 /*static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
2165 {
2166 PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
2167 PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
2168 PA_DEBUG(( "chanelCount = %d\n", channelCount ));
2169
2170 pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
2171 pwfext->Format.nChannels = (WORD)channelCount;
2172 pwfext->Format.nSamplesPerSec = (DWORD)sampleRate;
2173 if(channelCount == 1)
2174 pwfext->dwChannelMask = PAWIN_SPEAKER_DIRECTOUT;
2175 else
2176 pwfext->dwChannelMask = PAWIN_SPEAKER_STEREO;
2177 if(sampleFormat == paFloat32)
2178 {
2179 pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
2180 pwfext->Format.wBitsPerSample = 32;
2181 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
2182 pwfext->Samples.wValidBitsPerSample = 32;
2183 pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
2184 }
2185 else if(sampleFormat == paInt32)
2186 {
2187 pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
2188 pwfext->Format.wBitsPerSample = 32;
2189 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
2190 pwfext->Samples.wValidBitsPerSample = 32;
2191 pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
2192 }
2193 else if(sampleFormat == paInt24)
2194 {
2195 pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
2196 pwfext->Format.wBitsPerSample = 32; // 24-bit in 32-bit int container
2197 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
2198 pwfext->Samples.wValidBitsPerSample = 24;
2199 pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
2200 }
2201 else if(sampleFormat == paInt16)
2202 {
2203 pwfext->Format.nBlockAlign = (WORD)(channelCount * 2);
2204 pwfext->Format.wBitsPerSample = 16;
2205 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
2206 pwfext->Samples.wValidBitsPerSample = 16;
2207 pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
2208 }
2209 pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
2210 }*/
2211
2212 // ------------------------------------------------------------------------------------------
GetClosestFormat(IAudioClient * myClient,double sampleRate,const PaStreamParameters * _params,AUDCLNT_SHAREMODE shareMode,WAVEFORMATEXTENSIBLE * outWavex,BOOL output)2213 static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
2214 const PaStreamParameters *_params, AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex,
2215 BOOL output)
2216 {
2217 PaError answer = paInvalidSampleRate;
2218 WAVEFORMATEX *sharedClosestMatch = NULL;
2219 HRESULT hr = !S_OK;
2220 PaStreamParameters params = (*_params);
2221 (void)output;
2222
2223 /* It was not noticed that 24-bit Input producing no output while device accepts this format.
2224 To fix this issue let's ask for 32-bits and let PA converters convert host 32-bit data
2225 to 24-bit for user-space. The bug concerns Vista, if Windows 7 supports 24-bits for Input
2226 please report to PortAudio developers to exclude Windows 7.
2227 */
2228 /*if ((params.sampleFormat == paInt24) && (output == FALSE))
2229 params.sampleFormat = paFloat32;*/ // <<< The silence was due to missing Int32_To_Int24_Dither implementation
2230
2231 MakeWaveFormatFromParams(outWavex, ¶ms, sampleRate);
2232
2233 hr = IAudioClient_IsFormatSupported(myClient, shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
2234 if (hr == S_OK)
2235 answer = paFormatIsSupported;
2236 else
2237 if (sharedClosestMatch)
2238 {
2239 WORD bitsPerSample;
2240 WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;
2241
2242 GUID subf_guid = GUID_NULL;
2243 if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
2244 {
2245 memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEXTENSIBLE));
2246 subf_guid = ext->SubFormat;
2247 }
2248 else
2249 memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEX));
2250
2251 CoTaskMemFree(sharedClosestMatch);
2252
2253 // Make supported by default
2254 answer = paFormatIsSupported;
2255
2256 // Validate SampleRate
2257 if ((DWORD)sampleRate != outWavex->Format.nSamplesPerSec)
2258 return paInvalidSampleRate;
2259
2260 // Validate Channel count
2261 if ((WORD)params.channelCount != outWavex->Format.nChannels)
2262 {
2263 // If mono, then driver does not support 1 channel, we use internal workaround
2264 // of tiny software mixing functionality, e.g. we provide to user buffer 1 channel
2265 // but then mix into 2 for device buffer
2266 if ((params.channelCount == 1) && (outWavex->Format.nChannels == 2))
2267 return paFormatIsSupported;
2268 else
2269 return paInvalidChannelCount;
2270 }
2271
2272 // Validate Sample format
2273 if ((bitsPerSample = PaSampleFormatToBitsPerSample(params.sampleFormat)) == 0)
2274 return paSampleFormatNotSupported;
2275
2276 // Validate Sample format: bit size (WASAPI does not limit 'bit size')
2277 //if (bitsPerSample != outWavex->Format.wBitsPerSample)
2278 // return paSampleFormatNotSupported;
2279
2280 // Validate Sample format: paFloat32 (WASAPI does not limit 'bit type')
2281 //if ((params->sampleFormat == paFloat32) && (subf_guid != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
2282 // return paSampleFormatNotSupported;
2283
2284 // Validate Sample format: paInt32 (WASAPI does not limit 'bit type')
2285 //if ((params->sampleFormat == paInt32) && (subf_guid != KSDATAFORMAT_SUBTYPE_PCM))
2286 // return paSampleFormatNotSupported;
2287 }
2288 else
2289 {
2290 static const int BestToWorst[] = { paFloat32, paInt24, paInt16 };
2291 int i;
2292
2293 // Try combination stereo and we will use built-in mono-stereo mixer then
2294 if (params.channelCount == 1)
2295 {
2296 WAVEFORMATEXTENSIBLE stereo = { 0 };
2297
2298 PaStreamParameters stereo_params = params;
2299 stereo_params.channelCount = 2;
2300
2301 MakeWaveFormatFromParams(&stereo, &stereo_params, sampleRate);
2302
2303 hr = IAudioClient_IsFormatSupported(myClient, shareMode, &stereo.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
2304 if (hr == S_OK)
2305 {
2306 memcpy(outWavex, &stereo, sizeof(WAVEFORMATEXTENSIBLE));
2307 CoTaskMemFree(sharedClosestMatch);
2308 return (answer = paFormatIsSupported);
2309 }
2310
2311 // Try selecting suitable sample type
2312 for (i = 0; i < STATIC_ARRAY_SIZE(BestToWorst); ++i)
2313 {
2314 WAVEFORMATEXTENSIBLE sample = { 0 };
2315
2316 PaStreamParameters sample_params = stereo_params;
2317 sample_params.sampleFormat = BestToWorst[i];
2318
2319 MakeWaveFormatFromParams(&sample, &sample_params, sampleRate);
2320
2321 hr = IAudioClient_IsFormatSupported(myClient, shareMode, &sample.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
2322 if (hr == S_OK)
2323 {
2324 memcpy(outWavex, &sample, sizeof(WAVEFORMATEXTENSIBLE));
2325 CoTaskMemFree(sharedClosestMatch);
2326 return (answer = paFormatIsSupported);
2327 }
2328 }
2329 }
2330
2331 // Try selecting suitable sample type
2332 for (i = 0; i < STATIC_ARRAY_SIZE(BestToWorst); ++i)
2333 {
2334 WAVEFORMATEXTENSIBLE spfmt = { 0 };
2335
2336 PaStreamParameters spfmt_params = params;
2337 spfmt_params.sampleFormat = BestToWorst[i];
2338
2339 MakeWaveFormatFromParams(&spfmt, &spfmt_params, sampleRate);
2340
2341 hr = IAudioClient_IsFormatSupported(myClient, shareMode, &spfmt.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
2342 if (hr == S_OK)
2343 {
2344 memcpy(outWavex, &spfmt, sizeof(WAVEFORMATEXTENSIBLE));
2345 CoTaskMemFree(sharedClosestMatch);
2346 answer = paFormatIsSupported;
2347 break;
2348 }
2349 }
2350
2351 // Nothing helped
2352 LogHostError(hr);
2353 }
2354
2355 return answer;
2356 }
2357
2358 // ------------------------------------------------------------------------------------------
IsStreamParamsValid(struct PaUtilHostApiRepresentation * hostApi,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate)2359 static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,
2360 const PaStreamParameters *inputParameters,
2361 const PaStreamParameters *outputParameters,
2362 double sampleRate)
2363 {
2364 if (hostApi == NULL)
2365 return paHostApiNotFound;
2366 if ((UINT32)sampleRate == 0)
2367 return paInvalidSampleRate;
2368
2369 if (inputParameters != NULL)
2370 {
2371 /* all standard sample formats are supported by the buffer adapter,
2372 this implementation doesn't support any custom sample formats */
2373 if (inputParameters->sampleFormat & paCustomFormat)
2374 return paSampleFormatNotSupported;
2375
2376 /* unless alternate device specification is supported, reject the use of
2377 paUseHostApiSpecificDeviceSpecification */
2378 if (inputParameters->device == paUseHostApiSpecificDeviceSpecification)
2379 return paInvalidDevice;
2380
2381 /* check that input device can support inputChannelCount */
2382 if (inputParameters->channelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels)
2383 return paInvalidChannelCount;
2384
2385 /* validate inputStreamInfo */
2386 if (inputParameters->hostApiSpecificStreamInfo)
2387 {
2388 PaWasapiStreamInfo *inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
2389 if ((inputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||
2390 (inputStreamInfo->version != 1) ||
2391 (inputStreamInfo->hostApiType != paWASAPI))
2392 {
2393 return paIncompatibleHostApiSpecificStreamInfo;
2394 }
2395 }
2396
2397 return paNoError;
2398 }
2399
2400 if (outputParameters != NULL)
2401 {
2402 /* all standard sample formats are supported by the buffer adapter,
2403 this implementation doesn't support any custom sample formats */
2404 if (outputParameters->sampleFormat & paCustomFormat)
2405 return paSampleFormatNotSupported;
2406
2407 /* unless alternate device specification is supported, reject the use of
2408 paUseHostApiSpecificDeviceSpecification */
2409 if (outputParameters->device == paUseHostApiSpecificDeviceSpecification)
2410 return paInvalidDevice;
2411
2412 /* check that output device can support outputChannelCount */
2413 if (outputParameters->channelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels)
2414 return paInvalidChannelCount;
2415
2416 /* validate outputStreamInfo */
2417 if(outputParameters->hostApiSpecificStreamInfo)
2418 {
2419 PaWasapiStreamInfo *outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
2420 if ((outputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||
2421 (outputStreamInfo->version != 1) ||
2422 (outputStreamInfo->hostApiType != paWASAPI))
2423 {
2424 return paIncompatibleHostApiSpecificStreamInfo;
2425 }
2426 }
2427
2428 return paNoError;
2429 }
2430
2431 return (inputParameters || outputParameters ? paNoError : paInternalError);
2432 }
2433
2434 // ------------------------------------------------------------------------------------------
IsFormatSupported(struct PaUtilHostApiRepresentation * hostApi,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate)2435 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
2436 const PaStreamParameters *inputParameters,
2437 const PaStreamParameters *outputParameters,
2438 double sampleRate )
2439 {
2440 IAudioClient *tmpClient = NULL;
2441 PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
2442 PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
2443
2444 // Validate PaStreamParameters
2445 PaError error;
2446 if ((error = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
2447 return error;
2448
2449 if (inputParameters != NULL)
2450 {
2451 WAVEFORMATEXTENSIBLE wavex;
2452 HRESULT hr;
2453 PaError answer;
2454 AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
2455 inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
2456
2457 if (inputStreamInfo && (inputStreamInfo->flags & paWinWasapiExclusive))
2458 shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
2459
2460 hr = ActivateAudioInterface(&paWasapi->devInfo[inputParameters->device], &tmpClient);
2461 if (hr != S_OK)
2462 {
2463 LogHostError(hr);
2464 return paInvalidDevice;
2465 }
2466
2467 answer = GetClosestFormat(tmpClient, sampleRate, inputParameters, shareMode, &wavex, FALSE);
2468 SAFE_RELEASE(tmpClient);
2469
2470 if (answer != paFormatIsSupported)
2471 return answer;
2472 }
2473
2474 if (outputParameters != NULL)
2475 {
2476 HRESULT hr;
2477 WAVEFORMATEXTENSIBLE wavex;
2478 PaError answer;
2479 AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
2480 outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
2481
2482 if (outputStreamInfo && (outputStreamInfo->flags & paWinWasapiExclusive))
2483 shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
2484
2485 hr = ActivateAudioInterface(&paWasapi->devInfo[outputParameters->device], &tmpClient);
2486 if (hr != S_OK)
2487 {
2488 LogHostError(hr);
2489 return paInvalidDevice;
2490 }
2491
2492 answer = GetClosestFormat(tmpClient, sampleRate, outputParameters, shareMode, &wavex, TRUE);
2493 SAFE_RELEASE(tmpClient);
2494
2495 if (answer != paFormatIsSupported)
2496 return answer;
2497 }
2498
2499 return paFormatIsSupported;
2500 }
2501
2502 // ------------------------------------------------------------------------------------------
PaUtil_GetFramesPerHostBuffer(PaUint32 userFramesPerBuffer,PaTime suggestedLatency,double sampleRate,PaUint32 TimerJitterMs)2503 static PaUint32 PaUtil_GetFramesPerHostBuffer(PaUint32 userFramesPerBuffer, PaTime suggestedLatency, double sampleRate, PaUint32 TimerJitterMs)
2504 {
2505 PaUint32 frames = userFramesPerBuffer + max( userFramesPerBuffer, (PaUint32)(suggestedLatency * sampleRate) );
2506 frames += (PaUint32)((sampleRate * 0.001) * TimerJitterMs);
2507 return frames;
2508 }
2509
2510 // ------------------------------------------------------------------------------------------
_RecalculateBuffersCount(PaWasapiSubStream * sub,UINT32 userFramesPerBuffer,UINT32 framesPerLatency,BOOL fullDuplex)2511 static void _RecalculateBuffersCount(PaWasapiSubStream *sub, UINT32 userFramesPerBuffer, UINT32 framesPerLatency, BOOL fullDuplex)
2512 {
2513 // Count buffers (must be at least 1)
2514 sub->buffers = (userFramesPerBuffer ? framesPerLatency / userFramesPerBuffer : 0);
2515 if (sub->buffers == 0)
2516 sub->buffers = 1;
2517
2518 // Determine amount of buffers used:
2519 // - Full-duplex mode will lead to period difference, thus only 1.
2520 // - Input mode, only 1, as WASAPI allows extraction of only 1 packet.
2521 // - For Shared mode we use double buffering.
2522 if ((sub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) || fullDuplex)
2523 {
2524 // Exclusive mode does not allow >1 buffers be used for Event interface, e.g. GetBuffer
2525 // call must acquire max buffer size and it all must be processed.
2526 if (sub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)
2527 sub->userBufferAndHostMatch = 1;
2528
2529 // Use paUtilBoundedHostBufferSize because exclusive mode will starve and produce
2530 // bad quality of audio
2531 sub->buffers = 1;
2532 }
2533 }
2534
2535 // ------------------------------------------------------------------------------------------
_CalculateAlignedPeriod(PaWasapiSubStream * pSub,UINT32 * nFramesPerLatency,ALIGN_FUNC pAlignFunc)2536 static void _CalculateAlignedPeriod(PaWasapiSubStream *pSub, UINT32 *nFramesPerLatency,
2537 ALIGN_FUNC pAlignFunc)
2538 {
2539 // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only.
2540 // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will
2541 // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary
2542 // for Exclusive mode only! when audio data is feeded directly to hardware.
2543 if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
2544 {
2545 (*nFramesPerLatency) = AlignFramesPerBuffer((*nFramesPerLatency),
2546 pSub->wavex.Format.nSamplesPerSec, pSub->wavex.Format.nBlockAlign, pAlignFunc);
2547 }
2548
2549 // Calculate period
2550 pSub->period = MakeHnsPeriod((*nFramesPerLatency), pSub->wavex.Format.nSamplesPerSec);
2551 }
2552
2553 // ------------------------------------------------------------------------------------------
CreateAudioClient(PaWasapiStream * pStream,PaWasapiSubStream * pSub,BOOL output,PaError * pa_error)2554 static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSub, BOOL output, PaError *pa_error)
2555 {
2556 PaError error;
2557 HRESULT hr;
2558
2559 const PaWasapiDeviceInfo *pInfo = pSub->params.device_info;
2560 const PaStreamParameters *params = &pSub->params.stream_params;
2561 UINT32 framesPerLatency = pSub->params.frames_per_buffer;
2562 double sampleRate = pSub->params.sample_rate;
2563 //BOOL blocking = pSub->params.blocking;
2564 BOOL fullDuplex = pSub->params.full_duplex;
2565
2566 const UINT32 userFramesPerBuffer = framesPerLatency;
2567 IAudioClient *audioClient = NULL;
2568
2569 // Assume default failure due to some reason
2570 (*pa_error) = paInvalidDevice;
2571
2572 // Validate parameters
2573 if (!pSub || !pInfo || !params)
2574 {
2575 (*pa_error) = paBadStreamPtr;
2576 return E_POINTER;
2577 }
2578 if ((UINT32)sampleRate == 0)
2579 {
2580 (*pa_error) = paInvalidSampleRate;
2581 return E_INVALIDARG;
2582 }
2583
2584 // Get the audio client
2585 hr = ActivateAudioInterface(pInfo, &audioClient);
2586 if (hr != S_OK)
2587 {
2588 (*pa_error) = paInsufficientMemory;
2589 LogHostError(hr);
2590 goto done;
2591 }
2592
2593 // Get closest format
2594 if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)
2595 {
2596 (*pa_error) = error;
2597 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2598 goto done; // fail, format not supported
2599 }
2600
2601 // Check for Mono <<>> Stereo workaround
2602 if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))
2603 {
2604 /*if (blocking)
2605 {
2606 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2607 goto done; // fail, blocking mode not supported
2608 }*/
2609
2610 // select mixer
2611 pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
2612 if (pSub->monoMixer == NULL)
2613 {
2614 (*pa_error) = paInvalidChannelCount;
2615 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2616 goto done; // fail, no mixer for format
2617 }
2618 }
2619
2620 #if 0
2621 // Add suggestd latency
2622 framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavex.Format.nSamplesPerSec);
2623 #else
2624 // Calculate host buffer size
2625 if ((pSub->shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE) &&
2626 (!pSub->streamFlags || ((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
2627 {
2628 framesPerLatency = PaUtil_GetFramesPerHostBuffer(userFramesPerBuffer,
2629 params->suggestedLatency, pSub->wavex.Format.nSamplesPerSec, 0/*,
2630 (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/);
2631 }
2632 else
2633 {
2634 REFERENCE_TIME overall;
2635
2636 // Work 1:1 with user buffer (only polling allows to use >1)
2637 framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavex.Format.nSamplesPerSec);
2638
2639 // Use Polling if overall latency is > 5ms as it allows to use 100% CPU in a callback,
2640 // or user specified latency parameter
2641 overall = MakeHnsPeriod(framesPerLatency, pSub->wavex.Format.nSamplesPerSec);
2642 if ((overall >= (106667*2)/*21.33ms*/) || ((INT32)(params->suggestedLatency*100000.0) != 0/*0.01 msec granularity*/))
2643 {
2644 framesPerLatency = PaUtil_GetFramesPerHostBuffer(userFramesPerBuffer,
2645 params->suggestedLatency, pSub->wavex.Format.nSamplesPerSec, 0/*,
2646 (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/);
2647
2648 // Use Polling interface
2649 pSub->streamFlags &= ~AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
2650 PRINT(("WASAPI: CreateAudioClient: forcing POLL mode\n"));
2651 }
2652 }
2653 #endif
2654
2655 // For full-duplex output resize buffer to be the same as for input
2656 if (output && fullDuplex)
2657 framesPerLatency = pStream->in.framesPerHostCallback;
2658
2659 // Avoid 0 frames
2660 if (framesPerLatency == 0)
2661 framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec);
2662
2663 // Exclusive Input stream renders data in 6 packets, we must set then the size of
2664 // single packet, total buffer size, e.g. required latency will be PacketSize * 6
2665 if (!output && (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE))
2666 {
2667 // Do it only for Polling mode
2668 if ((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)
2669 {
2670 framesPerLatency /= WASAPI_PACKETS_PER_INPUT_BUFFER;
2671 }
2672 }
2673
2674 // Calculate aligned period
2675 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
2676
2677 /*! Enforce min/max period for device in Shared mode to avoid bad audio quality.
2678 Avoid doing so for Exclusive mode as alignment will suffer.
2679 */
2680 if (pSub->shareMode == AUDCLNT_SHAREMODE_SHARED)
2681 {
2682 if (pSub->period < pInfo->DefaultDevicePeriod)
2683 {
2684 pSub->period = pInfo->DefaultDevicePeriod;
2685 // Recalculate aligned period
2686 framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
2687 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
2688 }
2689 }
2690 else
2691 {
2692 if (pSub->period < pInfo->MinimumDevicePeriod)
2693 {
2694 pSub->period = pInfo->MinimumDevicePeriod;
2695 // Recalculate aligned period
2696 framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
2697 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_FWD);
2698 }
2699 }
2700
2701 /*! Windows 7 does not allow to set latency lower than minimal device period and will
2702 return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista we enforce the same behavior
2703 manually for unified behavior on all platforms.
2704 */
2705 {
2706 /*! AUDCLNT_E_BUFFER_SIZE_ERROR: Applies to Windows 7 and later.
2707 Indicates that the buffer duration value requested by an exclusive-mode client is
2708 out of range. The requested duration value for pull mode must not be greater than
2709 500 milliseconds; for push mode the duration value must not be greater than 2 seconds.
2710 */
2711 if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
2712 {
2713 static const REFERENCE_TIME MAX_BUFFER_EVENT_DURATION = 500 * 10000;
2714 static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION = 2000 * 10000;
2715
2716 if (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) // pull mode, max 500ms
2717 {
2718 if (pSub->period > MAX_BUFFER_EVENT_DURATION)
2719 {
2720 pSub->period = MAX_BUFFER_EVENT_DURATION;
2721 // Recalculate aligned period
2722 framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
2723 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
2724 }
2725 }
2726 else // push mode, max 2000ms
2727 {
2728 if (pSub->period > MAX_BUFFER_POLL_DURATION)
2729 {
2730 pSub->period = MAX_BUFFER_POLL_DURATION;
2731 // Recalculate aligned period
2732 framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
2733 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
2734 }
2735 }
2736 }
2737 }
2738
2739 // Set Raw mode (applicable only to IAudioClient2)
2740 #ifdef __IAudioClient2_INTERFACE_DEFINED__
2741 if (GetAudioClientVersion() >= 2)
2742 {
2743 pa_AudioClientProperties audioProps = { 0 };
2744 audioProps.cbSize = sizeof(pa_AudioClientProperties);
2745 audioProps.bIsOffload = FALSE;
2746 audioProps.eCategory = (AUDIO_STREAM_CATEGORY)pSub->params.wasapi_params.streamCategory;
2747 switch (pSub->params.wasapi_params.streamOption)
2748 {
2749 case eStreamOptionRaw:
2750 if (GetWindowsVersion() >= WINDOWS_8_1_SERVER2012R2)
2751 audioProps.Options = pa_AUDCLNT_STREAMOPTIONS_RAW;
2752 break;
2753 case eStreamOptionMatchFormat:
2754 if (GetWindowsVersion() >= WINDOWS_10_SERVER2016)
2755 audioProps.Options = pa_AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;
2756 break;
2757 }
2758
2759 hr = IAudioClient2_SetClientProperties((IAudioClient2 *)audioClient, (AudioClientProperties *)&audioProps);
2760 if (hr != S_OK)
2761 {
2762 PRINT(("WASAPI: IAudioClient2_SetClientProperties(Category = %d, Options = %d) failed with error = %08X\n", audioProps.eCategory, audioProps.Options, (UINT32)hr));
2763 }
2764 else
2765 {
2766 PRINT(("WASAPI: IAudioClient2 set properties: IsOffload = %d, Category = %d, Options = %d\n", audioProps.bIsOffload, audioProps.eCategory, audioProps.Options));
2767 }
2768 }
2769 #endif
2770
2771 // Open the stream and associate it with an audio session
2772 hr = IAudioClient_Initialize(audioClient,
2773 pSub->shareMode,
2774 pSub->streamFlags,
2775 pSub->period,
2776 (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
2777 &pSub->wavex.Format,
2778 NULL);
2779
2780 /*! WASAPI is tricky on large device buffer, sometimes 2000ms can be allocated sometimes
2781 less. There is no known guaranteed level thus we make subsequent tries by decreasing
2782 buffer by 100ms per try.
2783 */
2784 while ((hr == E_OUTOFMEMORY) && (pSub->period > (100 * 10000)))
2785 {
2786 PRINT(("WASAPI: CreateAudioClient: decreasing buffer size to %d milliseconds\n", (pSub->period / 10000)));
2787
2788 // Decrease by 100ms and try again
2789 pSub->period -= (100 * 10000);
2790
2791 // Recalculate aligned period
2792 framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
2793 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
2794
2795 // Release the previous allocations
2796 SAFE_RELEASE(audioClient);
2797
2798 // Create a new audio client
2799 hr = ActivateAudioInterface(pInfo, &audioClient);
2800 if (hr != S_OK)
2801 {
2802 (*pa_error) = paInsufficientMemory;
2803 LogHostError(hr);
2804 goto done;
2805 }
2806
2807 // Open the stream and associate it with an audio session
2808 hr = IAudioClient_Initialize(audioClient,
2809 pSub->shareMode,
2810 pSub->streamFlags,
2811 pSub->period,
2812 (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
2813 &pSub->wavex.Format,
2814 NULL);
2815 }
2816
2817 /*! WASAPI buffer size failure. Fallback to using default size.
2818 */
2819 if (hr == AUDCLNT_E_BUFFER_SIZE_ERROR)
2820 {
2821 // Use default
2822 pSub->period = pInfo->DefaultDevicePeriod;
2823
2824 PRINT(("WASAPI: CreateAudioClient: correcting buffer size to device default\n"));
2825
2826 // Release the previous allocations
2827 SAFE_RELEASE(audioClient);
2828
2829 // Create a new audio client
2830 hr = ActivateAudioInterface(pInfo, &audioClient);
2831 if (hr != S_OK)
2832 {
2833 (*pa_error) = paInsufficientMemory;
2834 LogHostError(hr);
2835 goto done;
2836 }
2837
2838 // Open the stream and associate it with an audio session
2839 hr = IAudioClient_Initialize(audioClient,
2840 pSub->shareMode,
2841 pSub->streamFlags,
2842 pSub->period,
2843 (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
2844 &pSub->wavex.Format,
2845 NULL);
2846 }
2847
2848 /*! If the requested buffer size is not aligned. Can be triggered by Windows 7 and up.
2849 Should not be be triggered ever as we do align buffers always with _CalculateAlignedPeriod.
2850 */
2851 if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
2852 {
2853 UINT32 frames = 0;
2854
2855 // Get the next aligned frame
2856 hr = IAudioClient_GetBufferSize(audioClient, &frames);
2857 if (hr != S_OK)
2858 {
2859 (*pa_error) = paInvalidDevice;
2860 LogHostError(hr);
2861 goto done;
2862 }
2863
2864 PRINT(("WASAPI: CreateAudioClient: aligning buffer size to % frames\n", frames));
2865
2866 // Release the previous allocations
2867 SAFE_RELEASE(audioClient);
2868
2869 // Create a new audio client
2870 hr = ActivateAudioInterface(pInfo, &audioClient);
2871 if (hr != S_OK)
2872 {
2873 (*pa_error) = paInsufficientMemory;
2874 LogHostError(hr);
2875 goto done;
2876 }
2877
2878 // Get closest format
2879 if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)
2880 {
2881 (*pa_error) = error;
2882 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); // fail, format not supported
2883 goto done;
2884 }
2885
2886 // Check for Mono >> Stereo workaround
2887 if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))
2888 {
2889 /*if (blocking)
2890 {
2891 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2892 goto done; // fail, blocking mode not supported
2893 }*/
2894
2895 // Select mixer
2896 pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
2897 if (pSub->monoMixer == NULL)
2898 {
2899 (*pa_error) = paInvalidChannelCount;
2900 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2901 goto done; // fail, no mixer for format
2902 }
2903 }
2904
2905 // Calculate period
2906 pSub->period = MakeHnsPeriod(frames, pSub->wavex.Format.nSamplesPerSec);
2907
2908 // Open the stream and associate it with an audio session
2909 hr = IAudioClient_Initialize(audioClient,
2910 pSub->shareMode,
2911 pSub->streamFlags,
2912 pSub->period,
2913 (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
2914 &pSub->wavex.Format,
2915 NULL);
2916 if (hr != S_OK)
2917 {
2918 (*pa_error) = paInvalidDevice;
2919 LogHostError(hr);
2920 goto done;
2921 }
2922 }
2923 else
2924 if (hr != S_OK)
2925 {
2926 (*pa_error) = paInvalidDevice;
2927 LogHostError(hr);
2928 goto done;
2929 }
2930
2931 // Set client
2932 pSub->clientParent = audioClient;
2933 IAudioClient_AddRef(pSub->clientParent);
2934
2935 // Recalculate buffers count
2936 _RecalculateBuffersCount(pSub,
2937 userFramesPerBuffer,
2938 MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec),
2939 fullDuplex);
2940
2941 // No error, client is succesfully created
2942 (*pa_error) = paNoError;
2943
2944 done:
2945
2946 // Clean up
2947 SAFE_RELEASE(audioClient);
2948 return hr;
2949 }
2950
2951 // ------------------------------------------------------------------------------------------
ActivateAudioClientOutput(PaWasapiStream * stream)2952 static PaError ActivateAudioClientOutput(PaWasapiStream *stream)
2953 {
2954 HRESULT hr;
2955 PaError result;
2956
2957 UINT32 maxBufferSize = 0;
2958 PaTime buffer_latency = 0;
2959 UINT32 framesPerBuffer = stream->out.params.frames_per_buffer;
2960
2961 // Create Audio client
2962 hr = CreateAudioClient(stream, &stream->out, TRUE, &result);
2963 if (hr != S_OK)
2964 {
2965 LogPaError(result);
2966 goto error;
2967 }
2968 LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
2969
2970 // Activate volume
2971 stream->outVol = NULL;
2972 /*hr = info->device->Activate(
2973 __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
2974 (void**)&stream->outVol);
2975 if (hr != S_OK)
2976 return paInvalidDevice;*/
2977
2978 // Get max possible buffer size to check if it is not less than that we request
2979 hr = IAudioClient_GetBufferSize(stream->out.clientParent, &maxBufferSize);
2980 if (hr != S_OK)
2981 {
2982 LogHostError(hr);
2983 LogPaError(result = paInvalidDevice);
2984 goto error;
2985 }
2986
2987 // Correct buffer to max size if it maxed out result of GetBufferSize
2988 stream->out.bufferSize = maxBufferSize;
2989
2990 // Get interface latency (actually uneeded as we calculate latency from the size
2991 // of maxBufferSize).
2992 hr = IAudioClient_GetStreamLatency(stream->out.clientParent, &stream->out.deviceLatency);
2993 if (hr != S_OK)
2994 {
2995 LogHostError(hr);
2996 LogPaError(result = paInvalidDevice);
2997 goto error;
2998 }
2999 //stream->out.latencySeconds = nano100ToSeconds(stream->out.deviceLatency);
3000
3001 // Number of frames that are required at each period
3002 stream->out.framesPerHostCallback = maxBufferSize;
3003
3004 // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
3005 stream->out.framesPerBuffer =
3006 (stream->out.userBufferAndHostMatch ? stream->out.framesPerHostCallback : framesPerBuffer);
3007
3008 // Calculate buffer latency
3009 buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec;
3010
3011 // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
3012 stream->out.latencySeconds = buffer_latency;
3013
3014 PRINT(("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latencySeconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->out.params.wow64_workaround ? "YES" : "NO"), (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
3015
3016 return paNoError;
3017
3018 error:
3019
3020 return result;
3021 }
3022
3023 // ------------------------------------------------------------------------------------------
ActivateAudioClientInput(PaWasapiStream * stream)3024 static PaError ActivateAudioClientInput(PaWasapiStream *stream)
3025 {
3026 HRESULT hr;
3027 PaError result;
3028
3029 UINT32 maxBufferSize = 0;
3030 PaTime buffer_latency = 0;
3031 UINT32 framesPerBuffer = stream->in.params.frames_per_buffer;
3032
3033 // Create Audio client
3034 hr = CreateAudioClient(stream, &stream->in, FALSE, &result);
3035 if (hr != S_OK)
3036 {
3037 LogPaError(result);
3038 goto error;
3039 }
3040 LogWAVEFORMATEXTENSIBLE(&stream->in.wavex);
3041
3042 // Create volume mgr
3043 stream->inVol = NULL;
3044 /*hr = info->device->Activate(
3045 __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
3046 (void**)&stream->inVol);
3047 if (hr != S_OK)
3048 return paInvalidDevice;*/
3049
3050 // Get max possible buffer size to check if it is not less than that we request
3051 hr = IAudioClient_GetBufferSize(stream->in.clientParent, &maxBufferSize);
3052 if (hr != S_OK)
3053 {
3054 LogHostError(hr);
3055 LogPaError(result = paInvalidDevice);
3056 goto error;
3057 }
3058
3059 // Correct buffer to max size if it maxed out result of GetBufferSize
3060 stream->in.bufferSize = maxBufferSize;
3061
3062 // Get interface latency (actually uneeded as we calculate latency from the size
3063 // of maxBufferSize).
3064 hr = IAudioClient_GetStreamLatency(stream->in.clientParent, &stream->in.deviceLatency);
3065 if (hr != S_OK)
3066 {
3067 LogHostError(hr);
3068 LogPaError(result = paInvalidDevice);
3069 goto error;
3070 }
3071 //stream->in.latencySeconds = nano100ToSeconds(stream->in.deviceLatency);
3072
3073 // Number of frames that are required at each period
3074 stream->in.framesPerHostCallback = maxBufferSize;
3075
3076 // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
3077 stream->in.framesPerBuffer =
3078 (stream->in.userBufferAndHostMatch ? stream->in.framesPerHostCallback : framesPerBuffer);
3079
3080 // Calculate buffer latency
3081 buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec;
3082
3083 // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
3084 stream->in.latencySeconds = buffer_latency;
3085
3086 PRINT(("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latencySeconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->in.params.wow64_workaround ? "YES" : "NO"), (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
3087
3088 return paNoError;
3089
3090 error:
3091
3092 return result;
3093 }
3094
3095 // ------------------------------------------------------------------------------------------
OpenStream(struct PaUtilHostApiRepresentation * hostApi,PaStream ** s,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate,unsigned long framesPerBuffer,PaStreamFlags streamFlags,PaStreamCallback * streamCallback,void * userData)3096 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
3097 PaStream** s,
3098 const PaStreamParameters *inputParameters,
3099 const PaStreamParameters *outputParameters,
3100 double sampleRate,
3101 unsigned long framesPerBuffer,
3102 PaStreamFlags streamFlags,
3103 PaStreamCallback *streamCallback,
3104 void *userData )
3105 {
3106 PaError result = paNoError;
3107 HRESULT hr;
3108 PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
3109 PaWasapiStream *stream = NULL;
3110 int inputChannelCount, outputChannelCount;
3111 PaSampleFormat inputSampleFormat, outputSampleFormat;
3112 PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
3113 PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
3114 PaWasapiDeviceInfo *info = NULL;
3115 ULONG framesPerHostCallback;
3116 PaUtilHostBufferSizeMode bufferMode;
3117 const BOOL fullDuplex = ((inputParameters != NULL) && (outputParameters != NULL));
3118
3119 // validate PaStreamParameters
3120 if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
3121 return LogPaError(result);
3122
3123 // Validate platform specific flags
3124 if ((streamFlags & paPlatformSpecificFlags) != 0)
3125 {
3126 LogPaError(result = paInvalidFlag); /* unexpected platform specific flag */
3127 goto error;
3128 }
3129
3130 // Allocate memory for PaWasapiStream
3131 if ((stream = (PaWasapiStream *)PaUtil_AllocateMemory(sizeof(PaWasapiStream))) == NULL)
3132 {
3133 LogPaError(result = paInsufficientMemory);
3134 goto error;
3135 }
3136
3137 // Default thread priority is Audio: for exclusive mode we will use Pro Audio.
3138 stream->nThreadPriority = eThreadPriorityAudio;
3139
3140 // Set default number of frames: paFramesPerBufferUnspecified
3141 if (framesPerBuffer == paFramesPerBufferUnspecified)
3142 {
3143 UINT32 framesPerBufferIn = 0, framesPerBufferOut = 0;
3144 if (inputParameters != NULL)
3145 {
3146 info = &paWasapi->devInfo[inputParameters->device];
3147 framesPerBufferIn = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate);
3148 }
3149 if (outputParameters != NULL)
3150 {
3151 info = &paWasapi->devInfo[outputParameters->device];
3152 framesPerBufferOut = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate);
3153 }
3154 // choosing maximum default size
3155 framesPerBuffer = max(framesPerBufferIn, framesPerBufferOut);
3156 }
3157 if (framesPerBuffer == 0)
3158 framesPerBuffer = ((UINT32)sampleRate / 100) * 2;
3159
3160 // Try create device: Input
3161 if (inputParameters != NULL)
3162 {
3163 inputChannelCount = inputParameters->channelCount;
3164 inputSampleFormat = inputParameters->sampleFormat;
3165 info = &paWasapi->devInfo[inputParameters->device];
3166
3167 // default Shared Mode
3168 stream->in.shareMode = AUDCLNT_SHAREMODE_SHARED;
3169
3170 // PaWasapiStreamInfo
3171 if (inputParameters->hostApiSpecificStreamInfo != NULL)
3172 {
3173 memcpy(&stream->in.params.wasapi_params, inputParameters->hostApiSpecificStreamInfo, min(sizeof(stream->in.params.wasapi_params), ((PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo)->size));
3174 stream->in.params.wasapi_params.size = sizeof(stream->in.params.wasapi_params);
3175
3176 stream->in.params.stream_params.hostApiSpecificStreamInfo = &stream->in.params.wasapi_params;
3177 inputStreamInfo = &stream->in.params.wasapi_params;
3178
3179 stream->in.flags = inputStreamInfo->flags;
3180
3181 // Exclusive Mode
3182 if (inputStreamInfo->flags & paWinWasapiExclusive)
3183 {
3184 // Boost thread priority
3185 stream->nThreadPriority = eThreadPriorityProAudio;
3186 // Make Exclusive
3187 stream->in.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
3188 }
3189
3190 // explicit thread priority level
3191 if (inputStreamInfo->flags & paWinWasapiThreadPriority)
3192 {
3193 if ((inputStreamInfo->threadPriority > eThreadPriorityNone) &&
3194 (inputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
3195 stream->nThreadPriority = inputStreamInfo->threadPriority;
3196 }
3197 }
3198
3199 // Choose processing mode
3200 stream->in.streamFlags = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0);
3201 if (paWasapi->useWOW64Workaround)
3202 stream->in.streamFlags = 0; // polling interface
3203 else
3204 if (streamCallback == NULL)
3205 stream->in.streamFlags = 0; // polling interface
3206 else
3207 if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling))
3208 stream->in.streamFlags = 0; // polling interface
3209 else
3210 if (fullDuplex)
3211 stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also
3212
3213 // Fill parameters for Audio Client creation
3214 stream->in.params.device_info = info;
3215 stream->in.params.stream_params = (*inputParameters);
3216 stream->in.params.frames_per_buffer = framesPerBuffer;
3217 stream->in.params.sample_rate = sampleRate;
3218 stream->in.params.blocking = (streamCallback == NULL);
3219 stream->in.params.full_duplex = fullDuplex;
3220 stream->in.params.wow64_workaround = paWasapi->useWOW64Workaround;
3221
3222 // Create and activate audio client
3223 hr = ActivateAudioClientInput(stream);
3224 if (hr != S_OK)
3225 {
3226 LogPaError(result = paInvalidDevice);
3227 goto error;
3228 }
3229
3230 // Get closest format
3231 hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->in.wavex), inputSampleFormat );
3232
3233 // Set user-side custom host processor
3234 if ((inputStreamInfo != NULL) &&
3235 (inputStreamInfo->flags & paWinWasapiRedirectHostProcessor))
3236 {
3237 stream->hostProcessOverrideInput.processor = inputStreamInfo->hostProcessorInput;
3238 stream->hostProcessOverrideInput.userData = userData;
3239 }
3240
3241 // Only get IAudioCaptureClient input once here instead of getting it at multiple places based on the use
3242 hr = IAudioClient_GetService(stream->in.clientParent, &pa_IID_IAudioCaptureClient, (void **)&stream->captureClientParent);
3243 if (hr != S_OK)
3244 {
3245 LogHostError(hr);
3246 LogPaError(result = paUnanticipatedHostError);
3247 goto error;
3248 }
3249
3250 // Create ring buffer for blocking mode (It is needed because we fetch Input packets, not frames,
3251 // and thus we have to save partial packet if such remains unread)
3252 if (stream->in.params.blocking == TRUE)
3253 {
3254 UINT32 bufferFrames = ALIGN_NEXT_POW2((stream->in.framesPerHostCallback / WASAPI_PACKETS_PER_INPUT_BUFFER) * 2);
3255 UINT32 frameSize = stream->in.wavex.Format.nBlockAlign;
3256
3257 // buffer
3258 if ((stream->in.tailBuffer = PaUtil_AllocateMemory(sizeof(PaUtilRingBuffer))) == NULL)
3259 {
3260 LogPaError(result = paInsufficientMemory);
3261 goto error;
3262 }
3263 memset(stream->in.tailBuffer, 0, sizeof(PaUtilRingBuffer));
3264
3265 // buffer memory region
3266 stream->in.tailBufferMemory = PaUtil_AllocateMemory(frameSize * bufferFrames);
3267 if (stream->in.tailBufferMemory == NULL)
3268 {
3269 LogPaError(result = paInsufficientMemory);
3270 goto error;
3271 }
3272
3273 // initialize
3274 if (PaUtil_InitializeRingBuffer(stream->in.tailBuffer, frameSize, bufferFrames, stream->in.tailBufferMemory) != 0)
3275 {
3276 LogPaError(result = paInternalError);
3277 goto error;
3278 }
3279 }
3280 }
3281 else
3282 {
3283 inputChannelCount = 0;
3284 inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
3285 }
3286
3287 // Try create device: Output
3288 if (outputParameters != NULL)
3289 {
3290 outputChannelCount = outputParameters->channelCount;
3291 outputSampleFormat = outputParameters->sampleFormat;
3292 info = &paWasapi->devInfo[outputParameters->device];
3293
3294 // default Shared Mode
3295 stream->out.shareMode = AUDCLNT_SHAREMODE_SHARED;
3296
3297 // set PaWasapiStreamInfo
3298 if (outputParameters->hostApiSpecificStreamInfo != NULL)
3299 {
3300 memcpy(&stream->out.params.wasapi_params, outputParameters->hostApiSpecificStreamInfo, min(sizeof(stream->out.params.wasapi_params), ((PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo)->size));
3301 stream->out.params.wasapi_params.size = sizeof(stream->out.params.wasapi_params);
3302
3303 stream->out.params.stream_params.hostApiSpecificStreamInfo = &stream->out.params.wasapi_params;
3304 outputStreamInfo = &stream->out.params.wasapi_params;
3305
3306 stream->out.flags = outputStreamInfo->flags;
3307
3308 // Exclusive Mode
3309 if (outputStreamInfo->flags & paWinWasapiExclusive)
3310 {
3311 // Boost thread priority
3312 stream->nThreadPriority = eThreadPriorityProAudio;
3313 // Make Exclusive
3314 stream->out.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
3315 }
3316
3317 // explicit thread priority level
3318 if (outputStreamInfo->flags & paWinWasapiThreadPriority)
3319 {
3320 if ((outputStreamInfo->threadPriority > eThreadPriorityNone) &&
3321 (outputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
3322 stream->nThreadPriority = outputStreamInfo->threadPriority;
3323 }
3324 }
3325
3326 // Choose processing mode
3327 stream->out.streamFlags = (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0);
3328 if (paWasapi->useWOW64Workaround)
3329 stream->out.streamFlags = 0; // polling interface
3330 else
3331 if (streamCallback == NULL)
3332 stream->out.streamFlags = 0; // polling interface
3333 else
3334 if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling))
3335 stream->out.streamFlags = 0; // polling interface
3336 else
3337 if (fullDuplex)
3338 stream->out.streamFlags = 0; // polling interface is implemented for full-duplex mode also
3339
3340 // Fill parameters for Audio Client creation
3341 stream->out.params.device_info = info;
3342 stream->out.params.stream_params = (*outputParameters);
3343 stream->out.params.frames_per_buffer = framesPerBuffer;
3344 stream->out.params.sample_rate = sampleRate;
3345 stream->out.params.blocking = (streamCallback == NULL);
3346 stream->out.params.full_duplex = fullDuplex;
3347 stream->out.params.wow64_workaround = paWasapi->useWOW64Workaround;
3348
3349 // Create and activate audio client
3350 hr = ActivateAudioClientOutput(stream);
3351 if (hr != S_OK)
3352 {
3353 LogPaError(result = paInvalidDevice);
3354 goto error;
3355 }
3356
3357 // Get closest format
3358 hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->out.wavex), outputSampleFormat );
3359
3360 // Set user-side custom host processor
3361 if ((outputStreamInfo != NULL) &&
3362 (outputStreamInfo->flags & paWinWasapiRedirectHostProcessor))
3363 {
3364 stream->hostProcessOverrideOutput.processor = outputStreamInfo->hostProcessorOutput;
3365 stream->hostProcessOverrideOutput.userData = userData;
3366 }
3367
3368 // Only get IAudioCaptureClient output once here instead of getting it at multiple places based on the use
3369 hr = IAudioClient_GetService(stream->out.clientParent, &pa_IID_IAudioRenderClient, (void **)&stream->renderClientParent);
3370 if (hr != S_OK)
3371 {
3372 LogHostError(hr);
3373 LogPaError(result = paUnanticipatedHostError);
3374 goto error;
3375 }
3376 }
3377 else
3378 {
3379 outputChannelCount = 0;
3380 outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
3381 }
3382
3383 // log full-duplex
3384 if (fullDuplex)
3385 PRINT(("WASAPI::OpenStream: full-duplex mode\n"));
3386
3387 // paWinWasapiPolling must be on/or not on both streams
3388 if ((inputParameters != NULL) && (outputParameters != NULL))
3389 {
3390 if ((inputStreamInfo != NULL) && (outputStreamInfo != NULL))
3391 {
3392 if (((inputStreamInfo->flags & paWinWasapiPolling) &&
3393 !(outputStreamInfo->flags & paWinWasapiPolling))
3394 ||
3395 (!(inputStreamInfo->flags & paWinWasapiPolling) &&
3396 (outputStreamInfo->flags & paWinWasapiPolling)))
3397 {
3398 LogPaError(result = paInvalidFlag);
3399 goto error;
3400 }
3401 }
3402 }
3403
3404 // Initialize stream representation
3405 if (streamCallback)
3406 {
3407 stream->bBlocking = FALSE;
3408 PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,
3409 &paWasapi->callbackStreamInterface,
3410 streamCallback, userData);
3411 }
3412 else
3413 {
3414 stream->bBlocking = TRUE;
3415 PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,
3416 &paWasapi->blockingStreamInterface,
3417 streamCallback, userData);
3418 }
3419
3420 // Initialize CPU measurer
3421 PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate);
3422
3423 if (outputParameters && inputParameters)
3424 {
3425 // serious problem #1 - No, Not a problem, especially concerning Exclusive mode.
3426 // Input device in exclusive mode somehow is getting large buffer always, thus we
3427 // adjust Output latency to reflect it, thus period will differ but playback will be
3428 // normal.
3429 /*if (stream->in.period != stream->out.period)
3430 {
3431 PRINT(("WASAPI: OpenStream: period discrepancy\n"));
3432 LogPaError(result = paBadIODeviceCombination);
3433 goto error;
3434 }*/
3435
3436 // serious problem #2 - No, Not a problem, as framesPerHostCallback take into account
3437 // sample size while it is not a problem for PA full-duplex, we must care of
3438 // preriod only!
3439 /*if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback)
3440 {
3441 PRINT(("WASAPI: OpenStream: framesPerHostCallback discrepancy\n"));
3442 goto error;
3443 }*/
3444 }
3445
3446 // Calculate frames per host for processor
3447 framesPerHostCallback = (outputParameters ? stream->out.framesPerBuffer : stream->in.framesPerBuffer);
3448
3449 // Choose correct mode of buffer processing:
3450 // Exclusive/Shared non paWinWasapiPolling mode: paUtilFixedHostBufferSize - always fixed
3451 // Exclusive/Shared paWinWasapiPolling mode: paUtilBoundedHostBufferSize - may vary for Exclusive or Full-duplex
3452 bufferMode = paUtilFixedHostBufferSize;
3453 if (inputParameters) // !!! WASAPI IAudioCaptureClient::GetBuffer extracts not number of frames but 1 packet, thus we always must adapt
3454 bufferMode = paUtilBoundedHostBufferSize;
3455 else
3456 if (outputParameters)
3457 {
3458 if ((stream->out.buffers == 1) &&
3459 (!stream->out.streamFlags || ((stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
3460 bufferMode = paUtilBoundedHostBufferSize;
3461 }
3462 stream->bufferMode = bufferMode;
3463
3464 // Initialize buffer processor
3465 result = PaUtil_InitializeBufferProcessor(
3466 &stream->bufferProcessor,
3467 inputChannelCount,
3468 inputSampleFormat,
3469 hostInputSampleFormat,
3470 outputChannelCount,
3471 outputSampleFormat,
3472 hostOutputSampleFormat,
3473 sampleRate,
3474 streamFlags,
3475 framesPerBuffer,
3476 framesPerHostCallback,
3477 bufferMode,
3478 streamCallback,
3479 userData);
3480 if (result != paNoError)
3481 {
3482 LogPaError(result);
3483 goto error;
3484 }
3485
3486 // Set Input latency
3487 stream->streamRepresentation.streamInfo.inputLatency =
3488 ((double)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate)
3489 + ((inputParameters)?stream->in.latencySeconds : 0);
3490
3491 // Set Output latency
3492 stream->streamRepresentation.streamInfo.outputLatency =
3493 ((double)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate)
3494 + ((outputParameters)?stream->out.latencySeconds : 0);
3495
3496 // Set SR
3497 stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
3498
3499 (*s) = (PaStream *)stream;
3500 return result;
3501
3502 error:
3503
3504 if (stream != NULL)
3505 CloseStream(stream);
3506
3507 return result;
3508 }
3509
3510 // ------------------------------------------------------------------------------------------
CloseStream(PaStream * s)3511 static PaError CloseStream( PaStream* s )
3512 {
3513 PaError result = paNoError;
3514 PaWasapiStream *stream = (PaWasapiStream*)s;
3515
3516 // abort active stream
3517 if (IsStreamActive(s))
3518 {
3519 result = AbortStream(s);
3520 }
3521
3522 SAFE_RELEASE(stream->captureClientParent);
3523 SAFE_RELEASE(stream->renderClientParent);
3524 SAFE_RELEASE(stream->out.clientParent);
3525 SAFE_RELEASE(stream->in.clientParent);
3526 SAFE_RELEASE(stream->inVol);
3527 SAFE_RELEASE(stream->outVol);
3528
3529 CloseHandle(stream->event[S_INPUT]);
3530 CloseHandle(stream->event[S_OUTPUT]);
3531
3532 _StreamCleanup(stream);
3533
3534 PaWasapi_FreeMemory(stream->in.monoBuffer);
3535 PaWasapi_FreeMemory(stream->out.monoBuffer);
3536
3537 PaUtil_FreeMemory(stream->in.tailBuffer);
3538 PaUtil_FreeMemory(stream->in.tailBufferMemory);
3539
3540 PaUtil_FreeMemory(stream->out.tailBuffer);
3541 PaUtil_FreeMemory(stream->out.tailBufferMemory);
3542
3543 PaUtil_TerminateBufferProcessor(&stream->bufferProcessor);
3544 PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation);
3545 PaUtil_FreeMemory(stream);
3546
3547 return result;
3548 }
3549
3550 // ------------------------------------------------------------------------------------------
UnmarshalSubStreamComPointers(PaWasapiSubStream * substream)3551 HRESULT UnmarshalSubStreamComPointers(PaWasapiSubStream *substream)
3552 {
3553 #ifndef PA_WINRT
3554 HRESULT hResult = S_OK;
3555 HRESULT hFirstBadResult = S_OK;
3556 substream->clientProc = NULL;
3557
3558 // IAudioClient
3559 hResult = CoGetInterfaceAndReleaseStream(substream->clientStream, GetAudioClientIID(), (LPVOID*)&substream->clientProc);
3560 substream->clientStream = NULL;
3561 if (hResult != S_OK)
3562 {
3563 hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
3564 }
3565
3566 return hFirstBadResult;
3567
3568 #else
3569 (void)substream;
3570 return S_OK;
3571 #endif
3572 }
3573
3574 // ------------------------------------------------------------------------------------------
UnmarshalStreamComPointers(PaWasapiStream * stream)3575 HRESULT UnmarshalStreamComPointers(PaWasapiStream *stream)
3576 {
3577 #ifndef PA_WINRT
3578 HRESULT hResult = S_OK;
3579 HRESULT hFirstBadResult = S_OK;
3580 stream->captureClient = NULL;
3581 stream->renderClient = NULL;
3582 stream->in.clientProc = NULL;
3583 stream->out.clientProc = NULL;
3584
3585 if (NULL != stream->in.clientParent)
3586 {
3587 // SubStream pointers
3588 hResult = UnmarshalSubStreamComPointers(&stream->in);
3589 if (hResult != S_OK)
3590 {
3591 hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
3592 }
3593
3594 // IAudioCaptureClient
3595 hResult = CoGetInterfaceAndReleaseStream(stream->captureClientStream, &pa_IID_IAudioCaptureClient, (LPVOID*)&stream->captureClient);
3596 stream->captureClientStream = NULL;
3597 if (hResult != S_OK)
3598 {
3599 hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
3600 }
3601 }
3602
3603 if (NULL != stream->out.clientParent)
3604 {
3605 // SubStream pointers
3606 hResult = UnmarshalSubStreamComPointers(&stream->out);
3607 if (hResult != S_OK)
3608 {
3609 hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
3610 }
3611
3612 // IAudioRenderClient
3613 hResult = CoGetInterfaceAndReleaseStream(stream->renderClientStream, &pa_IID_IAudioRenderClient, (LPVOID*)&stream->renderClient);
3614 stream->renderClientStream = NULL;
3615 if (hResult != S_OK)
3616 {
3617 hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
3618 }
3619 }
3620
3621 return hFirstBadResult;
3622 #else
3623 if (stream->in.clientParent != NULL)
3624 {
3625 stream->in.clientProc = stream->in.clientParent;
3626 IAudioClient_AddRef(stream->in.clientParent);
3627 }
3628
3629 if (stream->out.clientParent != NULL)
3630 {
3631 stream->out.clientProc = stream->out.clientParent;
3632 IAudioClient_AddRef(stream->out.clientParent);
3633 }
3634
3635 if (stream->renderClientParent != NULL)
3636 {
3637 stream->renderClient = stream->renderClientParent;
3638 IAudioRenderClient_AddRef(stream->renderClientParent);
3639 }
3640
3641 if (stream->captureClientParent != NULL)
3642 {
3643 stream->captureClient = stream->captureClientParent;
3644 IAudioCaptureClient_AddRef(stream->captureClientParent);
3645 }
3646
3647 return S_OK;
3648 #endif
3649 }
3650
3651 // -----------------------------------------------------------------------------------------
ReleaseUnmarshaledSubComPointers(PaWasapiSubStream * substream)3652 void ReleaseUnmarshaledSubComPointers(PaWasapiSubStream *substream)
3653 {
3654 SAFE_RELEASE(substream->clientProc);
3655 }
3656
3657 // -----------------------------------------------------------------------------------------
ReleaseUnmarshaledComPointers(PaWasapiStream * stream)3658 void ReleaseUnmarshaledComPointers(PaWasapiStream *stream)
3659 {
3660 // Release AudioClient services first
3661 SAFE_RELEASE(stream->captureClient);
3662 SAFE_RELEASE(stream->renderClient);
3663
3664 // Release AudioClients
3665 ReleaseUnmarshaledSubComPointers(&stream->in);
3666 ReleaseUnmarshaledSubComPointers(&stream->out);
3667 }
3668
3669 // ------------------------------------------------------------------------------------------
MarshalSubStreamComPointers(PaWasapiSubStream * substream)3670 HRESULT MarshalSubStreamComPointers(PaWasapiSubStream *substream)
3671 {
3672 #ifndef PA_WINRT
3673 HRESULT hResult;
3674 substream->clientStream = NULL;
3675
3676 // IAudioClient
3677 hResult = CoMarshalInterThreadInterfaceInStream(GetAudioClientIID(), (LPUNKNOWN)substream->clientParent, &substream->clientStream);
3678 if (hResult != S_OK)
3679 goto marshal_sub_error;
3680
3681 return hResult;
3682
3683 // If marshaling error occurred, make sure to release everything.
3684 marshal_sub_error:
3685
3686 UnmarshalSubStreamComPointers(substream);
3687 ReleaseUnmarshaledSubComPointers(substream);
3688 return hResult;
3689 #else
3690 (void)substream;
3691 return S_OK;
3692 #endif
3693 }
3694
3695 // ------------------------------------------------------------------------------------------
MarshalStreamComPointers(PaWasapiStream * stream)3696 HRESULT MarshalStreamComPointers(PaWasapiStream *stream)
3697 {
3698 #ifndef PA_WINRT
3699 HRESULT hResult = S_OK;
3700 stream->captureClientStream = NULL;
3701 stream->in.clientStream = NULL;
3702 stream->renderClientStream = NULL;
3703 stream->out.clientStream = NULL;
3704
3705 if (NULL != stream->in.clientParent)
3706 {
3707 // SubStream pointers
3708 hResult = MarshalSubStreamComPointers(&stream->in);
3709 if (hResult != S_OK)
3710 goto marshal_error;
3711
3712 // IAudioCaptureClient
3713 hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioCaptureClient, (LPUNKNOWN)stream->captureClientParent, &stream->captureClientStream);
3714 if (hResult != S_OK)
3715 goto marshal_error;
3716 }
3717
3718 if (NULL != stream->out.clientParent)
3719 {
3720 // SubStream pointers
3721 hResult = MarshalSubStreamComPointers(&stream->out);
3722 if (hResult != S_OK)
3723 goto marshal_error;
3724
3725 // IAudioRenderClient
3726 hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioRenderClient, (LPUNKNOWN)stream->renderClientParent, &stream->renderClientStream);
3727 if (hResult != S_OK)
3728 goto marshal_error;
3729 }
3730
3731 return hResult;
3732
3733 // If marshaling error occurred, make sure to release everything.
3734 marshal_error:
3735
3736 UnmarshalStreamComPointers(stream);
3737 ReleaseUnmarshaledComPointers(stream);
3738 return hResult;
3739 #else
3740 (void)stream;
3741 return S_OK;
3742 #endif
3743 }
3744
3745 // ------------------------------------------------------------------------------------------
StartStream(PaStream * s)3746 static PaError StartStream( PaStream *s )
3747 {
3748 HRESULT hr;
3749 PaWasapiStream *stream = (PaWasapiStream*)s;
3750 PaError result = paNoError;
3751
3752 // check if stream is active already
3753 if (IsStreamActive(s))
3754 return paStreamIsNotStopped;
3755
3756 PaUtil_ResetBufferProcessor(&stream->bufferProcessor);
3757
3758 // Cleanup handles (may be necessary if stream was stopped by itself due to error)
3759 _StreamCleanup(stream);
3760
3761 // Create close event
3762 if ((stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
3763 {
3764 result = paInsufficientMemory;
3765 goto start_error;
3766 }
3767
3768 // Create thread
3769 if (!stream->bBlocking)
3770 {
3771 // Create thread events
3772 stream->hThreadStart = CreateEvent(NULL, TRUE, FALSE, NULL);
3773 stream->hThreadExit = CreateEvent(NULL, TRUE, FALSE, NULL);
3774 if ((stream->hThreadStart == NULL) || (stream->hThreadExit == NULL))
3775 {
3776 result = paInsufficientMemory;
3777 goto start_error;
3778 }
3779
3780 // Marshal WASAPI interface pointers for safe use in thread created below.
3781 if ((hr = MarshalStreamComPointers(stream)) != S_OK)
3782 {
3783 PRINT(("Failed marshaling stream COM pointers."));
3784 result = paUnanticipatedHostError;
3785 goto nonblocking_start_error;
3786 }
3787
3788 if ((stream->in.clientParent && (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) ||
3789 (stream->out.clientParent && (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)))
3790 {
3791 if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL)
3792 {
3793 PRINT(("Failed creating thread: ProcThreadEvent."));
3794 result = paUnanticipatedHostError;
3795 goto nonblocking_start_error;
3796 }
3797 }
3798 else
3799 {
3800 if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL)
3801 {
3802 PRINT(("Failed creating thread: ProcThreadPoll."));
3803 result = paUnanticipatedHostError;
3804 goto nonblocking_start_error;
3805 }
3806 }
3807
3808 // Wait for thread to start
3809 if (WaitForSingleObject(stream->hThreadStart, 60*1000) == WAIT_TIMEOUT)
3810 {
3811 PRINT(("Failed starting thread: timeout."));
3812 result = paUnanticipatedHostError;
3813 goto nonblocking_start_error;
3814 }
3815 }
3816 else
3817 {
3818 // Create blocking operation events (non-signaled event means - blocking operation is pending)
3819 if (stream->out.clientParent != NULL)
3820 {
3821 if ((stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL)
3822 {
3823 result = paInsufficientMemory;
3824 goto start_error;
3825 }
3826 }
3827 if (stream->in.clientParent != NULL)
3828 {
3829 if ((stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL)
3830 {
3831 result = paInsufficientMemory;
3832 goto start_error;
3833 }
3834 }
3835
3836 // Initialize event & start INPUT stream
3837 if (stream->in.clientParent != NULL)
3838 {
3839 if ((hr = IAudioClient_Start(stream->in.clientParent)) != S_OK)
3840 {
3841 LogHostError(hr);
3842 result = paUnanticipatedHostError;
3843 goto start_error;
3844 }
3845 }
3846
3847 // Initialize event & start OUTPUT stream
3848 if (stream->out.clientParent != NULL)
3849 {
3850 // Start
3851 if ((hr = IAudioClient_Start(stream->out.clientParent)) != S_OK)
3852 {
3853 LogHostError(hr);
3854 result = paUnanticipatedHostError;
3855 goto start_error;
3856 }
3857 }
3858
3859 // Set parent to working pointers to use shared functions.
3860 stream->captureClient = stream->captureClientParent;
3861 stream->renderClient = stream->renderClientParent;
3862 stream->in.clientProc = stream->in.clientParent;
3863 stream->out.clientProc = stream->out.clientParent;
3864
3865 // Signal: stream running.
3866 stream->running = TRUE;
3867 }
3868
3869 return result;
3870
3871 nonblocking_start_error:
3872
3873 // Set hThreadExit event to prevent blocking during cleanup
3874 SetEvent(stream->hThreadExit);
3875 UnmarshalStreamComPointers(stream);
3876 ReleaseUnmarshaledComPointers(stream);
3877
3878 start_error:
3879
3880 StopStream(s);
3881 return result;
3882 }
3883
3884 // ------------------------------------------------------------------------------------------
_StreamFinish(PaWasapiStream * stream)3885 void _StreamFinish(PaWasapiStream *stream)
3886 {
3887 // Issue command to thread to stop processing and wait for thread exit
3888 if (!stream->bBlocking)
3889 {
3890 SignalObjectAndWait(stream->hCloseRequest, stream->hThreadExit, INFINITE, FALSE);
3891 }
3892 else
3893 // Blocking mode does not own thread
3894 {
3895 // Signal close event and wait for each of 2 blocking operations to complete
3896 if (stream->out.clientParent)
3897 SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE);
3898 if (stream->out.clientParent)
3899 SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE);
3900
3901 // Process stop
3902 _StreamOnStop(stream);
3903 }
3904
3905 // Cleanup handles
3906 _StreamCleanup(stream);
3907
3908 stream->running = FALSE;
3909 }
3910
3911 // ------------------------------------------------------------------------------------------
_StreamCleanup(PaWasapiStream * stream)3912 void _StreamCleanup(PaWasapiStream *stream)
3913 {
3914 // Close thread handles to allow restart
3915 SAFE_CLOSE(stream->hThread);
3916 SAFE_CLOSE(stream->hThreadStart);
3917 SAFE_CLOSE(stream->hThreadExit);
3918 SAFE_CLOSE(stream->hCloseRequest);
3919 SAFE_CLOSE(stream->hBlockingOpStreamRD);
3920 SAFE_CLOSE(stream->hBlockingOpStreamWR);
3921 }
3922
3923 // ------------------------------------------------------------------------------------------
StopStream(PaStream * s)3924 static PaError StopStream( PaStream *s )
3925 {
3926 // Finish stream
3927 _StreamFinish((PaWasapiStream *)s);
3928 return paNoError;
3929 }
3930
3931 // ------------------------------------------------------------------------------------------
AbortStream(PaStream * s)3932 static PaError AbortStream( PaStream *s )
3933 {
3934 // Finish stream
3935 _StreamFinish((PaWasapiStream *)s);
3936 return paNoError;
3937 }
3938
3939 // ------------------------------------------------------------------------------------------
IsStreamStopped(PaStream * s)3940 static PaError IsStreamStopped( PaStream *s )
3941 {
3942 return !((PaWasapiStream *)s)->running;
3943 }
3944
3945 // ------------------------------------------------------------------------------------------
IsStreamActive(PaStream * s)3946 static PaError IsStreamActive( PaStream *s )
3947 {
3948 return ((PaWasapiStream *)s)->running;
3949 }
3950
3951 // ------------------------------------------------------------------------------------------
GetStreamTime(PaStream * s)3952 static PaTime GetStreamTime( PaStream *s )
3953 {
3954 PaWasapiStream *stream = (PaWasapiStream*)s;
3955
3956 /* suppress unused variable warnings */
3957 (void) stream;
3958
3959 return PaUtil_GetTime();
3960 }
3961
3962 // ------------------------------------------------------------------------------------------
GetStreamCpuLoad(PaStream * s)3963 static double GetStreamCpuLoad( PaStream* s )
3964 {
3965 return PaUtil_GetCpuLoad(&((PaWasapiStream *)s)->cpuLoadMeasurer);
3966 }
3967
3968 // ------------------------------------------------------------------------------------------
ReadStream(PaStream * s,void * _buffer,unsigned long frames)3969 static PaError ReadStream( PaStream* s, void *_buffer, unsigned long frames )
3970 {
3971 PaWasapiStream *stream = (PaWasapiStream*)s;
3972
3973 HRESULT hr = S_OK;
3974 BYTE *user_buffer = (BYTE *)_buffer;
3975 BYTE *wasapi_buffer = NULL;
3976 DWORD flags = 0;
3977 UINT32 i, available, sleep = 0;
3978 unsigned long processed;
3979 ThreadIdleScheduler sched;
3980
3981 // validate
3982 if (!stream->running)
3983 return paStreamIsStopped;
3984 if (stream->captureClient == NULL)
3985 return paBadStreamPtr;
3986
3987 // Notify blocking op has begun
3988 ResetEvent(stream->hBlockingOpStreamRD);
3989
3990 // Use thread scheduling for 500 microseconds (emulated) when wait time for frames is less than
3991 // 1 milliseconds, emulation helps to normalize CPU consumption and avoids too busy waiting
3992 ThreadIdleScheduler_Setup(&sched, 1, 250/* microseconds */);
3993
3994 // Make a local copy of the user buffer pointer(s), this is necessary
3995 // because PaUtil_CopyOutput() advances these pointers every time it is called
3996 if (!stream->bufferProcessor.userInputIsInterleaved)
3997 {
3998 user_buffer = (BYTE *)alloca(sizeof(BYTE *) * stream->bufferProcessor.inputChannelCount);
3999 if (user_buffer == NULL)
4000 return paInsufficientMemory;
4001
4002 for (i = 0; i < stream->bufferProcessor.inputChannelCount; ++i)
4003 ((BYTE **)user_buffer)[i] = ((BYTE **)_buffer)[i];
4004 }
4005
4006 // Findout if there are tail frames, flush them all before reading hardware
4007 if ((available = PaUtil_GetRingBufferReadAvailable(stream->in.tailBuffer)) != 0)
4008 {
4009 ring_buffer_size_t buf1_size = 0, buf2_size = 0, read, desired;
4010 void *buf1 = NULL, *buf2 = NULL;
4011
4012 // Limit desired to amount of requested frames
4013 desired = available;
4014 if ((UINT32)desired > frames)
4015 desired = frames;
4016
4017 // Get pointers to read regions
4018 read = PaUtil_GetRingBufferReadRegions(stream->in.tailBuffer, desired, &buf1, &buf1_size, &buf2, &buf2_size);
4019
4020 if (buf1 != NULL)
4021 {
4022 // Register available frames to processor
4023 PaUtil_SetInputFrameCount(&stream->bufferProcessor, buf1_size);
4024
4025 // Register host buffer pointer to processor
4026 PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, buf1, stream->bufferProcessor.inputChannelCount);
4027
4028 // Copy user data to host buffer (with conversion if applicable)
4029 processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, buf1_size);
4030 frames -= processed;
4031 }
4032
4033 if (buf2 != NULL)
4034 {
4035 // Register available frames to processor
4036 PaUtil_SetInputFrameCount(&stream->bufferProcessor, buf2_size);
4037
4038 // Register host buffer pointer to processor
4039 PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, buf2, stream->bufferProcessor.inputChannelCount);
4040
4041 // Copy user data to host buffer (with conversion if applicable)
4042 processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, buf2_size);
4043 frames -= processed;
4044 }
4045
4046 // Advance
4047 PaUtil_AdvanceRingBufferReadIndex(stream->in.tailBuffer, read);
4048 }
4049
4050 // Read hardware
4051 while (frames != 0)
4052 {
4053 // Check if blocking call must be interrupted
4054 if (WaitForSingleObject(stream->hCloseRequest, sleep) != WAIT_TIMEOUT)
4055 break;
4056
4057 // Get available frames (must be finding out available frames before call to IAudioCaptureClient_GetBuffer
4058 // othervise audio glitches will occur inExclusive mode as it seems that WASAPI has some scheduling/
4059 // processing problems when such busy polling with IAudioCaptureClient_GetBuffer occurs)
4060 if ((hr = _PollGetInputFramesAvailable(stream, &available)) != S_OK)
4061 {
4062 LogHostError(hr);
4063 return paUnanticipatedHostError;
4064 }
4065
4066 // Wait for more frames to become available
4067 if (available == 0)
4068 {
4069 // Exclusive mode may require latency of 1 millisecond, thus we shall sleep
4070 // around 500 microseconds (emulated) to collect packets in time
4071 if (stream->in.shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE)
4072 {
4073 UINT32 sleep_frames = (frames < stream->in.framesPerHostCallback ? frames : stream->in.framesPerHostCallback);
4074
4075 sleep = GetFramesSleepTime(sleep_frames, stream->in.wavex.Format.nSamplesPerSec);
4076 sleep /= 4; // wait only for 1/4 of the buffer
4077
4078 // WASAPI input provides packets, thus expiring packet will result in bad audio
4079 // limit waiting time to 2 seconds (will always work for smallest buffer in Shared)
4080 if (sleep > 2)
4081 sleep = 2;
4082
4083 // Avoid busy waiting, schedule next 1 millesecond wait
4084 if (sleep == 0)
4085 sleep = ThreadIdleScheduler_NextSleep(&sched);
4086 }
4087 else
4088 {
4089 if ((sleep = ThreadIdleScheduler_NextSleep(&sched)) != 0)
4090 {
4091 Sleep(sleep);
4092 sleep = 0;
4093 }
4094 }
4095
4096 continue;
4097 }
4098
4099 // Get the available data in the shared buffer.
4100 if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &wasapi_buffer, &available, &flags, NULL, NULL)) != S_OK)
4101 {
4102 // Buffer size is too small, waiting
4103 if (hr != AUDCLNT_S_BUFFER_EMPTY)
4104 {
4105 LogHostError(hr);
4106 goto end;
4107 }
4108
4109 continue;
4110 }
4111
4112 // Register available frames to processor
4113 PaUtil_SetInputFrameCount(&stream->bufferProcessor, available);
4114
4115 // Register host buffer pointer to processor
4116 PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.inputChannelCount);
4117
4118 // Copy user data to host buffer (with conversion if applicable)
4119 processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, frames);
4120 frames -= processed;
4121
4122 // Save tail into buffer
4123 if ((frames == 0) && (available > processed))
4124 {
4125 UINT32 bytes_processed = processed * stream->in.wavex.Format.nBlockAlign;
4126 UINT32 frames_to_save = available - processed;
4127
4128 PaUtil_WriteRingBuffer(stream->in.tailBuffer, wasapi_buffer + bytes_processed, frames_to_save);
4129 }
4130
4131 // Release host buffer
4132 if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, available)) != S_OK)
4133 {
4134 LogHostError(hr);
4135 goto end;
4136 }
4137 }
4138
4139 end:
4140
4141 // Notify blocking op has ended
4142 SetEvent(stream->hBlockingOpStreamRD);
4143
4144 return (hr != S_OK ? paUnanticipatedHostError : paNoError);
4145 }
4146
4147 // ------------------------------------------------------------------------------------------
WriteStream(PaStream * s,const void * _buffer,unsigned long frames)4148 static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long frames )
4149 {
4150 PaWasapiStream *stream = (PaWasapiStream*)s;
4151
4152 //UINT32 frames;
4153 const BYTE *user_buffer = (const BYTE *)_buffer;
4154 BYTE *wasapi_buffer;
4155 HRESULT hr = S_OK;
4156 UINT32 i, available, sleep = 0;
4157 unsigned long processed;
4158 ThreadIdleScheduler sched;
4159
4160 // validate
4161 if (!stream->running)
4162 return paStreamIsStopped;
4163 if (stream->renderClient == NULL)
4164 return paBadStreamPtr;
4165
4166 // Notify blocking op has begun
4167 ResetEvent(stream->hBlockingOpStreamWR);
4168
4169 // Use thread scheduling for 500 microseconds (emulated) when wait time for frames is less than
4170 // 1 milliseconds, emulation helps to normalize CPU consumption and avoids too busy waiting
4171 ThreadIdleScheduler_Setup(&sched, 1, 500/* microseconds */);
4172
4173 // Make a local copy of the user buffer pointer(s), this is necessary
4174 // because PaUtil_CopyOutput() advances these pointers every time it is called
4175 if (!stream->bufferProcessor.userOutputIsInterleaved)
4176 {
4177 user_buffer = (const BYTE *)alloca(sizeof(const BYTE *) * stream->bufferProcessor.outputChannelCount);
4178 if (user_buffer == NULL)
4179 return paInsufficientMemory;
4180
4181 for (i = 0; i < stream->bufferProcessor.outputChannelCount; ++i)
4182 ((const BYTE **)user_buffer)[i] = ((const BYTE **)_buffer)[i];
4183 }
4184
4185 // Blocking (potentially, untill 'frames' are consumed) loop
4186 while (frames != 0)
4187 {
4188 // Check if blocking call must be interrupted
4189 if (WaitForSingleObject(stream->hCloseRequest, sleep) != WAIT_TIMEOUT)
4190 break;
4191
4192 // Get frames available
4193 if ((hr = _PollGetOutputFramesAvailable(stream, &available)) != S_OK)
4194 {
4195 LogHostError(hr);
4196 goto end;
4197 }
4198
4199 // Wait for more frames to become available
4200 if (available == 0)
4201 {
4202 UINT32 sleep_frames = (frames < stream->out.framesPerHostCallback ? frames : stream->out.framesPerHostCallback);
4203
4204 sleep = GetFramesSleepTime(sleep_frames, stream->out.wavex.Format.nSamplesPerSec);
4205 sleep /= 2; // wait only for half of the buffer
4206
4207 // Avoid busy waiting, schedule next 1 millesecond wait
4208 if (sleep == 0)
4209 sleep = ThreadIdleScheduler_NextSleep(&sched);
4210
4211 continue;
4212 }
4213
4214 // Keep in 'frmaes' range
4215 if (available > frames)
4216 available = frames;
4217
4218 // Get pointer to host buffer
4219 if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, available, &wasapi_buffer)) != S_OK)
4220 {
4221 // Buffer size is too big, waiting
4222 if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
4223 continue;
4224
4225 LogHostError(hr);
4226 goto end;
4227 }
4228
4229 // Keep waiting again (on Vista it was noticed that WASAPI could SOMETIMES return NULL pointer
4230 // to buffer without returning AUDCLNT_E_BUFFER_TOO_LARGE instead)
4231 if (wasapi_buffer == NULL)
4232 continue;
4233
4234 // Register available frames to processor
4235 PaUtil_SetOutputFrameCount(&stream->bufferProcessor, available);
4236
4237 // Register host buffer pointer to processor
4238 PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.outputChannelCount);
4239
4240 // Copy user data to host buffer (with conversion if applicable), this call will advance
4241 // pointer 'user_buffer' to consumed portion of data
4242 processed = PaUtil_CopyOutput(&stream->bufferProcessor, (const void **)&user_buffer, frames);
4243 frames -= processed;
4244
4245 // Release host buffer
4246 if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, available, 0)) != S_OK)
4247 {
4248 LogHostError(hr);
4249 goto end;
4250 }
4251 }
4252
4253 end:
4254
4255 // Notify blocking op has ended
4256 SetEvent(stream->hBlockingOpStreamWR);
4257
4258 return (hr != S_OK ? paUnanticipatedHostError : paNoError);
4259 }
4260
PaUtil_GetOutputFrameCount(PaUtilBufferProcessor * bp)4261 unsigned long PaUtil_GetOutputFrameCount( PaUtilBufferProcessor* bp )
4262 {
4263 return bp->hostOutputFrameCount[0];
4264 }
4265
4266 // ------------------------------------------------------------------------------------------
GetStreamReadAvailable(PaStream * s)4267 static signed long GetStreamReadAvailable( PaStream* s )
4268 {
4269 PaWasapiStream *stream = (PaWasapiStream*)s;
4270
4271 HRESULT hr;
4272 UINT32 available = 0;
4273
4274 // validate
4275 if (!stream->running)
4276 return paStreamIsStopped;
4277 if (stream->captureClient == NULL)
4278 return paBadStreamPtr;
4279
4280 // available in hardware buffer
4281 if ((hr = _PollGetInputFramesAvailable(stream, &available)) != S_OK)
4282 {
4283 LogHostError(hr);
4284 return paUnanticipatedHostError;
4285 }
4286
4287 // available in software tail buffer
4288 available += PaUtil_GetRingBufferReadAvailable(stream->in.tailBuffer);
4289
4290 return available;
4291 }
4292
4293 // ------------------------------------------------------------------------------------------
GetStreamWriteAvailable(PaStream * s)4294 static signed long GetStreamWriteAvailable( PaStream* s )
4295 {
4296 PaWasapiStream *stream = (PaWasapiStream*)s;
4297 HRESULT hr;
4298 UINT32 available = 0;
4299
4300 // validate
4301 if (!stream->running)
4302 return paStreamIsStopped;
4303 if (stream->renderClient == NULL)
4304 return paBadStreamPtr;
4305
4306 if ((hr = _PollGetOutputFramesAvailable(stream, &available)) != S_OK)
4307 {
4308 LogHostError(hr);
4309 return paUnanticipatedHostError;
4310 }
4311
4312 return (signed long)available;
4313 }
4314
4315
4316 // ------------------------------------------------------------------------------------------
WaspiHostProcessingLoop(void * inputBuffer,long inputFrames,void * outputBuffer,long outputFrames,void * userData)4317 static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
4318 void *outputBuffer, long outputFrames,
4319 void *userData )
4320 {
4321 PaWasapiStream *stream = (PaWasapiStream*)userData;
4322 PaStreamCallbackTimeInfo timeInfo = {0,0,0};
4323 PaStreamCallbackFlags flags = 0;
4324 int callbackResult;
4325 unsigned long framesProcessed;
4326 HRESULT hr;
4327 UINT32 pending;
4328
4329 PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
4330
4331 /*
4332 Pa_GetStreamTime:
4333 - generate timing information
4334 - handle buffer slips
4335 */
4336 timeInfo.currentTime = PaUtil_GetTime();
4337 // Query input latency
4338 if (stream->in.clientProc != NULL)
4339 {
4340 PaTime pending_time;
4341 if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, &pending)) == S_OK)
4342 pending_time = (PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec;
4343 else
4344 pending_time = (PaTime)stream->in.latencySeconds;
4345
4346 timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time;
4347 }
4348 // Query output current latency
4349 if (stream->out.clientProc != NULL)
4350 {
4351 PaTime pending_time;
4352 if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &pending)) == S_OK)
4353 pending_time = (PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec;
4354 else
4355 pending_time = (PaTime)stream->out.latencySeconds;
4356
4357 timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time;
4358 }
4359
4360 /*
4361 If you need to byte swap or shift inputBuffer to convert it into a
4362 portaudio format, do it here.
4363 */
4364
4365 PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, flags );
4366
4367 /*
4368 depending on whether the host buffers are interleaved, non-interleaved
4369 or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
4370 PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
4371 */
4372
4373 if (stream->bufferProcessor.inputChannelCount > 0)
4374 {
4375 PaUtil_SetInputFrameCount( &stream->bufferProcessor, inputFrames );
4376 PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
4377 0, /* first channel of inputBuffer is channel 0 */
4378 inputBuffer,
4379 0 ); /* 0 - use inputChannelCount passed to init buffer processor */
4380 }
4381
4382 if (stream->bufferProcessor.outputChannelCount > 0)
4383 {
4384 PaUtil_SetOutputFrameCount( &stream->bufferProcessor, outputFrames);
4385 PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
4386 0, /* first channel of outputBuffer is channel 0 */
4387 outputBuffer,
4388 0 ); /* 0 - use outputChannelCount passed to init buffer processor */
4389 }
4390
4391 /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
4392 in general you would pass paContinue for normal operation, and
4393 paComplete to drain the buffer processor's internal output buffer.
4394 You can check whether the buffer processor's output buffer is empty
4395 using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
4396 */
4397 callbackResult = paContinue;
4398 framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
4399
4400 /*
4401 If you need to byte swap or shift outputBuffer to convert it to
4402 host format, do it here.
4403 */
4404
4405 PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
4406
4407 if (callbackResult == paContinue)
4408 {
4409 /* nothing special to do */
4410 }
4411 else
4412 if (callbackResult == paAbort)
4413 {
4414 // stop stream
4415 SetEvent(stream->hCloseRequest);
4416 }
4417 else
4418 {
4419 // stop stream
4420 SetEvent(stream->hCloseRequest);
4421 }
4422 }
4423
4424 // ------------------------------------------------------------------------------------------
MMCSS_activate(const char * name)4425 HANDLE MMCSS_activate(const char *name)
4426 {
4427 #ifndef PA_WINRT
4428 DWORD task_idx = 0;
4429 HANDLE hTask = pAvSetMmThreadCharacteristics(name, &task_idx);
4430 if (hTask == NULL)
4431 {
4432 PRINT(("WASAPI: AvSetMmThreadCharacteristics failed!\n"));
4433 }
4434
4435 /*BOOL priority_ok = pAvSetMmThreadPriority(hTask, AVRT_PRIORITY_NORMAL);
4436 if (priority_ok == FALSE)
4437 {
4438 PRINT(("WASAPI: AvSetMmThreadPriority failed!\n"));
4439 }*/
4440
4441 // debug
4442 {
4443 int cur_priority = GetThreadPriority(GetCurrentThread());
4444 DWORD cur_priority_class = GetPriorityClass(GetCurrentProcess());
4445 PRINT(("WASAPI: thread[ priority-0x%X class-0x%X ]\n", cur_priority, cur_priority_class));
4446 }
4447
4448 return hTask;
4449 #else
4450 (void)name;
4451 return NULL;
4452 #endif
4453 }
4454
4455 // ------------------------------------------------------------------------------------------
MMCSS_deactivate(HANDLE hTask)4456 void MMCSS_deactivate(HANDLE hTask)
4457 {
4458 if (!hTask)
4459 return;
4460
4461 #ifndef PA_WINRT
4462 if (pAvRevertMmThreadCharacteristics(hTask) == FALSE)
4463 {
4464 PRINT(("WASAPI: AvRevertMmThreadCharacteristics failed!\n"));
4465 }
4466 #endif
4467 }
4468
4469 // ------------------------------------------------------------------------------------------
PaWasapi_ThreadPriorityBoost(void ** hTask,PaWasapiThreadPriority nPriorityClass)4470 PaError PaWasapi_ThreadPriorityBoost(void **hTask, PaWasapiThreadPriority nPriorityClass)
4471 {
4472 static const char *mmcs_name[] =
4473 {
4474 NULL,
4475 "Audio",
4476 "Capture",
4477 "Distribution",
4478 "Games",
4479 "Playback",
4480 "Pro Audio",
4481 "Window Manager"
4482 };
4483 HANDLE task;
4484
4485 if (hTask == NULL)
4486 return paUnanticipatedHostError;
4487
4488 if ((UINT32)nPriorityClass >= STATIC_ARRAY_SIZE(mmcs_name))
4489 return paUnanticipatedHostError;
4490
4491 task = MMCSS_activate(mmcs_name[nPriorityClass]);
4492 if (task == NULL)
4493 return paUnanticipatedHostError;
4494
4495 (*hTask) = task;
4496 return paNoError;
4497 }
4498
4499 // ------------------------------------------------------------------------------------------
PaWasapi_ThreadPriorityRevert(void * hTask)4500 PaError PaWasapi_ThreadPriorityRevert(void *hTask)
4501 {
4502 if (hTask == NULL)
4503 return paUnanticipatedHostError;
4504
4505 MMCSS_deactivate((HANDLE)hTask);
4506
4507 return paNoError;
4508 }
4509
4510 // ------------------------------------------------------------------------------------------
4511 // Described at:
4512 // http://msdn.microsoft.com/en-us/library/dd371387(v=VS.85).aspx
4513
PaWasapi_GetJackCount(PaDeviceIndex nDevice,int * jcount)4514 PaError PaWasapi_GetJackCount(PaDeviceIndex nDevice, int *jcount)
4515 {
4516 #ifndef PA_WINRT
4517 PaError ret;
4518 HRESULT hr = S_OK;
4519 PaDeviceIndex index;
4520 IDeviceTopology *pDeviceTopology = NULL;
4521 IConnector *pConnFrom = NULL;
4522 IConnector *pConnTo = NULL;
4523 IPart *pPart = NULL;
4524 IKsJackDescription *pJackDesc = NULL;
4525 UINT jackCount = 0;
4526
4527 PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
4528 if (paWasapi == NULL)
4529 return paNotInitialized;
4530
4531 // Get device index.
4532 ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
4533 if (ret != paNoError)
4534 return ret;
4535
4536 // Validate index.
4537 if ((UINT32)index >= paWasapi->deviceCount)
4538 return paInvalidDevice;
4539
4540 // Get the endpoint device's IDeviceTopology interface.
4541 hr = IMMDevice_Activate(paWasapi->devInfo[index].device, &pa_IID_IDeviceTopology,
4542 CLSCTX_INPROC_SERVER, NULL, (void**)&pDeviceTopology);
4543 IF_FAILED_JUMP(hr, error);
4544
4545 // The device topology for an endpoint device always contains just one connector (connector number 0).
4546 hr = IDeviceTopology_GetConnector(pDeviceTopology, 0, &pConnFrom);
4547 IF_FAILED_JUMP(hr, error);
4548
4549 // Step across the connection to the jack on the adapter.
4550 hr = IConnector_GetConnectedTo(pConnFrom, &pConnTo);
4551 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
4552 {
4553 // The adapter device is not currently active.
4554 hr = E_NOINTERFACE;
4555 }
4556 IF_FAILED_JUMP(hr, error);
4557
4558 // Get the connector's IPart interface.
4559 hr = IConnector_QueryInterface(pConnTo, &pa_IID_IPart, (void**)&pPart);
4560 IF_FAILED_JUMP(hr, error);
4561
4562 // Activate the connector's IKsJackDescription interface.
4563 hr = IPart_Activate(pPart, CLSCTX_INPROC_SERVER, &pa_IID_IKsJackDescription, (void**)&pJackDesc);
4564 IF_FAILED_JUMP(hr, error);
4565
4566 // Return jack count for this device.
4567 hr = IKsJackDescription_GetJackCount(pJackDesc, &jackCount);
4568 IF_FAILED_JUMP(hr, error);
4569
4570 // Set.
4571 (*jcount) = jackCount;
4572
4573 // Ok.
4574 ret = paNoError;
4575
4576 error:
4577
4578 SAFE_RELEASE(pDeviceTopology);
4579 SAFE_RELEASE(pConnFrom);
4580 SAFE_RELEASE(pConnTo);
4581 SAFE_RELEASE(pPart);
4582 SAFE_RELEASE(pJackDesc);
4583
4584 LogHostError(hr);
4585 return paNoError;
4586 #else
4587 (void)nDevice;
4588 (void)jcount;
4589 return paUnanticipatedHostError;
4590 #endif
4591 }
4592
4593 // ------------------------------------------------------------------------------------------
4594 #ifndef PA_WINRT
ConvertJackConnectionTypeWASAPIToPA(int connType)4595 static PaWasapiJackConnectionType ConvertJackConnectionTypeWASAPIToPA(int connType)
4596 {
4597 switch (connType)
4598 {
4599 case eConnTypeUnknown: return eJackConnTypeUnknown;
4600 #ifdef _KS_
4601 case eConnType3Point5mm: return eJackConnType3Point5mm;
4602 #else
4603 case eConnTypeEighth: return eJackConnType3Point5mm;
4604 #endif
4605 case eConnTypeQuarter: return eJackConnTypeQuarter;
4606 case eConnTypeAtapiInternal: return eJackConnTypeAtapiInternal;
4607 case eConnTypeRCA: return eJackConnTypeRCA;
4608 case eConnTypeOptical: return eJackConnTypeOptical;
4609 case eConnTypeOtherDigital: return eJackConnTypeOtherDigital;
4610 case eConnTypeOtherAnalog: return eJackConnTypeOtherAnalog;
4611 case eConnTypeMultichannelAnalogDIN: return eJackConnTypeMultichannelAnalogDIN;
4612 case eConnTypeXlrProfessional: return eJackConnTypeXlrProfessional;
4613 case eConnTypeRJ11Modem: return eJackConnTypeRJ11Modem;
4614 case eConnTypeCombination: return eJackConnTypeCombination;
4615 }
4616 return eJackConnTypeUnknown;
4617 }
4618 #endif
4619
4620 // ------------------------------------------------------------------------------------------
4621 #ifndef PA_WINRT
ConvertJackGeoLocationWASAPIToPA(int geoLoc)4622 static PaWasapiJackGeoLocation ConvertJackGeoLocationWASAPIToPA(int geoLoc)
4623 {
4624 switch (geoLoc)
4625 {
4626 case eGeoLocRear: return eJackGeoLocRear;
4627 case eGeoLocFront: return eJackGeoLocFront;
4628 case eGeoLocLeft: return eJackGeoLocLeft;
4629 case eGeoLocRight: return eJackGeoLocRight;
4630 case eGeoLocTop: return eJackGeoLocTop;
4631 case eGeoLocBottom: return eJackGeoLocBottom;
4632 #ifdef _KS_
4633 case eGeoLocRearPanel: return eJackGeoLocRearPanel;
4634 #else
4635 case eGeoLocRearOPanel: return eJackGeoLocRearPanel;
4636 #endif
4637 case eGeoLocRiser: return eJackGeoLocRiser;
4638 case eGeoLocInsideMobileLid: return eJackGeoLocInsideMobileLid;
4639 case eGeoLocDrivebay: return eJackGeoLocDrivebay;
4640 case eGeoLocHDMI: return eJackGeoLocHDMI;
4641 case eGeoLocOutsideMobileLid: return eJackGeoLocOutsideMobileLid;
4642 case eGeoLocATAPI: return eJackGeoLocATAPI;
4643 }
4644 return eJackGeoLocUnk;
4645 }
4646 #endif
4647
4648 // ------------------------------------------------------------------------------------------
4649 #ifndef PA_WINRT
ConvertJackGenLocationWASAPIToPA(int genLoc)4650 static PaWasapiJackGenLocation ConvertJackGenLocationWASAPIToPA(int genLoc)
4651 {
4652 switch (genLoc)
4653 {
4654 case eGenLocPrimaryBox: return eJackGenLocPrimaryBox;
4655 case eGenLocInternal: return eJackGenLocInternal;
4656 #ifdef _KS_
4657 case eGenLocSeparate: return eJackGenLocSeparate;
4658 #else
4659 case eGenLocSeperate: return eJackGenLocSeparate;
4660 #endif
4661 case eGenLocOther: return eJackGenLocOther;
4662 }
4663 return eJackGenLocPrimaryBox;
4664 }
4665 #endif
4666
4667 // ------------------------------------------------------------------------------------------
4668 #ifndef PA_WINRT
ConvertJackPortConnectionWASAPIToPA(int portConn)4669 static PaWasapiJackPortConnection ConvertJackPortConnectionWASAPIToPA(int portConn)
4670 {
4671 switch (portConn)
4672 {
4673 case ePortConnJack: return eJackPortConnJack;
4674 case ePortConnIntegratedDevice: return eJackPortConnIntegratedDevice;
4675 case ePortConnBothIntegratedAndJack:return eJackPortConnBothIntegratedAndJack;
4676 case ePortConnUnknown: return eJackPortConnUnknown;
4677 }
4678 return eJackPortConnJack;
4679 }
4680 #endif
4681
4682 // ------------------------------------------------------------------------------------------
4683 // Described at:
4684 // http://msdn.microsoft.com/en-us/library/dd371387(v=VS.85).aspx
4685
PaWasapi_GetJackDescription(PaDeviceIndex nDevice,int jindex,PaWasapiJackDescription * pJackDescription)4686 PaError PaWasapi_GetJackDescription(PaDeviceIndex nDevice, int jindex, PaWasapiJackDescription *pJackDescription)
4687 {
4688 #ifndef PA_WINRT
4689 PaError ret;
4690 HRESULT hr = S_OK;
4691 PaDeviceIndex index;
4692 IDeviceTopology *pDeviceTopology = NULL;
4693 IConnector *pConnFrom = NULL;
4694 IConnector *pConnTo = NULL;
4695 IPart *pPart = NULL;
4696 IKsJackDescription *pJackDesc = NULL;
4697 KSJACK_DESCRIPTION jack = { 0 };
4698
4699 PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
4700 if (paWasapi == NULL)
4701 return paNotInitialized;
4702
4703 // Get device index.
4704 ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
4705 if (ret != paNoError)
4706 return ret;
4707
4708 // Validate index.
4709 if ((UINT32)index >= paWasapi->deviceCount)
4710 return paInvalidDevice;
4711
4712 // Get the endpoint device's IDeviceTopology interface.
4713 hr = IMMDevice_Activate(paWasapi->devInfo[index].device, &pa_IID_IDeviceTopology,
4714 CLSCTX_INPROC_SERVER, NULL, (void**)&pDeviceTopology);
4715 IF_FAILED_JUMP(hr, error);
4716
4717 // The device topology for an endpoint device always contains just one connector (connector number 0).
4718 hr = IDeviceTopology_GetConnector(pDeviceTopology, 0, &pConnFrom);
4719 IF_FAILED_JUMP(hr, error);
4720
4721 // Step across the connection to the jack on the adapter.
4722 hr = IConnector_GetConnectedTo(pConnFrom, &pConnTo);
4723 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
4724 {
4725 // The adapter device is not currently active.
4726 hr = E_NOINTERFACE;
4727 }
4728 IF_FAILED_JUMP(hr, error);
4729
4730 // Get the connector's IPart interface.
4731 hr = IConnector_QueryInterface(pConnTo, &pa_IID_IPart, (void**)&pPart);
4732 IF_FAILED_JUMP(hr, error);
4733
4734 // Activate the connector's IKsJackDescription interface.
4735 hr = IPart_Activate(pPart, CLSCTX_INPROC_SERVER, &pa_IID_IKsJackDescription, (void**)&pJackDesc);
4736 IF_FAILED_JUMP(hr, error);
4737
4738 // Test to return jack description struct for index 0.
4739 hr = IKsJackDescription_GetJackDescription(pJackDesc, jindex, &jack);
4740 IF_FAILED_JUMP(hr, error);
4741
4742 // Convert WASAPI values to PA format.
4743 pJackDescription->channelMapping = jack.ChannelMapping;
4744 pJackDescription->color = jack.Color;
4745 pJackDescription->connectionType = ConvertJackConnectionTypeWASAPIToPA(jack.ConnectionType);
4746 pJackDescription->genLocation = ConvertJackGenLocationWASAPIToPA(jack.GenLocation);
4747 pJackDescription->geoLocation = ConvertJackGeoLocationWASAPIToPA(jack.GeoLocation);
4748 pJackDescription->isConnected = jack.IsConnected;
4749 pJackDescription->portConnection = ConvertJackPortConnectionWASAPIToPA(jack.PortConnection);
4750
4751 // Ok.
4752 ret = paNoError;
4753
4754 error:
4755
4756 SAFE_RELEASE(pDeviceTopology);
4757 SAFE_RELEASE(pConnFrom);
4758 SAFE_RELEASE(pConnTo);
4759 SAFE_RELEASE(pPart);
4760 SAFE_RELEASE(pJackDesc);
4761
4762 LogHostError(hr);
4763 return ret;
4764
4765 #else
4766 (void)nDevice;
4767 (void)jindex;
4768 (void)pJackDescription;
4769 return paUnanticipatedHostError;
4770 #endif
4771 }
4772
4773 // ------------------------------------------------------------------------------------------
_PollGetOutputFramesAvailable(PaWasapiStream * stream,UINT32 * available)4774 HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available)
4775 {
4776 HRESULT hr;
4777 UINT32 frames = stream->out.framesPerHostCallback,
4778 padding = 0;
4779
4780 (*available) = 0;
4781
4782 // get read position
4783 if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &padding)) != S_OK)
4784 return LogHostError(hr);
4785
4786 // get available
4787 frames -= padding;
4788
4789 // set
4790 (*available) = frames;
4791 return hr;
4792 }
4793
4794 // ------------------------------------------------------------------------------------------
_PollGetInputFramesAvailable(PaWasapiStream * stream,UINT32 * available)4795 HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available)
4796 {
4797 HRESULT hr;
4798
4799 (*available) = 0;
4800
4801 // GetCurrentPadding() has opposite meaning to Output stream
4802 if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, available)) != S_OK)
4803 return LogHostError(hr);
4804
4805 return hr;
4806 }
4807
4808 // ------------------------------------------------------------------------------------------
ProcessOutputBuffer(PaWasapiStream * stream,PaWasapiHostProcessor * processor,UINT32 frames)4809 HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)
4810 {
4811 HRESULT hr;
4812 BYTE *data = NULL;
4813
4814 // Get buffer
4815 if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, frames, &data)) != S_OK)
4816 {
4817 if (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED)
4818 {
4819 // Using GetCurrentPadding to overcome AUDCLNT_E_BUFFER_TOO_LARGE in
4820 // shared mode results in no sound in Event-driven mode (MSDN does not
4821 // document this, or is it WASAPI bug?), thus we better
4822 // try to acquire buffer next time when GetBuffer allows to do so.
4823 #if 0
4824 // Get Read position
4825 UINT32 padding = 0;
4826 hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &padding);
4827 if (hr != S_OK)
4828 return LogHostError(hr);
4829
4830 // Get frames to write
4831 frames -= padding;
4832 if (frames == 0)
4833 return S_OK;
4834
4835 if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, frames, &data)) != S_OK)
4836 return LogHostError(hr);
4837 #else
4838 if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
4839 return S_OK; // be silent in shared mode, try again next time
4840 #endif
4841 }
4842 else
4843 return LogHostError(hr);
4844 }
4845
4846 // Process data
4847 if (stream->out.monoMixer != NULL)
4848 {
4849 // expand buffer
4850 UINT32 mono_frames_size = frames * (stream->out.wavex.Format.wBitsPerSample / 8);
4851 if (mono_frames_size > stream->out.monoBufferSize)
4852 stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
4853
4854 // process
4855 processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);
4856
4857 // mix 1 to 2 channels
4858 stream->out.monoMixer(data, stream->out.monoBuffer, frames);
4859 }
4860 else
4861 {
4862 processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData);
4863 }
4864
4865 // Release buffer
4866 if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, frames, 0)) != S_OK)
4867 LogHostError(hr);
4868
4869 return hr;
4870 }
4871
4872 // ------------------------------------------------------------------------------------------
ProcessInputBuffer(PaWasapiStream * stream,PaWasapiHostProcessor * processor)4873 HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)
4874 {
4875 HRESULT hr = S_OK;
4876 UINT32 frames;
4877 BYTE *data = NULL;
4878 DWORD flags = 0;
4879
4880 for (;;)
4881 {
4882 // Check if blocking call must be interrupted
4883 if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)
4884 break;
4885
4886 // Findout if any frames available
4887 frames = 0;
4888 if ((hr = _PollGetInputFramesAvailable(stream, &frames)) != S_OK)
4889 return hr;
4890
4891 // Empty/consumed buffer
4892 if (frames == 0)
4893 break;
4894
4895 // Get the available data in the shared buffer.
4896 if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &data, &frames, &flags, NULL, NULL)) != S_OK)
4897 {
4898 if (hr == AUDCLNT_S_BUFFER_EMPTY)
4899 {
4900 hr = S_OK;
4901 break; // Empty/consumed buffer
4902 }
4903
4904 return LogHostError(hr);
4905 break;
4906 }
4907
4908 // Detect silence
4909 // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
4910 // data = NULL;
4911
4912 // Process data
4913 if (stream->in.monoMixer != NULL)
4914 {
4915 // expand buffer
4916 UINT32 mono_frames_size = frames * (stream->in.wavex.Format.wBitsPerSample / 8);
4917 if (mono_frames_size > stream->in.monoBufferSize)
4918 stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
4919
4920 // mix 1 to 2 channels
4921 stream->in.monoMixer(stream->in.monoBuffer, data, frames);
4922
4923 // process
4924 processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData);
4925 }
4926 else
4927 {
4928 processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData);
4929 }
4930
4931 // Release buffer
4932 if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, frames)) != S_OK)
4933 return LogHostError(hr);
4934
4935 //break;
4936 }
4937
4938 return hr;
4939 }
4940
4941 // ------------------------------------------------------------------------------------------
_StreamOnStop(PaWasapiStream * stream)4942 void _StreamOnStop(PaWasapiStream *stream)
4943 {
4944 // Stop INPUT/OUTPUT clients
4945 if (!stream->bBlocking)
4946 {
4947 if (stream->in.clientProc != NULL)
4948 IAudioClient_Stop(stream->in.clientProc);
4949 if (stream->out.clientProc != NULL)
4950 IAudioClient_Stop(stream->out.clientProc);
4951 }
4952 else
4953 {
4954 if (stream->in.clientParent != NULL)
4955 IAudioClient_Stop(stream->in.clientParent);
4956 if (stream->out.clientParent != NULL)
4957 IAudioClient_Stop(stream->out.clientParent);
4958 }
4959
4960 // Restore thread priority
4961 if (stream->hAvTask != NULL)
4962 {
4963 PaWasapi_ThreadPriorityRevert(stream->hAvTask);
4964 stream->hAvTask = NULL;
4965 }
4966
4967 // Notify
4968 if (stream->streamRepresentation.streamFinishedCallback != NULL)
4969 stream->streamRepresentation.streamFinishedCallback(stream->streamRepresentation.userData);
4970 }
4971
4972 // ------------------------------------------------------------------------------------------
ProcThreadEvent(void * param)4973 PA_THREAD_FUNC ProcThreadEvent(void *param)
4974 {
4975 PaWasapiHostProcessor processor[S_COUNT];
4976 HRESULT hr;
4977 DWORD dwResult;
4978 PaWasapiStream *stream = (PaWasapiStream *)param;
4979 PaWasapiHostProcessor defaultProcessor;
4980 BOOL set_event[S_COUNT] = { FALSE, FALSE };
4981 BOOL bWaitAllEvents = FALSE;
4982 BOOL bThreadComInitialized = FALSE;
4983
4984 /*
4985 If COM is already initialized CoInitialize will either return
4986 FALSE, or RPC_E_CHANGED_MODE if it was initialized in a different
4987 threading mode. In either case we shouldn't consider it an error
4988 but we need to be careful to not call CoUninitialize() if
4989 RPC_E_CHANGED_MODE was returned.
4990 */
4991 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
4992 if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))
4993 {
4994 PRINT(("WASAPI: failed ProcThreadEvent CoInitialize"));
4995 return (UINT32)paUnanticipatedHostError;
4996 }
4997 if (hr != RPC_E_CHANGED_MODE)
4998 bThreadComInitialized = TRUE;
4999
5000 // Unmarshal stream pointers for safe COM operation
5001 hr = UnmarshalStreamComPointers(stream);
5002 if (hr != S_OK) {
5003 PRINT(("Error unmarshaling stream COM pointers. HRESULT: %i\n", hr));
5004 goto thread_end;
5005 }
5006
5007 // Waiting on all events in case of Full-Duplex/Exclusive mode.
5008 if ((stream->in.clientProc != NULL) && (stream->out.clientProc != NULL))
5009 {
5010 bWaitAllEvents = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) &&
5011 (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE);
5012 }
5013
5014 // Setup data processors
5015 defaultProcessor.processor = WaspiHostProcessingLoop;
5016 defaultProcessor.userData = stream;
5017 processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);
5018 processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);
5019
5020 // Boost thread priority
5021 PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
5022
5023 // Create events
5024 if (stream->event[S_OUTPUT] == NULL)
5025 {
5026 stream->event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
5027 set_event[S_OUTPUT] = TRUE;
5028 }
5029 if (stream->event[S_INPUT] == NULL)
5030 {
5031 stream->event[S_INPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
5032 set_event[S_INPUT] = TRUE;
5033 }
5034 if ((stream->event[S_OUTPUT] == NULL) || (stream->event[S_INPUT] == NULL))
5035 {
5036 PRINT(("WASAPI Thread: failed creating Input/Output event handle\n"));
5037 goto thread_error;
5038 }
5039
5040 // Initialize event & start INPUT stream
5041 if (stream->in.clientProc)
5042 {
5043 // Create & set handle
5044 if (set_event[S_INPUT])
5045 {
5046 if ((hr = IAudioClient_SetEventHandle(stream->in.clientProc, stream->event[S_INPUT])) != S_OK)
5047 {
5048 LogHostError(hr);
5049 goto thread_error;
5050 }
5051 }
5052
5053 // Start
5054 if ((hr = IAudioClient_Start(stream->in.clientProc)) != S_OK)
5055 {
5056 LogHostError(hr);
5057 goto thread_error;
5058 }
5059 }
5060
5061 // Initialize event & start OUTPUT stream
5062 if (stream->out.clientProc)
5063 {
5064 // Create & set handle
5065 if (set_event[S_OUTPUT])
5066 {
5067 if ((hr = IAudioClient_SetEventHandle(stream->out.clientProc, stream->event[S_OUTPUT])) != S_OK)
5068 {
5069 LogHostError(hr);
5070 goto thread_error;
5071 }
5072 }
5073
5074 // Preload buffer before start
5075 if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
5076 {
5077 LogHostError(hr);
5078 goto thread_error;
5079 }
5080
5081 // Start
5082 if ((hr = IAudioClient_Start(stream->out.clientProc)) != S_OK)
5083 {
5084 LogHostError(hr);
5085 goto thread_error;
5086 }
5087
5088 }
5089
5090 // Signal: stream running
5091 stream->running = TRUE;
5092
5093 // Notify: thread started
5094 SetEvent(stream->hThreadStart);
5095
5096 // Processing Loop
5097 for (;;)
5098 {
5099 // 10 sec timeout (on timeout stream will auto-stop when processed by WAIT_TIMEOUT case)
5100 dwResult = WaitForMultipleObjects(S_COUNT, stream->event, bWaitAllEvents, 10*1000);
5101
5102 // Check for close event (after wait for buffers to avoid any calls to user
5103 // callback when hCloseRequest was set)
5104 if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)
5105 break;
5106
5107 // Process S_INPUT/S_OUTPUT
5108 switch (dwResult)
5109 {
5110 case WAIT_TIMEOUT: {
5111 PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n"));
5112 goto thread_end;
5113 break; }
5114
5115 // Input stream
5116 case WAIT_OBJECT_0 + S_INPUT: {
5117
5118 if (stream->captureClient == NULL)
5119 break;
5120
5121 if ((hr = ProcessInputBuffer(stream, processor)) != S_OK)
5122 {
5123 LogHostError(hr);
5124 goto thread_error;
5125 }
5126
5127 break; }
5128
5129 // Output stream
5130 case WAIT_OBJECT_0 + S_OUTPUT: {
5131
5132 if (stream->renderClient == NULL)
5133 break;
5134
5135 if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
5136 {
5137 LogHostError(hr);
5138 goto thread_error;
5139 }
5140
5141 break; }
5142 }
5143 }
5144
5145 thread_end:
5146
5147 // Process stop
5148 _StreamOnStop(stream);
5149
5150 // Release unmarshaled COM pointers
5151 ReleaseUnmarshaledComPointers(stream);
5152
5153 // Cleanup COM for this thread
5154 if (bThreadComInitialized == TRUE)
5155 CoUninitialize();
5156
5157 // Notify: not running
5158 stream->running = FALSE;
5159
5160 // Notify: thread exited
5161 SetEvent(stream->hThreadExit);
5162
5163 return 0;
5164
5165 thread_error:
5166
5167 // Prevent deadlocking in Pa_StreamStart
5168 SetEvent(stream->hThreadStart);
5169
5170 // Exit
5171 goto thread_end;
5172 }
5173
5174 // ------------------------------------------------------------------------------------------
ProcThreadPoll(void * param)5175 PA_THREAD_FUNC ProcThreadPoll(void *param)
5176 {
5177 PaWasapiHostProcessor processor[S_COUNT];
5178 HRESULT hr;
5179 PaWasapiStream *stream = (PaWasapiStream *)param;
5180 PaWasapiHostProcessor defaultProcessor;
5181 INT32 i;
5182 ThreadIdleScheduler scheduler;
5183
5184 // Calculate the actual duration of the allocated buffer.
5185 DWORD sleep_ms = 0;
5186 DWORD sleep_ms_in;
5187 DWORD sleep_ms_out;
5188
5189 BOOL bThreadComInitialized = FALSE;
5190
5191 /*
5192 If COM is already initialized CoInitialize will either return
5193 FALSE, or RPC_E_CHANGED_MODE if it was initialized in a different
5194 threading mode. In either case we shouldn't consider it an error
5195 but we need to be careful to not call CoUninitialize() if
5196 RPC_E_CHANGED_MODE was returned.
5197 */
5198 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
5199 if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))
5200 {
5201 PRINT(("WASAPI: failed ProcThreadPoll CoInitialize"));
5202 return (UINT32)paUnanticipatedHostError;
5203 }
5204 if (hr != RPC_E_CHANGED_MODE)
5205 bThreadComInitialized = TRUE;
5206
5207 // Unmarshal stream pointers for safe COM operation
5208 hr = UnmarshalStreamComPointers(stream);
5209 if (hr != S_OK)
5210 {
5211 PRINT(("Error unmarshaling stream COM pointers. HRESULT: %i\n", hr));
5212 return 0;
5213 }
5214
5215 // Calculate timeout for next polling attempt.
5216 sleep_ms_in = GetFramesSleepTime(stream->in.framesPerHostCallback/WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec);
5217 sleep_ms_out = GetFramesSleepTime(stream->out.framesPerBuffer, stream->out.wavex.Format.nSamplesPerSec);
5218
5219 // WASAPI Input packets tend to expire very easily, let's limit sleep time to 2 milliseconds
5220 // for all cases. Please propose better solution if any.
5221 if (sleep_ms_in > 2)
5222 sleep_ms_in = 2;
5223
5224 // Adjust polling time for non-paUtilFixedHostBufferSize. Input stream is not adjustable as it is being
5225 // polled according its packet length.
5226 if (stream->bufferMode != paUtilFixedHostBufferSize)
5227 {
5228 //sleep_ms_in = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec);
5229 sleep_ms_out = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);
5230 }
5231
5232 // Choose smallest
5233 if ((sleep_ms_in != 0) && (sleep_ms_out != 0))
5234 sleep_ms = min(sleep_ms_in, sleep_ms_out);
5235 else
5236 {
5237 sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);
5238 }
5239 // Make sure not 0, othervise use ThreadIdleScheduler
5240 if (sleep_ms == 0)
5241 {
5242 sleep_ms_in = GetFramesSleepTimeMicroseconds(stream->in.framesPerHostCallback/WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec);
5243 sleep_ms_out = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);
5244
5245 // Choose smallest
5246 if ((sleep_ms_in != 0) && (sleep_ms_out != 0))
5247 sleep_ms = min(sleep_ms_in, sleep_ms_out);
5248 else
5249 {
5250 sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);
5251 }
5252
5253 // Setup thread sleep scheduler
5254 ThreadIdleScheduler_Setup(&scheduler, 1, sleep_ms/* microseconds here */);
5255 sleep_ms = 0;
5256 }
5257
5258 // Setup data processors
5259 defaultProcessor.processor = WaspiHostProcessingLoop;
5260 defaultProcessor.userData = stream;
5261 processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);
5262 processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);
5263
5264 // Boost thread priority
5265 PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
5266
5267 // Initialize event & start INPUT stream
5268 if (stream->in.clientProc)
5269 {
5270 if ((hr = IAudioClient_Start(stream->in.clientProc)) != S_OK)
5271 {
5272 LogHostError(hr);
5273 goto thread_error;
5274 }
5275 }
5276
5277 // Initialize event & start OUTPUT stream
5278 if (stream->out.clientProc)
5279 {
5280 // Preload buffer (obligatory, othervise ->Start() will fail), avoid processing
5281 // when in full-duplex mode as it requires input processing as well
5282 if (!PA_WASAPI__IS_FULLDUPLEX(stream))
5283 {
5284 UINT32 frames = 0;
5285 if ((hr = _PollGetOutputFramesAvailable(stream, &frames)) == S_OK)
5286 {
5287 if (stream->bufferMode == paUtilFixedHostBufferSize)
5288 {
5289 if (frames >= stream->out.framesPerBuffer)
5290 {
5291 frames = stream->out.framesPerBuffer;
5292
5293 if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK)
5294 {
5295 LogHostError(hr); // not fatal, just log
5296 }
5297 }
5298 }
5299 else
5300 {
5301 if (frames != 0)
5302 {
5303 if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK)
5304 {
5305 LogHostError(hr); // not fatal, just log
5306 }
5307 }
5308 }
5309 }
5310 else
5311 {
5312 LogHostError(hr); // not fatal, just log
5313 }
5314 }
5315
5316 // Start
5317 if ((hr = IAudioClient_Start(stream->out.clientProc)) != S_OK)
5318 {
5319 LogHostError(hr);
5320 goto thread_error;
5321 }
5322 }
5323
5324 // Signal: stream running
5325 stream->running = TRUE;
5326
5327 // Notify: thread started
5328 SetEvent(stream->hThreadStart);
5329
5330 if (!PA_WASAPI__IS_FULLDUPLEX(stream))
5331 {
5332 // Processing Loop
5333 UINT32 next_sleep = sleep_ms;
5334 while (WaitForSingleObject(stream->hCloseRequest, next_sleep) == WAIT_TIMEOUT)
5335 {
5336 // Get next sleep time
5337 if (sleep_ms == 0)
5338 {
5339 next_sleep = ThreadIdleScheduler_NextSleep(&scheduler);
5340 }
5341
5342 for (i = 0; i < S_COUNT; ++i)
5343 {
5344 // Process S_INPUT/S_OUTPUT
5345 switch (i)
5346 {
5347 // Input stream
5348 case S_INPUT: {
5349
5350 if (stream->captureClient == NULL)
5351 break;
5352
5353 if ((hr = ProcessInputBuffer(stream, processor)) != S_OK)
5354 {
5355 LogHostError(hr);
5356 goto thread_error;
5357 }
5358
5359 break; }
5360
5361 // Output stream
5362 case S_OUTPUT: {
5363
5364 UINT32 frames;
5365 if (stream->renderClient == NULL)
5366 break;
5367
5368 // get available frames
5369 if ((hr = _PollGetOutputFramesAvailable(stream, &frames)) != S_OK)
5370 {
5371 LogHostError(hr);
5372 goto thread_error;
5373 }
5374
5375 // output
5376 if (stream->bufferMode == paUtilFixedHostBufferSize)
5377 {
5378 while (frames >= stream->out.framesPerBuffer)
5379 {
5380 if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
5381 {
5382 LogHostError(hr);
5383 goto thread_error;
5384 }
5385
5386 frames -= stream->out.framesPerBuffer;
5387 }
5388 }
5389 else
5390 {
5391 if (frames != 0)
5392 {
5393 if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK)
5394 {
5395 LogHostError(hr);
5396 goto thread_error;
5397 }
5398 }
5399 }
5400
5401 break; }
5402 }
5403 }
5404 }
5405 }
5406 else
5407 {
5408 #if 0
5409 // Processing Loop
5410 while (WaitForSingleObject(stream->hCloseRequest, 1) == WAIT_TIMEOUT)
5411 {
5412 UINT32 i_frames = 0, i_processed = 0;
5413 BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;
5414 DWORD i_flags = 0;
5415 UINT32 o_frames = 0;
5416
5417 // get host input buffer
5418 if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)
5419 {
5420 if (hr == AUDCLNT_S_BUFFER_EMPTY)
5421 continue; // no data in capture buffer
5422
5423 LogHostError(hr);
5424 break;
5425 }
5426
5427 // get available frames
5428 if ((hr = _PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)
5429 {
5430 // release input buffer
5431 IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
5432
5433 LogHostError(hr);
5434 break;
5435 }
5436
5437 // process equal ammount of frames
5438 if (o_frames >= i_frames)
5439 {
5440 // process input ammount of frames
5441 UINT32 o_processed = i_frames;
5442
5443 // get host output buffer
5444 if ((hr = IAudioRenderClient_GetBuffer(stream->procRCClient, o_processed, &o_data)) == S_OK)
5445 {
5446 // processed amount of i_frames
5447 i_processed = i_frames;
5448 o_data_host = o_data;
5449
5450 // convert output mono
5451 if (stream->out.monoMixer)
5452 {
5453 UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8);
5454 // expand buffer
5455 if (mono_frames_size > stream->out.monoBufferSize)
5456 {
5457 stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
5458 if (stream->out.monoBuffer == NULL)
5459 {
5460 // release input buffer
5461 IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
5462 // release output buffer
5463 IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0);
5464
5465 LogPaError(paInsufficientMemory);
5466 break;
5467 }
5468 }
5469
5470 // replace buffer pointer
5471 o_data = (BYTE *)stream->out.monoBuffer;
5472 }
5473
5474 // convert input mono
5475 if (stream->in.monoMixer)
5476 {
5477 UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8);
5478 // expand buffer
5479 if (mono_frames_size > stream->in.monoBufferSize)
5480 {
5481 stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
5482 if (stream->in.monoBuffer == NULL)
5483 {
5484 // release input buffer
5485 IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
5486 // release output buffer
5487 IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0);
5488
5489 LogPaError(paInsufficientMemory);
5490 break;
5491 }
5492 }
5493
5494 // mix 2 to 1 input channels
5495 stream->in.monoMixer(stream->in.monoBuffer, i_data, i_processed);
5496
5497 // replace buffer pointer
5498 i_data = (BYTE *)stream->in.monoBuffer;
5499 }
5500
5501 // process
5502 processor[S_FULLDUPLEX].processor(i_data, i_processed, o_data, o_processed, processor[S_FULLDUPLEX].userData);
5503
5504 // mix 1 to 2 output channels
5505 if (stream->out.monoBuffer)
5506 stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed);
5507
5508 // release host output buffer
5509 if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, o_processed, 0)) != S_OK)
5510 LogHostError(hr);
5511 }
5512 else
5513 {
5514 if (stream->out.shareMode != AUDCLNT_SHAREMODE_SHARED)
5515 LogHostError(hr); // be silent in shared mode, try again next time
5516 }
5517 }
5518
5519 // release host input buffer
5520 if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, i_processed)) != S_OK)
5521 {
5522 LogHostError(hr);
5523 break;
5524 }
5525 }
5526 #else
5527 // Processing Loop
5528 UINT32 next_sleep = sleep_ms;
5529 while (WaitForSingleObject(stream->hCloseRequest, next_sleep) == WAIT_TIMEOUT)
5530 {
5531 UINT32 i_frames = 0, i_processed = 0;
5532 BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;
5533 DWORD i_flags = 0;
5534 UINT32 o_frames = 0;
5535
5536 // Get next sleep time
5537 if (sleep_ms == 0)
5538 {
5539 next_sleep = ThreadIdleScheduler_NextSleep(&scheduler);
5540 }
5541
5542 // get available frames
5543 if ((hr = _PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)
5544 {
5545 LogHostError(hr);
5546 break;
5547 }
5548
5549 while (o_frames != 0)
5550 {
5551 // get host input buffer
5552 if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)
5553 {
5554 if (hr == AUDCLNT_S_BUFFER_EMPTY)
5555 break; // no data in capture buffer
5556
5557 LogHostError(hr);
5558 break;
5559 }
5560
5561 // process equal ammount of frames
5562 if (o_frames >= i_frames)
5563 {
5564 // process input ammount of frames
5565 UINT32 o_processed = i_frames;
5566
5567 // get host output buffer
5568 if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, o_processed, &o_data)) == S_OK)
5569 {
5570 // processed amount of i_frames
5571 i_processed = i_frames;
5572 o_data_host = o_data;
5573
5574 // convert output mono
5575 if (stream->out.monoMixer)
5576 {
5577 UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8);
5578 // expand buffer
5579 if (mono_frames_size > stream->out.monoBufferSize)
5580 {
5581 stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
5582 if (stream->out.monoBuffer == NULL)
5583 {
5584 // release input buffer
5585 IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
5586 // release output buffer
5587 IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0);
5588
5589 LogPaError(paInsufficientMemory);
5590 goto thread_error;
5591 }
5592 }
5593
5594 // replace buffer pointer
5595 o_data = (BYTE *)stream->out.monoBuffer;
5596 }
5597
5598 // convert input mono
5599 if (stream->in.monoMixer)
5600 {
5601 UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8);
5602 // expand buffer
5603 if (mono_frames_size > stream->in.monoBufferSize)
5604 {
5605 stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
5606 if (stream->in.monoBuffer == NULL)
5607 {
5608 // release input buffer
5609 IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
5610 // release output buffer
5611 IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0);
5612
5613 LogPaError(paInsufficientMemory);
5614 goto thread_error;
5615 }
5616 }
5617
5618 // mix 2 to 1 input channels
5619 stream->in.monoMixer(stream->in.monoBuffer, i_data, i_processed);
5620
5621 // replace buffer pointer
5622 i_data = (BYTE *)stream->in.monoBuffer;
5623 }
5624
5625 // process
5626 processor[S_FULLDUPLEX].processor(i_data, i_processed, o_data, o_processed, processor[S_FULLDUPLEX].userData);
5627
5628 // mix 1 to 2 output channels
5629 if (stream->out.monoBuffer)
5630 stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed);
5631
5632 // release host output buffer
5633 if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, o_processed, 0)) != S_OK)
5634 LogHostError(hr);
5635
5636 o_frames -= o_processed;
5637 }
5638 else
5639 {
5640 if (stream->out.shareMode != AUDCLNT_SHAREMODE_SHARED)
5641 LogHostError(hr); // be silent in shared mode, try again next time
5642 }
5643 }
5644 else
5645 {
5646 i_processed = 0;
5647 goto fd_release_buffer_in;
5648 }
5649
5650 fd_release_buffer_in:
5651
5652 // release host input buffer
5653 if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, i_processed)) != S_OK)
5654 {
5655 LogHostError(hr);
5656 break;
5657 }
5658
5659 // break processing, input hasn't been accumulated yet
5660 if (i_processed == 0)
5661 break;
5662 }
5663 }
5664 #endif
5665 }
5666
5667 thread_end:
5668
5669 // Process stop
5670 _StreamOnStop(stream);
5671
5672 // Release unmarshaled COM pointers
5673 ReleaseUnmarshaledComPointers(stream);
5674
5675 // Cleanup COM for this thread
5676 if (bThreadComInitialized == TRUE)
5677 CoUninitialize();
5678
5679 // Notify: not running
5680 stream->running = FALSE;
5681
5682 // Notify: thread exited
5683 SetEvent(stream->hThreadExit);
5684
5685 return 0;
5686
5687 thread_error:
5688
5689 // Prevent deadlocking in Pa_StreamStart
5690 SetEvent(stream->hThreadStart);
5691
5692 // Exit
5693 goto thread_end;
5694 }
5695
5696 // ------------------------------------------------------------------------------------------
PaWasapi_ReallocateMemory(void * ptr,size_t size)5697 void *PaWasapi_ReallocateMemory(void *ptr, size_t size)
5698 {
5699 return realloc(ptr, size);
5700 }
5701
5702 // ------------------------------------------------------------------------------------------
PaWasapi_FreeMemory(void * ptr)5703 void PaWasapi_FreeMemory(void *ptr)
5704 {
5705 free(ptr);
5706 }
5707
5708 //#endif //VC 2005
5709
5710
5711
5712
5713 #if 0
5714 if(bFirst) {
5715 float masteur;
5716 hr = stream->outVol->GetMasterVolumeLevelScalar(&masteur);
5717 if (hr != S_OK)
5718 LogHostError(hr);
5719 float chan1, chan2;
5720 hr = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);
5721 if (hr != S_OK)
5722 LogHostError(hr);
5723 hr = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);
5724 if (hr != S_OK)
5725 LogHostError(hr);
5726
5727 BOOL bMute;
5728 hr = stream->outVol->GetMute(&bMute);
5729 if (hr != S_OK)
5730 LogHostError(hr);
5731
5732 stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);
5733 stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);
5734 stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL);
5735 stream->outVol->SetMute(FALSE, NULL);
5736 bFirst = FALSE;
5737 }
5738 #endif
5739