~ubuntu-branches/ubuntu/trusty/teeworlds/trusty-updates

« back to all changes in this revision

Viewing changes to src/engine/external/portaudio/src/hostapi/wasapi/pa_win_wasapi.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jack Coulter
  • Date: 2008-04-13 18:48:12 UTC
  • Revision ID: james.westby@ubuntu.com-20080413184812-efc80waq2er6p1bs
Tags: upstream-0.4.2
ImportĀ upstreamĀ versionĀ 0.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Portable Audio I/O Library WASAPI implementation
 
3
 * Copyright (c) 2006-2007 David Viens
 
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
 
 
43
 @note pa_wasapi currently requires VC 2005, and the latest Vista SDK
 
44
*/
 
45
 
 
46
#if _MSC_VER >= 1400
 
47
#include <windows.h>
 
48
#include <MMReg.h>  //must be before other Wasapi headers
 
49
#include <strsafe.h>
 
50
#include <mmdeviceapi.h>
 
51
#include <Avrt.h>
 
52
#include <audioclient.h>
 
53
#include <Endpointvolume.h>
 
54
 
 
55
#include <KsMedia.h>
 
56
#include <functiondiscoverykeys.h>  // PKEY_Device_FriendlyName
 
57
#endif
 
58
 
 
59
#include "pa_util.h"
 
60
#include "pa_allocation.h"
 
61
#include "pa_hostapi.h"
 
62
#include "pa_stream.h"
 
63
#include "pa_cpuload.h"
 
64
#include "pa_process.h"
 
65
#include "pa_debugprint.h"
 
66
 
 
67
 
 
68
/*
 
69
  davidv : work in progress. try using with 48000 , then 44100
 
70
  and shared mode FIRST.
 
71
 */
 
72
 
 
73
#define PORTAUDIO_SHAREMODE     AUDCLNT_SHAREMODE_SHARED
 
74
//#define PORTAUDIO_SHAREMODE   AUDCLNT_SHAREMODE_EXCLUSIVE
 
75
 
 
76
 
 
77
 
 
78
/* prototypes for functions declared in this file */
 
79
 
 
80
#ifdef __cplusplus
 
81
extern "C"
 
82
{
 
83
#endif /* __cplusplus */
 
84
 
 
85
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
 
86
 
 
87
#ifdef __cplusplus
 
88
}
 
89
#endif /* __cplusplus */
 
90
 
 
91
 
 
92
 
 
93
 
 
94
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
 
95
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 
96
                                  const PaStreamParameters *inputParameters,
 
97
                                  const PaStreamParameters *outputParameters,
 
98
                                  double sampleRate );
 
99
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 
100
                           PaStream** s,
 
101
                           const PaStreamParameters *inputParameters,
 
102
                           const PaStreamParameters *outputParameters,
 
103
                           double sampleRate,
 
104
                           unsigned long framesPerBuffer,
 
105
                           PaStreamFlags streamFlags,
 
106
                           PaStreamCallback *streamCallback,
 
107
                           void *userData );
 
108
static PaError CloseStream( PaStream* stream );
 
109
static PaError StartStream( PaStream *stream );
 
110
static PaError StopStream( PaStream *stream );
 
111
static PaError AbortStream( PaStream *stream );
 
112
static PaError IsStreamStopped( PaStream *s );
 
113
static PaError IsStreamActive( PaStream *stream );
 
114
static PaTime GetStreamTime( PaStream *stream );
 
115
static double GetStreamCpuLoad( PaStream* stream );
 
116
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
 
117
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
 
118
static signed long GetStreamReadAvailable( PaStream* stream );
 
119
static signed long GetStreamWriteAvailable( PaStream* stream );
 
120
 
 
121
 
 
122
/* IMPLEMENT ME: a macro like the following one should be used for reporting
 
123
 host errors */
 
124
#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
 
125
    PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
 
126
 
 
127
/* PaWinWasapiHostApiRepresentation - host api datastructure specific to this implementation */
 
128
 
 
129
 
 
130
 
 
131
//dummy entry point for other compilers and sdks
 
132
//currently built using RC1 SDK (5600)
 
133
#if _MSC_VER < 1400
 
134
 
 
135
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ){
 
136
    return paNoError;
 
137
}
 
138
 
 
139
#else
 
140
 
 
141
 
 
142
 
 
143
 
 
144
#define MAX_STR_LEN 512
 
145
 
 
146
/*
 
147
 These are fields that can be gathered from IDevice
 
148
 and IAudioDevice PRIOR to Initialize, and done in first pass
 
149
 i assume that neither of these will cause the Driver to "load",
 
150
 but again, who knows how they implement their stuff
 
151
 */
 
152
typedef struct PaWinWasapiDeviceInfo
 
153
{
 
154
    //hmm is it wise to keep a reference until Terminate?
 
155
    //TODO Check if that interface requires the driver to be loaded!
 
156
    IMMDevice * device;
 
157
 
 
158
    //Fields filled from IDevice
 
159
    //from GetId
 
160
    WCHAR szDeviceID[MAX_STR_LEN];
 
161
    //from GetState
 
162
    DWORD state;
 
163
 
 
164
    //Fields filled from IMMEndpoint'sGetDataFlow
 
165
    EDataFlow  flow;
 
166
 
 
167
    //Fields filled from IAudioDevice (_prior_ to Initialize)
 
168
    //from GetDevicePeriod(
 
169
    REFERENCE_TIME  DefaultDevicePeriod;
 
170
    REFERENCE_TIME  MinimumDevicePeriod;
 
171
    //from GetMixFormat
 
172
    WAVEFORMATEX   *MixFormat;//needs to be CoTaskMemFree'd after use!
 
173
 
 
174
} PaWinWasapiDeviceInfo;
 
175
 
 
176
 
 
177
typedef struct
 
178
{
 
179
    PaUtilHostApiRepresentation inheritedHostApiRep;
 
180
    PaUtilStreamInterface callbackStreamInterface;
 
181
    PaUtilStreamInterface blockingStreamInterface;
 
182
 
 
183
    PaUtilAllocationGroup *allocations;
 
184
 
 
185
    /* implementation specific data goes here */
 
186
 
 
187
    //in case we later need the synch
 
188
    IMMDeviceEnumerator * enumerator;
 
189
 
 
190
    //this is the REAL number of devices, whether they are usefull to PA or not!
 
191
    UINT deviceCount;
 
192
 
 
193
    WCHAR defaultRenderer [MAX_STR_LEN];
 
194
    WCHAR defaultCapturer [MAX_STR_LEN];
 
195
 
 
196
    PaWinWasapiDeviceInfo   *devInfo;
 
197
}PaWinWasapiHostApiRepresentation;
 
198
 
 
199
 
 
200
/* PaWinWasapiStream - a stream data structure specifically for this implementation */
 
201
 
 
202
typedef struct PaWinWasapiSubStream{
 
203
    IAudioClient        *client;
 
204
    WAVEFORMATEXTENSIBLE wavex;
 
205
    UINT32               bufferSize;
 
206
    REFERENCE_TIME       latency;
 
207
    REFERENCE_TIME       period;
 
208
    unsigned long framesPerHostCallback; /* just an example */
 
209
}PaWinWasapiSubStream;
 
210
 
 
211
typedef struct PaWinWasapiStream
 
212
{ /* IMPLEMENT ME: rename this */
 
213
    PaUtilStreamRepresentation streamRepresentation;
 
214
    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
 
215
    PaUtilBufferProcessor bufferProcessor;
 
216
 
 
217
    /* IMPLEMENT ME:
 
218
            - implementation specific data goes here
 
219
    */
 
220
 
 
221
 
 
222
    //input
 
223
        PaWinWasapiSubStream in;
 
224
    IAudioCaptureClient *cclient;
 
225
    IAudioEndpointVolume *inVol;
 
226
        //output
 
227
        PaWinWasapiSubStream out;
 
228
    IAudioRenderClient  *rclient;
 
229
        IAudioEndpointVolume *outVol;
 
230
 
 
231
    bool running;
 
232
    bool closeRequest;
 
233
 
 
234
    DWORD dwThreadId;
 
235
    HANDLE hThread;
 
236
        HANDLE hNotificationEvent; 
 
237
 
 
238
    GUID  session;
 
239
 
 
240
}PaWinWasapiStream;
 
241
 
 
242
#define PRINT(x) PA_DEBUG(x);
 
243
 
 
244
void
 
