]> Repos - portaudio/commitdiff
Pa_RefreshDevice functionality
authorAndrew Gundersen <gundersena@xavier.edu>
Fri, 20 Aug 2021 16:09:19 +0000 (11:09 -0500)
committerAndrew Gundersen <gundersena@xavier.edu>
Fri, 20 Aug 2021 16:09:19 +0000 (11:09 -0500)
examples/paex_record_file.c
examples/paex_refresh_devs.c [new file with mode: 0644]
examples/paex_sine.c
include/portaudio.h
src/common/pa_front.c
src/common/pa_hostapi.h
src/hostapi/coreaudio/pa_mac_core.c
src/hostapi/dsound/pa_win_ds.c

index 562a8e9038761a2b6ce8f6b468fcfc906f7740ed..c437d19aed48a3fe304b3b29ba6b5e807a2e780d 100644 (file)
@@ -258,7 +258,6 @@ static int recordCallback( const void *inputBuffer, void *outputBuffer,
 
     data->frameIndex += PaUtil_WriteRingBuffer(&data->ringBuffer, rptr, elementsToWrite);
 
-    return paContinue;
 }
 
 /* This routine will be called by the PortAudio engine when audio is needed.
@@ -272,8 +271,11 @@ static int playCallback( const void *inputBuffer, void *outputBuffer,
                          void *userData )
 {
     paTestData *data = (paTestData*)userData;
+
     ring_buffer_size_t elementsToPlay = PaUtil_GetRingBufferReadAvailable(&data->ringBuffer);
-    ring_buffer_size_t elementsToRead = rbs_min(elementsToPlay, (ring_buffer_size_t)(framesPerBuffer * NUM_CHANNELS));
+
+    ring_buffer_size_t elementsToRead = rbs_min( elementsToPlay, 
+                                                 (ring_buffer_size_t)(framesPerBuffer * NUM_CHANNELS) );
     SAMPLE* wptr = (SAMPLE*)outputBuffer;
 
     (void) inputBuffer; /* Prevent unused variable warnings. */
diff --git a/examples/paex_refresh_devs.c b/examples/paex_refresh_devs.c
new file mode 100644 (file)
index 0000000..c31b233
--- /dev/null
@@ -0,0 +1,46 @@
+#include <time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "portaudio.h"
+
+int main() 
+{
+    PaError err = paNoError;
+
+    PaDeviceIndex defaultOutputDevice;
+    PaDeviceIndex defaultInputDevice;
+
+    err = Pa_Initialize();
+    if( err != paNoError )
+    {
+        printf( "ERROR: Pa_Initialize returned 0x%x\n", err );
+        goto error;
+    }
+
+    printf( "PortAudio version: 0x%08X\n", Pa_GetVersion());
+    // printf( "Version text: '%s'\n", Pa_GetVersionInfo()->versionText );
+
+    const int count = 20;
+    for (int i = 0; i < count; ++i)
+    {
+        defaultInputDevice = Pa_GetDefaultInputDevice();
+        defaultOutputDevice = Pa_GetDefaultOutputDevice();
+        printf("Input: %i, Output: %i\n", defaultInputDevice, defaultOutputDevice);
+
+        err = Pa_RefreshDevices();
+        if( err != paNoError )
+        {
+            printf( "ERROR: Pa_RefreshDevices returned 0x%x\n", err );
+            goto error;
+        }
+
+        sleep(2);
+    }
+
+    Pa_Terminate();
+
+    printf("----------------------------------------------\n");
+    return 0;
+error:
+    return 1;
+}
\ No newline at end of file
index 50ef205c9b18728bb74a1f9b108d9b23a72d3e30..99ae625b1bb4c3ad2325b7db4d809b30eb0d31f0 100644 (file)
@@ -154,6 +154,7 @@ int main(void)
     if( err != paNoError ) goto error;
 
     printf("Play for %d seconds.\n", NUM_SECONDS );
+
     Pa_Sleep( NUM_SECONDS * 1000 );
 
     err = Pa_StopStream( stream );
index c3e174472f9e4b2434f6d86758cb238939c80e08..8ee92897babb032c56a35a8747bde84fc4b64552 100644 (file)
@@ -449,6 +449,17 @@ PaDeviceIndex Pa_GetDefaultInputDevice( void );
 PaDeviceIndex Pa_GetDefaultOutputDevice( void );
 
 
