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
68 extern "C"
69 {
70 #endif /* __cplusplus */
71 
72 PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
73 
74 #ifdef __cplusplus
75 }
76 #endif /* __cplusplus */
77 
78 
79 
80 
81 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
82 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
83                                   const PaStreamParameters *inputParameters,
84                                   const PaStreamParameters *outputParameters,
85                                   double sampleRate );
86 static 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 );
95 static PaError CloseStream( PaStream* stream );
96 static PaError StartStream( PaStream *stream );
97 static PaError StopStream( PaStream *stream );
98 static PaError AbortStream( PaStream *stream );
99 static PaError IsStreamStopped( PaStream *s );
100 static PaError IsStreamActive( PaStream *stream );
101 static PaTime GetStreamTime( PaStream *stream );
102 static double GetStreamCpuLoad( PaStream* stream );
103 static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
104 static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
105 static signed long GetStreamReadAvailable( PaStream* stream );
106 static 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 
PaWinWasapi_Initialize(PaUtilHostApiRepresentation ** hostApi,PaHostApiIndex hostApiIndex)122 PaError 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  */
139 typedef 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 
164 typedef 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 
189 typedef 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 
198 typedef 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 
230 void
logAUDCLNT_E(HRESULT res)231 logAUDCLNT_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 
262 inline double
nano100ToMillis(const REFERENCE_TIME & ref)263 nano100ToMillis(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 
270 inline double
nano100ToSeconds(const REFERENCE_TIME & ref)271 nano100ToSeconds(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 
286 typedef BOOL   (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);
287 typedef BOOL   (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);
288 typedef BOOL   (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE);
289 typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics)  (LPCTSTR,LPDWORD);
290 typedef BOOL   (WINAPI *FAvSetMmThreadPriority)         (HANDLE,AVRT_PRIORITY);
291 
292 HMODULE  hDInputDLL = 0;
293 FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup=0;
294 FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup=0;
295 FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup=0;
296 FAvSetMmThreadCharacteristics  pAvSetMmThreadCharacteristics=0;
297 FAvSetMmThreadPriority         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 
307 bool
setupAVRT()308 setupAVRT(){
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 
PaWinWasapi_Initialize(PaUtilHostApiRepresentation ** hostApi,PaHostApiIndex hostApiIndex)325 PaError 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 
621 error:
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 
Terminate(struct PaUtilHostApiRepresentation * hostApi)643 static 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 
670 static void
LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE & in)671 LogWAVEFORMATEXTENSIBLE(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  */
722 static PaSampleFormat
waveformatToPaFormat(const WAVEFORMATEXTENSIBLE & in)723 waveformatToPaFormat(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 
777 static PaError
waveformatFromParams(WAVEFORMATEXTENSIBLE & wav,const PaStreamParameters * params,double sampleRate)778 waveformatFromParams(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 
835 enum PaWasapiFormatAnswer {PWFA_OK,PWFA_NO,PWFA_SUGGESTED};
836 
837 
838 static PaWasapiFormatAnswer
IsFormatSupportedInternal(IAudioClient * myClient,WAVEFORMATEXTENSIBLE & wavex)839 IsFormatSupportedInternal(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 
IsFormatSupported(struct PaUtilHostApiRepresentation * hostApi,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate)871 static 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 
OpenStream(struct PaUtilHostApiRepresentation * hostApi,PaStream ** s,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate,unsigned long framesPerBuffer,PaStreamFlags streamFlags,PaStreamCallback * streamCallback,void * userData)1015 static 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 
1325 error:
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 
CloseStream(PaStream * s)1343 static 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 
1366 VOID ProcThread(void *client);
1367 
StartStream(PaStream * s)1368 static 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 
StopStream(PaStream * s)1407 static 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 
AbortStream(PaStream * s)1426 static 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 
IsStreamStopped(PaStream * s)1443 static PaError IsStreamStopped( PaStream *s )
1444 {
1445     PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1446 
1447     return !stream->running;
1448 }
1449 
1450 
IsStreamActive(PaStream * s)1451 static PaError IsStreamActive( PaStream *s )
1452 {
1453     PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1454     return stream->running;
1455 }
1456 
1457 
GetStreamTime(PaStream * s)1458 static 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 
GetStreamCpuLoad(PaStream * s)1474 static 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 
ReadStream(PaStream * s,void * buffer,unsigned long frames)1488 static 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 
WriteStream(PaStream * s,const void * buffer,unsigned long frames)1505 static 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 
GetStreamReadAvailable(PaStream * s)1522 static 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 
GetStreamWriteAvailable(PaStream * s)1535 static 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 */
WaspiHostProcessingLoop(void * inputBuffer,long inputFrames,void * outputBuffer,long outputFrames,void * userData)1554 static 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 
1649 VOID
ProcThread(void * param)1650 ProcThread(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