245
logAUDCLNT_E(HRESULT res){
 
246
 
 
247
    char *text = 0;
 
248
    switch(res){
 
249
        case S_OK: return; break;
 
250
        case E_POINTER                              :text ="E_POINTER"; break;
 
251
        case E_INVALIDARG                           :text ="E_INVALIDARG"; break;
 
252
 
 
253
        case AUDCLNT_E_NOT_INITIALIZED              :text ="AUDCLNT_E_NOT_INITIALIZED"; break;
 
254
        case AUDCLNT_E_ALREADY_INITIALIZED          :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;
 
255
        case AUDCLNT_E_WRONG_ENDPOINT_TYPE          :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;
 
256
        case AUDCLNT_E_DEVICE_INVALIDATED           :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;
 
257
        case AUDCLNT_E_NOT_STOPPED                  :text ="AUDCLNT_E_NOT_STOPPED"; break;
 
258
        case AUDCLNT_E_BUFFER_TOO_LARGE             :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;
 
259
        case AUDCLNT_E_OUT_OF_ORDER                 :text ="AUDCLNT_E_OUT_OF_ORDER"; break;
 
260
        case AUDCLNT_E_UNSUPPORTED_FORMAT           :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
 
261
        case AUDCLNT_E_INVALID_SIZE                 :text ="AUDCLNT_E_INVALID_SIZE"; break;
 
262
        case AUDCLNT_E_DEVICE_IN_USE                :text ="AUDCLNT_E_DEVICE_IN_USE"; break;
 
263
        case AUDCLNT_E_BUFFER_OPERATION_PENDING     :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;
 
264
        case AUDCLNT_E_THREAD_NOT_REGISTERED        :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;      
 
265
                case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED   :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;
 
266
        case AUDCLNT_E_ENDPOINT_CREATE_FAILED       :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;
 
267
        case AUDCLNT_E_SERVICE_NOT_RUNNING          :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
 
268
     //  case AUDCLNT_E_CPUUSAGE_EXCEEDED            :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;
 
269
     //Header error?
 
270
        case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED     :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;
 
271
        case AUDCLNT_E_EXCLUSIVE_MODE_ONLY          :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;
 
272
        case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;
 
273
        case AUDCLNT_E_EVENTHANDLE_NOT_SET          :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
 
274
        case AUDCLNT_E_INCORRECT_BUFFER_SIZE        :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
 
275
        case AUDCLNT_E_BUFFER_SIZE_ERROR            :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
 
276
        case AUDCLNT_S_BUFFER_EMPTY                 :text ="AUDCLNT_S_BUFFER_EMPTY"; break;
 
277
        case AUDCLNT_S_THREAD_ALREADY_REGISTERED    :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
 
278
        default:
 
279
            text =" dunno!";
 
280
            return ;
 
281
        break;
 
282
 
 
283
    }
 
284
    PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n",res,text));
 
285
}
 
286
 
 
287
inline double
 
288
nano100ToMillis(const REFERENCE_TIME &ref){
 
289
    //  1 nano = 0.000000001 seconds
 
290
    //100 nano = 0.0000001   seconds
 
291
    //100 nano = 0.0001   milliseconds
 
292
    return ((double)ref)*0.0001;
 
293
}
 
294
 
 
295
inline double
 
296
nano100ToSeconds(const REFERENCE_TIME &ref){
 
297
    //  1 nano = 0.000000001 seconds
 
298
    //100 nano = 0.0000001   seconds
 
299
    //100 nano = 0.0001   milliseconds
 
300
    return ((double)ref)*0.0000001;
 
301
}
 
302
 
 
303
#ifndef IF_FAILED_JUMP
 
304
#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;
 
305
#endif
 
306
 
 
307
 
 
308
 
 
309
//AVRT is the new "multimedia schedulling stuff"
 
310
 
 
311
typedef BOOL   (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);
 
312
typedef BOOL   (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);
 
313
typedef BOOL   (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE);
 
314
typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics)  (LPCTSTR,LPDWORD);
 
315
typedef BOOL   (WINAPI *FAvSetMmThreadPriority)         (HANDLE,AVRT_PRIORITY);
 
316
 
 
317
HMODULE  hDInputDLL = 0;
 
318
FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup=0;
 
319
FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup=0;
 
320
FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup=0;
 
321
FAvSetMmThreadCharacteristics  pAvSetMmThreadCharacteristics=0;
 
322
FAvSetMmThreadPriority         pAvSetMmThreadPriority=0;
 
323
 
 
324
 
 
325
 
 
326
#define setupPTR(fun, type, name)  {                                                        \
 
327
                                        fun = (type) GetProcAddress(hDInputDLL,name);       \
 
328
                                        if(fun == NULL) {                                   \
 
329
                                            PRINT(("GetProcAddr failed for %s" ,name));     \
 
330
                                            return false;                                   \
 
331
                                        }                                                   \
 
332
                                    }                                                       \
 
333
 
 
334
bool
 
335
setupAVRT(){
 
336
 
 
337
    hDInputDLL = LoadLibraryA("avrt.dll");
 
338
    if(hDInputDLL == NULL)
 
339
        return false;
 
340
 
 
341
    setupPTR(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup");
 
342
    setupPTR(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup");
 
343
    setupPTR(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup");
 
344
    setupPTR(pAvSetMmThreadCharacteristics,  FAvSetMmThreadCharacteristics,  "AvSetMmThreadCharacteristicsA");
 
345
    setupPTR(pAvSetMmThreadPriority,         FAvSetMmThreadPriority,         "AvSetMmThreadPriority");
 
346
 
 
347
    return true;
 
348
}
 
349
 
 
350
 
 
351
 
 
352
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
 
353
{
 
354
    if (!setupAVRT()){
 
355
        PRINT(("Windows WASAPI : No AVRT! (not VISTA?)"));
 
356
        return paNoError;
 
357
    }
 
358
 
 
359
    CoInitialize(NULL);
 
360
 
 
361
    PaError result = paNoError;
 
362
    PaWinWasapiHostApiRepresentation *paWasapi;
 
363
    PaDeviceInfo *deviceInfoArray;
 
364
 
 
365
    paWasapi = (PaWinWasapiHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWasapiHostApiRepresentation) );
 
366
    if( !paWasapi ){
 
367
        result = paInsufficientMemory;
 
368
        goto error;
 
369
    }
 
370
 
 
371
    paWasapi->allocations = PaUtil_CreateAllocationGroup();
 
372
    if( !paWasapi->allocations ){
 
373
        result = paInsufficientMemory;
 
374
        goto error;
 
375
    }
 
376
 
 
377
    *hostApi = &paWasapi->inheritedHostApiRep;
 
378
    (*hostApi)->info.structVersion = 1;
 
379
    (*hostApi)->info.type = paWASAPI;
 
380
    (*hostApi)->info.name = "Windows WASAPI";
 
381
    (*hostApi)->info.deviceCount = 0;   //so far, we must investigate each
 
382
    (*hostApi)->info.defaultInputDevice  = paNoDevice;  /* IMPLEMENT ME */
 
383
    (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */
 
384
 
 
385
 
 
386
    HRESULT hResult = S_OK;
 
387
    IMMDeviceCollection* spEndpoints=0;
 
388
    paWasapi->enumerator = 0;
 
389
 
 
390
    hResult = CoCreateInstance(
 
391
             __uuidof(MMDeviceEnumerator), NULL,CLSCTX_INPROC_SERVER,
 
392
             __uuidof(IMMDeviceEnumerator),
 
393
             (void**)&paWasapi->enumerator);
 
394
 
 
395
    IF_FAILED_JUMP(hResult, error);
 
396
 
 
397
    //getting default device ids in the eMultimedia "role"
 
398
    {
 
399
        {
 
400
            IMMDevice* defaultRenderer=0;
 
401
            hResult = paWasapi->enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &defaultRenderer);
 
402
            IF_FAILED_JUMP(hResult, error);
 
403
            WCHAR* pszDeviceId = NULL;
 
404
            hResult = defaultRenderer->GetId(&pszDeviceId);
 
405
            IF_FAILED_JUMP(hResult, error);
 
406
            StringCchCopyW(paWasapi->defaultRenderer, MAX_STR_LEN-1, pszDeviceId);
 
407
            CoTaskMemFree(pszDeviceId);
 
408
            defaultRenderer->Release();
 
409
        }
 
410
 
 
411
        {
 
412
            IMMDevice* defaultCapturer=0;
 
413
            hResult = paWasapi->enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, &defaultCapturer);
 
414
            IF_FAILED_JUMP(hResult, error);
 
415
            WCHAR* pszDeviceId = NULL;
 
416
            hResult = defaultCapturer->GetId(&pszDeviceId);
 
417
            IF_FAILED_JUMP(hResult, error);
 
418
            StringCchCopyW(paWasapi->defaultCapturer, MAX_STR_LEN-1, pszDeviceId);
 
419
            CoTaskMemFree(pszDeviceId);
 
420
            defaultCapturer->Release();
 
421
        }
 
422
    }
 
423
 
 
424
 
 
425
    hResult = paWasapi->enumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &spEndpoints);
 
426
    IF_FAILED_JUMP(hResult, error);
 
427
 
 
428
    hResult = spEndpoints->GetCount(&paWasapi->deviceCount);
 
429
    IF_FAILED_JUMP(hResult, error);
 
430
 
 
431
    paWasapi->devInfo = new PaWinWasapiDeviceInfo[paWasapi->deviceCount];
 
432
    {
 
433
        for (size_t step=0;step<paWasapi->deviceCount;++step)
 
434
            memset(&paWasapi->devInfo[step],0,sizeof(PaWinWasapiDeviceInfo));
 
435
    }
 
436
 
 
437
 
 
438
 
 
439
    if( paWasapi->deviceCount > 0 )
 
440
    {
 
441
        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
 
442
                paWasapi->allocations, sizeof(PaDeviceInfo*) * paWasapi->deviceCount );
 
443
        if( !(*hostApi)->deviceInfos ){
 
444
            result = paInsufficientMemory;
 
445
            goto error;
 
446
        }
 
447
 
 
448
        /* allocate all device info structs in a contiguous block */
 
449
        deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
 
450
                paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount );
 
451
        if( !deviceInfoArray ){
 
452
            result = paInsufficientMemory;
 
453
            goto error;
 
454
        }
 
