]> Repos - portaudio/commitdiff
An implementation of runtime host API selection (see ticket #10). See the changes...
authorRoss Bencina <rossb@audiomulch.com>
Tue, 30 Aug 2016 08:10:38 +0000 (18:10 +1000)
committerRoss Bencina <rossb@audiomulch.com>
Tue, 30 Aug 2016 08:10:38 +0000 (18:10 +1000)
include/portaudio.h
src/common/pa_front.c
src/common/pa_hostapi.h
src/os/unix/pa_unix_hostapis.c
src/os/win/pa_win_hostapis.c
test/patest_select_hostapis.c [new file with mode: 0644]

index 3bb3b3197a77efe9a8b03092f13c31bd4a18ba9c..6d90d2ff01e1995111954c5f02f8670bdd5d80a3 100644 (file)
@@ -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
index 06327109efc4435e31c8f6b3d220da9115870101..ba831b92b15592a422656db0f76b0d0cda76aba2 100644 (file)
@@ -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;
 
index 54b527ea819f054bb48d10801c778878a67733d2..c954231ba15db1166e97ce767010d7a802794241 100644 (file)
@@ -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
index a9b4a05d7f489290a8a309610b487d0188dee87a..cc297a59a5c33053540e41cbda6b53b790a59d72 100644 (file)
@@ -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 */
     };
index 9c9927ab77ce201f0a4de3a2e75079c13f11b428..980c368da86bc01461631fc1c1e298a2ceb9b4aa 100644 (file)
@@ -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 (file)
index 0000000..a50ef60
--- /dev/null
@@ -0,0 +1,148 @@
+/** @file patest_select_hostapis.c
+       @ingroup test_src
+       @brief Test of Pa_SelectHostApis
+       @author Ross Bencina <rossb@audiomulch.com>
+*/
+/*
+ * $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 <stdio.h>
+#include <assert.h>
+#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();
+    }
+}