source: qutecom-coip/libs/3rdparty/iaxclient/portaudio/src/hostapi/wasapi/pa_win_wasapi.cpp @ 125:d648f4cb122f

Last change on this file since 125:d648f4cb122f was 125:d648f4cb122f, checked in by laurent, 3 years ago

wengo => qutecom

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