To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at https://github.com/sonic-visualiser/sv-dependency-builds .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / src / portaudio_20161030_catalina_patch / src / hostapi / wasapi / pa_win_wasapi.c @ 164:9fa11135915a

History | View | Annotate | Download (188 KB)

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, &params, 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