455
 
 
456
        for( UINT i=0; i < paWasapi->deviceCount; ++i ){
 
457
 
 
458
                        PA_DEBUG(("i:%d\n",i));
 
459
            PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
 
460
            deviceInfo->structVersion = 2;
 
461
            deviceInfo->hostApi = hostApiIndex;
 
462
 
 
463
            hResult = spEndpoints->Item(i, &paWasapi->devInfo[i].device);
 
464
            IF_FAILED_JUMP(hResult, error);
 
465
 
 
466
            //getting ID
 
467
            {
 
468
                WCHAR* pszDeviceId = NULL;
 
469
                hResult = paWasapi->devInfo[i].device->GetId(&pszDeviceId);
 
470
                IF_FAILED_JUMP(hResult, error);
 
471
                StringCchCopyW(paWasapi->devInfo[i].szDeviceID, MAX_STR_LEN-1, pszDeviceId);
 
472
                CoTaskMemFree(pszDeviceId);
 
473
 
 
474
                if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer)==0){
 
475
                    //we found the default input!
 
476
                    (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
 
477
                }
 
478
                if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer)==0){
 
479
                    //we found the default output!
 
480
                    (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
 
481
                }
 
482
            }
 
483
 
 
484
            DWORD state=0;
 
485
            hResult = paWasapi->devInfo[i].device->GetState(&paWasapi->devInfo[i].state);
 
486
            IF_FAILED_JUMP(hResult, error);
 
487
 
 
488
            if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE){
 
489
                PRINT(("WASAPI device:%d is not currently available (state:%d)\n",i,state));
 
490
                //spDevice->Release();
 
491
                //continue;
 
492
            }
 
493
 
 
494
            {
 
495
                IPropertyStore* spProperties;
 
496
                hResult = paWasapi->devInfo[i].device->OpenPropertyStore(STGM_READ, &spProperties);
 
497
                IF_FAILED_JUMP(hResult, error);
 
498
 
 
499
                //getting "Friendly" Name
 
500
                {
 
501
                    PROPVARIANT value;
 
502
                    PropVariantInit(&value);
 
503
                    hResult = spProperties->GetValue(PKEY_Device_FriendlyName, &value);
 
504
                    IF_FAILED_JUMP(hResult, error);
 
505
                    deviceInfo->name = 0;
 
506
                    char* deviceName = (char*)PaUtil_GroupAllocateMemory( paWasapi->allocations, MAX_STR_LEN + 1 );
 
507
                    if( !deviceName ){
 
508
                        result = paInsufficientMemory;
 
509
                        goto error;
 
510
                    }
 
511
                                        if (value.pwszVal)
 
512
                                                wcstombs(deviceName,   value.pwszVal,MAX_STR_LEN-1); //todo proper size 
 
513
                                        else{
 
514
                                                sprintf(deviceName,"baddev%d",i);
 
515
                                        }
 
516
 
 
517
                    deviceInfo->name = deviceName;
 
518
                    PropVariantClear(&value);
 
519
                }
 
520
 
 
521
#if 0
 
522
                DWORD numProps = 0;
 
523
                hResult = spProperties->GetCount(&numProps);
 
524
                IF_FAILED_JUMP(hResult, error);
 
525
                {
 
526
                    for (DWORD i=0;i<numProps;++i){
 
527
                        PROPERTYKEY pkey;
 
528
                        hResult = spProperties->GetAt(i,&pkey);
 
529
 
 
530
                        PROPVARIANT value;
 
531
                        PropVariantInit(&value);
 
532
                        hResult = spProperties->GetValue(pkey, &value);
 
533
 
 
534
                        switch(value.vt){
 
535
                            case 11:
 
536
                                PRINT(("property*%u*\n",value.ulVal));
 
537
                            break;
 
538
                            case 19:
 
539
                                PRINT(("property*%d*\n",value.boolVal));
 
540
                            break;
 
541
                            case 31:
 
542
                            {
 
543
                                char temp[512];
 
544
                                wcstombs(temp,    value.pwszVal,MAX_STR_LEN-1);
 
545
                                PRINT(("property*%s*\n",temp));
 
546
                            }
 
547
                            break;
 
548
                            default:break;
 
549
                        }
 
550
 
 
551
                        PropVariantClear(&value);
 
552
                    }
 
553
                }
 
554
#endif
 
555
 
 
556
                /*  These look interresting... but they are undocumented
 
557
                PKEY_AudioEndpoint_FormFactor
 
558
                PKEY_AudioEndpoint_ControlPanelPageProvider
 
559
                PKEY_AudioEndpoint_Association
 
560
                PKEY_AudioEndpoint_PhysicalSpeakerConfig
 
561
                PKEY_AudioEngine_DeviceFormat
 
562
                */
 
563
                spProperties->Release();
 
564
            }
 
565
 
 
566
 
 
567
            //getting the Endpoint data
 
568
            {
 
569
                IMMEndpoint *endpoint=0;
 
570
                hResult = paWasapi->devInfo[i].device->QueryInterface(__uuidof(IMMEndpoint),(void **)&endpoint);
 
571
                if (SUCCEEDED(hResult)){
 
572
                    hResult = endpoint->GetDataFlow(&paWasapi->devInfo[i].flow);
 
573
                    endpoint->Release();
 
574
                }
 
575
            }
 
576
 
 
577
            //Getting a temporary IAudioDevice for more fields
 
578
            //we make sure NOT to call Initialize yet!
 
579
            {
 
580
                IAudioClient *myClient=0;
 
581
 
 
582
                hResult = paWasapi->devInfo[i].device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
 
583
                IF_FAILED_JUMP(hResult, error);
 
584
 
 
585
                hResult = myClient->GetDevicePeriod(
 
586
                    &paWasapi->devInfo[i].DefaultDevicePeriod,
 
587
                    &paWasapi->devInfo[i].MinimumDevicePeriod);
 
588
                IF_FAILED_JUMP(hResult, error);
 
589
 
 
590
                hResult = myClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);
 
591
 
 
592
                                if (hResult != S_OK){
 
593
                                        /*davidv: this happened with my hardware, previously for that same device in DirectSound:
 
594
                                          Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f} 
 
595
                                          so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat*/
 
596
                                        logAUDCLNT_E(hResult);
 
597
                                        goto error;
 
598
                                }
 
599
 
 
600
                myClient->Release();
 
601
            }
 
602
 
 
603
            //we can now fill in portaudio device data
 
604
            deviceInfo->maxInputChannels  = 0;  //for now
 
605
            deviceInfo->maxOutputChannels = 0;  //for now
 
606
 
 
607
            switch(paWasapi->devInfo[i].flow){
 
608
                case eRender:
 
609
                    //hum not exaclty maximum, more like "default"
 
610
                    deviceInfo->maxOutputChannels = paWasapi->devInfo[i].MixFormat->nChannels;
 
611
 
 
612
                    deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
 
613
                    deviceInfo->defaultLowOutputLatency  = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
 
614
                break;
 
615
                case eCapture:
 
616
                    //hum not exaclty maximum, more like "default"
 
617
                    deviceInfo->maxInputChannels  = paWasapi->devInfo[i].MixFormat->nChannels;
 
618
 
 
619
                    deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
 
620
                    deviceInfo->defaultLowInputLatency  = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
 
621
                break;
 
622
                default:
 
623
                    PRINT(("WASAPI device:%d bad Data FLow! \n",i));
 
624
                    goto error;
 
625
                break;
 
626
            }
 
627
 
 
628
            deviceInfo->defaultSampleRate = (double)paWasapi->devInfo[i].MixFormat->nSamplesPerSec;
 
629
 
 
630
            (*hostApi)->deviceInfos[i] = deviceInfo;
 
631
            ++(*hostApi)->info.deviceCount;
 
632
        }
 
633
    }
 
634
 
 
635
    spEndpoints->Release();
 
636
 
 
637
    (*hostApi)->Terminate = Terminate;
 
638
    (*hostApi)->OpenStream = OpenStream;
 
639
    (*hostApi)->IsFormatSupported = IsFormatSupported;
 
640
 
 
641
    PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream,
 
642
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
 
643
                                      GetStreamTime, GetStreamCpuLoad,
 
644
                                      PaUtil_DummyRead, PaUtil_DummyWrite,
 
645
                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
 
646
 
 
647
    PaUtil_InitializeStreamInterface( &paWasapi->blockingStreamInterface, CloseStream, StartStream,
 
648
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
 
649
                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
 
650
                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
 
651
 
 
652
    return result;
 
653
 
 
654
error:
 
655
 
 
656
    if (spEndpoints)
 
657
        spEndpoints->Release();
 
658
 
 
659
    if (paWasapi->enumerator)
 
660
        paWasapi->enumerator->Release();
 
661
 
 
662
    if( paWasapi )
 
663
    {
 
664
        if( paWasapi->allocations )
 
665
        {
 
666
            PaUtil_FreeAllAllocations( paWasapi->allocations );
 
667
            PaUtil_DestroyAllocationGroup( paWasapi->allocations );
 
668
        }
 
669
 
 
670
        PaUtil_FreeMemory( paWasapi );
 
671
    }
 
672
    return result;
 
673
}
 
674
 
 
675
 
 
676
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
 
677
{
 
678
    PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
 
679
 
 
680
    paWasapi->enumerator->Release();
 
681
 
 
682
    for (UINT i=0;i<paWasapi->deviceCount;++i){
 
683
        PaWinWasapiDeviceInfo *info = &paWasapi->devInfo[i];
 
684
 
 
685
        if (info->device)
 
686
            info->device->Release();
 
687
 
 
688
        if (info->MixFormat)
 
689
            CoTaskMemFree(info->MixFormat);
 
690
    }
 
691
    delete [] paWasapi->devInfo;
 
692
 
 
693
    CoUninitialize();
 
694
 
 
695
    if( paWasapi->allocations ){
 
696
        PaUtil_FreeAllAllocations( paWasapi->allocations );
 
697
        PaUtil_DestroyAllocationGroup( paWasapi->allocations );
 
698
    }
 
699
 
 
700
    PaUtil_FreeMemory( paWasapi );
 
701
}
 