+/** Normally the list of devices is frozen after initialization, until the
+  next time port audio is re-initialized. This provides API stability but
+  has the side effect that hot-pluggable devices such as USB audio headsets
+  might not be up-to-date while the program is running. This method is used
+  to refresh the list of devices within port audio without closing open
+  streams.
+  @return an error code which indicates whether the device refresh was successful.
+  */
+ PaError Pa_RefreshDevices( void );
+
+
 /** The type used to represent monotonic time in seconds. PaTime is
  used for the fields of the PaStreamCallbackTimeInfo argument to the
  PaStreamCallback and as the result of Pa_GetStreamTime().
index 8da873a74e22fc3f4341060e7de138a2342b526f..bd19cd9a1bf879e8c1ddbc53ab9bc37723776dc8 100644 (file)
@@ -743,6 +743,55 @@ PaDeviceIndex Pa_GetDefaultOutputDevice( void )
 }
 
 
+PaError Pa_RefreshDevices( void )
+ {
+     int i, baseDeviceIndex;
+     PaError result = paNoError;
+
+     PA_LOGAPI_ENTER( "Pa_RefreshDeviceList" );
+     if( !PA_IS_INITIALISED_ )
+         return paNotInitialized;
+
+     baseDeviceIndex = 0;
+     deviceCount_ = 0;
+
+     for( i=0; i < hostApisCount_; ++i )
+     {
+         PaUtilHostApiRepresentation *hostApi = hostApis_[i];
+         if( hostApi->RefreshDevices == NULL )
+         {
+             /* RefreshDevices not yet implemented for this backend. Just
+                assume that the baseDeviceIndex and the deviceCount_ are
+                incremented according to the values in the info */
+             baseDeviceIndex += hostApi->info.deviceCount;
+             deviceCount_ += hostApi->info.deviceCount;
+             continue;
+         }
+
+         PA_DEBUG(( "refreshing device list for host api %d.\n",i));
+
+         if(( result = hostApi->RefreshDevices( hostApi, i )) != paNoError )
+            return result;
+
+         assert( hostApi->info.defaultInputDevice < hostApi->info.deviceCount );
+         assert( hostApi->info.defaultOutputDevice < hostApi->info.deviceCount );
+
+         hostApi->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
+
+         if( hostApi->info.defaultInputDevice != paNoDevice )
+             hostApi->info.defaultInputDevice += baseDeviceIndex;
+
+         if( hostApi->info.defaultOutputDevice != paNoDevice )
+             hostApi->info.defaultOutputDevice += baseDeviceIndex;
+
+         baseDeviceIndex += hostApi->info.deviceCount;
+         deviceCount_ += hostApi->info.deviceCount;
+     }
+
+     return result;
+ }
+
+
 const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
 {
     int hostSpecificDeviceIndex;
index 4ac3ab60e9299f32e5ec78912fc199d6cfdfbdf3..0af8d535517b4de7a862b294065317e34e36b9a6 100644 (file)
@@ -322,6 +322,8 @@ typedef struct PaUtilHostApiRepresentation {
                                   const PaStreamParameters *inputParameters,
                                   const PaStreamParameters *outputParameters,
                                   double sampleRate );
+
+    PaError (*RefreshDevices)( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index );
 } PaUtilHostApiRepresentation;
 
 
index aaa5bf40c51a0fa18b31a3a0f547102c8bc02748..4a9172b7761758a6c4afbd747f9114f7c6c67596 100644 (file)
@@ -269,6 +269,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                            PaStreamFlags streamFlags,
                            PaStreamCallback *streamCallback,
                            void *userData );
+static PaError RefreshDevices( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index );
 static PaError CloseStream( PaStream* stream );
 static PaError StartStream( PaStream *stream );
 static PaError StopStream( PaStream *stream );
@@ -820,6 +821,7 @@ PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIn
     (*hostApi)->Terminate = Terminate;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
+    (*hostApi)->RefreshDevices = RefreshDevices;
 
     PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface,
                                       CloseStream, StartStream,
@@ -2096,6 +2098,85 @@ error:
 
 #define HOST_TIME_TO_PA_TIME( x ) ( AudioConvertHostTimeToNanos( (x) ) * 1.0E-09) /* convert to nanoseconds and then to seconds */
 
