2
* Portable Audio I/O Library WASAPI implementation
3
* Copyright (c) 2006-2007 David Viens
5
* Based on the Open Source API proposed by Ross Bencina
6
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
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:
16
* The above copyright notice and this permission notice shall be
17
* included in all copies or substantial portions of the Software.
19
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
23
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
* The text above constitutes the entire PortAudio license; however,
30
* the PortAudio community also makes the following non-binding requests:
32
* Any person wishing to distribute modifications to the Software is
33
* requested to send the modifications to the original developer so that
34
* they can be incorporated into the canonical version. It is also
35
* requested that these non-binding requests be included along with the
41
@brief WASAPI implementation of support for a host API.
43
@note pa_wasapi currently requires VC 2005, and the latest Vista SDK
48
#include <MMReg.h> //must be before other Wasapi headers
50
#include <mmdeviceapi.h>
52
#include <audioclient.h>
53
#include <Endpointvolume.h>
56
#include <functiondiscoverykeys.h> // PKEY_Device_FriendlyName
62
#include "pa_allocation.h"
63
#include "pa_hostapi.h"
64
#include "pa_stream.h"
65
#include "pa_cpuload.h"
66
#include "pa_process.h"
67
#include "pa_debugprint.h"
71
davidv : work in progress. try using with 48000 , then 44100
72
and shared mode FIRST.
75
#define PORTAUDIO_SHAREMODE AUDCLNT_SHAREMODE_SHARED
76
//#define PORTAUDIO_SHAREMODE AUDCLNT_SHAREMODE_EXCLUSIVE
78
/* use CreateThread for CYGWIN, _beginthreadex for all others */
80
#define CREATE_THREAD (HANDLE) _beginthreadex(NULL, 0, (unsigned (_stdcall *)(void *))ProcThread, (LPVOID) stream, 0, (unsigned *)&stream->dwThreadId)
82
#define CREATE_THREAD CreateThread(NULL, 0, ProcThread, (LPVOID) stream, 0, &stream->dwThreadId)
85
/* prototypes for functions declared in this file */
90
#endif /* __cplusplus */
92
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
96
#endif /* __cplusplus */
101
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
102
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
103
const PaStreamParameters *inputParameters,
104
const PaStreamParameters *outputParameters,
106
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
108
const PaStreamParameters *inputParameters,
109
const PaStreamParameters *outputParameters,
111
unsigned long framesPerBuffer,
112
PaStreamFlags streamFlags,
113
PaStreamCallback *streamCallback,
115
static PaError CloseStream( PaStream* stream );
116
static PaError StartStream( PaStream *stream );
117
static PaError StopStream( PaStream *stream );
118
static PaError AbortStream( PaStream *stream );
119
static PaError IsStreamStopped( PaStream *s );
120
static PaError IsStreamActive( PaStream *stream );
121
static PaTime GetStreamTime( PaStream *stream );
122
static double GetStreamCpuLoad( PaStream* stream );
123
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
124
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
125
static signed long GetStreamReadAvailable( PaStream* stream );
126
static signed long GetStreamWriteAvailable( PaStream* stream );
129
/* IMPLEMENT ME: a macro like the following one should be used for reporting
131
#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
132
PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
134
/* PaWinWasapiHostApiRepresentation - host api datastructure specific to this implementation */
138
//dummy entry point for other compilers and sdks
139
//currently built using RC1 SDK (5600)
142
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ){
151
#define MAX_STR_LEN 512
154
These are fields that can be gathered from IDevice
155
and IAudioDevice PRIOR to Initialize, and done in first pass
156
i assume that neither of these will cause the Driver to "load",
157
but again, who knows how they implement their stuff
159
typedef struct PaWinWasapiDeviceInfo
161
//hmm is it wise to keep a reference until Terminate?
162
//TODO Check if that interface requires the driver to be loaded!
165
//Fields filled from IDevice
167
WCHAR szDeviceID[MAX_STR_LEN];
171
//Fields filled from IMMEndpoint'sGetDataFlow
174
//Fields filled from IAudioDevice (_prior_ to Initialize)
175
//from GetDevicePeriod(
176
REFERENCE_TIME DefaultDevicePeriod;
177
REFERENCE_TIME MinimumDevicePeriod;
179
WAVEFORMATEX *MixFormat;//needs to be CoTaskMemFree'd after use!
181
} PaWinWasapiDeviceInfo;
186
PaUtilHostApiRepresentation inheritedHostApiRep;
187
PaUtilStreamInterface callbackStreamInterface;
188
PaUtilStreamInterface blockingStreamInterface;
190
PaUtilAllocationGroup *allocations;
192
/* implementation specific data goes here */
194
//in case we later need the synch
195
IMMDeviceEnumerator * enumerator;
197
//this is the REAL number of devices, whether they are usefull to PA or not!
200
WCHAR defaultRenderer [MAX_STR_LEN];
201
WCHAR defaultCapturer [MAX_STR_LEN];
203
PaWinWasapiDeviceInfo *devInfo;
204
}PaWinWasapiHostApiRepresentation;
207
/* PaWinWasapiStream - a stream data structure specifically for this implementation */
209
typedef struct PaWinWasapiSubStream{
210
IAudioClient *client;
211
WAVEFORMATEXTENSIBLE wavex;
213
REFERENCE_TIME latency;
214
REFERENCE_TIME period;
215
unsigned long framesPerHostCallback; /* just an example */
216
}PaWinWasapiSubStream;
218
typedef struct PaWinWasapiStream
219
{ /* IMPLEMENT ME: rename this */
220
PaUtilStreamRepresentation streamRepresentation;
221
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
222
PaUtilBufferProcessor bufferProcessor;
225
- implementation specific data goes here
230
PaWinWasapiSubStream in;
231
IAudioCaptureClient *cclient;
232
IAudioEndpointVolume *inVol;
234
PaWinWasapiSubStream out;
235
IAudioRenderClient *rclient;
236
IAudioEndpointVolume *outVol;
243
HANDLE hNotificationEvent;
249
#define PRINT(x) PA_DEBUG(x);
252
logAUDCLNT_E(HRESULT res){
256
case S_OK: return; break;
257
case E_POINTER :text ="E_POINTER"; break;
258
case E_INVALIDARG :text ="E_INVALIDARG"; break;
260
case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break;
261
case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;
262
case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;
263
case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;
264
case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break;
265
case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;
266
case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break;
267
case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
268
case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break;
269
case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break;
270
case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;
271
case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;
272
case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;
273
case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;
274
case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
275
// case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;
277
case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;
278
case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;
279
case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;
280
case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
281
case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
282
case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
283
case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break;
284
case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
291
PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n",res,text));
295
nano100ToMillis(const REFERENCE_TIME &ref){
296
// 1 nano = 0.000000001 seconds
297
//100 nano = 0.0000001 seconds
298
//100 nano = 0.0001 milliseconds
299
return ((double)ref)*0.0001;
303
nano100ToSeconds(const REFERENCE_TIME &ref){
304
// 1 nano = 0.000000001 seconds
305
//100 nano = 0.0000001 seconds
306
//100 nano = 0.0001 milliseconds
307
return ((double)ref)*0.0000001;
310
#ifndef IF_FAILED_JUMP
311
#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;
316
//AVRT is the new "multimedia schedulling stuff"
318
typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);
319
typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);
320
typedef BOOL (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE);
321
typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics) (LPCTSTR,LPDWORD);
322
typedef BOOL (WINAPI *FAvSetMmThreadPriority) (HANDLE,AVRT_PRIORITY);
324
HMODULE hDInputDLL = 0;
325
FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup=0;
326
FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup=0;
327
FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup=0;
328
FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics=0;
329
FAvSetMmThreadPriority pAvSetMmThreadPriority=0;
333
#define setupPTR(fun, type, name) { \
334
fun = (type) GetProcAddress(hDInputDLL,name); \
336
PRINT(("GetProcAddr failed for %s" ,name)); \
344
hDInputDLL = LoadLibraryA("avrt.dll");
345
if(hDInputDLL == NULL)
348
setupPTR(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup");
349
setupPTR(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup");
350
setupPTR(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup");
351
setupPTR(pAvSetMmThreadCharacteristics, FAvSetMmThreadCharacteristics, "AvSetMmThreadCharacteristicsA");
352
setupPTR(pAvSetMmThreadPriority, FAvSetMmThreadPriority, "AvSetMmThreadPriority");
359
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
362
PRINT(("Windows WASAPI : No AVRT! (not VISTA?)"));
368
PaError result = paNoError;
369
PaWinWasapiHostApiRepresentation *paWasapi;
370
PaDeviceInfo *deviceInfoArray;
372
paWasapi = (PaWinWasapiHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWasapiHostApiRepresentation) );
374
result = paInsufficientMemory;
378
paWasapi->allocations = PaUtil_CreateAllocationGroup();
379
if( !paWasapi->allocations ){
380
result = paInsufficientMemory;
384
*hostApi = &paWasapi->inheritedHostApiRep;
385
(*hostApi)->info.structVersion = 1;
386
(*hostApi)->info.type = paWASAPI;
387
(*hostApi)->info.name = "Windows WASAPI";
388
(*hostApi)->info.deviceCount = 0; //so far, we must investigate each
389
(*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */
390
(*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */
393
HRESULT hResult = S_OK;
394
IMMDeviceCollection* spEndpoints=0;
395
paWasapi->enumerator = 0;
397
hResult = CoCreateInstance(
398
__uuidof(MMDeviceEnumerator), NULL,CLSCTX_INPROC_SERVER,
399
__uuidof(IMMDeviceEnumerator),
400
(void**)&paWasapi->enumerator);
402
IF_FAILED_JUMP(hResult, error);
404
//getting default device ids in the eMultimedia "role"
407
IMMDevice* defaultRenderer=0;
408
hResult = paWasapi->enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &defaultRenderer);
409
IF_FAILED_JUMP(hResult, error);
410
WCHAR* pszDeviceId = NULL;
411
hResult = defaultRenderer->GetId(&pszDeviceId);
412
IF_FAILED_JUMP(hResult, error);
413
StringCchCopyW(paWasapi->defaultRenderer, MAX_STR_LEN-1, pszDeviceId);
414
CoTaskMemFree(pszDeviceId);
415
defaultRenderer->Release();
419
IMMDevice* defaultCapturer=0;
420
hResult = paWasapi->enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, &defaultCapturer);
421
IF_FAILED_JUMP(hResult, error);
422
WCHAR* pszDeviceId = NULL;
423
hResult = defaultCapturer->GetId(&pszDeviceId);
424
IF_FAILED_JUMP(hResult, error);
425
StringCchCopyW(paWasapi->defaultCapturer, MAX_STR_LEN-1, pszDeviceId);
426
CoTaskMemFree(pszDeviceId);
427
defaultCapturer->Release();
432
hResult = paWasapi->enumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &spEndpoints);
433
IF_FAILED_JUMP(hResult, error);
435
hResult = spEndpoints->GetCount(&paWasapi->deviceCount);
436
IF_FAILED_JUMP(hResult, error);
438
paWasapi->devInfo = new PaWinWasapiDeviceInfo[paWasapi->deviceCount];
440
for (size_t step=0;step<paWasapi->deviceCount;++step)
441
memset(&paWasapi->devInfo[step],0,sizeof(PaWinWasapiDeviceInfo));
446
if( paWasapi->deviceCount > 0 )
448
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
449
paWasapi->allocations, sizeof(PaDeviceInfo*) * paWasapi->deviceCount );
450
if( !(*hostApi)->deviceInfos ){
451
result = paInsufficientMemory;
455
/* allocate all device info structs in a contiguous block */
456
deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
457
paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount );
458
if( !deviceInfoArray ){
459
result = paInsufficientMemory;
463
for( UINT i=0; i < paWasapi->deviceCount; ++i ){
465
PA_DEBUG(("i:%d\n",i));
466
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
467
deviceInfo->structVersion = 2;
468
deviceInfo->hostApi = hostApiIndex;
470
hResult = spEndpoints->Item(i, &paWasapi->devInfo[i].device);
471
IF_FAILED_JUMP(hResult, error);
475
WCHAR* pszDeviceId = NULL;
476
hResult = paWasapi->devInfo[i].device->GetId(&pszDeviceId);
477
IF_FAILED_JUMP(hResult, error);
478
StringCchCopyW(paWasapi->devInfo[i].szDeviceID, MAX_STR_LEN-1, pszDeviceId);
479
CoTaskMemFree(pszDeviceId);
481
if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer)==0){
482
//we found the default input!
483
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
485
if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer)==0){
486
//we found the default output!
487
(*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
492
hResult = paWasapi->devInfo[i].device->GetState(&paWasapi->devInfo[i].state);
493
IF_FAILED_JUMP(hResult, error);
495
if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE){
496
PRINT(("WASAPI device:%d is not currently available (state:%d)\n",i,state));
497
//spDevice->Release();
502
IPropertyStore* spProperties;
503
hResult = paWasapi->devInfo[i].device->OpenPropertyStore(STGM_READ, &spProperties);
504
IF_FAILED_JUMP(hResult, error);
506
//getting "Friendly" Name
509
PropVariantInit(&value);
510
hResult = spProperties->GetValue(PKEY_Device_FriendlyName, &value);
511
IF_FAILED_JUMP(hResult, error);
512
deviceInfo->name = 0;
513
char* deviceName = (char*)PaUtil_GroupAllocateMemory( paWasapi->allocations, MAX_STR_LEN + 1 );
515
result = paInsufficientMemory;
519
wcstombs(deviceName, value.pwszVal,MAX_STR_LEN-1); //todo proper size
521
_snprintf_s(deviceName,MAX_STR_LEN-1,MAX_STR_LEN-1,"baddev%d",i);
524
deviceInfo->name = deviceName;
525
PropVariantClear(&value);
530
hResult = spProperties->GetCount(&numProps);
531
IF_FAILED_JUMP(hResult, error);
533
for (DWORD i=0;i<numProps;++i){
535
hResult = spProperties->GetAt(i,&pkey);
538
PropVariantInit(&value);
539
hResult = spProperties->GetValue(pkey, &value);
543
PRINT(("property*%u*\n",value.ulVal));
546
PRINT(("property*%d*\n",value.boolVal));
551
wcstombs(temp, value.pwszVal,MAX_STR_LEN-1);
552
PRINT(("property*%s*\n",temp));
558
PropVariantClear(&value);
563
/* These look interresting... but they are undocumented
564
PKEY_AudioEndpoint_FormFactor
565
PKEY_AudioEndpoint_ControlPanelPageProvider
566
PKEY_AudioEndpoint_Association
567
PKEY_AudioEndpoint_PhysicalSpeakerConfig
568
PKEY_AudioEngine_DeviceFormat
570
spProperties->Release();
574
//getting the Endpoint data
576
IMMEndpoint *endpoint=0;
577
hResult = paWasapi->devInfo[i].device->QueryInterface(__uuidof(IMMEndpoint),(void **)&endpoint);
578
if (SUCCEEDED(hResult)){
579
hResult = endpoint->GetDataFlow(&paWasapi->devInfo[i].flow);
584
//Getting a temporary IAudioDevice for more fields
585
//we make sure NOT to call Initialize yet!
587
IAudioClient *myClient=0;
589
hResult = paWasapi->devInfo[i].device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
590
IF_FAILED_JUMP(hResult, error);
592
hResult = myClient->GetDevicePeriod(
593
&paWasapi->devInfo[i].DefaultDevicePeriod,
594
&paWasapi->devInfo[i].MinimumDevicePeriod);
595
IF_FAILED_JUMP(hResult, error);
597
hResult = myClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);
599
if (hResult != S_OK){
600
/*davidv: this happened with my hardware, previously for that same device in DirectSound:
601
Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
602
so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat*/
603
logAUDCLNT_E(hResult);
610
//we can now fill in portaudio device data
611
deviceInfo->maxInputChannels = 0; //for now
612
deviceInfo->maxOutputChannels = 0; //for now
614
switch(paWasapi->devInfo[i].flow){
616
//hum not exaclty maximum, more like "default"
617
deviceInfo->maxOutputChannels = paWasapi->devInfo[i].MixFormat->nChannels;
619
deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
620
deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
623
//hum not exaclty maximum, more like "default"
624
deviceInfo->maxInputChannels = paWasapi->devInfo[i].MixFormat->nChannels;
626
deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
627
deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
630
PRINT(("WASAPI device:%d bad Data FLow! \n",i));
635
deviceInfo->defaultSampleRate = (double)paWasapi->devInfo[i].MixFormat->nSamplesPerSec;
637
(*hostApi)->deviceInfos[i] = deviceInfo;
638
++(*hostApi)->info.deviceCount;
642
spEndpoints->Release();
644
(*hostApi)->Terminate = Terminate;
645
(*hostApi)->OpenStream = OpenStream;
646
(*hostApi)->IsFormatSupported = IsFormatSupported;
648
PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream,
649
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
650
GetStreamTime, GetStreamCpuLoad,
651
PaUtil_DummyRead, PaUtil_DummyWrite,
652
PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
654
PaUtil_InitializeStreamInterface( &paWasapi->blockingStreamInterface, CloseStream, StartStream,
655
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
656
GetStreamTime, PaUtil_DummyGetCpuLoad,
657
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
664
spEndpoints->Release();
666
if (paWasapi->enumerator)
667
paWasapi->enumerator->Release();
671
if( paWasapi->allocations )
673
PaUtil_FreeAllAllocations( paWasapi->allocations );
674
PaUtil_DestroyAllocationGroup( paWasapi->allocations );
677
PaUtil_FreeMemory( paWasapi );
683
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
685
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
687
paWasapi->enumerator->Release();
689
for (UINT i=0;i<paWasapi->deviceCount;++i){
690
PaWinWasapiDeviceInfo *info = &paWasapi->devInfo[i];
693
info->device->Release();
696
CoTaskMemFree(info->MixFormat);
698
delete [] paWasapi->devInfo;
702
if( paWasapi->allocations ){
703
PaUtil_FreeAllAllocations( paWasapi->allocations );
704
PaUtil_DestroyAllocationGroup( paWasapi->allocations );
707
PaUtil_FreeMemory( paWasapi );
711
LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in){
713
const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
715
switch (old->wFormatTag){
716
case WAVE_FORMAT_EXTENSIBLE:{
718
PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));
720
if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
721
PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));
723
else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
724
PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));
727
PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",
731
(int)in->SubFormat.Data4[0],
732
(int)in->SubFormat.Data4[1],
733
(int)in->SubFormat.Data4[2],
734
(int)in->SubFormat.Data4[3],
735
(int)in->SubFormat.Data4[4],
736
(int)in->SubFormat.Data4[5],
737
(int)in->SubFormat.Data4[6],
738
(int)in->SubFormat.Data4[7]));
740
PRINT(("Samples.wValidBitsPerSample=%d\n", in->Samples.wValidBitsPerSample));
741
PRINT(("dwChannelMask=0x%X\n",in->dwChannelMask));
744
case WAVE_FORMAT_PCM: PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;
745
case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break;
746
default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break;
749
PRINT(("nChannels =%d\n",old->nChannels));
750
PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));
751
PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));
752
PRINT(("nBlockAlign =%d\n",old->nBlockAlign));
753
PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));
754
PRINT(("cbSize =%d\n",old->cbSize));
760
WAVEFORMATXXX is always interleaved
762
static PaSampleFormat
763
waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in){
765
const WAVEFORMATEX *old = (WAVEFORMATEX*)in;
767
switch (old->wFormatTag){
769
case WAVE_FORMAT_EXTENSIBLE:
771
if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
772
if (in->Samples.wValidBitsPerSample == 32)
775
return paCustomFormat;
777
else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
778
switch (old->wBitsPerSample){
779
case 32: return paInt32; break;
780
case 24: return paInt24;break;
781
case 8: return paUInt8;break;
782
case 16: return paInt16;break;
783
default: return paCustomFormat;break;
787
return paCustomFormat;
791
case WAVE_FORMAT_IEEE_FLOAT:
795
case WAVE_FORMAT_PCM:
797
switch (old->wBitsPerSample){
798
case 32: return paInt32; break;
799
case 24: return paInt24;break;
800
case 8: return paUInt8;break;
801
case 16: return paInt16;break;
802
default: return paCustomFormat;break;
808
return paCustomFormat;
812
return paCustomFormat;
818
waveformatFromParams(WAVEFORMATEXTENSIBLE*wavex,
819
const PaStreamParameters * params,
822
size_t bytesPerSample = 0;
823
switch( params->sampleFormat & ~paNonInterleaved ){
825
case paInt32: bytesPerSample=4;break;
826
case paInt16: bytesPerSample=2;break;
827
case paInt24: bytesPerSample=3;break;
829
case paUInt8: bytesPerSample=1;break;
831
default: return paSampleFormatNotSupported;break;
834
memset(wavex,0,sizeof(WAVEFORMATEXTENSIBLE));
836
WAVEFORMATEX *old = (WAVEFORMATEX *)wavex;
837
old->nChannels = (WORD)params->channelCount;
838
old->nSamplesPerSec = (DWORD)sampleRate;
839
old->wBitsPerSample = (WORD)(bytesPerSample*8);
840
old->nAvgBytesPerSec = (DWORD)(old->nSamplesPerSec * old->nChannels * bytesPerSample);
841
old->nBlockAlign = (WORD)(old->nChannels * bytesPerSample);
844
if (params->channelCount <=2 && (bytesPerSample == 2 || bytesPerSample == 1)){
846
old->wFormatTag = WAVE_FORMAT_PCM;
848
//WAVEFORMATEXTENSIBLE
850
old->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
852
old->cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);
854
if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
855
wavex->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
857
wavex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
859
wavex->Samples.wValidBitsPerSample = old->wBitsPerSample; //no extra padding!
861
switch(params->channelCount){
862
case 1: wavex->dwChannelMask = SPEAKER_FRONT_CENTER; break;
863
case 2: wavex->dwChannelMask = 0x1 | 0x2; break;
864
case 4: wavex->dwChannelMask = 0x1 | 0x2 | 0x10 | 0x20; break;
865
case 6: wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20; break;
866
case 8: wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80; break;
867
default: wavex->dwChannelMask = 0; break;
880
#define paFloat32 ((PaSampleFormat) 0x00000001)
881
#define paInt32 ((PaSampleFormat) 0x00000002)
882
#define paInt24 ((PaSampleFormat) 0x00000004)
883
#define paInt16 ((PaSampleFormat) 0x00000008)
885
//lifted from pa_wdmks
886
static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
888
PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
889
PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
890
PA_DEBUG(( "chanelCount = %d\n", channelCount ));
892
pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
893
pwfext->Format.nChannels = channelCount;
894
pwfext->Format.nSamplesPerSec = (int)sampleRate;
895
if(channelCount == 1)
896
pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
898
pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
899
if(sampleFormat == paFloat32)
901
pwfext->Format.nBlockAlign = channelCount * 4;
902
pwfext->Format.wBitsPerSample = 32;
903
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
904
pwfext->Samples.wValidBitsPerSample = 32;
905
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
907
else if(sampleFormat == paInt32)
909
pwfext->Format.nBlockAlign = channelCount * 4;
910
pwfext->Format.wBitsPerSample = 32;
911
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
912
pwfext->Samples.wValidBitsPerSample = 32;
913
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
915
else if(sampleFormat == paInt24)
917
pwfext->Format.nBlockAlign = channelCount * 3;
918
pwfext->Format.wBitsPerSample = 24;
919
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
920
pwfext->Samples.wValidBitsPerSample = 24;
921
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
923
else if(sampleFormat == paInt16)
925
pwfext->Format.nBlockAlign = channelCount * 2;
926
pwfext->Format.wBitsPerSample = 16;
927
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
928
pwfext->Samples.wValidBitsPerSample = 16;
929
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
931
pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
937
#define FORMATTESTS 4
938
const int BestToWorst[FORMATTESTS]={paFloat32,paInt32,paInt24,paInt16};
941
#define FORMATTESTS 3
942
const int BestToWorst[FORMATTESTS]={paFloat32,paInt24,paInt16};
946
GetClosestFormat(IAudioClient * myClient, double sampleRate,const PaStreamParameters * params,
947
AUDCLNT_SHAREMODE *shareMode, WAVEFORMATEXTENSIBLE *outWavex)
949
//TODO we should try exclusive first and shared after
950
*shareMode = PORTAUDIO_SHAREMODE;
952
PaError answer = paInvalidSampleRate;
954
waveformatFromParams(outWavex,params,sampleRate);
955
WAVEFORMATEX *sharedClosestMatch=0;
956
HRESULT hResult=!S_OK;
958
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
959
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,&outWavex->Format,NULL);
961
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &outWavex->Format,&sharedClosestMatch);
964
answer = paFormatIsSupported;
965
else if (sharedClosestMatch){
966
WAVEFORMATEXTENSIBLE* ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;
968
int closestMatchSR = (int)sharedClosestMatch->nSamplesPerSec;
970
if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
971
memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEXTENSIBLE));
973
memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEX));
975
CoTaskMemFree(sharedClosestMatch);
977
if ((int)sampleRate == closestMatchSR)
978
answer = paFormatIsSupported;
980
answer = paInvalidSampleRate;
984
//it doesnt suggest anything?? ok lets show it the MENU!
986
//ok fun time as with pa_win_mme, we know only a refusal of the user-requested
987
//sampleRate+num Channel is disastrous, as the portaudio buffer processor converts between anything
988
//so lets only use the number
989
for (int i=0;i<FORMATTESTS;++i){
990
WAVEFORMATEXTENSIBLE ext;
991
wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount);
992
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
993
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,&ext.Format,NULL);
995
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &ext.Format,&sharedClosestMatch);
997
if (hResult == S_OK){
998
memcpy(outWavex,&ext,sizeof(WAVEFORMATEXTENSIBLE));
999
answer = paFormatIsSupported;
1004
if (answer!=paFormatIsSupported) {
1006
//why did it HAVE to come to this ....
1007
WAVEFORMATEX pcm16WaveFormat;
1008
memset(&pcm16WaveFormat,0,sizeof(WAVEFORMATEX));
1009
pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
1010
pcm16WaveFormat.nChannels = 2;
1011
pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate;
1012
pcm16WaveFormat.nBlockAlign = 4;
1013
pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign;
1014
pcm16WaveFormat.wBitsPerSample = 16;
1015
pcm16WaveFormat.cbSize = 0;
1017
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1018
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,&pcm16WaveFormat,NULL);
1020
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &pcm16WaveFormat,&sharedClosestMatch);
1022
if (hResult == S_OK){
1023
memcpy(outWavex,&pcm16WaveFormat,sizeof(WAVEFORMATEX));
1024
answer = paFormatIsSupported;
1028
logAUDCLNT_E(hResult);
1035
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1036
const PaStreamParameters *inputParameters,
1037
const PaStreamParameters *outputParameters,
1041
int inputChannelCount, outputChannelCount;
1042
PaSampleFormat inputSampleFormat, outputSampleFormat;
1044
if( inputParameters )
1046
inputChannelCount = inputParameters->channelCount;
1047
inputSampleFormat = inputParameters->sampleFormat;
1049
/* all standard sample formats are supported by the buffer adapter,
1050
this implementation doesn't support any custom sample formats */
1051
if( inputSampleFormat & paCustomFormat )
1052
return paSampleFormatNotSupported;
1054
/* unless alternate device specification is supported, reject the use of
1055
paUseHostApiSpecificDeviceSpecification */
1057
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1058
return paInvalidDevice;
1060
/* check that input device can support inputChannelCount */
1061
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
1062
return paInvalidChannelCount;
1064
/* validate inputStreamInfo */
1065
if( inputParameters->hostApiSpecificStreamInfo )
1066
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1069
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
1072
IAudioClient *myClient=0;
1073
HRESULT hResult = paWasapi->devInfo[inputParameters->device].device->Activate(
1074
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
1075
if (hResult != S_OK){
1076
logAUDCLNT_E(hResult);
1077
return paInvalidDevice;
1080
WAVEFORMATEXTENSIBLE wavex;
1081
AUDCLNT_SHAREMODE shareMode;
1082
PaError answer = GetClosestFormat(myClient,sampleRate,inputParameters,&shareMode,&wavex);
1083
myClient->Release();
1085
if (answer !=paFormatIsSupported)
1090
inputChannelCount = 0;
1093
if( outputParameters )
1095
outputChannelCount = outputParameters->channelCount;
1096
outputSampleFormat = outputParameters->sampleFormat;
1098
/* all standard sample formats are supported by the buffer adapter,
1099
this implementation doesn't support any custom sample formats */
1100
if( outputSampleFormat & paCustomFormat )
1101
return paSampleFormatNotSupported;
1103
/* unless alternate device specification is supported, reject the use of
1104
paUseHostApiSpecificDeviceSpecification */
1106
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1107
return paInvalidDevice;
1109
/* check that output device can support outputChannelCount */
1110
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
1111
return paInvalidChannelCount;
1113
/* validate outputStreamInfo */
1114
if( outputParameters->hostApiSpecificStreamInfo )
1115
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1118
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
1120
IAudioClient *myClient=0;
1121
HRESULT hResult = paWasapi->devInfo[outputParameters->device].device->Activate(
1122
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
1123
if (hResult != S_OK){
1124
logAUDCLNT_E(hResult);
1125
return paInvalidDevice;
1128
WAVEFORMATEXTENSIBLE wavex;
1129
AUDCLNT_SHAREMODE shareMode;
1130
PaError answer = GetClosestFormat(myClient,sampleRate,outputParameters,&shareMode,&wavex);
1131
myClient->Release();
1133
if (answer !=paFormatIsSupported)
1138
outputChannelCount = 0;
1142
return paFormatIsSupported;
1147
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
1149
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1151
const PaStreamParameters *inputParameters,
1152
const PaStreamParameters *outputParameters,
1154
unsigned long framesPerBuffer,
1155
PaStreamFlags streamFlags,
1156
PaStreamCallback *streamCallback,
1159
PaError result = paNoError;
1160
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
1161
PaWinWasapiStream *stream = 0;
1162
int inputChannelCount, outputChannelCount;
1163
PaSampleFormat inputSampleFormat, outputSampleFormat;
1164
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
1167
stream = (PaWinWasapiStream*)PaUtil_AllocateMemory( sizeof(PaWinWasapiStream) );
1169
result = paInsufficientMemory;
1173
if( inputParameters )
1175
inputChannelCount = inputParameters->channelCount;
1176
inputSampleFormat = inputParameters->sampleFormat;
1178
/* unless alternate device specification is supported, reject the use of
1179
paUseHostApiSpecificDeviceSpecification */
1181
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1182
return paInvalidDevice;
1184
/* check that input device can support inputChannelCount */
1185
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
1186
return paInvalidChannelCount;
1188
/* validate inputStreamInfo */
1189
if( inputParameters->hostApiSpecificStreamInfo )
1190
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1193
PaWinWasapiDeviceInfo &info = paWasapi->devInfo[inputParameters->device];
1195
HRESULT hResult = info.device->Activate(
1196
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
1197
(void**)&stream->in.client);
1199
if (hResult != S_OK)
1200
return paInvalidDevice;
1202
hResult = info.device->Activate(
1203
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
1204
(void**)&stream->inVol);
1206
if (hResult != S_OK)
1207
return paInvalidDevice;
1209
AUDCLNT_SHAREMODE shareMode;
1210
PaError answer = GetClosestFormat(stream->in.client,sampleRate,inputParameters,&shareMode,&stream->in.wavex);
1212
if (answer !=paFormatIsSupported)
1215
//stream->out.period = info.DefaultDevicePeriod;
1216
stream->in.period = info.MinimumDevicePeriod;
1218
hResult = stream->in.client->Initialize(
1222
0,//stream->out.period,
1223
(WAVEFORMATEX*)&stream->in.wavex,
1227
if (hResult != S_OK){
1228
logAUDCLNT_E(hResult);
1229
return paInvalidDevice;
1232
hResult = stream->in.client->GetBufferSize(&stream->in.bufferSize);
1233
if (hResult != S_OK)
1234
return paInvalidDevice;
1236
hResult = stream->in.client->GetStreamLatency(&stream->in.latency);
1237
if (hResult != S_OK)
1238
return paInvalidDevice;
1240
double periodsPerSecond = 1.0/nano100ToSeconds(stream->in.period);
1241
double samplesPerPeriod = (double)(stream->in.wavex.Format.nSamplesPerSec)/periodsPerSecond;
1243
//this is the number of samples that are required at each period
1244
stream->in.framesPerHostCallback = (unsigned long)samplesPerPeriod;//unrelated to channels
1246
/* IMPLEMENT ME - establish which host formats are available */
1247
hostInputSampleFormat =
1248
PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );
1252
inputChannelCount = 0;
1253
inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
1256
if( outputParameters )
1258
outputChannelCount = outputParameters->channelCount;
1259
outputSampleFormat = outputParameters->sampleFormat;
1261
/* unless alternate device specification is supported, reject the use of
1262
paUseHostApiSpecificDeviceSpecification */
1264
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1265
return paInvalidDevice;
1267
/* check that output device can support inputChannelCount */
1268
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
1269
return paInvalidChannelCount;
1271
/* validate outputStreamInfo */
1272
if( outputParameters->hostApiSpecificStreamInfo )
1273
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1276
PaWinWasapiDeviceInfo &info = paWasapi->devInfo[outputParameters->device];
1278
HRESULT hResult = info.device->Activate(
1279
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
1280
(void**)&stream->out.client);
1282
if (hResult != S_OK)
1283
return paInvalidDevice;
1285
AUDCLNT_SHAREMODE shareMode;
1286
PaError answer = GetClosestFormat(stream->out.client,sampleRate,outputParameters,&shareMode,&stream->out.wavex);
1288
if (answer !=paFormatIsSupported)
1290
LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
1292
// stream->out.period = info.DefaultDevicePeriod;
1293
stream->out.period = info.MinimumDevicePeriod;
1295
/*For an exclusive-mode stream that uses event-driven buffering,
1296
the caller must specify nonzero values for hnsPeriodicity and hnsBufferDuration,
1297
and the values of these two parameters must be equal */
1298
if (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE){
1299
hResult = stream->out.client->Initialize(
1301
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1304
(WAVEFORMATEX*)&stream->out.wavex,
1309
hResult = stream->out.client->Initialize(
1311
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1314
(WAVEFORMATEX*)&stream->out.wavex,
1320
if (hResult != S_OK){
1321
logAUDCLNT_E(hResult);
1322
return paInvalidDevice;
1325
hResult = info.device->Activate(
1326
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
1327
(void**)&stream->outVol);
1329
if (hResult != S_OK)
1330
return paInvalidDevice;
1332
hResult = stream->out.client->GetBufferSize(&stream->out.bufferSize);
1333
if (hResult != S_OK)
1334
return paInvalidDevice;
1336
hResult = stream->out.client->GetStreamLatency(&stream->out.latency);
1337
if (hResult != S_OK)
1338
return paInvalidDevice;
1340
double periodsPerSecond = 1.0/nano100ToSeconds(stream->out.period);
1341
double samplesPerPeriod = (double)(stream->out.wavex.Format.nSamplesPerSec)/periodsPerSecond;
1343
//this is the number of samples that are required at each period
1344
stream->out.framesPerHostCallback = stream->out.bufferSize; //(unsigned long)samplesPerPeriod;//unrelated to channels
1346
/* IMPLEMENT ME - establish which host formats are available */
1347
hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat );
1351
outputChannelCount = 0;
1352
outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
1360
( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )
1362
- check that input device can support inputSampleFormat, or that
1363
we have the capability to convert from outputSampleFormat to
1366
- check that output device can support outputSampleFormat, or that
1367
we have the capability to convert from outputSampleFormat to
1370
- if a full duplex stream is requested, check that the combination
1371
of input and output parameters is supported
1373
- check that the device supports sampleRate
1375
- alter sampleRate to a close allowable rate if possible / necessary
1377
- validate suggestedInputLatency and suggestedOutputLatency parameters,
1378
use default values where necessary
1383
/* validate platform specific flags */
1384
if( (streamFlags & paPlatformSpecificFlags) != 0 )
1385
return paInvalidFlag; /* unexpected platform specific flag */
1389
if( streamCallback )
1391
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
1392
&paWasapi->callbackStreamInterface, streamCallback, userData );
1396
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
1397
&paWasapi->blockingStreamInterface, streamCallback, userData );
1400
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
1403
if (outputParameters && inputParameters){
1405
//serious problem #1
1406
if (stream->in.period != stream->out.period){
1407
PRINT(("OpenStream: period discrepancy\n"));
1411
//serious problem #2
1412
if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback){
1413
PRINT(("OpenStream: framesPerHostCallback discrepancy\n"));
1418
unsigned long framesPerHostCallback = (outputParameters)?
1419
stream->out.framesPerHostCallback:
1420
stream->in.framesPerHostCallback;
1422
/* we assume a fixed host buffer size in this example, but the buffer processor
1423
can also support bounded and unknown host buffer sizes by passing
1424
paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
1425
paUtilFixedHostBufferSize below. */
1427
result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
1428
inputChannelCount, inputSampleFormat, hostInputSampleFormat,
1429
outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
1430
sampleRate, streamFlags, framesPerBuffer,
1431
framesPerHostCallback, paUtilFixedHostBufferSize,
1432
streamCallback, userData );
1433
if( result != paNoError )
1438
IMPLEMENT ME: initialise the following fields with estimated or actual
1441
stream->streamRepresentation.streamInfo.inputLatency =
1442
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
1443
+ ((inputParameters)?nano100ToSeconds(stream->in.latency) :0);
1445
stream->streamRepresentation.streamInfo.outputLatency =
1446
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
1447
+ ((outputParameters)?nano100ToSeconds(stream->out.latency) :0);
1449
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
1452
*s = (PaStream*)stream;
1459
PaUtil_FreeMemory( stream );
1467
When CloseStream() is called, the multi-api layer ensures that
1468
the stream has already been stopped or aborted.
1471
#define SAFE_RELEASE(punk) \
1472
if ((punk) != NULL) \
1473
{ (punk)->Release(); (punk) = NULL; }
1475
static PaError CloseStream( PaStream* s )
1477
PaError result = paNoError;
1478
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1482
- additional stream closing + cleanup
1485
SAFE_RELEASE(stream->out.client);
1486
SAFE_RELEASE(stream->in.client);
1487
SAFE_RELEASE(stream->cclient);
1488
SAFE_RELEASE(stream->rclient);
1489
SAFE_RELEASE(stream->inVol);
1490
SAFE_RELEASE(stream->outVol);
1491
CloseHandle(stream->hThread);
1492
CloseHandle(stream->hNotificationEvent);
1494
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
1495
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
1496
PaUtil_FreeMemory( stream );
1501
DWORD WINAPI ProcThread(void *client);
1503
static PaError StartStream( PaStream *s )
1505
PaError result = paNoError;
1506
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1508
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
1510
HRESULT hResult=S_OK;
1512
if (stream->out.client){
1513
hResult = stream->out.client->GetService(__uuidof(IAudioRenderClient),(void**)&stream->rclient);
1514
logAUDCLNT_E(hResult);
1516
return paUnanticipatedHostError;
1519
if (stream->in.client){
1520
hResult = stream->in.client->GetService(__uuidof(IAudioCaptureClient),(void**)&stream->cclient);
1521
logAUDCLNT_E(hResult);
1523
return paUnanticipatedHostError;
1526
// Create a thread for this client.
1527
stream->hThread = CREATE_THREAD;
1529
if (stream->hThread == NULL)
1530
return paUnanticipatedHostError;
1536
static PaError StopStream( PaStream *s )
1538
PaError result = paNoError;
1539
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1541
/* suppress unused variable warnings */
1542
stream->closeRequest = true;
1543
//todo something MUCH better than this
1544
while(stream->closeRequest)
1547
/* IMPLEMENT ME, see portaudio.h for required behavior */
1549
stream->running = false;
1555
static PaError AbortStream( PaStream *s )
1557
PaError result = paNoError;
1558
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1560
/* suppress unused variable warnings */
1561
stream->closeRequest = true;
1562
//todo something MUCH better than this
1563
while(stream->closeRequest)
1566
/* IMPLEMENT ME, see portaudio.h for required behavior */
1572
static PaError IsStreamStopped( PaStream *s )
1574
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1576
return !stream->running;
1580
static PaError IsStreamActive( PaStream *s )
1582
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1583
return stream->running;
1587
static PaTime GetStreamTime( PaStream *s )
1589
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1591
/* suppress unused variable warnings */
1594
/* IMPLEMENT ME, see portaudio.h for required behavior*/
1596
//this is lame ds and mme does the same thing, quite useless method imho
1597
//why dont we fetch the time in the pa callbacks?
1598
//at least its doing to be clocked to something
1599
return PaUtil_GetTime();
1603
static double GetStreamCpuLoad( PaStream* s )
1605
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1607
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
1612
As separate stream interfaces are used for blocking and callback
1613
streams, the following functions can be guaranteed to only be called
1614
for blocking streams.
1617
static PaError ReadStream( PaStream* s,
1619
unsigned long frames )
1621
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1623
/* suppress unused variable warnings */
1628
/* IMPLEMENT ME, see portaudio.h for required behavior*/
1634
static PaError WriteStream( PaStream* s,
1636
unsigned long frames )
1638
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1640
/* suppress unused variable warnings */
1645
/* IMPLEMENT ME, see portaudio.h for required behavior*/
1651
static signed long GetStreamReadAvailable( PaStream* s )
1653
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1655
/* suppress unused variable warnings */
1658
/* IMPLEMENT ME, see portaudio.h for required behavior*/
1664
static signed long GetStreamWriteAvailable( PaStream* s )
1666
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1668
/* suppress unused variable warnings */
1671
/* IMPLEMENT ME, see portaudio.h for required behavior*/
1679
ExampleHostProcessingLoop() illustrates the kind of processing which may
1680
occur in a host implementation.
1683
static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
1684
void *outputBuffer, long outputFrames,
1687
PaWinWasapiStream *stream = (PaWinWasapiStream*)userData;
1688
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
1690
unsigned long framesProcessed;
1692
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
1697
- generate timing information
1698
- handle buffer slips
1702
If you need to byte swap or shift inputBuffer to convert it into a
1703
portaudio format, do it here.
1708
PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );
1711
depending on whether the host buffers are interleaved, non-interleaved
1712
or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
1713
PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
1716
if( stream->bufferProcessor.inputChannelCount > 0 )
1718
PaUtil_SetInputFrameCount( &stream->bufferProcessor, inputFrames );
1719
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
1720
0, /* first channel of inputBuffer is channel 0 */
1722
0 ); /* 0 - use inputChannelCount passed to init buffer processor */
1725
if( stream->bufferProcessor.outputChannelCount > 0 )
1727
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, outputFrames);
1728
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
1729
0, /* first channel of outputBuffer is channel 0 */
1731
0 ); /* 0 - use outputChannelCount passed to init buffer processor */
1734
/* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
1735
in general you would pass paContinue for normal operation, and
1736
paComplete to drain the buffer processor's internal output buffer.
1737
You can check whether the buffer processor's output buffer is empty
1738
using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
1740
callbackResult = paContinue;
1741
framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
1745
If you need to byte swap or shift outputBuffer to convert it to
1746
host format, do it here.
1749
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
1752
if( callbackResult == paContinue )
1754
/* nothing special to do */
1756
else if( callbackResult == paAbort )
1758
/* IMPLEMENT ME - finish playback immediately */
1760
/* once finished, call the finished callback */
1761
if( stream->streamRepresentation.streamFinishedCallback != 0 )
1762
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
1766
/* User callback has asked us to stop with paComplete or other non-zero value */
1768
/* IMPLEMENT ME - finish playback once currently queued audio has completed */
1770
/* once finished, call the finished callback */
1771
if( stream->streamRepresentation.streamFinishedCallback != 0 )
1772
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
1781
HANDLE thCarac = pAvSetMmThreadCharacteristics("Pro Audio",&stuff);
1783
PRINT(("AvSetMmThreadCharacteristics failed!\n"));
1786
BOOL prio = pAvSetMmThreadPriority(thCarac,AVRT_PRIORITY_NORMAL);
1788
PRINT(("AvSetMmThreadPriority failed!\n"));
1793
HANDLE hh = GetCurrentThread();
1794
int currprio = GetThreadPriority(hh);
1795
DWORD currclass = GetPriorityClass(GetCurrentProcess());
1796
PRINT(("currprio 0x%X currclass 0x%X\n",currprio,currclass));
1802
ProcThread(void* param){
1806
PaWinWasapiStream *stream = (PaWinWasapiStream*)param;
1808
stream->hNotificationEvent = CreateEvent(NULL,
1809
FALSE, //bManualReset are we sure??
1812
hResult = stream->out.client->SetEventHandle(stream->hNotificationEvent);
1813
if (hResult != S_OK)
1814
logAUDCLNT_E(hResult);
1816
if (stream->out.client){
1817
hResult = stream->out.client->Start();
1818
if (hResult != S_OK)
1819
logAUDCLNT_E(hResult);
1822
stream->running = true;
1825
while( !stream->closeRequest )
1827
//lets wait but have a 1 second timeout
1828
DWORD dwResult = WaitForSingleObject(stream->hNotificationEvent, 1000);
1829
switch( dwResult ) {
1830
case WAIT_OBJECT_0: {
1832
unsigned long usingBS = stream->out.framesPerHostCallback;
1837
hResult = stream->rclient->GetBuffer(usingBS, &outdata);
1839
if (hResult != S_OK || !outdata) {
1840
//logAUDCLNT_E(hResult);
1841
//most probably shared mode and hResult=AUDCLNT_E_BUFFER_TOO_LARGE
1843
hResult = stream->out.client->GetCurrentPadding(&padding);
1846
usingBS = usingBS-padding;
1849
hResult = stream->rclient->GetBuffer(usingBS, &outdata);
1850
if (hResult != S_OK)//what can we do NOW??
1852
//logAUDCLNT_E(hResult);
1855
WaspiHostProcessingLoop(indata, usingBS ,outdata, usingBS, stream);
1857
hResult = stream->rclient->ReleaseBuffer(usingBS, 0);
1858
if (hResult != S_OK)
1859
logAUDCLNT_E(hResult);
1861
/* This was suggested, but in my tests it doesnt seem to improve the
1862
locking behaviour some drivers have running in exclusive mode.
1863
if(!ResetEvent(stream->hNotificationEvent)){
1864
logAUDCLNT_E(hResult);
1873
stream->out.client->Stop();
1874
stream->closeRequest = false;
1890
hResult = stream->outVol->GetMasterVolumeLevelScalar(&masteur);
1891
if (hResult != S_OK)
1892
logAUDCLNT_E(hResult);
1894
hResult = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);
1895
if (hResult != S_OK)
1896
logAUDCLNT_E(hResult);
1897
hResult = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);
1898
if (hResult != S_OK)
1899
logAUDCLNT_E(hResult);
1902
hResult = stream->outVol->GetMute(&bMute);
1903
if (hResult != S_OK)
1904
logAUDCLNT_E(hResult);
1906
stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);
1907
stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);
1908
stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL);
1909
stream->outVol->SetMute(FALSE, NULL);
b'\\ No newline at end of file'