Pa_RefreshDevice functionality

This commit is contained in:
Andrew Gundersen 2021-08-20 11:09:19 -05:00
commit 1f01a3ddc0
8 changed files with 337 additions and 2 deletions

View file

@ -258,7 +258,6 @@ static int recordCallback( const void *inputBuffer, void *outputBuffer,
data->frameIndex += PaUtil_WriteRingBuffer(&data->ringBuffer, rptr, elementsToWrite);
return paContinue;
}
/* This routine will be called by the PortAudio engine when audio is needed.
@ -272,8 +271,11 @@ static int playCallback( const void *inputBuffer, void *outputBuffer,
void *userData )
{
paTestData *data = (paTestData*)userData;
ring_buffer_size_t elementsToPlay = PaUtil_GetRingBufferReadAvailable(&data->ringBuffer);
ring_buffer_size_t elementsToRead = rbs_min(elementsToPlay, (ring_buffer_size_t)(framesPerBuffer * NUM_CHANNELS));
ring_buffer_size_t elementsToRead = rbs_min( elementsToPlay,
(ring_buffer_size_t)(framesPerBuffer * NUM_CHANNELS) );
SAMPLE* wptr = (SAMPLE*)outputBuffer;
(void) inputBuffer; /* Prevent unused variable warnings. */

View file

@ -0,0 +1,46 @@
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include "portaudio.h"
int main()
{
PaError err = paNoError;
PaDeviceIndex defaultOutputDevice;
PaDeviceIndex defaultInputDevice;
err = Pa_Initialize();
if( err != paNoError )
{
printf( "ERROR: Pa_Initialize returned 0x%x\n", err );
goto error;
}
printf( "PortAudio version: 0x%08X\n", Pa_GetVersion());
// printf( "Version text: '%s'\n", Pa_GetVersionInfo()->versionText );
const int count = 20;
for (int i = 0; i < count; ++i)
{
defaultInputDevice = Pa_GetDefaultInputDevice();
defaultOutputDevice = Pa_GetDefaultOutputDevice();
printf("Input: %i, Output: %i\n", defaultInputDevice, defaultOutputDevice);
err = Pa_RefreshDevices();
if( err != paNoError )
{
printf( "ERROR: Pa_RefreshDevices returned 0x%x\n", err );
goto error;
}
sleep(2);
}
Pa_Terminate();
printf("----------------------------------------------\n");
return 0;
error:
return 1;
}

View file

@ -154,6 +154,7 @@ int main(void)
if( err != paNoError ) goto error;
printf("Play for %d seconds.\n", NUM_SECONDS );
Pa_Sleep( NUM_SECONDS * 1000 );
err = Pa_StopStream( stream );

View file

@ -449,6 +449,17 @@ PaDeviceIndex Pa_GetDefaultInputDevice( void );
PaDeviceIndex Pa_GetDefaultOutputDevice( void );
/** Normally the list of devices is frozen after initialization, until the
next time port audio is re-initialized. This provides API stability but
has the side effect that hot-pluggable devices such as USB audio headsets
might not be up-to-date while the program is running. This method is used
to refresh the list of devices within port audio without closing open
streams.
@return an error code which indicates whether the device refresh was successful.
*/
PaError Pa_RefreshDevices( void );
/** The type used to represent monotonic time in seconds. PaTime is
used for the fields of the PaStreamCallbackTimeInfo argument to the
PaStreamCallback and as the result of Pa_GetStreamTime().

View file

@ -743,6 +743,55 @@ PaDeviceIndex Pa_GetDefaultOutputDevice( void )
}
PaError Pa_RefreshDevices( void )
{
int i, baseDeviceIndex;
PaError result = paNoError;
PA_LOGAPI_ENTER( "Pa_RefreshDeviceList" );
if( !PA_IS_INITIALISED_ )
return paNotInitialized;
baseDeviceIndex = 0;
deviceCount_ = 0;
for( i=0; i < hostApisCount_; ++i )
{
PaUtilHostApiRepresentation *hostApi = hostApis_[i];
if( hostApi->RefreshDevices == NULL )
{
/* RefreshDevices not yet implemented for this backend. Just
assume that the baseDeviceIndex and the deviceCount_ are
incremented according to the values in the info */
baseDeviceIndex += hostApi->info.deviceCount;
deviceCount_ += hostApi->info.deviceCount;
continue;
}
PA_DEBUG(( "refreshing device list for host api %d.\n",i));
if(( result = hostApi->RefreshDevices( hostApi, i )) != paNoError )
return result;
assert( hostApi->info.defaultInputDevice < hostApi->info.deviceCount );
assert( hostApi->info.defaultOutputDevice < hostApi->info.deviceCount );
hostApi->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
if( hostApi->info.defaultInputDevice != paNoDevice )
hostApi->info.defaultInputDevice += baseDeviceIndex;
if( hostApi->info.defaultOutputDevice != paNoDevice )
hostApi->info.defaultOutputDevice += baseDeviceIndex;
baseDeviceIndex += hostApi->info.deviceCount;
deviceCount_ += hostApi->info.deviceCount;
}
return result;
}
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
{
int hostSpecificDeviceIndex;

View file

@ -322,6 +322,8 @@ typedef struct PaUtilHostApiRepresentation {
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate );
PaError (*RefreshDevices)( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index );
} PaUtilHostApiRepresentation;