702
 
 
703
static void
 
704
LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in){
 
705
 
 
706
    const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
 
707
 
 
708
        switch (old->wFormatTag){
 
709
                case WAVE_FORMAT_EXTENSIBLE:{
 
710
 
 
711
                        PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));
 
712
 
 
713
                        if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
 
714
                                PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));
 
715
                        }
 
716
                        else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
 
717
                                PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));
 
718
                        }
 
719
                        else{
 
720
                                PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",    
 
721
                                                                                        in->SubFormat.Data1,
 
722
                                                                                        in->SubFormat.Data2,
 
723
                                                                                        in->SubFormat.Data3,
 
724
                                                                                        (int)in->SubFormat.Data4[0],
 
725
                                                                                        (int)in->SubFormat.Data4[1],
 
726
                                                                                        (int)in->SubFormat.Data4[2],
 
727
                                                                                        (int)in->SubFormat.Data4[3],
 
728
                                                                                        (int)in->SubFormat.Data4[4],
 
729
                                                                                        (int)in->SubFormat.Data4[5],
 
730
                                                                                        (int)in->SubFormat.Data4[6],
 
731
                                                                                        (int)in->SubFormat.Data4[7]));
 
732
                        }
 
733
                        PRINT(("Samples.wValidBitsPerSample=%d\n",  in->Samples.wValidBitsPerSample));
 
734
                        PRINT(("dwChannelMask=0x%X\n",in->dwChannelMask));
 
735
                }break;
 
736
                
 
737
                case WAVE_FORMAT_PCM:        PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;
 
738
                case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break;
 
739
                default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break;
 
740
        }
 
741
 
 
742
        PRINT(("nChannels      =%d\n",old->nChannels)); 
 
743
        PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));  
 
744
        PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));  
 
745
        PRINT(("nBlockAlign    =%d\n",old->nBlockAlign));  
 
746
        PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));  
 
747
        PRINT(("cbSize         =%d\n",old->cbSize));  
 
748
}
 
749
 
 
750
 
 
751
 
 
752
/*
 
753
 WAVEFORMATXXX is always interleaved
 
754
 */
 
755
static PaSampleFormat
 
756
waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in){
 
757
 
 
758
    const WAVEFORMATEX *old = (WAVEFORMATEX*)in;
 
759
 
 
760
    switch (old->wFormatTag){
 
761
 
 
762
        case WAVE_FORMAT_EXTENSIBLE:
 
763
        {
 
764
            if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
 
765
                if (in->Samples.wValidBitsPerSample == 32)
 
766
                    return paFloat32;
 
767
                else
 
768
                    return paCustomFormat;
 
769
            }
 
770
            else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
 
771
                switch (old->wBitsPerSample){
 
772
                    case 32: return paInt32; break;
 
773
                    case 24: return paInt24;break;
 
774
                    case  8: return paUInt8;break;
 
775
                    case 16: return paInt16;break;
 
776
                    default: return paCustomFormat;break;
 
777
                }
 
778
            }
 
779
            else
 
780
                return paCustomFormat;
 
781
        }
 
782
        break;
 
783
 
 
784
        case WAVE_FORMAT_IEEE_FLOAT:
 
785
            return paFloat32;
 
786
        break;
 
787
 
 
788
        case WAVE_FORMAT_PCM:
 
789
        {
 
790
            switch (old->wBitsPerSample){
 
791
                case 32: return paInt32; break;
 
792
                case 24: return paInt24;break;
 
793
                case  8: return paUInt8;break;
 
794
                case 16: return paInt16;break;
 
795
                default: return paCustomFormat;break;
 
796
            }
 
797
        }
 
798
        break;
 
799
 
 
800
        default:
 
801
            return paCustomFormat;
 
802
        break;
 
803
    }
 
804
 
 
805
    return paCustomFormat;
 
806
}
 
807
 
 
808
 
 
809
 
 
810
static PaError
 
811
waveformatFromParams(WAVEFORMATEXTENSIBLE*wavex,
 
812
                          const PaStreamParameters * params,
 
813
                          double sampleRate){
 
814
 
 
815
    size_t bytesPerSample = 0;
 
816
    switch( params->sampleFormat & ~paNonInterleaved ){
 
817
        case paFloat32:
 
818
        case paInt32: bytesPerSample=4;break;
 
819
        case paInt16: bytesPerSample=2;break;
 
820
        case paInt24: bytesPerSample=3;break;
 
821
        case paInt8:
 
822
        case paUInt8: bytesPerSample=1;break;
 
823
        case paCustomFormat:
 
824
        default: return paSampleFormatNotSupported;break;
 
825
    }
 
826
 
 
827
    memset(wavex,0,sizeof(WAVEFORMATEXTENSIBLE));
 
828
 
 
829
    WAVEFORMATEX *old    = (WAVEFORMATEX *)wavex;
 
830
    old->nChannels       = (WORD)params->channelCount;
 
831
    old->nSamplesPerSec  = (DWORD)sampleRate;
 
832
    old->wBitsPerSample  = (WORD)(bytesPerSample*8);
 
833
    old->nAvgBytesPerSec = (DWORD)(old->nSamplesPerSec * old->nChannels * bytesPerSample);
 
834
    old->nBlockAlign     = (WORD)(old->nChannels * bytesPerSample);
 
835
 
 
836
    //WAVEFORMATEX
 
837
    if (params->channelCount <=2 && (bytesPerSample == 2 || bytesPerSample == 1)){
 
838
        old->cbSize          = 0;
 
839
        old->wFormatTag      = WAVE_FORMAT_PCM;
 
840
    }
 
841
    //WAVEFORMATEXTENSIBLE
 
842
    else{
 
843
        old->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
 
844
 
 
845
        old->cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);
 
846
 
 
847
        if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
 
848
            wavex->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
 
849
        else
 
850
            wavex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
851
 
 
852
        wavex->Samples.wValidBitsPerSample = old->wBitsPerSample; //no extra padding!
 
853
 
 
854
        switch(params->channelCount){
 
855
            case 1:  wavex->dwChannelMask = SPEAKER_FRONT_CENTER; break;
 
856
            case 2:  wavex->dwChannelMask = 0x1 | 0x2; break;
 
857
            case 4:  wavex->dwChannelMask = 0x1 | 0x2 | 0x10 | 0x20; break;
 
858
            case 6:  wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20; break;
 
859
            case 8:  wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80; break;
 
860
            default: wavex->dwChannelMask = 0; break;
 
861
        }
 
862
    }
 
863
 
 
864
    return paNoError;
 
865
}
 
866
 
 
867
 
 
868
 
 
869
 
 
870
 
 
871
 
 
872
/*
 
873
#define paFloat32        ((PaSampleFormat) 0x00000001) 
 
874
#define paInt32          ((PaSampleFormat) 0x00000002)
 
875
#define paInt24          ((PaSampleFormat) 0x00000004) 
 
876
#define paInt16          ((PaSampleFormat) 0x00000008) 
 
877
*/
 
878
//lifted from pa_wdmks
 
879
static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
 
880
{
 
881
    PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
 
882
    PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
 
883
    PA_DEBUG(( "chanelCount = %d\n", channelCount ));
 
884
 
 
885
    pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
 
886
    pwfext->Format.nChannels = channelCount;
 
887
    pwfext->Format.nSamplesPerSec = (int)sampleRate;
 
888
    if(channelCount == 1)
 
889
        pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
 
890
    else
 
891
        pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
 
892
    if(sampleFormat == paFloat32)
 
893
    {
 
894
        pwfext->Format.nBlockAlign = channelCount * 4;
 
895
        pwfext->Format.wBitsPerSample = 32;
 
896
        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
 
897
        pwfext->Samples.wValidBitsPerSample = 32;
 
898
        pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
 
899
    }
 
900
    else if(sampleFormat == paInt32)
 
901
    {
 
902
        pwfext->Format.nBlockAlign = channelCount * 4;
 
903
        pwfext->Format.wBitsPerSample = 32;
 
904
        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
 
905
        pwfext->Samples.wValidBitsPerSample = 32;
 
906
        pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
907
    }
 
908
    else if(sampleFormat == paInt24)
 
909
    {
 
910
        pwfext->Format.nBlockAlign = channelCount * 3;
 
911
        pwfext->Format.wBitsPerSample = 24;
 
912
        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
 
913
        pwfext->Samples.wValidBitsPerSample = 24;
 
914
        pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
915
    }
 
916
    else if(sampleFormat == paInt16)
 
917
    {
 
918
        pwfext->Format.nBlockAlign = channelCount * 2;
 
919
        pwfext->Format.wBitsPerSample = 16;
 
920
        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
 
921
        pwfext->Samples.wValidBitsPerSample = 16;
 
922
        pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
923
    }
 
924
    pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
 
925
}
 
926
 
 
927
 
 
928
 
 
929
/*
 
930
#define FORMATTESTS 4
 
931
const int BestToWorst[FORMATTESTS]={paFloat32,paInt32,paInt24,paInt16};
 
932
*/
 
933
 
 
934
#define FORMATTESTS 3
 
935
const int BestToWorst[FORMATTESTS]={paFloat32,paInt24,paInt16};
 
936
 
 
937
 
 
938
static PaError
 
