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 *)∈
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 *)∈
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