diff --git a/include/portaudio.h b/include/portaudio.h index 3bb3b31..6d90d2f 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -274,6 +274,64 @@ typedef enum PaHostApiTypeId } PaHostApiTypeId; +/** Select host APIs and their initialization order. + + The selected host APIs take effect the next time that Pa_Initialize() is + invoked. + + @param hostApiTypes An array of host API identifiers belonging to the + PaHostApiTypeId enumeration. + + @param count The number of elements in the hostApiTypes array. A count of + zero causes the default host API selection to be restored. + + @return A PaErrorCode indicating whether the call succeeded or failed. + + The paHostApiNotFound error code indicates that a host API specified by the + hostApiTypeIds parameter is not available. + + The paInvalidHostApi error indicates that there was a problem with the + hostApiTypes array. E.g. it contained duplicate entries. + + @note The host API initialization order determines default devices. + + @see PaHostApiTypeId +*/ +PaError Pa_SelectHostApis( const PaHostApiTypeId *hostApiTypes, int count ); + +/** Returns the type ids of the selected host APIs in initialization order. + + @param hostApiTypes (IN/OUT) An array that will be filled with + host API identifiers belonging to the PaHostApiTypeId enumeration. + + @param count (OUT) The number of selected host APIs, returned even on error. + + @param countAvailable The number of available elements in the hostApiTypes array. + + @return A PaErrorCode indicating whether the call succeeded or failed. + + The paInsufficientMemory error indicates that countAvailable was not large enough + to accommodate the list of selected host APIs. In this case, the needed + count is returned in the count parameter. + + @see Pa_SelectHostApis +*/ +PaError Pa_GetSelectedHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ); + +/** Returns the type ids of all available host APIs in initialization order. + + @param hostApiTypes (IN/OUT) An array that will be filled with + host API identifiers belonging to the PaHostApiTypeId enumeration. + + @param count (OUT) The number of host APIs, returned even on error. + + @param countAvailable The number of available elements in the hostApiTypes array. + + @see Pa_SelectHostApis +*/ +PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ); + + /** A structure containing information about a particular host API. */ typedef struct PaHostApiInfo diff --git a/src/common/pa_front.c b/src/common/pa_front.c index 0632710..ba831b9 100644 --- a/src/common/pa_front.c +++ b/src/common/pa_front.c @@ -152,7 +152,6 @@ void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode, } - static PaUtilHostApiRepresentation **hostApis_ = 0; static int hostApisCount_ = 0; static int defaultHostApiIndex_ = 0; @@ -161,19 +160,161 @@ static int deviceCount_ = 0; PaUtilStreamRepresentation *firstOpenStream_ = NULL; - #define PA_IS_INITIALISED_ (initializationCount_ != 0) +/* + By default, selectedHostApiTypes_ == NULL and PortAudio initializes all + host APIs in the order that they are listed in paHostApiInitializers[]. + + If selectedHostApiTypes_ != NULL, then APIs are initialized in the + order specified in selectedHostApiTypes_. +*/ +static PaHostApiTypeId *selectedHostApiTypes_ = NULL; +static int selectedHostApiCount_ = 0; static int CountHostApiInitializers( void ) { int result = 0; - while( paHostApiInitializers[ result ] != 0 ) + while( paHostApiInitializers[ result ].initFunction != 0 ) ++result; return result; } +static const PaUtilHostApiInitializerEntry* FindHostApiInitializerEntry( PaHostApiTypeId hostApiType ) +{ + int i = 0; + + while( paHostApiInitializers[ i ].initFunction != 0 ) + { + if( paHostApiInitializers[i].hostApiType == hostApiType ) + return &paHostApiInitializers[i]; + ++i; + } + + return NULL; +} + +static int CountSelectedHostApis() +{ + if( selectedHostApiTypes_ == NULL ) + return CountHostApiInitializers(); + else + return selectedHostApiCount_; +} + +static const PaUtilHostApiInitializerEntry* GetSelectedHostApi( int index ) +{ + if( selectedHostApiTypes_ == NULL ) + return &paHostApiInitializers[index]; + else + return FindHostApiInitializerEntry( selectedHostApiTypes_[index] ); +} + +PaError Pa_SelectHostApis( const PaHostApiTypeId *hostApiTypes, int count ) +{ + int i, j; + PaHostApiTypeId *oldSelectedHostApiTypes = selectedHostApiTypes_; + PaHostApiTypeId *newSelectedHostApiTypes = NULL; + + if( count == 0 ) + { + /* revert to default state */ + if( selectedHostApiTypes_ ) + { + PaUtil_FreeMemory( selectedHostApiTypes_ ); + selectedHostApiTypes_ = NULL; + selectedHostApiCount_ = 0; + } + + return paNoError; + } + + if( count < 0 ) + return paInvalidHostApi; + + if( hostApiTypes == NULL ) + return paInvalidHostApi; + + /* validation: + - verify that each hostApiTypes value is available (present in paHostApiInitializers) + - verify that hostApiTypes contains no duplicates + */ + for( i=0; i < count; ++i ) + { + if( FindHostApiInitializerEntry( hostApiTypes[i] ) == NULL ) + return paHostApiNotFound; + + for( j=i+1; j < count; ++j ) + { + if (hostApiTypes[i] == hostApiTypes[j]) + return paInvalidHostApi; + } + } + + /* allocate a newSelectedHostApiTypes, copy ids into it */ + + newSelectedHostApiTypes = (PaHostApiTypeId*)PaUtil_AllocateMemory( + sizeof(PaHostApiTypeId) * count ); + if( newSelectedHostApiTypes == NULL ) + return paInsufficientMemory; + + memcpy( newSelectedHostApiTypes, hostApiTypes, count*sizeof(PaHostApiTypeId) ); + + /* install new selectedHostApis and free old selectedHostApis_ */ + + selectedHostApiTypes_ = newSelectedHostApiTypes; + selectedHostApiCount_ = count; + + if( oldSelectedHostApiTypes != NULL ) + { + PaUtil_FreeMemory( oldSelectedHostApiTypes ); + oldSelectedHostApiTypes = NULL; + } + + return paNoError; +} + +PaError Pa_GetSelectedHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ) +{ + if( selectedHostApiTypes_ == NULL ) + { + return Pa_GetAvailableHostApis( hostApiTypes, countAvailable, count ); + } + else + { + *count = selectedHostApiCount_; + if( countAvailable >= selectedHostApiCount_ ) + { + memcpy( hostApiTypes, selectedHostApiTypes_, selectedHostApiCount_*sizeof(PaHostApiTypeId) ); + return paNoError; + } + else + { + return paInsufficientMemory; + } + } +} + +PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ) +{ + int i; + int initializerCount = CountHostApiInitializers(); + + *count = initializerCount; + if( countAvailable >= initializerCount ) + { + for( i=0; i < initializerCount; ++i ) + hostApiTypes[i] = paHostApiInitializers[i].hostApiType; + } + else + { + return paInsufficientMemory; + } + + return paNoError; +} + static void TerminateHostApis( void ) { @@ -201,8 +342,9 @@ static PaError InitializeHostApis( void ) { PaError result = paNoError; int i, initializerCount, baseDeviceIndex; + const PaUtilHostApiInitializerEntry *hostApiInitializer; - initializerCount = CountHostApiInitializers(); + initializerCount = CountSelectedHostApis(); hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory( sizeof(PaUtilHostApiRepresentation*) * initializerCount ); @@ -223,7 +365,9 @@ static PaError InitializeHostApis( void ) PA_DEBUG(( "before paHostApiInitializers[%d].\n",i)); - result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ ); + hostApiInitializer = GetSelectedHostApi(i); + assert( hostApiInitializer != NULL ); + result = hostApiInitializer->initFunction( &hostApis_[hostApisCount_], hostApisCount_ ); if( result != paNoError ) goto error; diff --git a/src/common/pa_hostapi.h b/src/common/pa_hostapi.h index 54b527e..c954231 100644 --- a/src/common/pa_hostapi.h +++ b/src/common/pa_hostapi.h @@ -338,12 +338,22 @@ typedef struct PaUtilHostApiRepresentation { */ typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex ); +/** Associate a PaHostApiTypeId with each host API initialization function. + +*/ +typedef struct { + PaHostApiTypeId hostApiType; + PaUtilHostApiInitializer *initFunction; +} PaUtilHostApiInitializerEntry; + /** paHostApiInitializers is a NULL-terminated array of host API initialization - functions. These functions are called by pa_front.c to initialize the host APIs - when the client calls Pa_Initialize(). + entries, each containing an initialization function. The initialization + functions are called by pa_front.c to initialize the host APIs when the client + calls Pa_Initialize(). - The initialization functions are invoked in order. + By default initialization functions are invoked in order. The initialization + order may be modified by the client using Pa_SelectHostApis(). The first successfully initialized host API that has a default input *or* output device is used as the default PortAudio host API. This is based on the logic that @@ -353,7 +363,7 @@ typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostA There is a platform specific file that defines paHostApiInitializers for that platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example. */ -extern PaUtilHostApiInitializer *paHostApiInitializers[]; +extern PaUtilHostApiInitializerEntry paHostApiInitializers[]; #ifdef __cplusplus diff --git a/src/os/unix/pa_unix_hostapis.c b/src/os/unix/pa_unix_hostapis.c index a9b4a05..cc297a5 100644 --- a/src/os/unix/pa_unix_hostapis.c +++ b/src/os/unix/pa_unix_hostapis.c @@ -55,49 +55,50 @@ PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiI /** Note that on Linux, ALSA is placed before OSS so that the former is preferred over the latter. */ -PaUtilHostApiInitializer *paHostApiInitializers[] = +PaUtilHostApiInitializerEntry paHostApiInitializers[] = { #ifdef __linux__ #if PA_USE_ALSA - PaAlsa_Initialize, + { paALSA, PaAlsa_Initialize }, #endif #if PA_USE_OSS - PaOSS_Initialize, + { paOSS, PaOSS_Initialize }, #endif #else /* __linux__ */ #if PA_USE_OSS - PaOSS_Initialize, + { paOSS, PaOSS_Initialize }, #endif #if PA_USE_ALSA - PaAlsa_Initialize, + { paALSA, PaAlsa_Initialize }, #endif #endif /* __linux__ */ #if PA_USE_JACK - PaJack_Initialize, + { paJACK, PaJack_Initialize }, #endif /* Added for IRIX, Pieter, oct 2, 2003: */ #if PA_USE_SGI - PaSGI_Initialize, + { paAL, PaSGI_Initialize }, #endif #if PA_USE_ASIHPI - PaAsiHpi_Initialize, + { paAudioScienceHPI, PaAsiHpi_Initialize }, #endif #if PA_USE_COREAUDIO - PaMacCore_Initialize, + { paCoreAudio, PaMacCore_Initialize }, #endif #if PA_USE_SKELETON - PaSkeleton_Initialize, + /* just for testing. last in list so it isn't marked as default. */ + { paInDevelopment, PaSkeleton_Initialize }, #endif - 0 /* NULL terminated array */ + { -1, 0 }, /* NULL terminated array */ }; diff --git a/src/os/win/pa_win_hostapis.c b/src/os/win/pa_win_hostapis.c index 9c9927a..980c368 100644 --- a/src/os/win/pa_win_hostapis.c +++ b/src/os/win/pa_win_hostapis.c @@ -69,34 +69,35 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd #endif /* __cplusplus */ -PaUtilHostApiInitializer *paHostApiInitializers[] = +PaUtilHostApiInitializerEntry paHostApiInitializers[] = { #if PA_USE_WMME - PaWinMme_Initialize, + { paMME, PaWinMme_Initialize }, #endif #if PA_USE_DS - PaWinDs_Initialize, + { paDirectSound, PaWinDs_Initialize }, #endif #if PA_USE_ASIO - PaAsio_Initialize, + { paASIO, PaAsio_Initialize }, #endif #if PA_USE_WASAPI - PaWasapi_Initialize, + { paWASAPI, PaWasapi_Initialize }, #endif #if PA_USE_WDMKS - PaWinWdm_Initialize, + { paWDMKS, PaWinWdm_Initialize }, #endif #if PA_USE_SKELETON - PaSkeleton_Initialize, /* just for testing. last in list so it isn't marked as default. */ + /* just for testing. last in list so it isn't marked as default. */ + { paInDevelopment, PaSkeleton_Initialize }, #endif - 0 /* NULL terminated array */ + { -1, 0 }, /* NULL terminated array */ }; diff --git a/test/patest_select_hostapis.c b/test/patest_select_hostapis.c new file mode 100644 index 0000000..a50ef60 --- /dev/null +++ b/test/patest_select_hostapis.c @@ -0,0 +1,148 @@ +/** @file patest_select_hostapis.c + @ingroup test_src + @brief Test of Pa_SelectHostApis + @author Ross Bencina +*/ +/* + * $Id$ + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com/ + * Copyright (c) 1999-2016 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ +#include +#include +#include "portaudio.h" + +/*******************************************************************/ +int main(void); +int main(void) +{ + int i, j, k; + int availableHostApiCount, scratchHostApiCount; +#define MAX_HOST_API_COUNT 100 + PaHostApiTypeId availableHostApis[MAX_HOST_API_COUNT]; + PaHostApiTypeId scratchHostApis[MAX_HOST_API_COUNT]; + PaError err; + + availableHostApiCount = 0; + err = Pa_GetAvailableHostApis( availableHostApis, MAX_HOST_API_COUNT, &availableHostApiCount ); + assert( err == paNoError ); + assert( availableHostApiCount > 0 ); + + printf("available host api type ids:\n"); + for (i = 0; i < availableHostApiCount; ++i ) + { + printf("%d\n", availableHostApis[i]); + } + + err = Pa_Initialize(); + assert( err == paNoError ); + Pa_Terminate(); + + /* excercise Pa_SelectHostApis and Pa_GetSelectedHostApis */ + + /* each API in turn */ + for(i = 0; i < availableHostApiCount; ++i) + { + PaHostApiTypeId hostApiType = availableHostApis[i]; + + printf("selecting api type %d\n", hostApiType); + + err = Pa_SelectHostApis(&hostApiType, 1); + assert( err == paNoError ); + + /* read back and verify selected apis*/ + err = Pa_GetSelectedHostApis( scratchHostApis, MAX_HOST_API_COUNT, &scratchHostApiCount ); + assert( err == paNoError ); + assert( scratchHostApiCount == 1 ); + assert( scratchHostApis[0] == hostApiType ); + + err = Pa_Initialize(); + assert( err == paNoError ); + + /* verify that all devices match the selected API */ + + for(j = 0; j < Pa_GetDeviceCount(); ++j) + { + const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(j); + assert( deviceInfo != NULL ); + assert( Pa_GetHostApiInfo(deviceInfo->hostApi)->type == hostApiType ); + } + + Pa_Terminate(); + } + + /* i counts 1 .. n simultaneously selected APIs */ + for(i = 1; i < availableHostApiCount; ++i) + { + printf("selecting %d apis\n", i); + + err = Pa_SelectHostApis(availableHostApis, i); + assert( err == paNoError ); + + /* read back and verify selected apis*/ + err = Pa_GetSelectedHostApis( scratchHostApis, MAX_HOST_API_COUNT, &scratchHostApiCount ); + assert( err == paNoError ); + assert( scratchHostApiCount == i ); + for( j = 0; j < i; ++j ) + assert( scratchHostApis[j] == availableHostApis[j] ); + + err = Pa_Initialize(); + assert( err == paNoError ); + + /* verify that all devices match one of the selected APIs */ + + for(j = 0; j < Pa_GetDeviceCount(); ++j) + { + const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(j); + assert( deviceInfo != NULL ); + + /* search for whether the device's host API is one of the selected host APIs */ + err = paHostApiNotFound; + for(k = 0; k < i; ++k) + { + PaHostApiTypeId hostApiType = availableHostApis[k]; + if( Pa_GetHostApiInfo(deviceInfo->hostApi)->type == hostApiType ) + { + err = paNoError; + break; + } + } + + assert( err != paHostApiNotFound ); + } + + Pa_Terminate(); + } +}