939
GetClosestFormat(IAudioClient * myClient, double sampleRate,const  PaStreamParameters * params, 
 
940
                                 AUDCLNT_SHAREMODE *shareMode, WAVEFORMATEXTENSIBLE *outWavex)
 
941
{
 
942
        //TODO we should try exclusive first and shared after
 
943
        *shareMode = PORTAUDIO_SHAREMODE;
 
944
 
 
945
        PaError answer = paInvalidSampleRate;
 
946
 
 
947
    waveformatFromParams(outWavex,params,sampleRate);
 
948
        WAVEFORMATEX *sharedClosestMatch=0;
 
949
        HRESULT hResult=!S_OK;
 
950
 
 
951
        if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
 
952
                hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)outWavex,NULL);
 
953
        else
 
954
                hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,   (WAVEFORMATEX*)&outWavex,&sharedClosestMatch);
 
955
 
 
956
        if (hResult == S_OK)
 
957
                answer = paFormatIsSupported;
 
958
    else if (sharedClosestMatch){
 
959
        WAVEFORMATEXTENSIBLE* ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;
 
960
                
 
961
                int closestMatchSR = (int)sharedClosestMatch->nSamplesPerSec;
 
962
 
 
963
                if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
 
964
                        memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEXTENSIBLE));
 
965
                else
 
966
                        memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEX));
 
967
 
 
968
        CoTaskMemFree(sharedClosestMatch);
 
969
 
 
970
                if ((int)sampleRate == closestMatchSR)
 
971
                answer = paFormatIsSupported;
 
972
                else
 
973
                        answer = paInvalidSampleRate;
 
974
        
 
975
        }else {
 
976
 
 
977
                //it doesnt suggest anything?? ok lets show it the MENU!
 
978
 
 
979
                //ok fun time as with pa_win_mme, we know only a refusal of the user-requested
 
980
                //sampleRate+num Channel is disastrous, as the portaudio buffer processor converts between anything
 
981
                //so lets only use the number 
 
982
                for (int i=0;i<FORMATTESTS;++i){
 
983
                        WAVEFORMATEXTENSIBLE ext;
 
984
                        wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount);           
 
985
                        if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
 
986
                                hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)&ext,NULL);
 
987
                        else
 
988
                                hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,   (WAVEFORMATEX*)&ext,&sharedClosestMatch);
 
989
 
 
990
                        if (hResult == S_OK){
 
991
                                memcpy(outWavex,&ext,sizeof(WAVEFORMATEXTENSIBLE));
 
992
                                answer = paFormatIsSupported;
 
993
                                break;
 
994
                        }
 
995
                }
 
996
 
 
997
                if (answer!=paFormatIsSupported) {
 
998
                        //try MIX format?
 
999
                        //why did it HAVE to come to this ....
 
1000
                        WAVEFORMATEX pcm16WaveFormat;
 
1001
                        memset(&pcm16WaveFormat,0,sizeof(WAVEFORMATEX));
 
1002
                        pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM; 
 
1003
                        pcm16WaveFormat.nChannels = 2; 
 
1004
                        pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate; 
 
1005
                        pcm16WaveFormat.nBlockAlign = 4; 
 
1006
                        pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign; 
 
1007
                        pcm16WaveFormat.wBitsPerSample = 16; 
 
1008
                        pcm16WaveFormat.cbSize = 0;
 
1009
 
 
1010
                        if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
 
1011
                                hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)&pcm16WaveFormat,NULL);
 
1012
                        else
 
1013
                                hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,   (WAVEFORMATEX*)&pcm16WaveFormat,&sharedClosestMatch);
 
1014
 
 
1015
                        if (hResult == S_OK){
 
1016
                                memcpy(outWavex,&pcm16WaveFormat,sizeof(WAVEFORMATEX));
 
1017
                                answer = paFormatIsSupported;
 
1018
                        }
 
1019
                }
 
1020
 
 
1021
                logAUDCLNT_E(hResult);
 
1022
        }
 
1023
 
 
1024
        return answer;
 
1025
}
 
1026
 
 
1027
 
 
1028
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 
1029
                                  const  PaStreamParameters *inputParameters,
 
1030
                                  const  PaStreamParameters *outputParameters,
 
1031
                                  double sampleRate )
 