+
+PaError RefreshDevices( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
+    PaError result = paNoError;
+    PaDeviceInfo *deviceInfoArray;
+    int i;
+
+    VVDBUG(("RefreshDevices(): hostApiIndex=%d\n", hostApiIndex));
+    
+    auhalHostApi->devIds = NULL;
+    auhalHostApi->devCount = 0;
+
+    /* get the info we need about the devices */
+    result = gatherDeviceInfo( auhalHostApi );
+    if( result != paNoError )
+       goto error;
+
+    hostApi->info.defaultInputDevice = paNoDevice;
+    hostApi->info.defaultOutputDevice = paNoDevice;
+    hostApi->info.deviceCount = 0;  
+
+    /* If the device infos already exist, free the old memory */
+    if( hostApi->deviceInfos != NULL )
+    {
+        PaUtil_GroupFreeMemory( auhalHostApi->allocations, hostApi->deviceInfos );
+        hostApi->deviceInfos = NULL;
+    }
+
+    if( auhalHostApi->devCount > 0 )
+    {
+        hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount);
+        if( !hostApi->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all device info structs in a contiguous block */
+        deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+                auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( i=0; i < auhalHostApi->devCount; ++i )
+        {
+            int err;
+            err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i],
+                                      auhalHostApi->devIds[i],
+                                      hostApiIndex );
+            if (err == paNoError)
+            { /* copy some info and set the defaults */
+                hostApi->deviceInfos[hostApi->info.deviceCount] = &deviceInfoArray[i];
+                if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn)
+                    hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
+                if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut)
+                    hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
+                hostApi->info.deviceCount++;
+            }
+            else
+            { /* there was an error. we need to shift the devices down, so we ignore this one */
+                int j;
+                auhalHostApi->devCount--;
+                for( j=i; j<auhalHostApi->devCount; ++j )
+                   auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1];
+                i--;
+            }
+        }
+    }
+
+error:
+    return result;
+}
+
+
 PaTime GetStreamTime( PaStream *s )
 {
     return HOST_TIME_TO_PA_TIME( AudioGetCurrentHostTime() );
index 46587f96eccee9c40ff7b93d65cba6b74c08dc6c..a554bb1103704c1f0f561990c47324e2d5341070 100644 (file)
@@ -190,6 +190,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
                                   const PaStreamParameters *inputParameters,
                                   const PaStreamParameters *outputParameters,
                                   double sampleRate );
+static PaError RefreshDevices( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index );
 static PaError CloseStream( PaStream* stream );
 static PaError StartStream( PaStream *stream );
 static PaError StopStream( PaStream *stream );
@@ -1325,6 +1326,7 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
     (*hostApi)->Terminate = Terminate;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
+    (*hostApi)->RefreshDevices = RefreshDevices;
 
     PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
                                       StopStream, AbortStream, IsStreamStopped, IsStreamActive,
@@ -1488,6 +1490,147 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 }
 
 
+/***********************************************************************************/
+static PaError RefreshDevices( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+    DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
+    PaWinDsDeviceInfo *deviceInfoArray;
+    int i = 0, deviceCount = 0;
+    char comWasInitialized = winDsHostApi->comWasInitialized;
+    PaError result = paNoError;
+
+    if( !comWasInitialized )
+       return paInternalError;
+
+    /* initialise guid vectors so they can be safely deleted on error */
+    deviceNamesAndGUIDs.winDsHostApi = NULL;
+    deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
+    deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
+
+    hostApi->info.deviceCount = 0;
+    hostApi->info.defaultInputDevice = paNoDevice;
+    hostApi->info.defaultOutputDevice = paNoDevice;
+
+    /* DSound - enumerate devices to count them and to gather their GUIDs */
+    result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
+    if( result != paNoError )
+        goto error;
+
+    result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
+    if( result != paNoError )
+        goto error;
+
+    paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
+    paWinDsDSoundEntryPoints.DirectSoundEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
+
+    if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
+    {
+        result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
+        goto error;
+    }
+
+    if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
+    {
+        result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
+        goto error;
+    }
+
+    /* Free any old memory which might be in the device info */
+    if( hostApi->deviceInfos )
+    {
+        PaUtil_GroupFreeMemory( winDsHostApi->allocations, hostApi->deviceInfos );
+        hostApi->deviceInfos = NULL;
+    }
+
+    deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
+
+#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
+    if( deviceCount > 0 )
+    {
+        deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
+        FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
+    }
+#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
+
+    if( deviceCount > 0 )
+    {
+        /* allocate array for pointers to PaDeviceInfo structs */
+        hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+        if( !hostApi->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all PaDeviceInfo structs in a contiguous block */
+        deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
+                winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( i=0; i < deviceCount; ++i )
+        {
+            PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
+            deviceInfo->structVersion = 2;
+            deviceInfo->hostApi = hostApiIndex;
+            deviceInfo->name = 0;
+            hostApi->deviceInfos[i] = deviceInfo;
+        }
+
+        for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
+        {
+            result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
+                    deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
+                    deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
+                    deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
+            if( result != paNoError )
+                goto error;
+        }
+
+        for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
+        {
+            result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
+                    deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
+                    deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
+                    deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
+            if( result != paNoError )
+                goto error;
+        }
+    }    
+
+    result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
+    if( result != paNoError )
+        goto error;
+
+    result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
+    if( result != paNoError )
+        goto error;
+
+    return result;
+
+error:
+    if( winDsHostApi )
+    {
+        if( winDsHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( winDsHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
+        }
+    }
+
+    TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
+    TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
+
+
+    return result;
+}
+
+
 #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
 static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
                                        PaWinDsDeviceInfo *inputDevice,