diff --git a/build/msvc/portaudio.def b/build/msvc/portaudio.def index bdaa8ee..c1361a8 100644 --- a/build/msvc/portaudio.def +++ b/build/msvc/portaudio.def @@ -35,6 +35,10 @@ Pa_GetStreamReadAvailable @31 Pa_GetStreamWriteAvailable @32 Pa_GetSampleSize @33 Pa_Sleep @34 +Pa_GetAvailableHostApisCount @35 +Pa_GetAvailableHostApis @36 +Pa_SelectHostApis @37 +Pa_GetSelectedHostApis @38 PaAsio_GetAvailableBufferSizes @50 PaAsio_ShowControlPanel @51 PaUtil_InitializeX86PlainConverters @52 diff --git a/include/portaudio.h b/include/portaudio.h index 3bb3b31..19d0131 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -134,7 +134,8 @@ typedef enum PaErrorCode paCanNotReadFromAnOutputOnlyStream, paCanNotWriteToAnInputOnlyStream, paIncompatibleStreamHostApi, - paBadBufferPtr + paBadBufferPtr, + paIsInitialized } PaErrorCode; @@ -274,6 +275,103 @@ typedef enum PaHostApiTypeId } PaHostApiTypeId; +/** Returns the number of compiled-in host APIs in this build of PortAudio. + + The returned value is large enough that it can be used to dimension + arrays passed to Pa_GetAvailableHostApis and Pa_GetSelectedHostApis. + + FIXME REVIEW The "Available" name should match whatever is chosen for + Pa_GetAvailableHostApis + + @see Pa_GetAvailableHostApis, Pa_GetSelectedHostApis +*/ +int Pa_GetAvailableHostApisCount( void ); + + +/** Returns the type ids of all compiled-in host APIs in initialization order. + + Note that the compiled-in host APIs are not necessarily those that are + installed on the system. It is possible for compiled-in dynamically loaded + host APIs to be not installed on the system, and it is possible for + installed audio APIs to not be supported (compiled in to) PortAudio. + + @param hostApiTypes (OUT) An array that will be filled with + host API identifiers, having values belonging to the PaHostApiTypeId enumeration. + + @param arrayCapacity The number of usable elements in the hostApiTypes array. + This value never need by greater than the value returned by + Pa_GetAvailableHostApisCount(). + + @param count (OUT) The number of host APIs, returned even on error. + Upon success, this will be the number of valid elements stored in hostApiTypes. + + FIXME REVIEW: Consider a different name for this function, both "available" + and "supported" are ambiguous between what is available/supported on the + target platform and what is compiled into PA. Keep in mind that + Pa_IsFormatSupported refers to formats supported by a device. + Proposals: + GetConfiguredHostApis, GetCompiledHostApis + NOTE: also fix name oPa_GetAvailableHostApisCount + + @see Pa_SelectHostApis +*/ +PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int arrayCapacity, int *count ); + + +/** Select host APIs and their initialization order. + + This function may only be called prior to calling Pa_Initialize() + or after calling Pa_Terminate(). The selected host APIs take effect the + next time that Pa_Initialize() is invoked. + + @param hostApiTypes An array of host API identifiers, having values belonging + to the PaHostApiTypeId enumeration. The specified host APIs must selected + from thosed returned by Pa_GetAvailableHostApis(). For example, it would be an + error to specify a Windows host API while using PortAudio on Mac OS X. + + @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 invalid or duplicate entries. + + @note The host API initialization order determines default devices. + There is no predictable relationship between the order that host APIs appear + in hostApiTypes, and their hostApiIndexes assigned by Pa_Initialize(). + + @see PaHostApiTypeId +*/ +PaError Pa_SelectHostApis( const PaHostApiTypeId *hostApiTypes, int count ); + + +/** Returns the type ids of the selected host APIs in initialization order. + + @param hostApiTypes (OUT) An array that will be filled with + host API identifiers, having values belonging to the PaHostApiTypeId enumeration. + + @param arrayCapacity The number of usable elements in the hostApiTypes array. + This value never need by greater than the value returned by + Pa_GetAvailableHostApisCount(). + + @param count (OUT) The number of selected host APIs, returned even on error. + Upon success, this will be the number of valid elements stored in hostApiTypes. + + @return A PaErrorCode indicating whether the call succeeded or failed. + + The paInsufficientMemory error indicates that arrayCapacity 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 arrayCapacity, 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 2cd9c54..c12f4c9 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,20 +160,179 @@ 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; +} + + +int Pa_GetAvailableHostApisCount( void ) +{ + return CountHostApiInitializers(); +} + + +PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int arrayCapacity, int *count ) +{ + int i; + int initializerCount = CountHostApiInitializers(); + + *count = initializerCount; + if( arrayCapacity >= initializerCount ) + { + for( i=0; i < initializerCount; ++i ) + hostApiTypes[i] = paHostApiInitializers[i].hostApiType; + } + else + { + return paInsufficientMemory; + } + + return paNoError; +} + + +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( PA_IS_INITIALISED_ ) + { + return paIsInitialized; + } + + 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 arrayCapacity, int *count ) +{ + if( selectedHostApiTypes_ == NULL ) + { + return Pa_GetAvailableHostApis( hostApiTypes, arrayCapacity, count ); + } + else + { + *count = selectedHostApiCount_; + if( arrayCapacity >= selectedHostApiCount_ ) + { + memcpy( hostApiTypes, selectedHostApiTypes_, selectedHostApiCount_*sizeof(PaHostApiTypeId) ); + return paNoError; + } + else + { + return paInsufficientMemory; + } + } +} + + static void TerminateHostApis( void ) { /* terminate in reverse order from initialization */ @@ -201,8 +359,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 +382,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..ceacf48 --- /dev/null +++ b/test/patest_select_hostapis.c @@ -0,0 +1,159 @@ +/** @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 + +#include "portaudio.h" + +/*******************************************************************/ +int main(void); +int main(void) +{ + int i, j, k; + int availableHostApiCount, scratchHostApiCount; + int maxHostApiCount; + PaHostApiTypeId *availableHostApis; + PaHostApiTypeId *scratchHostApis; + PaError err; + + maxHostApiCount = Pa_GetAvailableHostApisCount(); + availableHostApis = (PaHostApiTypeId*)malloc( sizeof(PaHostApiTypeId) * maxHostApiCount ); + assert( availableHostApis != NULL ); + scratchHostApis = (PaHostApiTypeId*)malloc( sizeof(PaHostApiTypeId) * maxHostApiCount ); + assert( scratchHostApis != NULL ); + + availableHostApiCount = 0; + err = Pa_GetAvailableHostApis( availableHostApis, maxHostApiCount, &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, maxHostApiCount, &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, maxHostApiCount, &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(); + } + + free(availableHostApis); + free(scratchHostApis); +}