1032
{
 
1033
 
 
1034
    int inputChannelCount, outputChannelCount;
 
1035
    PaSampleFormat inputSampleFormat, outputSampleFormat;
 
1036
 
 
1037
    if( inputParameters )
 
1038
    {
 
1039
        inputChannelCount = inputParameters->channelCount;
 
1040
        inputSampleFormat = inputParameters->sampleFormat;
 
1041
 
 
1042
        /* all standard sample formats are supported by the buffer adapter,
 
1043
            this implementation doesn't support any custom sample formats */
 
1044
        if( inputSampleFormat & paCustomFormat )
 
1045
            return paSampleFormatNotSupported;
 
1046
 
 
1047
        /* unless alternate device specification is supported, reject the use of
 
1048
            paUseHostApiSpecificDeviceSpecification */
 
1049
 
 
1050
        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
 
1051
            return paInvalidDevice;
 
1052
 
 
1053
        /* check that input device can support inputChannelCount */
 
1054
        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
 
1055
            return paInvalidChannelCount;
 
1056
 
 
1057
        /* validate inputStreamInfo */
 
1058
        if( inputParameters->hostApiSpecificStreamInfo )
 
1059
            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
 
1060
 
 
1061
 
 
1062
        PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
 
1063
 
 
1064
 
 
1065
                IAudioClient *myClient=0;
 
1066
                HRESULT hResult = paWasapi->devInfo[inputParameters->device].device->Activate(
 
1067
                        __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
 
1068
                if (hResult != S_OK){
 
1069
                        logAUDCLNT_E(hResult);
 
1070
                        return paInvalidDevice;
 
1071
                }
 
1072
 
 
1073
        WAVEFORMATEXTENSIBLE wavex;
 
1074
                AUDCLNT_SHAREMODE shareMode;
 
1075
                PaError answer = GetClosestFormat(myClient,sampleRate,inputParameters,&shareMode,&wavex);
 
1076
                myClient->Release();
 
1077
 
 
1078
                if (answer !=paFormatIsSupported)
 
1079
                        return answer;
 
1080
    }
 
1081
    else
 
1082
    {
 
1083
        inputChannelCount = 0;
 
1084
    }
 
1085
 
 
1086
    if( outputParameters )
 
1087
    {
 
1088
        outputChannelCount = outputParameters->channelCount;
 
1089
        outputSampleFormat = outputParameters->sampleFormat;
 
1090
 
 
1091
        /* all standard sample formats are supported by the buffer adapter,
 
1092
            this implementation doesn't support any custom sample formats */
 
1093
        if( outputSampleFormat & paCustomFormat )
 
1094
            return paSampleFormatNotSupported;
 
1095
 
 
1096
        /* unless alternate device specification is supported, reject the use of
 
1097
            paUseHostApiSpecificDeviceSpecification */
 
1098
 
 
1099
        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
 
1100
            return paInvalidDevice;
 
1101
 
 
1102
        /* check that output device can support outputChannelCount */
 
1103
        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
 
1104
            return paInvalidChannelCount;
 
1105
 
 
1106
        /* validate outputStreamInfo */
 
1107
        if( outputParameters->hostApiSpecificStreamInfo )
 
1108
            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
 
1109
 
 
1110
 
 
1111
        PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
 
1112
 
 
1113
                IAudioClient *myClient=0;
 
1114
                HRESULT hResult = paWasapi->devInfo[outputParameters->device].device->Activate(
 
1115
                        __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
 
1116
                if (hResult != S_OK){
 
1117
                        logAUDCLNT_E(hResult);
 
1118
                        return paInvalidDevice;
 
1119
                }
 
1120
 
 
1121
        WAVEFORMATEXTENSIBLE wavex;
 
1122
                AUDCLNT_SHAREMODE shareMode;
 
1123
                PaError answer = GetClosestFormat(myClient,sampleRate,outputParameters,&shareMode,&wavex);
 
1124
                myClient->Release();
 
1125
 
 
1126
                if (answer !=paFormatIsSupported)
 
1127
                        return answer;          
 
1128
    }
 
1129
    else
 
1130
    {
 
1131
        outputChannelCount = 0;
 
1132
    }
 
1133
 
 
1134
 
 
1135
    return paFormatIsSupported;
 
1136
}
 
1137
 
 
1138
 
 
1139
 
 
1140
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
 
1141
 
 
1142
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 
1143
                           PaStream** s,
 
1144
                           const PaStreamParameters *inputParameters,
 
1145
                           const PaStreamParameters *outputParameters,
 
1146
                           double sampleRate,
 
1147
                           unsigned long framesPerBuffer,
 
1148
                           PaStreamFlags streamFlags,
 
1149
                           PaStreamCallback *streamCallback,
 
1150
                           void *userData )
 
1151
{
 
1152
    PaError result = paNoError;
 
1153
    PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
 
1154
    PaWinWasapiStream *stream = 0;
 
1155
    int inputChannelCount, outputChannelCount;
 
1156
    PaSampleFormat inputSampleFormat, outputSampleFormat;
 
1157
    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
 
1158
 
 
1159
 
 
1160
    stream = (PaWinWasapiStream*)PaUtil_AllocateMemory( sizeof(PaWinWasapiStream) );
 
1161
    if( !stream ){
 
1162
        result = paInsufficientMemory;
 
1163
        goto error;
 
1164
    }
 
1165
 
 
1166
    if( inputParameters )
 
1167
    {
 
1168
        inputChannelCount = inputParameters->channelCount;
 
1169
        inputSampleFormat = inputParameters->sampleFormat;
 
1170
 
 
1171
        /* unless alternate device specification is supported, reject the use of
 
1172
            paUseHostApiSpecificDeviceSpecification */
 
1173
 
 
1174
        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
 
1175
            return paInvalidDevice;
 
1176
 
 
1177
        /* check that input device can support inputChannelCount */
 
1178
        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
 
1179
            return paInvalidChannelCount;
 
1180
 
 
1181
        /* validate inputStreamInfo */
 
1182
        if( inputParameters->hostApiSpecificStreamInfo )
 
1183
            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
 
1184
 
 
1185
 
 
1186
        PaWinWasapiDeviceInfo &info = paWasapi->devInfo[inputParameters->device];
 
1187
 
 
1188
        HRESULT hResult = info.device->Activate(
 
1189
            __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
 
1190
            (void**)&stream->in.client);
 
1191
 
 
1192
        if (hResult != S_OK)
 
1193
            return paInvalidDevice;
 
1194
 
 
1195
        hResult = info.device->Activate(
 
1196
            __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
 
1197
            (void**)&stream->inVol);
 
1198
 
 
1199
        if (hResult != S_OK)
 
1200
            return paInvalidDevice;
 
1201
        
 
1202
                AUDCLNT_SHAREMODE shareMode;
 
1203
                PaError answer = GetClosestFormat(stream->in.client,sampleRate,inputParameters,&shareMode,&stream->in.wavex);
 
1204
                
 
1205
                if (answer !=paFormatIsSupported)
 
1206
                        return answer;
 
1207
 
 
1208
        //stream->out.period = info.DefaultDevicePeriod;
 
1209
        stream->in.period = info.MinimumDevicePeriod;
 
1210
 
 
1211
        hResult = stream->in.client->Initialize(
 
1212
            shareMode,
 
1213
            0,  //no flags
 
1214
            stream->in.period,
 
1215
            0,//stream->out.period,
 
1216
            (WAVEFORMATEX*)&stream->in.wavex,
 
1217
            &stream->session
 
1218
            );
 
1219
 
 
1220
        if (hResult != S_OK){
 
1221
            logAUDCLNT_E(hResult);
 
1222
            return paInvalidDevice;
 
1223
        }
 
1224
 
 
1225
        hResult = stream->in.client->GetBufferSize(&stream->in.bufferSize);
 
1226
        if (hResult != S_OK)
 
1227
            return paInvalidDevice;
 
1228
 
 
1229
        hResult = stream->in.client->GetStreamLatency(&stream->in.latency);
 
1230
        if (hResult != S_OK)
 
1231
            return paInvalidDevice;
 
1232
 
 
1233
        double periodsPerSecond = 1.0/nano100ToSeconds(stream->in.period);
 
1234
        double samplesPerPeriod = (double)(stream->in.wavex.Format.nSamplesPerSec)/periodsPerSecond;
 
1235
 
 
1236
        //this is the number of samples that are required at each period
 
1237
        stream->in.framesPerHostCallback = (unsigned long)samplesPerPeriod;//unrelated to channels
 
1238
 
 
1239
        /* IMPLEMENT ME - establish which  host formats are available */
 
1240
        hostInputSampleFormat =
 
1241
            PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );
 
1242
        }
 
1243
    else
 
1244
    {
 
1245
        inputChannelCount = 0;
 
1246
        inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
 
1247
    }
 
1248
 
 
1249
    if( outputParameters )
 
1250
    {
 
1251
        outputChannelCount = outputParameters->channelCount;
 
1252
        outputSampleFormat = outputParameters->sampleFormat;
 
1253
 
 
1254
        /* unless alternate device specification is supported, reject the use of
 
1255
            paUseHostApiSpecificDeviceSpecification */
 
1256
 
 
1257
        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
 
1258
            return paInvalidDevice;
 
1259
 
 
1260
        /* check that output device can support inputChannelCount */
 
1261
        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
 
1262
            return paInvalidChannelCount;
 
1263
 
 
1264
        /* validate outputStreamInfo */
 
1265
        if( outputParameters->hostApiSpecificStreamInfo )
 
1266
            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
 
1267
 
 
1268
 
 
1269
        PaWinWasapiDeviceInfo &info = paWasapi->devInfo[outputParameters->device];
 
1270
 
 
1271
        HRESULT hResult = info.device->Activate(
 
1272
            __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
 
1273
            (void**)&stream->out.client);
 
1274
 
 
1275
        if (hResult != S_OK)
 
1276
            return paInvalidDevice;
 
1277
 
 
1278
                AUDCLNT_SHAREMODE shareMode;
 
1279
                PaError answer = GetClosestFormat(stream->out.client,sampleRate,outputParameters,&shareMode,&stream->out.wavex);
 
1280
                
 
1281
                if (answer !=paFormatIsSupported)
 
1282
                        return answer;
 
1283
                LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
 
1284
 
 
1285
       // stream->out.period = info.DefaultDevicePeriod;
 
1286
        stream->out.period = info.MinimumDevicePeriod;
 
1287
 
 
1288
                /*For an exclusive-mode stream that uses event-driven buffering, 
 
1289
                the caller must specify nonzero values for hnsPeriodicity and hnsBufferDuration, 
 
1290
                and the values of these two parameters must be equal */
 
1291
                if (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE){
 
1292
                        hResult = stream->out.client->Initialize(
 
1293
                                shareMode,
 
1294
                                AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 
 
1295
                                stream->out.period,
 
1296
                                stream->out.period,
 
1297
                                (WAVEFORMATEX*)&stream->out.wavex,
 
1298
                                &stream->session
 
1299
                                );
 
1300
                }
 
1301
                else{
 
1302
                        hResult = stream->out.client->Initialize(
 
1303
                                shareMode,
 
1304
                                AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 
 
1305
                                0,
 
1306
                                0,
 
1307
                                (WAVEFORMATEX*)&stream->out.wavex,
 
1308
                                &stream->session
 
1309
                                );
 
1310
                }
 
1311
        
 
1312
 
 
1313
        if (hResult != S_OK){
 
1314
            logAUDCLNT_E(hResult);
 
1315
            return paInvalidDevice;
 
1316
        }
 
1317
 
 
1318
        hResult = info.device->Activate(
 
1319
            __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
 
1320
            (void**)&stream->outVol);
 
1321
 
 
1322
        if (hResult != S_OK)
 
1323
            return paInvalidDevice;
 
1324
 
 
1325
        hResult = stream->out.client->GetBufferSize(&stream->out.bufferSize);
 
1326
        if (hResult != S_OK)
 
1327
            return paInvalidDevice;
 
1328
 
 
1329
        hResult = stream->out.client->GetStreamLatency(&stream->out.latency);
 
1330
        if (hResult != S_OK)
 
1331
            return paInvalidDevice;
 
1332
                
 
1333
        double periodsPerSecond = 1.0/nano100ToSeconds(stream->out.period);
 
1334
        double samplesPerPeriod = (double)(stream->out.wavex.Format.nSamplesPerSec)/periodsPerSecond;
 
1335
 
 
1336
        //this is the number of samples that are required at each period
 
1337
        stream->out.framesPerHostCallback = stream->out.bufferSize; //(unsigned long)samplesPerPeriod;//unrelated to channels
 
1338
 
 
1339
        /* IMPLEMENT ME - establish which  host formats are available */
 
1340
        hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat );
 
1341
    }
 
1342
    else
 
1343
    {
 
1344
        outputChannelCount = 0;
 
1345
        outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
 
1346
    }
 
1347
 
 
1348
 
 
1349
 
 
1350
    /*
 
1351
        IMPLEMENT ME:
 
1352
 
 
1353
        ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )
 
1354
 
 
1355
            - check that input device can support inputSampleFormat, or that
 
1356
                we have the capability to convert from outputSampleFormat to
 
1357
                a native format
 
1358
 
 
1359
            - check that output device can support outputSampleFormat, or that
 
1360
                we have the capability to convert from outputSampleFormat to
 
1361
                a native format
 
1362
 
 
1363
            - if a full duplex stream is requested, check that the combination
 
1364
                of input and output parameters is supported
 
1365
 
 
1366
            - check that the device supports sampleRate
 
1367
 
 
1368
            - alter sampleRate to a close allowable rate if possible / necessary
 
1369
 
 
1370
            - validate suggestedInputLatency and suggestedOutputLatency parameters,
 
1371
                use default values where necessary
 
1372
    */
 
1373
 
 
1374
 
 
1375
 
 
1376
    /* validate platform specific flags */
 
1377
    if( (streamFlags & paPlatformSpecificFlags) != 0 )
 
1378
        return paInvalidFlag; /* unexpected platform specific flag */
 
1379
 
 
1380
 
 
1381
 
 
1382
    if( streamCallback )
 
1383
    {
 
1384
        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
 
1385
                                               &paWasapi->callbackStreamInterface, streamCallback, userData );
 
1386
    }
 
1387
    else
 
1388
    {
 
1389
        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
 
1390
                                               &paWasapi->blockingStreamInterface, streamCallback, userData );
 
1391
    }
 
1392
 
 
1393
    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
 
1394
 
 
1395
 
 
1396
        if (outputParameters && inputParameters){
 
1397
 
 
1398
                //serious problem #1
 
1399
                if (stream->in.period != stream->out.period){
 
1400
                        PRINT(("OpenStream: period discrepancy\n"));
 
1401
                        goto error;
 
1402
                }
 
1403
 
 
1404
                //serious problem #2
 
1405
                if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback){
 
1406
                        PRINT(("OpenStream: framesPerHostCallback discrepancy\n"));
 
1407
                        goto error;
 
1408
                }
 
