From b8d4bd665db4c89c159c51c807628295a3ada5f9 Mon Sep 17 00:00:00 2001 From: Ross Bencina Date: Tue, 30 Aug 2016 18:10:38 +1000 Subject: [PATCH 1/7] An implementation of runtime host API selection (see ticket #10). See the changes to portaudio.h for documentation for the API. Note that unlike other proposals, this version of the interface allows the client to specify the order of host API initialization. The API is excercised by patest_select_hostapis.c. To reviewers: please verify that the implementation of Pa_SelectHostApis, Pa_GetSelectedHostApis and Pa_GetAvailableHostApis in pa_front.c matches the documentation in portaudio.h --- include/portaudio.h | 58 +++++++++++++ src/common/pa_front.c | 154 +++++++++++++++++++++++++++++++-- src/common/pa_hostapi.h | 18 +++- src/os/unix/pa_unix_hostapis.c | 23 ++--- src/os/win/pa_win_hostapis.c | 17 ++-- test/patest_select_hostapis.c | 148 +++++++++++++++++++++++++++++++ 6 files changed, 390 insertions(+), 28 deletions(-) create mode 100644 test/patest_select_hostapis.c 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(); + } +} From f13a626eef9c2b7515405283dba2b2ddcba21a17 Mon Sep 17 00:00:00 2001 From: Ross Bencina Date: Sat, 3 Sep 2016 16:16:12 +1000 Subject: [PATCH 2/7] calling Pa_SelectHostApis now returns an error if it is called while PA is initialized. --- include/portaudio.h | 8 +++++--- src/common/pa_front.c | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/portaudio.h b/include/portaudio.h index 6d90d2f..5f98e15 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -134,7 +134,8 @@ typedef enum PaErrorCode paCanNotReadFromAnOutputOnlyStream, paCanNotWriteToAnInputOnlyStream, paIncompatibleStreamHostApi, - paBadBufferPtr + paBadBufferPtr, + paIsInitialized } PaErrorCode; @@ -276,8 +277,9 @@ typedef enum PaHostApiTypeId /** Select host APIs and their initialization order. - The selected host APIs take effect the next time that Pa_Initialize() is - invoked. + 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 belonging to the PaHostApiTypeId enumeration. diff --git a/src/common/pa_front.c b/src/common/pa_front.c index 79aa108..de74457 100644 --- a/src/common/pa_front.c +++ b/src/common/pa_front.c @@ -217,6 +217,11 @@ PaError Pa_SelectHostApis( const PaHostApiTypeId *hostApiTypes, int count ) PaHostApiTypeId *oldSelectedHostApiTypes = selectedHostApiTypes_; PaHostApiTypeId *newSelectedHostApiTypes = NULL; + if( PA_IS_INITIALISED_ ) + { + return paIsInitialized; + } + if( count == 0 ) { /* revert to default state */ From 196f0259814bee77567287603dc2fe419224f74e Mon Sep 17 00:00:00 2001 From: Ross Bencina Date: Sat, 3 Sep 2016 16:20:52 +1000 Subject: [PATCH 3/7] add FIXME comment about changing the name of Pa_GetAvailableHostApis --- include/portaudio.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/portaudio.h b/include/portaudio.h index 5f98e15..0d72b52 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -320,7 +320,10 @@ PaError Pa_SelectHostApis( const PaHostApiTypeId *hostApiTypes, int count ); */ PaError Pa_GetSelectedHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ); -/** Returns the type ids of all available host APIs in initialization order. +/** 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 target system. @param hostApiTypes (IN/OUT) An array that will be filled with host API identifiers belonging to the PaHostApiTypeId enumeration. @@ -329,6 +332,13 @@ PaError Pa_GetSelectedHostApis( PaHostApiTypeId *hostApiTypes, int countAvailabl @param countAvailable The number of available elements in the hostApiTypes array. + 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 + @see Pa_SelectHostApis */ PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ); From a3d9f6cd3cb3f629a4235ae9276e781782bf8931 Mon Sep 17 00:00:00 2001 From: Ross Bencina Date: Sat, 3 Sep 2016 16:28:39 +1000 Subject: [PATCH 4/7] document that initialization order does not predictably determine host api indexes --- include/portaudio.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/portaudio.h b/include/portaudio.h index 0d72b52..311d0b0 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -293,9 +293,11 @@ typedef enum PaHostApiTypeId hostApiTypeIds parameter is not available. The paInvalidHostApi error indicates that there was a problem with the - hostApiTypes array. E.g. it contained duplicate entries. + 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 */ From d4ed26e49ac57d9fd41cad56c632c27e7006b3ff Mon Sep 17 00:00:00 2001 From: Ross Bencina Date: Sat, 3 Sep 2016 16:50:27 +1000 Subject: [PATCH 5/7] add Pa_GetAvailableHostApisCount(), fix test to use it. reshuffle code --- include/portaudio.h | 64 ++++++++++++++++++++++------------- src/common/pa_front.c | 50 ++++++++++++++++----------- test/patest_select_hostapis.c | 23 +++++++++---- 3 files changed, 89 insertions(+), 48 deletions(-) diff --git a/include/portaudio.h b/include/portaudio.h index 311d0b0..e05bece 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -275,6 +275,46 @@ 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 (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. + + 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 countAvailable, int *count ); + + /** Select host APIs and their initialization order. This function may only be called prior to calling Pa_Initialize() @@ -303,6 +343,7 @@ typedef enum 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 @@ -322,29 +363,6 @@ PaError Pa_SelectHostApis( const PaHostApiTypeId *hostApiTypes, int count ); */ PaError Pa_GetSelectedHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ); -/** 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 target system. - - @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. - - 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 - - @see Pa_SelectHostApis -*/ -PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ); - /** A structure containing information about a particular host API. */ diff --git a/src/common/pa_front.c b/src/common/pa_front.c index de74457..ebc9c10 100644 --- a/src/common/pa_front.c +++ b/src/common/pa_front.c @@ -181,6 +181,7 @@ static int CountHostApiInitializers( void ) return result; } + static const PaUtilHostApiInitializerEntry* FindHostApiInitializerEntry( PaHostApiTypeId hostApiType ) { int i = 0; @@ -195,6 +196,33 @@ static const PaUtilHostApiInitializerEntry* FindHostApiInitializerEntry( PaHostA return NULL; } + +int Pa_GetAvailableHostApisCount( void ) +{ + return CountHostApiInitializers(); +} + + +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 int CountSelectedHostApis() { if( selectedHostApiTypes_ == NULL ) @@ -203,6 +231,7 @@ static int CountSelectedHostApis() return selectedHostApiCount_; } + static const PaUtilHostApiInitializerEntry* GetSelectedHostApi( int index ) { if( selectedHostApiTypes_ == NULL ) @@ -211,6 +240,7 @@ static const PaUtilHostApiInitializerEntry* GetSelectedHostApi( int index ) return FindHostApiInitializerEntry( selectedHostApiTypes_[index] ); } + PaError Pa_SelectHostApis( const PaHostApiTypeId *hostApiTypes, int count ) { int i, j; @@ -280,6 +310,7 @@ PaError Pa_SelectHostApis( const PaHostApiTypeId *hostApiTypes, int count ) return paNoError; } + PaError Pa_GetSelectedHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ) { if( selectedHostApiTypes_ == NULL ) @@ -301,25 +332,6 @@ PaError Pa_GetSelectedHostApis( PaHostApiTypeId *hostApiTypes, int countAvailabl } } -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 ) { diff --git a/test/patest_select_hostapis.c b/test/patest_select_hostapis.c index a50ef60..ceacf48 100644 --- a/test/patest_select_hostapis.c +++ b/test/patest_select_hostapis.c @@ -42,6 +42,8 @@ */ #include #include +#include + #include "portaudio.h" /*******************************************************************/ @@ -50,13 +52,19 @@ 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]; + 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, MAX_HOST_API_COUNT, &availableHostApiCount ); + err = Pa_GetAvailableHostApis( availableHostApis, maxHostApiCount, &availableHostApiCount ); assert( err == paNoError ); assert( availableHostApiCount > 0 ); @@ -83,7 +91,7 @@ int main(void) assert( err == paNoError ); /* read back and verify selected apis*/ - err = Pa_GetSelectedHostApis( scratchHostApis, MAX_HOST_API_COUNT, &scratchHostApiCount ); + err = Pa_GetSelectedHostApis( scratchHostApis, maxHostApiCount, &scratchHostApiCount ); assert( err == paNoError ); assert( scratchHostApiCount == 1 ); assert( scratchHostApis[0] == hostApiType ); @@ -112,7 +120,7 @@ int main(void) assert( err == paNoError ); /* read back and verify selected apis*/ - err = Pa_GetSelectedHostApis( scratchHostApis, MAX_HOST_API_COUNT, &scratchHostApiCount ); + err = Pa_GetSelectedHostApis( scratchHostApis, maxHostApiCount, &scratchHostApiCount ); assert( err == paNoError ); assert( scratchHostApiCount == i ); for( j = 0; j < i; ++j ) @@ -145,4 +153,7 @@ int main(void) Pa_Terminate(); } + + free(availableHostApis); + free(scratchHostApis); } From 90fa011a6b829eea6df50d5e7a1f38a795ffddd7 Mon Sep 17 00:00:00 2001 From: Ross Bencina Date: Sat, 3 Sep 2016 17:03:44 +1000 Subject: [PATCH 6/7] further documentation additions. rename countAvailable parameter to arrayCapacity --- include/portaudio.h | 34 +++++++++++++++++++++------------- src/common/pa_front.c | 10 +++++----- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/include/portaudio.h b/include/portaudio.h index e05bece..19d0131 100644 --- a/include/portaudio.h +++ b/include/portaudio.h @@ -295,12 +295,15 @@ int Pa_GetAvailableHostApisCount( void ); 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 (IN/OUT) An array that will be filled with - host API identifiers belonging to the PaHostApiTypeId enumeration. + @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. - - @param countAvailable The number of available elements in the hostApiTypes array. + 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 @@ -312,7 +315,7 @@ int Pa_GetAvailableHostApisCount( void ); @see Pa_SelectHostApis */ -PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ); +PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int arrayCapacity, int *count ); /** Select host APIs and their initialization order. @@ -321,8 +324,10 @@ PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int countAvailab 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 belonging to the - PaHostApiTypeId enumeration. + @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. @@ -346,22 +351,25 @@ 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 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. - - @param countAvailable The number of available elements in the hostApiTypes array. + 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 countAvailable was not large enough + 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 countAvailable, int *count ); +PaError Pa_GetSelectedHostApis( PaHostApiTypeId *hostApiTypes, int arrayCapacity, int *count ); /** A structure containing information about a particular host API. */ diff --git a/src/common/pa_front.c b/src/common/pa_front.c index ebc9c10..c12f4c9 100644 --- a/src/common/pa_front.c +++ b/src/common/pa_front.c @@ -203,13 +203,13 @@ int Pa_GetAvailableHostApisCount( void ) } -PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ) +PaError Pa_GetAvailableHostApis( PaHostApiTypeId *hostApiTypes, int arrayCapacity, int *count ) { int i; int initializerCount = CountHostApiInitializers(); *count = initializerCount; - if( countAvailable >= initializerCount ) + if( arrayCapacity >= initializerCount ) { for( i=0; i < initializerCount; ++i ) hostApiTypes[i] = paHostApiInitializers[i].hostApiType; @@ -311,16 +311,16 @@ PaError Pa_SelectHostApis( const PaHostApiTypeId *hostApiTypes, int count ) } -PaError Pa_GetSelectedHostApis( PaHostApiTypeId *hostApiTypes, int countAvailable, int *count ) +PaError Pa_GetSelectedHostApis( PaHostApiTypeId *hostApiTypes, int arrayCapacity, int *count ) { if( selectedHostApiTypes_ == NULL ) { - return Pa_GetAvailableHostApis( hostApiTypes, countAvailable, count ); + return Pa_GetAvailableHostApis( hostApiTypes, arrayCapacity, count ); } else { *count = selectedHostApiCount_; - if( countAvailable >= selectedHostApiCount_ ) + if( arrayCapacity >= selectedHostApiCount_ ) { memcpy( hostApiTypes, selectedHostApiTypes_, selectedHostApiCount_*sizeof(PaHostApiTypeId) ); return paNoError; From 5affcb101286fac6a1ab6665ae65c41772848c3c Mon Sep 17 00:00:00 2001 From: Ross Bencina Date: Sun, 4 Sep 2016 21:53:09 +1000 Subject: [PATCH 7/7] added 4 new public API functions to def file --- build/msvc/portaudio.def | 4 ++++ 1 file changed, 4 insertions(+) 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