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