1409
        }
 
1410
 
 
1411
        unsigned long framesPerHostCallback = (outputParameters)?
 
1412
                stream->out.framesPerHostCallback: 
 
1413
                stream->in.framesPerHostCallback;
 
1414
 
 
1415
    /* we assume a fixed host buffer size in this example, but the buffer processor
 
1416
        can also support bounded and unknown host buffer sizes by passing
 
1417
        paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
 
1418
        paUtilFixedHostBufferSize below. */
 
1419
 
 
1420
    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
 
1421
              inputChannelCount, inputSampleFormat, hostInputSampleFormat,
 
1422
              outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
 
1423
              sampleRate, streamFlags, framesPerBuffer,
 
1424
              framesPerHostCallback, paUtilFixedHostBufferSize,
 
1425
              streamCallback, userData );
 
1426
    if( result != paNoError )
 
1427
        goto error;
 
1428
 
 
1429
 
 
1430
    /*
 
1431
        IMPLEMENT ME: initialise the following fields with estimated or actual
 
1432
        values.
 
1433
    */
 
1434
    stream->streamRepresentation.streamInfo.inputLatency =
 
1435
            PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
 
1436
                        + ((inputParameters)?nano100ToSeconds(stream->in.latency) :0);
 
1437
 
 
1438
    stream->streamRepresentation.streamInfo.outputLatency =
 
1439
            PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
 
1440
                        + ((outputParameters)?nano100ToSeconds(stream->out.latency) :0);
 
1441
 
 
1442
    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
 
1443
 
 
1444
 
 
1445
    *s = (PaStream*)stream;
 
1446
 
 
1447
 
 
1448
    return result;
 
1449
 
 
1450
error:
 
1451
    if( stream )
 
1452
        PaUtil_FreeMemory( stream );
 
1453
 
 
1454
    return result;
 
1455
}
 
1456
 
 
1457
 
 
1458
 
 
1459
/*
 
1460
    When CloseStream() is called, the multi-api layer ensures that
 
1461
    the stream has already been stopped or aborted.
 
1462
*/
 
1463
 
 
1464
#define SAFE_RELEASE(punk)  \
 
1465
              if ((punk) != NULL)  \
 
1466
                { (punk)->Release(); (punk) = NULL; }
 
1467
 
 
1468
static PaError CloseStream( PaStream* s )
 
1469
{
 
1470
    PaError result = paNoError;
 
1471
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1472
 
 
1473
    /*
 
1474
        IMPLEMENT ME:
 
1475
            - additional stream closing + cleanup
 
1476
    */
 
1477
 
 
1478
    SAFE_RELEASE(stream->out.client);
 
1479
    SAFE_RELEASE(stream->in.client);
 
1480
    SAFE_RELEASE(stream->cclient);
 
1481
    SAFE_RELEASE(stream->rclient);
 
1482
        SAFE_RELEASE(stream->inVol);
 
1483
        SAFE_RELEASE(stream->outVol);
 
1484
    CloseHandle(stream->hThread);
 
1485
        CloseHandle(stream->hNotificationEvent);
 
1486
 
 
1487
    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
 
1488
    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
 
1489
    PaUtil_FreeMemory( stream );
 
1490
 
 
1491
    return result;
 
1492
}
 
1493
 
 
1494
DWORD WINAPI ProcThread(void *client);
 
1495
 
 
1496
static PaError StartStream( PaStream *s )
 
1497
{
 
1498
    PaError result = paNoError;
 
1499
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1500
 
 
1501
    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
 
1502
        
 
1503
        HRESULT hResult=S_OK;
 
1504
 
 
1505
        if (stream->out.client){
 
1506
                hResult = stream->out.client->GetService(__uuidof(IAudioRenderClient),(void**)&stream->rclient);
 
1507
                logAUDCLNT_E(hResult);
 
1508
                if (hResult!=S_OK)
 
1509
                        return paUnanticipatedHostError;
 
1510
        }
 
1511
        
 
1512
        if (stream->in.client){
 
1513
         hResult = stream->in.client->GetService(__uuidof(IAudioCaptureClient),(void**)&stream->cclient);
 
1514
                logAUDCLNT_E(hResult);
 
1515
                if (hResult!=S_OK)
 
1516
                        return paUnanticipatedHostError;
 
1517
        }
 
1518
 
 
1519
    // Create a thread for this client.
 
1520
    stream->hThread = CreateThread(
 
1521
        NULL,              // no security attribute
 
1522
        0,                 // default stack size
 
1523
        ProcThread,
 
1524
        (LPVOID) stream,    // thread parameter
 
1525
        0,                 // not suspended
 
1526
        &stream->dwThreadId);      // returns thread ID
 
1527
 
 
1528
    if (stream->hThread == NULL)
 
1529
        return paUnanticipatedHostError;
 
1530
 
 
1531
    return paNoError;
 
1532
}
 
1533
 
 
1534
 
 
1535
static PaError StopStream( PaStream *s )
 
1536
{
 
1537
    PaError result = paNoError;
 
1538
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1539
 
 
1540
    /* suppress unused variable warnings */
 
1541
    stream->closeRequest = true;
 
1542
    //todo something MUCH better than this
 
1543
    while(stream->closeRequest)
 
1544
        Sleep(100);
 
1545
 
 
1546
    /* IMPLEMENT ME, see portaudio.h for required behavior */
 
1547
 
 
1548
    stream->running = false;
 
1549
 
 
1550
    return result;
 
1551
}
 
1552
 
 
1553
 
 
1554
static PaError AbortStream( PaStream *s )
 
1555
{
 
1556
    PaError result = paNoError;
 
1557
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1558
 
 
1559
    /* suppress unused variable warnings */
 
1560
    stream->closeRequest = true;
 
1561
    //todo something MUCH better than this
 
1562
    while(stream->closeRequest)
 
1563
        Sleep(100);
 
1564
 
 
1565
    /* IMPLEMENT ME, see portaudio.h for required behavior */
 
1566
 
 
1567
    return result;
 
1568
}
 
1569
 
 
1570
 
 
1571
static PaError IsStreamStopped( PaStream *s )
 
1572
{
 
1573
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1574
 
 
1575
    return !stream->running;
 
1576
}
 
1577
 
 
1578
 
 
1579
static PaError IsStreamActive( PaStream *s )
 
1580
{
 
1581
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1582
    return stream->running;
 
1583
}
 
1584
 
 
1585
 
 
1586
static PaTime GetStreamTime( PaStream *s )
 
1587
{
 
1588
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1589
 
 
1590
    /* suppress unused variable warnings */
 
1591
    (void) stream;
 
1592
 
 
1593
    /* IMPLEMENT ME, see portaudio.h for required behavior*/
 
1594
 
 
1595
        //this is lame ds and mme does the same thing, quite useless method imho
 
1596
        //why dont we fetch the time in the pa callbacks?
 
1597
        //at least its doing to be clocked to something
 
1598
    return PaUtil_GetTime();
 
1599
}
 
1600
 
 
1601
 
 
1602
static double GetStreamCpuLoad( PaStream* s )
 
1603
{
 
1604
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1605
 
 
1606
    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
 
1607
}
 
1608
 
 
1609
 
 
1610
/*
 
1611
    As separate stream interfaces are used for blocking and callback
 
1612
    streams, the following functions can be guaranteed to only be called
 
1613
    for blocking streams.
 
1614
*/
 
1615
 
 
1616
static PaError ReadStream( PaStream* s,
 
1617
                           void *buffer,
 
1618
                           unsigned long frames )
 
1619
{
 
1620
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1621
 
 
1622
    /* suppress unused variable warnings */
 
1623
    (void) buffer;
 
1624
    (void) frames;
 
1625
    (void) stream;
 
1626
 
 
1627
    /* IMPLEMENT ME, see portaudio.h for required behavior*/
 
1628
 
 
1629
    return paNoError;
 
1630
}
 
1631
 
 
1632
 
 
1633
static PaError WriteStream( PaStream* s,
 
1634
                            const void *buffer,
 
1635
                            unsigned long frames )
 
1636
{
 
1637
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1638
 
 
1639
    /* suppress unused variable warnings */
 
1640
    (void) buffer;
 
1641
    (void) frames;
 
1642
    (void) stream;
 
1643
 
 
1644
    /* IMPLEMENT ME, see portaudio.h for required behavior*/
 
1645
 
 
1646
    return paNoError;
 
1647
}
 
1648
 
 
1649
 
 
1650
static signed long GetStreamReadAvailable( PaStream* s )
 
1651
{
 
1652
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1653
 
 
1654
    /* suppress unused variable warnings */
 
1655
    (void) stream;
 
1656
 
 
1657
    /* IMPLEMENT ME, see portaudio.h for required behavior*/
 
1658
 
 
1659
    return 0;
 
1660
}
 
1661
 
 
1662
 
 
1663
static signed long GetStreamWriteAvailable( PaStream* s )
 
1664
{
 
1665
    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
 
1666
 
 
1667
    /* suppress unused variable warnings */
 
1668
    (void) stream;
 
1669
 
 
1670
    /* IMPLEMENT ME, see portaudio.h for required behavior*/
 
1671
 
 
1672
    return 0;
 
1673
}
 
