1
1
/*
2
2
* Portable Audio I/O Library WASAPI implementation
3
3
* Copyright (c) 2006-2010 David Viens
4
- * Copyright (c) 2010-2022 Dmitry Kostjuchenko
4
+ * Copyright (c) 2010-2023 Dmitry Kostjuchenko
5
5
*
6
6
* Based on the Open Source API proposed by Ross Bencina
7
7
* Copyright (c) 1999-2019 Ross Bencina, Phil Burk
@@ -584,7 +584,6 @@ typedef struct PaWasapiStream
584
584
IStream * captureClientStream ;
585
585
#endif
586
586
IAudioCaptureClient * captureClient ;
587
- IAudioEndpointVolume * inVol ;
588
587
589
588
// output
590
589
PaWasapiSubStream out ;
@@ -593,7 +592,6 @@ typedef struct PaWasapiStream
593
592
IStream * renderClientStream ;
594
593
#endif
595
594
IAudioRenderClient * renderClient ;
596
- IAudioEndpointVolume * outVol ;
597
595
598
596
// Event handles for event-driven processing mode
599
597
HANDLE event [S_COUNT ];
@@ -3304,11 +3302,14 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
3304
3302
}
3305
3303
3306
3304
// ------------------------------------------------------------------------------------------
3307
- static PaUint32 _GetFramesPerHostBuffer ( PaUint32 userFramesPerBuffer , PaTime suggestedLatency , double sampleRate , PaUint32 TimerJitterMs )
3305
+ static UINT32 _CalculateFramesPerHostBuffer ( UINT32 userFramesPerBuffer , PaTime suggestedLatency , double sampleRate )
3308
3306
{
3309
- PaUint32 frames = userFramesPerBuffer + max ( userFramesPerBuffer , (PaUint32 )(suggestedLatency * sampleRate ) );
3310
- frames += (PaUint32 )((sampleRate * 0.001 ) * TimerJitterMs );
3311
- return frames ;
3307
+ // Note: WASAPI tends to enforce double buffering by itself therefore enforcing double buffering
3308
+ // on PA side looks as an excessive measure.
3309
+ const BOOL doubleBuffering = TRUE;
3310
+
3311
+ return userFramesPerBuffer + max ((doubleBuffering ? userFramesPerBuffer : 0 ),
3312
+ (UINT32 )(suggestedLatency * sampleRate ));
3312
3313
}
3313
3314
3314
3315
// ------------------------------------------------------------------------------------------
@@ -3323,15 +3324,15 @@ static void _RecalculateBuffersCount(PaWasapiSubStream *sub, UINT32 userFramesPe
3323
3324
// Determine number of buffers used:
3324
3325
// - Full-duplex mode will lead to period difference, thus only 1
3325
3326
// - Input mode, only 1, as WASAPI allows extraction of only 1 packet
3326
- // - For Shared mode we use double buffering
3327
+ // - For Shared mode we use double buffering (see _CalculateFramesPerHostBuffer)
3327
3328
if ((sub -> shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ) || fullDuplex )
3328
3329
{
3329
3330
BOOL eventMode = ((sub -> streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ) == AUDCLNT_STREAMFLAGS_EVENTCALLBACK );
3330
3331
3331
3332
// Exclusive mode does not allow >1 buffers be used for Event interface, e.g. GetBuffer
3332
3333
// call must acquire max buffer size and it all must be processed.
3333
3334
if (eventMode )
3334
- sub -> userBufferAndHostMatch = 1 ;
3335
+ sub -> userBufferAndHostMatch = TRUE ;
3335
3336
3336
3337
// Full-duplex or Event mode: prefer paUtilBoundedHostBufferSize because exclusive mode will starve
3337
3338
// and produce glitchy audio
@@ -3468,9 +3469,8 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
3468
3469
if ((pSub -> shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE ) &&
3469
3470
(!pSub -> streamFlags || ((pSub -> streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ) == 0 )))
3470
3471
{
3471
- framesPerLatency = _GetFramesPerHostBuffer (userFramesPerBuffer ,
3472
- params -> suggestedLatency , pSub -> wavex .Format .nSamplesPerSec , 0 /*,
3473
- (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/ );
3472
+ framesPerLatency = _CalculateFramesPerHostBuffer (userFramesPerBuffer , params -> suggestedLatency ,
3473
+ pSub -> wavex .Format .nSamplesPerSec );
3474
3474
}
3475
3475
else
3476
3476
{
@@ -3487,9 +3487,8 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
3487
3487
overall = MakeHnsPeriod (framesPerLatency , pSub -> wavex .Format .nSamplesPerSec );
3488
3488
if (overall >= (106667 * 2 )/*21.33ms*/ )
3489
3489
{
3490
- framesPerLatency = _GetFramesPerHostBuffer (userFramesPerBuffer ,
3491
- params -> suggestedLatency , pSub -> wavex .Format .nSamplesPerSec , 0 /*,
3492
- (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/ );
3490
+ framesPerLatency = _CalculateFramesPerHostBuffer (userFramesPerBuffer , params -> suggestedLatency ,
3491
+ pSub -> wavex .Format .nSamplesPerSec );
3493
3492
3494
3493
// Use Polling interface
3495
3494
pSub -> streamFlags &= ~AUDCLNT_STREAMFLAGS_EVENTCALLBACK ;
@@ -3753,123 +3752,64 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
3753
3752
}
3754
3753
3755
3754
// ------------------------------------------------------------------------------------------
3756
- static PaError ActivateAudioClientOutput (PaWasapiStream * stream )
3757
- {
3758
- HRESULT hr ;
3759
- PaError result ;
3760
- UINT32 maxBufferSize ;
3761
- PaTime bufferLatency ;
3762
- const UINT32 framesPerBuffer = stream -> out .params .frames_per_buffer ;
3763
-
3764
- // Create Audio client
3765
- if (FAILED (hr = CreateAudioClient (stream , & stream -> out , TRUE, & result )))
3766
- {
3767
- LogPaError (result );
3768
- goto error ;
3769
- }
3770
- LogWAVEFORMATEXTENSIBLE (& stream -> out .wavex );
3771
-
3772
- // Activate volume
3773
- stream -> outVol = NULL ;
3774
- /*hr = info->device->Activate(
3775
- __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
3776
- (void**)&stream->outVol);
3777
- if (hr != S_OK)
3778
- return paInvalidDevice;*/
3779
-
3780
- // Get max possible buffer size to check if it is not less than that we request
3781
- if (FAILED (hr = IAudioClient_GetBufferSize (stream -> out .clientParent , & maxBufferSize )))
3782
- {
3783
- LogHostError (hr );
3784
- LogPaError (result = paInvalidDevice );
3785
- goto error ;
3786
- }
3787
-
3788
- // Correct buffer to max size if it maxed out result of GetBufferSize
3789
- stream -> out .bufferSize = maxBufferSize ;
3790
-
3791
- // Number of frames that are required at each period
3792
- stream -> out .framesPerHostCallback = maxBufferSize ;
3793
-
3794
- // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
3795
- stream -> out .framesPerBuffer =
3796
- (stream -> out .userBufferAndHostMatch ? stream -> out .framesPerHostCallback : framesPerBuffer );
3797
-
3798
- // Calculate buffer latency
3799
- bufferLatency = (PaTime )maxBufferSize / stream -> out .wavex .Format .nSamplesPerSec ;
3800
-
3801
- // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
3802
- stream -> out .latencySeconds = bufferLatency ;
3803
-
3804
- PRINT (("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n" , (UINT32 )framesPerBuffer , (UINT32 )stream -> out .framesPerHostCallback , (float )(stream -> out .latencySeconds * 1000.0f ), (stream -> out .shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO" ), (stream -> out .params .wow64_workaround ? "YES" : "NO" ), (stream -> out .streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL" )));
3805
-
3806
- return paNoError ;
3807
-
3808
- error :
3809
-
3810
- return result ;
3811
- }
3812
-
3813
- // ------------------------------------------------------------------------------------------
3814
- static PaError ActivateAudioClientInput (PaWasapiStream * stream )
3755
+ static PaError ActivateAudioClient (PaWasapiStream * stream , BOOL output )
3815
3756
{
3816
3757
HRESULT hr ;
3817
3758
PaError result ;
3818
3759
UINT32 maxBufferSize ;
3819
3760
PaTime bufferLatency ;
3820
- const UINT32 framesPerBuffer = stream -> in . params . frames_per_buffer ;
3761
+ PaWasapiSubStream * subStream = ( output ? & stream -> out : & stream -> in ) ;
3821
3762
3822
3763
// Create Audio client
3823
- if (FAILED (hr = CreateAudioClient (stream , & stream -> in , FALSE , & result )))
3764
+ if (FAILED (hr = CreateAudioClient (stream , subStream , output , & result )))
3824
3765
{
3825
3766
LogPaError (result );
3826
3767
goto error ;
3827
3768
}
3828
- LogWAVEFORMATEXTENSIBLE (& stream -> in .wavex );
3829
-
3830
- // Create volume mgr
3831
- stream -> inVol = NULL ;
3832
- /*hr = info->device->Activate(
3833
- __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
3834
- (void**)&stream->inVol);
3835
- if (hr != S_OK)
3836
- return paInvalidDevice;*/
3769
+ LogWAVEFORMATEXTENSIBLE (& subStream -> wavex );
3837
3770
3838
3771
// Get max possible buffer size to check if it is not less than that we request
3839
- if (FAILED (hr = IAudioClient_GetBufferSize (stream -> in . clientParent , & maxBufferSize )))
3772
+ if (FAILED (hr = IAudioClient_GetBufferSize (subStream -> clientParent , & maxBufferSize )))
3840
3773
{
3841
3774
LogHostError (hr );
3842
3775
LogPaError (result = paInvalidDevice );
3843
3776
goto error ;
3844
3777
}
3845
3778
3846
3779
// Correct buffer to max size if it maxed out result of GetBufferSize
3847
- stream -> in . bufferSize = maxBufferSize ;
3780
+ subStream -> bufferSize = maxBufferSize ;
3848
3781
3849
- // Get interface latency (actually unneeded as we calculate latency from the size
3850
- // of maxBufferSize).
3851
- if (FAILED (hr = IAudioClient_GetStreamLatency (stream -> in .clientParent , & stream -> in .deviceLatency )))
3782
+ if (!output )
3852
3783
{
3853
- LogHostError (hr );
3854
- LogPaError (result = paInvalidDevice );
3855
- goto error ;
3784
+ // Get interface latency (actually unneeded as we calculate latency from the size of maxBufferSize)
3785
+ if (FAILED (hr = IAudioClient_GetStreamLatency (stream -> in .clientParent , & stream -> in .deviceLatency )))
3786
+ {
3787
+ LogHostError (hr );
3788
+ LogPaError (result = paInvalidDevice );
3789
+ goto error ;
3790
+ }
3791
+ //stream->in.latencySeconds = nano100ToSeconds(stream->in.deviceLatency);
3856
3792
}
3857
- //stream->in.latencySeconds = nano100ToSeconds(stream->in.deviceLatency);
3858
3793
3859
3794
// Number of frames that are required at each period
3860
- stream -> in . framesPerHostCallback = maxBufferSize ;
3795
+ subStream -> framesPerHostCallback = maxBufferSize ;
3861
3796
3862
3797
// Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
3863
- stream -> in . framesPerBuffer =
3864
- ( stream -> in . userBufferAndHostMatch ? stream -> in . framesPerHostCallback : framesPerBuffer );
3798
+ subStream -> framesPerBuffer = ( subStream -> userBufferAndHostMatch ?
3799
+ subStream -> framesPerHostCallback : subStream -> params . frames_per_buffer );
3865
3800
3866
3801
// Calculate buffer latency
3867
- bufferLatency = (PaTime )maxBufferSize / stream -> in . wavex .Format .nSamplesPerSec ;
3802
+ bufferLatency = (PaTime )maxBufferSize / subStream -> wavex .Format .nSamplesPerSec ;
3868
3803
3869
3804
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
3870
- stream -> in . latencySeconds = bufferLatency ;
3805
+ subStream -> latencySeconds = bufferLatency ;
3871
3806
3872
- PRINT (("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n" , (UINT32 )framesPerBuffer , (UINT32 )stream -> in .framesPerHostCallback , (float )(stream -> in .latencySeconds * 1000.0f ), (stream -> in .shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO" ), (stream -> in .params .wow64_workaround ? "YES" : "NO" ), (stream -> in .streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL" )));
3807
+ PRINT (("WASAPI::OpenStream(%s): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n" ,
3808
+ (output ? "output" : "input" ),
3809
+ subStream -> params .frames_per_buffer , subStream -> framesPerHostCallback , (subStream -> latencySeconds * 1000 ),
3810
+ (subStream -> shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO" ),
3811
+ (subStream -> params .wow64_workaround ? "YES" : "NO" ),
3812
+ (subStream -> streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL" )));
3873
3813
3874
3814
return paNoError ;
3875
3815
@@ -4025,7 +3965,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
4025
3965
stream -> in .params .wow64_workaround = paWasapi -> useWOW64Workaround ;
4026
3966
4027
3967
// Create and activate audio client
4028
- if ((result = ActivateAudioClientInput (stream )) != paNoError )
3968
+ if ((result = ActivateAudioClient (stream , FALSE )) != paNoError )
4029
3969
{
4030
3970
LogPaError (result );
4031
3971
goto error ;
@@ -4158,7 +4098,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
4158
4098
stream -> out .params .wow64_workaround = paWasapi -> useWOW64Workaround ;
4159
4099
4160
4100
// Create and activate audio client
4161
- if ((result = ActivateAudioClientOutput (stream )) != paNoError )
4101
+ if ((result = ActivateAudioClient (stream , TRUE )) != paNoError )
4162
4102
{
4163
4103
LogPaError (result );
4164
4104
goto error ;
@@ -4335,8 +4275,6 @@ static PaError CloseStream( PaStream* s )
4335
4275
SAFE_RELEASE (stream -> renderClientParent );
4336
4276
SAFE_RELEASE (stream -> out .clientParent );
4337
4277
SAFE_RELEASE (stream -> in .clientParent );
4338
- SAFE_RELEASE (stream -> inVol );
4339
- SAFE_RELEASE (stream -> outVol );
4340
4278
4341
4279
CloseHandle (stream -> event [S_INPUT ]);
4342
4280
CloseHandle (stream -> event [S_OUTPUT ]);
0 commit comments