View file

@ -269,6 +269,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStreamFlags streamFlags,
PaStreamCallback *streamCallback,
void *userData );
static PaError RefreshDevices( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
@ -820,6 +821,7 @@ PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIn
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
(*hostApi)->RefreshDevices = RefreshDevices;
PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface,
CloseStream, StartStream,
@ -2096,6 +2098,85 @@ error:
#define HOST_TIME_TO_PA_TIME( x ) ( AudioConvertHostTimeToNanos( (x) ) * 1.0E-09) /* convert to nanoseconds and then to seconds */
PaError RefreshDevices( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex )
{
PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
PaError result = paNoError;
PaDeviceInfo *deviceInfoArray;
int i;
VVDBUG(("RefreshDevices(): hostApiIndex=%d\n", hostApiIndex));
auhalHostApi->devIds = NULL;
auhalHostApi->devCount = 0;
/* get the info we need about the devices */
result = gatherDeviceInfo( auhalHostApi );
if( result != paNoError )
goto error;
hostApi->info.defaultInputDevice = paNoDevice;
hostApi->info.defaultOutputDevice = paNoDevice;
hostApi->info.deviceCount = 0;
/* If the device infos already exist, free the old memory */
if( hostApi->deviceInfos != NULL )
{
PaUtil_GroupFreeMemory( auhalHostApi->allocations, hostApi->deviceInfos );
hostApi->deviceInfos = NULL;
}
if( auhalHostApi->devCount > 0 )
{
hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount);
if( !hostApi->deviceInfos )
{
result = paInsufficientMemory;
goto error;
}
/* allocate all device info structs in a contiguous block */
deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount );
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
}
for( i=0; i < auhalHostApi->devCount; ++i )
{
int err;
err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i],
auhalHostApi->devIds[i],
hostApiIndex );
if (err == paNoError)
{ /* copy some info and set the defaults */
hostApi->deviceInfos[hostApi->info.deviceCount] = &deviceInfoArray[i];
if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn)
hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut)
hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
hostApi->info.deviceCount++;
}
else
{ /* there was an error. we need to shift the devices down, so we ignore this one */
int j;
auhalHostApi->devCount--;
for( j=i; j<auhalHostApi->devCount; ++j )
auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1];
i--;
}
}
}
error:
return result;
}
PaTime GetStreamTime( PaStream *s )
{
return HOST_TIME_TO_PA_TIME( AudioGetCurrentHostTime() );

View file

@ -190,6 +190,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate );
static PaError RefreshDevices( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
@ -1325,6 +1326,7 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
(*hostApi)->RefreshDevices = RefreshDevices;
PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
@ -1488,6 +1490,147 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
}
/***********************************************************************************/
static PaError RefreshDevices( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex )
{
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
PaWinDsDeviceInfo *deviceInfoArray;
int i = 0, deviceCount = 0;
char comWasInitialized = winDsHostApi->comWasInitialized;
PaError result = paNoError;
if( !comWasInitialized )
return paInternalError;
/* initialise guid vectors so they can be safely deleted on error */
deviceNamesAndGUIDs.winDsHostApi = NULL;
deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
hostApi->info.deviceCount = 0;
hostApi->info.defaultInputDevice = paNoDevice;
hostApi->info.defaultOutputDevice = paNoDevice;
/* DSound - enumerate devices to count them and to gather their GUIDs */
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
{
result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
goto error;
}
if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
{
result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
goto error;
}
/* Free any old memory which might be in the device info */
if( hostApi->deviceInfos )
{
PaUtil_GroupFreeMemory( winDsHostApi->allocations, hostApi->deviceInfos );
hostApi->deviceInfos = NULL;
}
deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
if( deviceCount > 0 )
{
deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
if( deviceCount > 0 )
{
/* allocate array for pointers to PaDeviceInfo structs */
hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
if( !hostApi->deviceInfos )
{
result = paInsufficientMemory;
goto error;
}
/* allocate all PaDeviceInfo structs in a contiguous block */
deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
}
for( i=0; i < deviceCount; ++i )
{
PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
deviceInfo->name = 0;
hostApi->deviceInfos[i] = deviceInfo;
}
for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
{
result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
if( result != paNoError )
goto error;
}
for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
{
result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
if( result != paNoError )
goto error;
}
}
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
if( result != paNoError )
goto error;
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( result != paNoError )
goto error;
return result;
error:
if( winDsHostApi )
{
if( winDsHostApi->allocations )
{
PaUtil_FreeAllAllocations( winDsHostApi->allocations );
PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
}
}
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
return result;
}
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
PaWinDsDeviceInfo *inputDevice,