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