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
60
#include "pa_allocation.h"
61
#include "pa_hostapi.h"
62
#include "pa_stream.h"
63
#include "pa_cpuload.h"
64
#include "pa_process.h"
65
#include "pa_debugprint.h"
69
davidv : work in progress. try using with 48000 , then 44100
70
and shared mode FIRST.
73
#define PORTAUDIO_SHAREMODE AUDCLNT_SHAREMODE_SHARED
74
//#define PORTAUDIO_SHAREMODE AUDCLNT_SHAREMODE_EXCLUSIVE
78
/* prototypes for functions declared in this file */
83
#endif /* __cplusplus */
85
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
89
#endif /* __cplusplus */
94
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
95
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
96
const PaStreamParameters *inputParameters,
97
const PaStreamParameters *outputParameters,
99
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
101
const PaStreamParameters *inputParameters,
102
const PaStreamParameters *outputParameters,
104
unsigned long framesPerBuffer,
105
PaStreamFlags streamFlags,
106
PaStreamCallback *streamCallback,
108
static PaError CloseStream( PaStream* stream );
109
static PaError StartStream( PaStream *stream );
110
static PaError StopStream( PaStream *stream );
111
static PaError AbortStream( PaStream *stream );
112
static PaError IsStreamStopped( PaStream *s );
113
static PaError IsStreamActive( PaStream *stream );
114
static PaTime GetStreamTime( PaStream *stream );
115
static double GetStreamCpuLoad( PaStream* stream );
116
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
117
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
118
static signed long GetStreamReadAvailable( PaStream* stream );
119
static signed long GetStreamWriteAvailable( PaStream* stream );
122
/* IMPLEMENT ME: a macro like the following one should be used for reporting
124
#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
125
PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
127
/* PaWinWasapiHostApiRepresentation - host api datastructure specific to this implementation */
131
//dummy entry point for other compilers and sdks
132
//currently built using RC1 SDK (5600)
135
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ){
144
#define MAX_STR_LEN 512
147
These are fields that can be gathered from IDevice
148
and IAudioDevice PRIOR to Initialize, and done in first pass
149
i assume that neither of these will cause the Driver to "load",
150
but again, who knows how they implement their stuff
152
typedef struct PaWinWasapiDeviceInfo
154
//hmm is it wise to keep a reference until Terminate?
155
//TODO Check if that interface requires the driver to be loaded!
158
//Fields filled from IDevice
160
WCHAR szDeviceID[MAX_STR_LEN];
164
//Fields filled from IMMEndpoint'sGetDataFlow
167
//Fields filled from IAudioDevice (_prior_ to Initialize)
168
//from GetDevicePeriod(
169
REFERENCE_TIME DefaultDevicePeriod;
170
REFERENCE_TIME MinimumDevicePeriod;
172
WAVEFORMATEX *MixFormat;//needs to be CoTaskMemFree'd after use!
174
} PaWinWasapiDeviceInfo;
179
PaUtilHostApiRepresentation inheritedHostApiRep;
180
PaUtilStreamInterface callbackStreamInterface;
181
PaUtilStreamInterface blockingStreamInterface;
183
PaUtilAllocationGroup *allocations;
185
/* implementation specific data goes here */
187
//in case we later need the synch
188
IMMDeviceEnumerator * enumerator;
190
//this is the REAL number of devices, whether they are usefull to PA or not!
193
WCHAR defaultRenderer [MAX_STR_LEN];
194
WCHAR defaultCapturer [MAX_STR_LEN];
196
PaWinWasapiDeviceInfo *devInfo;
197
}PaWinWasapiHostApiRepresentation;
200
/* PaWinWasapiStream - a stream data structure specifically for this implementation */
202
typedef struct PaWinWasapiSubStream{
203
IAudioClient *client;
204
WAVEFORMATEXTENSIBLE wavex;
206
REFERENCE_TIME latency;
207
REFERENCE_TIME period;
208
unsigned long framesPerHostCallback; /* just an example */
209
}PaWinWasapiSubStream;
211
typedef struct PaWinWasapiStream
212
{ /* IMPLEMENT ME: rename this */
213
PaUtilStreamRepresentation streamRepresentation;
214
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
215
PaUtilBufferProcessor bufferProcessor;
218
- implementation specific data goes here
223
PaWinWasapiSubStream in;
224
IAudioCaptureClient *cclient;
225
IAudioEndpointVolume *inVol;
227
PaWinWasapiSubStream out;
228
IAudioRenderClient *rclient;
229
IAudioEndpointVolume *outVol;
236
HANDLE hNotificationEvent;
242
#define PRINT(x) PA_DEBUG(x);
245
logAUDCLNT_E(HRESULT res){
249
case S_OK: return; break;
250
case E_POINTER :text ="E_POINTER"; break;
251
case E_INVALIDARG :text ="E_INVALIDARG"; break;
253
case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break;
254
case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;
255
case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;
256
case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;
257
case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break;
258
case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;
259
case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break;
260
case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
261
case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break;
262
case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break;
263
case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;
264
case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;
265
case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;
266
case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;
267
case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
268
// case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;
270
case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;
271
case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;
272
case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;
273
case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
274
case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
275
case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
276
case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break;
277
case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
284
PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n",res,text));
288
nano100ToMillis(const REFERENCE_TIME &ref){
289
// 1 nano = 0.000000001 seconds
290
//100 nano = 0.0000001 seconds
291
//100 nano = 0.0001 milliseconds
292
return ((double)ref)*0.0001;
296
nano100ToSeconds(const REFERENCE_TIME &ref){
297
// 1 nano = 0.000000001 seconds
298
//100 nano = 0.0000001 seconds
299
//100 nano = 0.0001 milliseconds
300
return ((double)ref)*0.0000001;
303
#ifndef IF_FAILED_JUMP
304
#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;
309
//AVRT is the new "multimedia schedulling stuff"
311
typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);
312
typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);
313
typedef BOOL (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE);
314
typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics) (LPCTSTR,LPDWORD);
315
typedef BOOL (WINAPI *FAvSetMmThreadPriority) (HANDLE,AVRT_PRIORITY);
317
HMODULE hDInputDLL = 0;
318
FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup=0;
319
FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup=0;
320
FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup=0;
321
FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics=0;
322
FAvSetMmThreadPriority pAvSetMmThreadPriority=0;
326
#define setupPTR(fun, type, name) { \
327
fun = (type) GetProcAddress(hDInputDLL,name); \
329
PRINT(("GetProcAddr failed for %s" ,name)); \
337
hDInputDLL = LoadLibraryA("avrt.dll");
338
if(hDInputDLL == NULL)
341
setupPTR(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup");
342
setupPTR(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup");
343
setupPTR(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup");
344
setupPTR(pAvSetMmThreadCharacteristics, FAvSetMmThreadCharacteristics, "AvSetMmThreadCharacteristicsA");
345
setupPTR(pAvSetMmThreadPriority, FAvSetMmThreadPriority, "AvSetMmThreadPriority");
352
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
355
PRINT(("Windows WASAPI : No AVRT! (not VISTA?)"));
361
PaError result = paNoError;
362
PaWinWasapiHostApiRepresentation *paWasapi;
363
PaDeviceInfo *deviceInfoArray;
365
paWasapi = (PaWinWasapiHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWasapiHostApiRepresentation) );
367
result = paInsufficientMemory;
371
paWasapi->allocations = PaUtil_CreateAllocationGroup();
372
if( !paWasapi->allocations ){
373
result = paInsufficientMemory;
377
*hostApi = &paWasapi->inheritedHostApiRep;
378
(*hostApi)->info.structVersion = 1;
379
(*hostApi)->info.type = paWASAPI;
380
(*hostApi)->info.name = "Windows WASAPI";
381
(*hostApi)->info.deviceCount = 0; //so far, we must investigate each
382
(*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */
383
(*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */
386
HRESULT hResult = S_OK;
387
IMMDeviceCollection* spEndpoints=0;
388
paWasapi->enumerator = 0;
390
hResult = CoCreateInstance(
391
__uuidof(MMDeviceEnumerator), NULL,CLSCTX_INPROC_SERVER,
392
__uuidof(IMMDeviceEnumerator),
393
(void**)&paWasapi->enumerator);
395
IF_FAILED_JUMP(hResult, error);
397
//getting default device ids in the eMultimedia "role"
400
IMMDevice* defaultRenderer=0;
401
hResult = paWasapi->enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &defaultRenderer);
402
IF_FAILED_JUMP(hResult, error);
403
WCHAR* pszDeviceId = NULL;
404
hResult = defaultRenderer->GetId(&pszDeviceId);
405
IF_FAILED_JUMP(hResult, error);
406
StringCchCopyW(paWasapi->defaultRenderer, MAX_STR_LEN-1, pszDeviceId);
407
CoTaskMemFree(pszDeviceId);
408
defaultRenderer->Release();
412
IMMDevice* defaultCapturer=0;
413
hResult = paWasapi->enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, &defaultCapturer);
414
IF_FAILED_JUMP(hResult, error);
415
WCHAR* pszDeviceId = NULL;
416
hResult = defaultCapturer->GetId(&pszDeviceId);
417
IF_FAILED_JUMP(hResult, error);
418
StringCchCopyW(paWasapi->defaultCapturer, MAX_STR_LEN-1, pszDeviceId);
419
CoTaskMemFree(pszDeviceId);
420
defaultCapturer->Release();
425
hResult = paWasapi->enumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &spEndpoints);
426
IF_FAILED_JUMP(hResult, error);
428
hResult = spEndpoints->GetCount(&paWasapi->deviceCount);
429
IF_FAILED_JUMP(hResult, error);
431
paWasapi->devInfo = new PaWinWasapiDeviceInfo[paWasapi->deviceCount];
433
for (size_t step=0;step<paWasapi->deviceCount;++step)
434
memset(&paWasapi->devInfo[step],0,sizeof(PaWinWasapiDeviceInfo));
439
if( paWasapi->deviceCount > 0 )
441
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
442
paWasapi->allocations, sizeof(PaDeviceInfo*) * paWasapi->deviceCount );
443
if( !(*hostApi)->deviceInfos ){
444
result = paInsufficientMemory;
448
/* allocate all device info structs in a contiguous block */
449
deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
450
paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount );
451
if( !deviceInfoArray ){
452
result = paInsufficientMemory;
456
for( UINT i=0; i < paWasapi->deviceCount; ++i ){
458
PA_DEBUG(("i:%d\n",i));
459
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
460
deviceInfo->structVersion = 2;
461
deviceInfo->hostApi = hostApiIndex;
463
hResult = spEndpoints->Item(i, &paWasapi->devInfo[i].device);
464
IF_FAILED_JUMP(hResult, error);
468
WCHAR* pszDeviceId = NULL;
469
hResult = paWasapi->devInfo[i].device->GetId(&pszDeviceId);
470
IF_FAILED_JUMP(hResult, error);
471
StringCchCopyW(paWasapi->devInfo[i].szDeviceID, MAX_STR_LEN-1, pszDeviceId);
472
CoTaskMemFree(pszDeviceId);
474
if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer)==0){
475
//we found the default input!
476
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
478
if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer)==0){
479
//we found the default output!
480
(*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
485
hResult = paWasapi->devInfo[i].device->GetState(&paWasapi->devInfo[i].state);
486
IF_FAILED_JUMP(hResult, error);
488
if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE){
489
PRINT(("WASAPI device:%d is not currently available (state:%d)\n",i,state));
490
//spDevice->Release();
495
IPropertyStore* spProperties;
496
hResult = paWasapi->devInfo[i].device->OpenPropertyStore(STGM_READ, &spProperties);
497
IF_FAILED_JUMP(hResult, error);
499
//getting "Friendly" Name
502
PropVariantInit(&value);
503
hResult = spProperties->GetValue(PKEY_Device_FriendlyName, &value);
504
IF_FAILED_JUMP(hResult, error);
505
deviceInfo->name = 0;
506
char* deviceName = (char*)PaUtil_GroupAllocateMemory( paWasapi->allocations, MAX_STR_LEN + 1 );
508
result = paInsufficientMemory;
512
wcstombs(deviceName, value.pwszVal,MAX_STR_LEN-1); //todo proper size
514
sprintf(deviceName,"baddev%d",i);
517
deviceInfo->name = deviceName;
518
PropVariantClear(&value);
523
hResult = spProperties->GetCount(&numProps);
524
IF_FAILED_JUMP(hResult, error);
526
for (DWORD i=0;i<numProps;++i){
528
hResult = spProperties->GetAt(i,&pkey);
531
PropVariantInit(&value);
532
hResult = spProperties->GetValue(pkey, &value);
536
PRINT(("property*%u*\n",value.ulVal));
539
PRINT(("property*%d*\n",value.boolVal));
544
wcstombs(temp, value.pwszVal,MAX_STR_LEN-1);
545
PRINT(("property*%s*\n",temp));
551
PropVariantClear(&value);
556
/* These look interresting... but they are undocumented
557
PKEY_AudioEndpoint_FormFactor
558
PKEY_AudioEndpoint_ControlPanelPageProvider
559
PKEY_AudioEndpoint_Association
560
PKEY_AudioEndpoint_PhysicalSpeakerConfig
561
PKEY_AudioEngine_DeviceFormat
563
spProperties->Release();
567
//getting the Endpoint data
569
IMMEndpoint *endpoint=0;
570
hResult = paWasapi->devInfo[i].device->QueryInterface(__uuidof(IMMEndpoint),(void **)&endpoint);
571
if (SUCCEEDED(hResult)){
572
hResult = endpoint->GetDataFlow(&paWasapi->devInfo[i].flow);
577
//Getting a temporary IAudioDevice for more fields
578
//we make sure NOT to call Initialize yet!
580
IAudioClient *myClient=0;
582
hResult = paWasapi->devInfo[i].device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
583
IF_FAILED_JUMP(hResult, error);
585
hResult = myClient->GetDevicePeriod(
586
&paWasapi->devInfo[i].DefaultDevicePeriod,
587
&paWasapi->devInfo[i].MinimumDevicePeriod);
588
IF_FAILED_JUMP(hResult, error);
590
hResult = myClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);
592
if (hResult != S_OK){
593
/*davidv: this happened with my hardware, previously for that same device in DirectSound:
594
Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
595
so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat*/
596
logAUDCLNT_E(hResult);
603
//we can now fill in portaudio device data
604
deviceInfo->maxInputChannels = 0; //for now
605
deviceInfo->maxOutputChannels = 0; //for now
607
switch(paWasapi->devInfo[i].flow){
609
//hum not exaclty maximum, more like "default"
610
deviceInfo->maxOutputChannels = paWasapi->devInfo[i].MixFormat->nChannels;
612
deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
613
deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
616
//hum not exaclty maximum, more like "default"
617
deviceInfo->maxInputChannels = paWasapi->devInfo[i].MixFormat->nChannels;
619
deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
620
deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
623
PRINT(("WASAPI device:%d bad Data FLow! \n",i));
628
deviceInfo->defaultSampleRate = (double)paWasapi->devInfo[i].MixFormat->nSamplesPerSec;
630
(*hostApi)->deviceInfos[i] = deviceInfo;
631
++(*hostApi)->info.deviceCount;
635
spEndpoints->Release();
637
(*hostApi)->Terminate = Terminate;
638
(*hostApi)->OpenStream = OpenStream;
639
(*hostApi)->IsFormatSupported = IsFormatSupported;
641
PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream,
642
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
643
GetStreamTime, GetStreamCpuLoad,
644
PaUtil_DummyRead, PaUtil_DummyWrite,
645
PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
647
PaUtil_InitializeStreamInterface( &paWasapi->blockingStreamInterface, CloseStream, StartStream,
648
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
649
GetStreamTime, PaUtil_DummyGetCpuLoad,
650
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
657
spEndpoints->Release();
659
if (paWasapi->enumerator)
660
paWasapi->enumerator->Release();
664
if( paWasapi->allocations )
666
PaUtil_FreeAllAllocations( paWasapi->allocations );
667
PaUtil_DestroyAllocationGroup( paWasapi->allocations );
670
PaUtil_FreeMemory( paWasapi );
676
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
678
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
680
paWasapi->enumerator->Release();
682
for (UINT i=0;i<paWasapi->deviceCount;++i){
683
PaWinWasapiDeviceInfo *info = &paWasapi->devInfo[i];
686
info->device->Release();
689
CoTaskMemFree(info->MixFormat);
691
delete [] paWasapi->devInfo;
695
if( paWasapi->allocations ){
696
PaUtil_FreeAllAllocations( paWasapi->allocations );
697
PaUtil_DestroyAllocationGroup( paWasapi->allocations );
700
PaUtil_FreeMemory( paWasapi );
704
LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in){
706
const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
708
switch (old->wFormatTag){
709
case WAVE_FORMAT_EXTENSIBLE:{
711
PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));
713
if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
714
PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));
716
else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
717
PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));
720
PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",
724
(int)in->SubFormat.Data4[0],
725
(int)in->SubFormat.Data4[1],
726
(int)in->SubFormat.Data4[2],
727
(int)in->SubFormat.Data4[3],
728
(int)in->SubFormat.Data4[4],
729
(int)in->SubFormat.Data4[5],
730
(int)in->SubFormat.Data4[6],
731
(int)in->SubFormat.Data4[7]));
733
PRINT(("Samples.wValidBitsPerSample=%d\n", in->Samples.wValidBitsPerSample));
734
PRINT(("dwChannelMask=0x%X\n",in->dwChannelMask));
737
case WAVE_FORMAT_PCM: PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;
738
case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break;
739
default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break;
742
PRINT(("nChannels =%d\n",old->nChannels));
743
PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));
744
PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));
745
PRINT(("nBlockAlign =%d\n",old->nBlockAlign));
746
PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));
747
PRINT(("cbSize =%d\n",old->cbSize));
753
WAVEFORMATXXX is always interleaved
755
static PaSampleFormat
756
waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in){
758
const WAVEFORMATEX *old = (WAVEFORMATEX*)in;
760
switch (old->wFormatTag){
762
case WAVE_FORMAT_EXTENSIBLE:
764
if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
765
if (in->Samples.wValidBitsPerSample == 32)
768
return paCustomFormat;
770
else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
771
switch (old->wBitsPerSample){
772
case 32: return paInt32; break;
773
case 24: return paInt24;break;
774
case 8: return paUInt8;break;
775
case 16: return paInt16;break;
776
default: return paCustomFormat;break;
780
return paCustomFormat;
784
case WAVE_FORMAT_IEEE_FLOAT:
788
case WAVE_FORMAT_PCM:
790
switch (old->wBitsPerSample){
791
case 32: return paInt32; break;
792
case 24: return paInt24;break;
793
case 8: return paUInt8;break;
794
case 16: return paInt16;break;
795
default: return paCustomFormat;break;
801
return paCustomFormat;
805
return paCustomFormat;
811
waveformatFromParams(WAVEFORMATEXTENSIBLE*wavex,
812
const PaStreamParameters * params,
815
size_t bytesPerSample = 0;
816
switch( params->sampleFormat & ~paNonInterleaved ){
818
case paInt32: bytesPerSample=4;break;
819
case paInt16: bytesPerSample=2;break;
820
case paInt24: bytesPerSample=3;break;
822
case paUInt8: bytesPerSample=1;break;
824
default: return paSampleFormatNotSupported;break;
827
memset(wavex,0,sizeof(WAVEFORMATEXTENSIBLE));
829
WAVEFORMATEX *old = (WAVEFORMATEX *)wavex;
830
old->nChannels = (WORD)params->channelCount;
831
old->nSamplesPerSec = (DWORD)sampleRate;
832
old->wBitsPerSample = (WORD)(bytesPerSample*8);
833
old->nAvgBytesPerSec = (DWORD)(old->nSamplesPerSec * old->nChannels * bytesPerSample);
834
old->nBlockAlign = (WORD)(old->nChannels * bytesPerSample);
837
if (params->channelCount <=2 && (bytesPerSample == 2 || bytesPerSample == 1)){
839
old->wFormatTag = WAVE_FORMAT_PCM;
841
//WAVEFORMATEXTENSIBLE
843
old->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
845
old->cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);
847
if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
848
wavex->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
850
wavex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
852
wavex->Samples.wValidBitsPerSample = old->wBitsPerSample; //no extra padding!
854
switch(params->channelCount){
855
case 1: wavex->dwChannelMask = SPEAKER_FRONT_CENTER; break;
856
case 2: wavex->dwChannelMask = 0x1 | 0x2; break;
857
case 4: wavex->dwChannelMask = 0x1 | 0x2 | 0x10 | 0x20; break;
858
case 6: wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20; break;
859
case 8: wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80; break;
860
default: wavex->dwChannelMask = 0; break;
873
#define paFloat32 ((PaSampleFormat) 0x00000001)
874
#define paInt32 ((PaSampleFormat) 0x00000002)
875
#define paInt24 ((PaSampleFormat) 0x00000004)
876
#define paInt16 ((PaSampleFormat) 0x00000008)
878
//lifted from pa_wdmks
879
static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
881
PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
882
PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
883
PA_DEBUG(( "chanelCount = %d\n", channelCount ));
885
pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
886
pwfext->Format.nChannels = channelCount;
887
pwfext->Format.nSamplesPerSec = (int)sampleRate;
888
if(channelCount == 1)
889
pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
891
pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
892
if(sampleFormat == paFloat32)
894
pwfext->Format.nBlockAlign = channelCount * 4;
895
pwfext->Format.wBitsPerSample = 32;
896
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
897
pwfext->Samples.wValidBitsPerSample = 32;
898
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
900
else if(sampleFormat == paInt32)
902
pwfext->Format.nBlockAlign = channelCount * 4;
903
pwfext->Format.wBitsPerSample = 32;
904
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
905
pwfext->Samples.wValidBitsPerSample = 32;
906
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
908
else if(sampleFormat == paInt24)
910
pwfext->Format.nBlockAlign = channelCount * 3;
911
pwfext->Format.wBitsPerSample = 24;
912
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
913
pwfext->Samples.wValidBitsPerSample = 24;
914
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
916
else if(sampleFormat == paInt16)
918
pwfext->Format.nBlockAlign = channelCount * 2;
919
pwfext->Format.wBitsPerSample = 16;
920
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
921
pwfext->Samples.wValidBitsPerSample = 16;
922
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
924
pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
930
#define FORMATTESTS 4
931
const int BestToWorst[FORMATTESTS]={paFloat32,paInt32,paInt24,paInt16};
934
#define FORMATTESTS 3
935
const int BestToWorst[FORMATTESTS]={paFloat32,paInt24,paInt16};
939
GetClosestFormat(IAudioClient * myClient, double sampleRate,const PaStreamParameters * params,
940
AUDCLNT_SHAREMODE *shareMode, WAVEFORMATEXTENSIBLE *outWavex)
942
//TODO we should try exclusive first and shared after
943
*shareMode = PORTAUDIO_SHAREMODE;
945
PaError answer = paInvalidSampleRate;
947
waveformatFromParams(outWavex,params,sampleRate);
948
WAVEFORMATEX *sharedClosestMatch=0;
949
HRESULT hResult=!S_OK;
951
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
952
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)outWavex,NULL);
954
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&outWavex,&sharedClosestMatch);
957
answer = paFormatIsSupported;
958
else if (sharedClosestMatch){
959
WAVEFORMATEXTENSIBLE* ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;
961
int closestMatchSR = (int)sharedClosestMatch->nSamplesPerSec;
963
if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
964
memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEXTENSIBLE));
966
memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEX));
968
CoTaskMemFree(sharedClosestMatch);
970
if ((int)sampleRate == closestMatchSR)
971
answer = paFormatIsSupported;
973
answer = paInvalidSampleRate;
977
//it doesnt suggest anything?? ok lets show it the MENU!
979
//ok fun time as with pa_win_mme, we know only a refusal of the user-requested
980
//sampleRate+num Channel is disastrous, as the portaudio buffer processor converts between anything
981
//so lets only use the number
982
for (int i=0;i<FORMATTESTS;++i){
983
WAVEFORMATEXTENSIBLE ext;
984
wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount);
985
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
986
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)&ext,NULL);
988
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&ext,&sharedClosestMatch);
990
if (hResult == S_OK){
991
memcpy(outWavex,&ext,sizeof(WAVEFORMATEXTENSIBLE));
992
answer = paFormatIsSupported;
997
if (answer!=paFormatIsSupported) {
999
//why did it HAVE to come to this ....
1000
WAVEFORMATEX pcm16WaveFormat;
1001
memset(&pcm16WaveFormat,0,sizeof(WAVEFORMATEX));
1002
pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
1003
pcm16WaveFormat.nChannels = 2;
1004
pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate;
1005
pcm16WaveFormat.nBlockAlign = 4;
1006
pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign;
1007
pcm16WaveFormat.wBitsPerSample = 16;
1008
pcm16WaveFormat.cbSize = 0;
1010
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1011
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)&pcm16WaveFormat,NULL);
1013
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&pcm16WaveFormat,&sharedClosestMatch);
1015
if (hResult == S_OK){
1016
memcpy(outWavex,&pcm16WaveFormat,sizeof(WAVEFORMATEX));
1017
answer = paFormatIsSupported;
1021
logAUDCLNT_E(hResult);
1028
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1029
const PaStreamParameters *inputParameters,
1030
const PaStreamParameters *outputParameters,
1034
int inputChannelCount, outputChannelCount;
1035
PaSampleFormat inputSampleFormat, outputSampleFormat;
1037
if( inputParameters )
1039
inputChannelCount = inputParameters->channelCount;
1040
inputSampleFormat = inputParameters->sampleFormat;
1042
/* all standard sample formats are supported by the buffer adapter,
1043
this implementation doesn't support any custom sample formats */
1044
if( inputSampleFormat & paCustomFormat )
1045
return paSampleFormatNotSupported;
1047
/* unless alternate device specification is supported, reject the use of
1048
paUseHostApiSpecificDeviceSpecification */
1050
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1051
return paInvalidDevice;
1053
/* check that input device can support inputChannelCount */
1054
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
1055
return paInvalidChannelCount;
1057
/* validate inputStreamInfo */
1058
if( inputParameters->hostApiSpecificStreamInfo )
1059
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1062
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
1065
IAudioClient *myClient=0;
1066
HRESULT hResult = paWasapi->devInfo[inputParameters->device].device->Activate(
1067
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
1068
if (hResult != S_OK){
1069
logAUDCLNT_E(hResult);
1070
return paInvalidDevice;
1073
WAVEFORMATEXTENSIBLE wavex;
1074
AUDCLNT_SHAREMODE shareMode;
1075
PaError answer = GetClosestFormat(myClient,sampleRate,inputParameters,&shareMode,&wavex);
1076
myClient->Release();
1078
if (answer !=paFormatIsSupported)
1083
inputChannelCount = 0;
1086
if( outputParameters )
1088
outputChannelCount = outputParameters->channelCount;
1089
outputSampleFormat = outputParameters->sampleFormat;
1091
/* all standard sample formats are supported by the buffer adapter,
1092
this implementation doesn't support any custom sample formats */
1093
if( outputSampleFormat & paCustomFormat )
1094
return paSampleFormatNotSupported;
1096
/* unless alternate device specification is supported, reject the use of
1097
paUseHostApiSpecificDeviceSpecification */
1099
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1100
return paInvalidDevice;
1102
/* check that output device can support outputChannelCount */
1103
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
1104
return paInvalidChannelCount;
1106
/* validate outputStreamInfo */
1107
if( outputParameters->hostApiSpecificStreamInfo )
1108
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1111
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
1113
IAudioClient *myClient=0;
1114
HRESULT hResult = paWasapi->devInfo[outputParameters->device].device->Activate(
1115
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
1116
if (hResult != S_OK){
1117
logAUDCLNT_E(hResult);
1118
return paInvalidDevice;
1121
WAVEFORMATEXTENSIBLE wavex;
1122
AUDCLNT_SHAREMODE shareMode;
1123
PaError answer = GetClosestFormat(myClient,sampleRate,outputParameters,&shareMode,&wavex);
1124
myClient->Release();
1126
if (answer !=paFormatIsSupported)
1131
outputChannelCount = 0;
1135
return paFormatIsSupported;
1140
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
1142
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1144
const PaStreamParameters *inputParameters,
1145
const PaStreamParameters *outputParameters,
1147
unsigned long framesPerBuffer,
1148
PaStreamFlags streamFlags,
1149
PaStreamCallback *streamCallback,
1152
PaError result = paNoError;
1153
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
1154
PaWinWasapiStream *stream = 0;
1155
int inputChannelCount, outputChannelCount;
1156
PaSampleFormat inputSampleFormat, outputSampleFormat;
1157
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
1160
stream = (PaWinWasapiStream*)PaUtil_AllocateMemory( sizeof(PaWinWasapiStream) );
1162
result = paInsufficientMemory;
1166
if( inputParameters )
1168
inputChannelCount = inputParameters->channelCount;
1169
inputSampleFormat = inputParameters->sampleFormat;
1171
/* unless alternate device specification is supported, reject the use of
1172
paUseHostApiSpecificDeviceSpecification */
1174
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1175
return paInvalidDevice;
1177
/* check that input device can support inputChannelCount */
1178
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
1179
return paInvalidChannelCount;
1181
/* validate inputStreamInfo */
1182
if( inputParameters->hostApiSpecificStreamInfo )
1183
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1186
PaWinWasapiDeviceInfo &info = paWasapi->devInfo[inputParameters->device];
1188
HRESULT hResult = info.device->Activate(
1189
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
1190
(void**)&stream->in.client);
1192
if (hResult != S_OK)
1193
return paInvalidDevice;
1195
hResult = info.device->Activate(
1196
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
1197
(void**)&stream->inVol);
1199
if (hResult != S_OK)
1200
return paInvalidDevice;
1202
AUDCLNT_SHAREMODE shareMode;
1203
PaError answer = GetClosestFormat(stream->in.client,sampleRate,inputParameters,&shareMode,&stream->in.wavex);
1205
if (answer !=paFormatIsSupported)
1208
//stream->out.period = info.DefaultDevicePeriod;
1209
stream->in.period = info.MinimumDevicePeriod;
1211
hResult = stream->in.client->Initialize(
1215
0,//stream->out.period,
1216
(WAVEFORMATEX*)&stream->in.wavex,
1220
if (hResult != S_OK){
1221
logAUDCLNT_E(hResult);
1222
return paInvalidDevice;
1225
hResult = stream->in.client->GetBufferSize(&stream->in.bufferSize);
1226
if (hResult != S_OK)
1227
return paInvalidDevice;
1229
hResult = stream->in.client->GetStreamLatency(&stream->in.latency);
1230
if (hResult != S_OK)
1231
return paInvalidDevice;
1233
double periodsPerSecond = 1.0/nano100ToSeconds(stream->in.period);
1234
double samplesPerPeriod = (double)(stream->in.wavex.Format.nSamplesPerSec)/periodsPerSecond;
1236
//this is the number of samples that are required at each period
1237
stream->in.framesPerHostCallback = (unsigned long)samplesPerPeriod;//unrelated to channels
1239
/* IMPLEMENT ME - establish which host formats are available */
1240
hostInputSampleFormat =
1241
PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );
1245
inputChannelCount = 0;
1246
inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
1249
if( outputParameters )
1251
outputChannelCount = outputParameters->channelCount;
1252
outputSampleFormat = outputParameters->sampleFormat;
1254
/* unless alternate device specification is supported, reject the use of
1255
paUseHostApiSpecificDeviceSpecification */
1257
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1258
return paInvalidDevice;
1260
/* check that output device can support inputChannelCount */
1261
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
1262
return paInvalidChannelCount;
1264
/* validate outputStreamInfo */
1265
if( outputParameters->hostApiSpecificStreamInfo )
1266
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1269
PaWinWasapiDeviceInfo &info = paWasapi->devInfo[outputParameters->device];
1271
HRESULT hResult = info.device->Activate(
1272
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
1273
(void**)&stream->out.client);
1275
if (hResult != S_OK)
1276
return paInvalidDevice;
1278
AUDCLNT_SHAREMODE shareMode;
1279
PaError answer = GetClosestFormat(stream->out.client,sampleRate,outputParameters,&shareMode,&stream->out.wavex);
1281
if (answer !=paFormatIsSupported)
1283
LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
1285
// stream->out.period = info.DefaultDevicePeriod;
1286
stream->out.period = info.MinimumDevicePeriod;
1288
/*For an exclusive-mode stream that uses event-driven buffering,
1289
the caller must specify nonzero values for hnsPeriodicity and hnsBufferDuration,
1290
and the values of these two parameters must be equal */
1291
if (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE){
1292
hResult = stream->out.client->Initialize(
1294
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1297
(WAVEFORMATEX*)&stream->out.wavex,
1302
hResult = stream->out.client->Initialize(
1304
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1307
(WAVEFORMATEX*)&stream->out.wavex,
1313
if (hResult != S_OK){
1314
logAUDCLNT_E(hResult);
1315
return paInvalidDevice;
1318
hResult = info.device->Activate(
1319
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
1320
(void**)&stream->outVol);
1322
if (hResult != S_OK)
1323
return paInvalidDevice;
1325
hResult = stream->out.client->GetBufferSize(&stream->out.bufferSize);
1326
if (hResult != S_OK)
1327
return paInvalidDevice;
1329
hResult = stream->out.client->GetStreamLatency(&stream->out.latency);
1330
if (hResult != S_OK)
1331
return paInvalidDevice;
1333
double periodsPerSecond = 1.0/nano100ToSeconds(stream->out.period);
1334
double samplesPerPeriod = (double)(stream->out.wavex.Format.nSamplesPerSec)/periodsPerSecond;
1336
//this is the number of samples that are required at each period
1337
stream->out.framesPerHostCallback = stream->out.bufferSize; //(unsigned long)samplesPerPeriod;//unrelated to channels
1339
/* IMPLEMENT ME - establish which host formats are available */
1340
hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat );
1344
outputChannelCount = 0;
1345
outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
1353
( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )
1355
- check that input device can support inputSampleFormat, or that
1356
we have the capability to convert from outputSampleFormat to
1359
- check that output device can support outputSampleFormat, or that
1360
we have the capability to convert from outputSampleFormat to
1363
- if a full duplex stream is requested, check that the combination
1364
of input and output parameters is supported
1366
- check that the device supports sampleRate
1368
- alter sampleRate to a close allowable rate if possible / necessary
1370
- validate suggestedInputLatency and suggestedOutputLatency parameters,
1371
use default values where necessary
1376
/* validate platform specific flags */
1377
if( (streamFlags & paPlatformSpecificFlags) != 0 )
1378
return paInvalidFlag; /* unexpected platform specific flag */
1382
if( streamCallback )
1384
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
1385
&paWasapi->callbackStreamInterface, streamCallback, userData );
1389
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
1390
&paWasapi->blockingStreamInterface, streamCallback, userData );
1393
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
1396
if (outputParameters && inputParameters){
1398
//serious problem #1
1399
if (stream->in.period != stream->out.period){
1400
PRINT(("OpenStream: period discrepancy\n"));
1404
//serious problem #2
1405
if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback){
1406
PRINT(("OpenStream: framesPerHostCallback discrepancy\n"));
1411
unsigned long framesPerHostCallback = (outputParameters)?
1412
stream->out.framesPerHostCallback:
1413
stream->in.framesPerHostCallback;
1415
/* we assume a fixed host buffer size in this example, but the buffer processor
1416
can also support bounded and unknown host buffer sizes by passing
1417
paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
1418
paUtilFixedHostBufferSize below. */
1420
result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
1421
inputChannelCount, inputSampleFormat, hostInputSampleFormat,
1422
outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
1423
sampleRate, streamFlags, framesPerBuffer,
1424
framesPerHostCallback, paUtilFixedHostBufferSize,
1425
streamCallback, userData );
1426
if( result != paNoError )
1431
IMPLEMENT ME: initialise the following fields with estimated or actual
1434
stream->streamRepresentation.streamInfo.inputLatency =
1435
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
1436
+ ((inputParameters)?nano100ToSeconds(stream->in.latency) :0);
1438
stream->streamRepresentation.streamInfo.outputLatency =
1439
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
1440
+ ((outputParameters)?nano100ToSeconds(stream->out.latency) :0);
1442
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
1445
*s = (PaStream*)stream;
1452
PaUtil_FreeMemory( stream );
1460
When CloseStream() is called, the multi-api layer ensures that
1461
the stream has already been stopped or aborted.
1464
#define SAFE_RELEASE(punk) \
1465
if ((punk) != NULL) \
1466
{ (punk)->Release(); (punk) = NULL; }
1468
static PaError CloseStream( PaStream* s )
1470
PaError result = paNoError;
1471
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1475
- additional stream closing + cleanup
1478
SAFE_RELEASE(stream->out.client);
1479
SAFE_RELEASE(stream->in.client);
1480
SAFE_RELEASE(stream->cclient);
1481
SAFE_RELEASE(stream->rclient);
1482
SAFE_RELEASE(stream->inVol);
1483
SAFE_RELEASE(stream->outVol);
1484
CloseHandle(stream->hThread);
1485
CloseHandle(stream->hNotificationEvent);
1487
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
1488
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
1489
PaUtil_FreeMemory( stream );
1494
DWORD WINAPI ProcThread(void *client);
1496
static PaError StartStream( PaStream *s )
1498
PaError result = paNoError;
1499
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1501
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
1503
HRESULT hResult=S_OK;
1505
if (stream->out.client){
1506
hResult = stream->out.client->GetService(__uuidof(IAudioRenderClient),(void**)&stream->rclient);
1507
logAUDCLNT_E(hResult);
1509
return paUnanticipatedHostError;
1512
if (stream->in.client){
1513
hResult = stream->in.client->GetService(__uuidof(IAudioCaptureClient),(void**)&stream->cclient);
1514
logAUDCLNT_E(hResult);
1516
return paUnanticipatedHostError;
1519
// Create a thread for this client.
1520
stream->hThread = CreateThread(
1521
NULL, // no security attribute
1522
0, // default stack size
1524
(LPVOID) stream, // thread parameter
1526
&stream->dwThreadId); // returns thread ID
1528
if (stream->hThread == NULL)
1529
return paUnanticipatedHostError;
1535
static PaError StopStream( PaStream *s )
1537
PaError result = paNoError;
1538
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1540
/* suppress unused variable warnings */
1541
stream->closeRequest = true;
1542
//todo something MUCH better than this
1543
while(stream->closeRequest)
1546
/* IMPLEMENT ME, see portaudio.h for required behavior */
1548
stream->running = false;
1554
static PaError AbortStream( PaStream *s )
1556
PaError result = paNoError;
1557
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1559
/* suppress unused variable warnings */
1560
stream->closeRequest = true;
1561
//todo something MUCH better than this
1562
while(stream->closeRequest)
1565
/* IMPLEMENT ME, see portaudio.h for required behavior */
1571
static PaError IsStreamStopped( PaStream *s )
1573
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1575
return !stream->running;
1579
static PaError IsStreamActive( PaStream *s )
1581
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1582
return stream->running;
1586
static PaTime GetStreamTime( PaStream *s )
1588
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1590
/* suppress unused variable warnings */
1593
/* IMPLEMENT ME, see portaudio.h for required behavior*/
1595
//this is lame ds and mme does the same thing, quite useless method imho
1596
//why dont we fetch the time in the pa callbacks?
1597
//at least its doing to be clocked to something
1598
return PaUtil_GetTime();
1602
static double GetStreamCpuLoad( PaStream* s )
1604
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1606
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
1611
As separate stream interfaces are used for blocking and callback
1612
streams, the following functions can be guaranteed to only be called
1613
for blocking streams.
1616
static PaError ReadStream( PaStream* s,
1618
unsigned long frames )
1620
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1622
/* suppress unused variable warnings */
1627
/* IMPLEMENT ME, see portaudio.h for required behavior*/
1633
static PaError WriteStream( PaStream* s,
1635
unsigned long frames )
1637
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1639
/* suppress unused variable warnings */
1644
/* IMPLEMENT ME, see portaudio.h for required behavior*/
1650
static signed long GetStreamReadAvailable( PaStream* s )
1652
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1654
/* suppress unused variable warnings */
1657
/* IMPLEMENT ME, see portaudio.h for required behavior*/
1663
static signed long GetStreamWriteAvailable( PaStream* s )
1665
PaWinWasapiStream *stream = (PaWinWasapiStream*)s;
1667
/* suppress unused variable warnings */
1670
/* IMPLEMENT ME, see portaudio.h for required behavior*/
1678
ExampleHostProcessingLoop() illustrates the kind of processing which may
1679
occur in a host implementation.
1682
static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
1683
void *outputBuffer, long outputFrames,
1686
PaWinWasapiStream *stream = (PaWinWasapiStream*)userData;
1687
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
1689
unsigned long framesProcessed;
1691
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
1696
- generate timing information
1697
- handle buffer slips
1701
If you need to byte swap or shift inputBuffer to convert it into a
1702
portaudio format, do it here.
1707
PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );
1710
depending on whether the host buffers are interleaved, non-interleaved
1711
or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
1712
PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
1715
if( stream->bufferProcessor.inputChannelCount > 0 )
1717
PaUtil_SetInputFrameCount( &stream->bufferProcessor, inputFrames );
1718
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
1719
0, /* first channel of inputBuffer is channel 0 */
1721
0 ); /* 0 - use inputChannelCount passed to init buffer processor */
1724
if( stream->bufferProcessor.outputChannelCount > 0 )
1726
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, outputFrames);
1727
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
1728
0, /* first channel of outputBuffer is channel 0 */
1730
0 ); /* 0 - use outputChannelCount passed to init buffer processor */
1733
/* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
1734
in general you would pass paContinue for normal operation, and
1735
paComplete to drain the buffer processor's internal output buffer.
1736
You can check whether the buffer processor's output buffer is empty
1737
using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
1739
callbackResult = paContinue;
1740
framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
1744
If you need to byte swap or shift outputBuffer to convert it to
1745
host format, do it here.
1748
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
1751
if( callbackResult == paContinue )
1753
/* nothing special to do */
1755
else if( callbackResult == paAbort )
1757
/* IMPLEMENT ME - finish playback immediately */
1759
/* once finished, call the finished callback */
1760
if( stream->streamRepresentation.streamFinishedCallback != 0 )
1761
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
1765
/* User callback has asked us to stop with paComplete or other non-zero value */
1767
/* IMPLEMENT ME - finish playback once currently queued audio has completed */
1769
/* once finished, call the finished callback */
1770
if( stream->streamRepresentation.streamFinishedCallback != 0 )
1771
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
1780
HANDLE thCarac = pAvSetMmThreadCharacteristics("Pro Audio",&stuff);
1782
PRINT(("AvSetMmThreadCharacteristics failed!\n"));
1785
BOOL prio = pAvSetMmThreadPriority(thCarac,AVRT_PRIORITY_NORMAL);
1787
PRINT(("AvSetMmThreadPriority failed!\n"));
1792
HANDLE hh = GetCurrentThread();
1793
int currprio = GetThreadPriority(hh);
1794
DWORD currclass = GetPriorityClass(GetCurrentProcess());
1795
PRINT(("currprio 0x%X currclass 0x%X\n",currprio,currclass));
1801
ProcThread(void* param){
1805
PaWinWasapiStream *stream = (PaWinWasapiStream*)param;
1807
stream->hNotificationEvent = CreateEvent(NULL,
1808
FALSE, //bManualReset are we sure??
1811
hResult = stream->out.client->SetEventHandle(stream->hNotificationEvent);
1812
if (hResult != S_OK)
1813
logAUDCLNT_E(hResult);
1815
if (stream->out.client){
1816
hResult = stream->out.client->Start();
1817
if (hResult != S_OK)
1818
logAUDCLNT_E(hResult);
1821
stream->running = true;
1824
while( !stream->closeRequest )
1826
//lets wait but have a 1 second timeout
1827
DWORD dwResult = WaitForSingleObject(stream->hNotificationEvent, 1000);
1828
switch( dwResult ) {
1829
case WAIT_OBJECT_0: {
1831
unsigned long usingBS = stream->out.framesPerHostCallback;
1836
hResult = stream->rclient->GetBuffer(usingBS, &outdata);
1838
if (hResult != S_OK || !outdata) {
1839
//logAUDCLNT_E(hResult);
1840
//most probably shared mode and hResult=AUDCLNT_E_BUFFER_TOO_LARGE
1842
hResult = stream->out.client->GetCurrentPadding(&padding);
1845
usingBS = usingBS-padding;
1848
hResult = stream->rclient->GetBuffer(usingBS, &outdata);
1849
if (hResult != S_OK)//what can we do NOW??
1851
//logAUDCLNT_E(hResult);
1854
WaspiHostProcessingLoop(indata, usingBS ,outdata, usingBS, stream);
1856
hResult = stream->rclient->ReleaseBuffer(usingBS, 0);
1857
if (hResult != S_OK)
1858
logAUDCLNT_E(hResult);
1860
/* This was suggested, but in my tests it doesnt seem to improve the
1861
locking behaviour some drivers have running in exclusive mode.
1862
if(!ResetEvent(stream->hNotificationEvent)){
1863
logAUDCLNT_E(hResult);
1872
stream->out.client->Stop();
1873
stream->closeRequest = false;
1889
hResult = stream->outVol->GetMasterVolumeLevelScalar(&masteur);
1890
if (hResult != S_OK)
1891
logAUDCLNT_E(hResult);
1893
hResult = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);
1894
if (hResult != S_OK)
1895
logAUDCLNT_E(hResult);
1896
hResult = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);
1897
if (hResult != S_OK)
1898
logAUDCLNT_E(hResult);
1901
hResult = stream->outVol->GetMute(&bMute);
1902
if (hResult != S_OK)
1903
logAUDCLNT_E(hResult);
1905
stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);
1906
stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);
1907
stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL);
1908
stream->outVol->SetMute(FALSE, NULL);
b'\\ No newline at end of file'