1674
 
 
1675
 
 
1676
 
 
1677
/*
 
1678
    ExampleHostProcessingLoop() illustrates the kind of processing which may
 
1679
    occur in a host implementation.
 
1680
 
 
1681
*/
 
1682
static void WaspiHostProcessingLoop( void *inputBuffer,  long inputFrames,
 
1683
                                     void *outputBuffer, long outputFrames,
 
1684
                                     void *userData )
 
1685
{
 
1686
    PaWinWasapiStream *stream = (PaWinWasapiStream*)userData;
 
1687
    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
 
1688
    int callbackResult;
 
1689
    unsigned long framesProcessed;
 
1690
 
 
1691
    PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
 
1692
 
 
1693
 
 
1694
    /*
 
1695
        IMPLEMENT ME:
 
1696
            - generate timing information
 
1697
            - handle buffer slips
 
1698
    */
 
1699
 
 
1700
    /*
 
1701
        If you need to byte swap or shift inputBuffer to convert it into a
 
1702
        portaudio format, do it here.
 
1703
    */
 
1704
 
 
1705
 
 
1706
 
 
1707
    PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );
 
1708
 
 
1709
    /*
 
1710
        depending on whether the host buffers are interleaved, non-interleaved
 
1711
        or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
 
1712
        PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
 
1713
    */
 
1714
 
 
1715
    if( stream->bufferProcessor.inputChannelCount > 0 )
 
1716
    {
 
1717
        PaUtil_SetInputFrameCount( &stream->bufferProcessor, inputFrames );
 
1718
        PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
 
1719
            0, /* first channel of inputBuffer is channel 0 */
 
1720
            inputBuffer,
 
1721
            0 ); /* 0 - use inputChannelCount passed to init buffer processor */
 
1722
    }
 
1723
 
 
1724
    if( stream->bufferProcessor.outputChannelCount > 0 )
 
1725
    {
 
1726
        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, outputFrames);
 
1727
        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
 
1728
            0, /* first channel of outputBuffer is channel 0 */
 
1729
            outputBuffer,
 
1730
            0 ); /* 0 - use outputChannelCount passed to init buffer processor */
 
1731
    }
 
1732
 
 
1733
    /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
 
1734
        in general you would pass paContinue for normal operation, and
 
1735
        paComplete to drain the buffer processor's internal output buffer.
 
1736
        You can check whether the buffer processor's output buffer is empty
 
1737
        using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
 
1738
    */
 
1739
    callbackResult = paContinue;
 
1740
    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
 
1741
 
 
1742
 
 
1743
    /*
 
1744
        If you need to byte swap or shift outputBuffer to convert it to
 
1745
        host format, do it here.
 
1746
    */
 
1747
 
 
1748
    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
 
1749
 
 
1750
 
 
1751
    if( callbackResult == paContinue )
 
1752
    {
 
1753
        /* nothing special to do */
 
1754
    }
 
1755
    else if( callbackResult == paAbort )
 
1756
    {
 
1757
        /* IMPLEMENT ME - finish playback immediately  */
 
1758
 
 
1759
        /* once finished, call the finished callback */
 
1760
        if( stream->streamRepresentation.streamFinishedCallback != 0 )
 
1761
            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
 
1762
    }
 
1763
    else
 
1764
    {
 
1765
        /* User callback has asked us to stop with paComplete or other non-zero value */
 
1766
 
 
1767
        /* IMPLEMENT ME - finish playback once currently queued audio has completed  */
 
1768
 
 
1769
        /* once finished, call the finished callback */
 
1770
        if( stream->streamRepresentation.streamFinishedCallback != 0 )
 
1771
            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
 
1772
    }
 
1773
}
 
1774
 
 
1775
 
 
1776
void 
 
1777
MMCSS_activate(){
 
1778
 
 
1779
    DWORD stuff=0;
 
1780
    HANDLE thCarac = pAvSetMmThreadCharacteristics("Pro Audio",&stuff);
 
1781
    if (!thCarac){
 
1782
        PRINT(("AvSetMmThreadCharacteristics failed!\n"));
 
1783
    }
 
1784
 
 
1785
    BOOL prio = pAvSetMmThreadPriority(thCarac,AVRT_PRIORITY_NORMAL);
 
1786
    if (!prio){
 
1787
        PRINT(("AvSetMmThreadPriority failed!\n"));
 
1788
    }
 
1789
 
 
1790
        //debug
 
1791
    {
 
1792
        HANDLE hh       = GetCurrentThread();
 
1793
        int  currprio   = GetThreadPriority(hh);
 
1794
        DWORD currclass = GetPriorityClass(GetCurrentProcess());
 
1795
        PRINT(("currprio 0x%X currclass 0x%X\n",currprio,currclass));
 
1796
    }
 
1797
}
 
1798
 
 
1799
 
 
1800
DWORD WINAPI
 
1801
ProcThread(void* param){
 
1802
        HRESULT hResult;
 
1803
        MMCSS_activate();
 
1804
 
 
1805
    PaWinWasapiStream *stream = (PaWinWasapiStream*)param;
 
1806
 
 
1807
        stream->hNotificationEvent = CreateEvent(NULL, 
 
1808
                                                 FALSE,  //bManualReset are we sure??
 
1809
                                                                                         FALSE, 
 
1810
                                                                                         "PAWASA");
 
1811
        hResult = stream->out.client->SetEventHandle(stream->hNotificationEvent);
 
1812
        if (hResult != S_OK)
 
1813
                logAUDCLNT_E(hResult);
 
1814
 
 
1815
        if (stream->out.client){
 
1816
                hResult = stream->out.client->Start();
 
1817
                if (hResult != S_OK)
 
1818
                        logAUDCLNT_E(hResult);
 
1819
        }
 
1820
 
 
1821
        stream->running = true;
 
1822
        bool bOne = false;
 
1823
 
 
1824
        while( !stream->closeRequest ) 
 
1825
    { 
 
1826
            //lets wait but have a 1 second timeout
 
1827
        DWORD dwResult = WaitForSingleObject(stream->hNotificationEvent, 1000);
 
1828
        switch( dwResult ) {
 
1829
                case WAIT_OBJECT_0: {
 
1830
 
 
1831
                        unsigned long usingBS = stream->out.framesPerHostCallback;
 
1832
 
 
1833
                        BYTE* indata  = 0;
 
1834
                        BYTE* outdata = 0;
 
1835
 
 
1836
                        hResult = stream->rclient->GetBuffer(usingBS, &outdata);
 
1837
 
 
1838
                        if (hResult != S_OK || !outdata) {
 
1839
                                //logAUDCLNT_E(hResult);
 
1840
                                //most probably shared mode and hResult=AUDCLNT_E_BUFFER_TOO_LARGE
 
1841
                                UINT32 padding = 0;
 
1842
                                hResult = stream->out.client->GetCurrentPadding(&padding);
 
1843
                                if (padding == 0)
 
1844
                                        break;  
 
1845
                                usingBS = usingBS-padding;
 
1846
                                if (usingBS == 0)
 
1847
                                        break;//huh?
 
1848
                                hResult = stream->rclient->GetBuffer(usingBS, &outdata);
 
1849
                                if (hResult != S_OK)//what can we do NOW??
 
1850
                                        break;
 
1851
                                //logAUDCLNT_E(hResult);                        
 
1852
                        }
 
1853
        
 
1854
                        WaspiHostProcessingLoop(indata, usingBS ,outdata, usingBS, stream);
 
1855
 
 
1856
                        hResult = stream->rclient->ReleaseBuffer(usingBS, 0);
 
1857
                        if (hResult != S_OK)
 
1858
                                logAUDCLNT_E(hResult);
 
1859
 
 
1860
                         /*     This was suggested, but in my tests it doesnt seem to improve the 
 
1861
                locking behaviour some drivers have running in exclusive mode.
 
1862
                if(!ResetEvent(stream->hNotificationEvent)){
 
1863
                                        logAUDCLNT_E(hResult);
 
1864
                                }
 
1865
             */
 
1866
 
 
1867
                } 
 
1868
                break;
 
1869
 
 
1870
        }
 
1871
    }
 
1872
        stream->out.client->Stop();
 
1873
    stream->closeRequest = false;
 
1874
    
 
1875
        return 0; 
 
1876
}
 
1877
 
 
1878
 
 
1879
 
 
1880
 
 
1881
#endif //VC 2005
 
1882
 
 
1883
 
 
1884
 
 
1885
 
 
1886
#if 0
 
1887
                        if(bFirst) {                    
 
1888
                                float masteur;
 
1889
                                hResult = stream->outVol->GetMasterVolumeLevelScalar(&masteur);
 
1890
                                if (hResult != S_OK)
 
1891
                                        logAUDCLNT_E(hResult);
 
1892
                                float chan1, chan2;
 
1893
                                hResult = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);
 
1894
                                if (hResult != S_OK)
 
1895
                                        logAUDCLNT_E(hResult);
 
1896
                                hResult = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);
 
1897
                                if (hResult != S_OK)
 
1898
                                        logAUDCLNT_E(hResult);
 
1899
 
 
1900
                                BOOL bMute;
 
1901
                                hResult = stream->outVol->GetMute(&bMute);
 
1902
                                if (hResult != S_OK)
 
1903
                                        logAUDCLNT_E(hResult);
 
1904
 
 
1905
                                stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);
 
1906
                                stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);
 
1907
                                stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL);
 
1908
                                stream->outVol->SetMute(FALSE, NULL);
 
1909
                                bFirst = false;
 
1910
                        }
 
1911
#endif
 
 
b'\\ No newline at end of file'