From: Ross Bencina Date: Thu, 1 Sep 2016 04:22:02 +0000 (+1000) Subject: DirectSound and CoreAudio draft version of Pa_RefreshDevices() from David Stuart... X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=7fcdfbf5dfbd43ee042bfbc72de7136ed5895c64;p=portaudio DirectSound and CoreAudio draft version of Pa_RefreshDevices() from David Stuart (Sourced from Ross private email June 30, 2010). This is an early version before we implemented 2-phase commit/rollback for device list changes. Pushed so that Evan can look at the CoreAudio code. --- diff --git a/include/portaudio.h b/include/portaudio.h index b55eb0e..1e81e6a 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -397,6 +397,16 @@ 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 that can be used for syncronisation. The type is used for the outTime argument to the diff --git a/src/common/pa_front.c b/src/common/pa_front.c index 22a7fda..c323be7 100644 --- a/src/common/pa_front.c +++ b/src/common/pa_front.c @@ -700,6 +700,54 @@ 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; diff --git a/src/common/pa_hostapi.h b/src/common/pa_hostapi.h index 7746747..eee4def 100644 --- a/src/common/pa_hostapi.h +++ b/src/common/pa_hostapi.h @@ -218,6 +218,8 @@ typedef struct PaUtilHostApiRepresentation { const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate ); + + PaError (*RefreshDevices)( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index ); } PaUtilHostApiRepresentation; diff --git a/src/hostapi/coreaudio/pa_mac_core.c b/src/hostapi/coreaudio/pa_mac_core.c index bd50973..18df1d4 100644 --- a/src/hostapi/coreaudio/pa_mac_core.c +++ b/src/hostapi/coreaudio/pa_mac_core.c @@ -242,6 +242,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 ); @@ -636,6 +637,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, @@ -1669,6 +1671,83 @@ error: return result; } +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; jdevCount; ++j ) + auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1]; + i--; + } + } + } + +error: + return result; +} + PaTime GetStreamTime( PaStream *s ) { /* FIXME: I am not at all sure this timing info stuff is right. diff --git a/src/hostapi/dsound/pa_win_ds.c b/src/hostapi/dsound/pa_win_ds.c index 5625810..31e69ca 100644 --- a/src/hostapi/dsound/pa_win_ds.c +++ b/src/hostapi/dsound/pa_win_ds.c @@ -167,6 +167,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 ); @@ -1188,6 +1189,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, @@ -1392,6 +1394,145 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, return paFormatIsSupported; } +/***********************************************************************************/ +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; +} /************************************************************************* ** Determine minimum number of buffers required for this host based