Index: test/patest2.c
===================================================================
--- test/patest2.c	(revision 0)
+++ test/patest2.c	(revision 0)
@@ -0,0 +1,301 @@
+/** @file patest2.c
+    @ingroup test_src
+    @brief Ring modulate the audio input with a sine wave for 20 seconds with
+    device change detection.
+    @author Ross Bencina <rossb@audiomulch.com>, 
+            Phil Burk http://www.softsynth.com,
+            Erik Bunce <kde@bunce.us>
+*/
+/*
+ * $Id$
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 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 <math.h>
+#include "portaudio.h"
+
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+
+#define SAMPLE_RATE (44100)
+
+typedef struct
+{
+    float sine[100];
+    int phase;
+    int sampsToGo;
+}
+patest1data;
+
+static void patest1DevicesChangedCallback( PaDevicesChangedFlags flags, 
+                                           void *userData )
+{
+    int     i, numDevices, defaultDisplayed;
+    const   PaDeviceInfo *deviceInfo;
+    printf( "========================================\n" );
+    printf( "patest1DevicesChangedCallback(%04x, %p)\n", flags, userData );
+    numDevices = Pa_GetDeviceCount();
+
+    if( numDevices < 0 )
+    {
+        printf( "ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
+        return;
+    }
+    
+    if ( ( flags & paDefaultInputDeviceChanged ) )
+    {
+        i = Pa_GetDefaultInputDevice();
+
+        deviceInfo = Pa_GetDeviceInfo( i );
+        if ( deviceInfo != NULL)
+            printf( "Default input device changed to %d: %s (%s)\n", i, deviceInfo->name, Pa_GetHostApiInfo( deviceInfo->hostApi )->name );
+        else
+            printf( "Default input device changed to %d\n", i );
+    }
+
+
+    if ( ( flags & paDefaultOutputDeviceChanged ) )
+    {
+        i = Pa_GetDefaultOutputDevice();
+
+        deviceInfo = Pa_GetDeviceInfo( i );
+        if ( deviceInfo )
+            printf( "Default output device changed to %d: %s (%s)\n", i, deviceInfo->name, Pa_GetHostApiInfo( deviceInfo->hostApi )->name );
+        else
+            printf( "Default output device changed to %d\n", i );
+    }
+
+    /* only display the new list if the list has already changed */
+    if ( ( flags & paDevicesListChanged ) == 0 )
+        return;
+
+    printf( "Hardware device list changed\n" );
+        
+    printf( "Number of devices = %d\n", numDevices );
+    for( i=0; i<numDevices; i++ )
+    {
+        deviceInfo = Pa_GetDeviceInfo( i );
+        printf( "--------------------------------------- device #%d\n", i );
+                
+    /* Mark global and API specific default devices */
+        defaultDisplayed = 0;
+        if( i == Pa_GetDefaultInputDevice() )
+        {
+            printf( "[ Default Input" );
+            defaultDisplayed = 1;
+        }
+        else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultInputDevice )
+        {
+            const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
+            printf( "[ Default %s Input", hostInfo->name );
+            defaultDisplayed = 1;
+        }
+        
+        if( i == Pa_GetDefaultOutputDevice() )
+        {
+            printf( (defaultDisplayed ? "," : "[") );
+            printf( " Default Output" );
+            defaultDisplayed = 1;
+        }
+        else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultOutputDevice )
+        {
+            const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
+            printf( (defaultDisplayed ? "," : "[") );                
+            printf( " Default %s Output", hostInfo->name );
+            defaultDisplayed = 1;
+        }
+
+        if( defaultDisplayed )
+            printf( " ]\n" );
+
+    /* print device info fields */
+        printf( "Name                        = %s\n", deviceInfo->name );
+        printf( "Host API                    = %s\n",  Pa_GetHostApiInfo( deviceInfo->hostApi )->name );
+        printf( "Max inputs = %d", deviceInfo->maxInputChannels  );
+        printf( ", Max outputs = %d\n", deviceInfo->maxOutputChannels  );
+
+        printf( "Default low input latency   = %8.3f\n", deviceInfo->defaultLowInputLatency  );
+        printf( "Default low output latency  = %8.3f\n", deviceInfo->defaultLowOutputLatency  );
+        printf( "Default high input latency  = %8.3f\n", deviceInfo->defaultHighInputLatency  );
+        printf( "Default high output latency = %8.3f\n", deviceInfo->defaultHighOutputLatency  );
+    }
+}
+
+static int patest1Callback( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    patest1data *data = (patest1data*)userData;
+    float *in = (float*)inputBuffer;
+    float *out = (float*)outputBuffer;
+    int framesToCalc = framesPerBuffer;
+    unsigned long i = 0;
+    int finished;
+
+    if( data->sampsToGo < framesPerBuffer )
+    {
+        framesToCalc = data->sampsToGo;
+        finished = paComplete;
+    }
+    else
+    {
+        finished = paContinue;
+    }
+
+    for( ; i<framesToCalc; i++ )
+    {
+        *out++ = *in++ * data->sine[data->phase];  /* left */
+        *out++ = *in++ * data->sine[data->phase++];  /* right */
+        if( data->phase >= 100 )
+            data->phase = 0;
+    }
+
+    data->sampsToGo -= framesToCalc;
+
+    /* zero remainder of final buffer if not already done */
+    for( ; i<framesPerBuffer; i++ )
+    {
+        *out++ = 0; /* left */
+        *out++ = 0; /* right */
+    }
+    
+    return finished;
+}
+
+int main(int argc, char* argv[]);
+int main(int argc, char* argv[])
+{
+    PaStream                *stream;
+    PaError                 err;
+    patest1data             data;
+    int                     i;
+    PaStreamParameters      inputParameters, outputParameters;
+    const PaHostErrorInfo*  herr;
+
+    printf("patest1.c\n"); fflush(stdout);
+    printf("Ring modulate input for 20 seconds.\n"); fflush(stdout);
+    
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<100; i++ )
+        data.sine[i] = sin( ((double)i/100.) * M_PI * 2. );
+    data.phase = 0;
+    data.sampsToGo = SAMPLE_RATE * 20;        /* 20 seconds. */
+
+    /* initialise portaudio subsytem */
+    err = Pa_Initialize();
+
+    err = Pa_AddDevicesChangedCallback(patest1DevicesChangedCallback, NULL);
+
+    inputParameters.device = Pa_GetDefaultInputDevice();    /* default input device */
+    inputParameters.channelCount = 2;                       /* stereo input */
+    inputParameters.sampleFormat = paFloat32;               /* 32 bit floating point input */
+    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
+    inputParameters.hostApiSpecificStreamInfo = NULL;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice();  /* default output device */
+    outputParameters.channelCount = 2;                      /* stereo output */
+    outputParameters.sampleFormat = paFloat32;              /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+                        &stream,
+                        &inputParameters,
+                        &outputParameters,
+                        (double)SAMPLE_RATE, /* Samplerate in Hertz. */
+                        512,                 /* Small buffers */
+                        paClipOff,           /* We won't output out of range samples so don't bother clipping them. */
+                        patest1Callback,
+                        &data );
+    if( err != paNoError ) goto done;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto done;
+    
+    printf( "Press any key to end.\n" ); fflush(stdout);
+         
+    getc( stdin ); /* wait for input before exiting */
+
+    err = Pa_AbortStream( stream );
+    if( err != paNoError ) goto done;
+    
+    printf( "Waiting for stream to complete...\n" );
+
+    /* sleep until playback has finished */
+    while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(1000);
+    if( err < 0 ) goto done;
+
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto done;
+
+done:
+    err = Pa_RemoveDevicesChangedCallback(patest1DevicesChangedCallback, NULL);
+
+    Pa_Terminate();
+
+    if( err != paNoError )
+    {
+        fprintf( stderr, "An error occured while using portaudio\n" );
+        if( err == paUnanticipatedHostError )
+        {
+            fprintf( stderr, " unanticipated host error.\n");
+            herr = Pa_GetLastHostErrorInfo();
+            if (herr)
+            {
+                fprintf( stderr, " Error number: %ld\n", herr->errorCode );
+                if (herr->errorText)
+                    fprintf( stderr, " Error text: %s\n", herr->errorText );
+            }
+            else
+                fprintf( stderr, " Pa_GetLastHostErrorInfo() failed!\n" );
+        }
+        else
+        {
+            fprintf( stderr, " Error number: %d\n", err );
+            fprintf( stderr, " Error text: %s\n", Pa_GetErrorText( err ) );
+        }
+
+        err = 1;          /* Always return 0 or 1, but no other return codes. */
+    }
+
+    printf( "bye\n" );
+
+    return err;
+}

Property changes on: test/patest2.c
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + author id date revision
Name: svn:eol-style
   + native

Index: Makefile.in
===================================================================
--- Makefile.in	(revision 1296)
+++ Makefile.in	(working copy)
@@ -59,6 +59,7 @@
 	bin/paqa_devs \
 	bin/paqa_errs \
 	bin/patest1 \
+	bin/patest2 \
 	bin/patest_buffer \
 	bin/patest_callbackstop \
 	bin/patest_clip \
Index: include/portaudio.h
===================================================================
--- include/portaudio.h	(revision 1296)
+++ include/portaudio.h	(working copy)
@@ -356,10 +356,97 @@
 */
 const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void );
 
+/* Device enumeration and capabilities */
 
+/** Flags used to indicate which device states have changed.
+ 
+    @see paDevicesListAboutToBeChanged, paDevicesListChanged,
+    paDefaultInputDeviceChanged, paDefaultOutputDeviceChanged
+*/
+typedef unsigned long PaDevicesChangedFlags;
 
-/* Device enumeration and capabilities */
+/** Indicates that the list of devices is about to be changed.
 
+ This is sent before the list of devices is changed.  All PaDeviceInfo 
+ structures retrieved prior to this notification are about to be freed.
+
+ @see PaDevicesChangedFlags
+*/
+#define paDevicesListAboutToBeChanged   ((PaDevicesChangedFlags) 0x00000001)
+
+/** Indicates that the list of devices has changed.
+
+ This is sent after the PortAudio list of devices has changed.
+ All PaDeviceInfo structures retrieved prior to this notification are now 
+ invalid.
+
+ @see PaDevicesChangedFlags
+*/
+#define paDevicesListChanged            ((PaDevicesChangedFlags) 0x00000002)
+
+/** Indicates that the systems default input device has changed.
+
+ @see PaDevicesChangedFlags
+*/
+#define paDefaultInputDeviceChanged     ((PaDevicesChangedFlags) 0x00000004)
+
+/** Indicates that the systems default output device has changed.
+
+ @see PaDevicesChangedFlags
+*/
+#define paDefaultOutputDeviceChanged    ((PaDevicesChangedFlags) 0x00000008)
+
+/** Functions of type PaDevicesChangedCallback are implemented by PortAudio
+ clients. They can be registered using the Pa_SetDevicesChangedCallback
+ function. Once registered they are called when the state of installed devices
+ changes.
+
+ @param flags The flags indicate what aspects of the known devices has
+ changed.
+ @param userData The userData parameter supplied t Pa_SetDevicesChangedCallback().
+
+ @see Pa_AddDevicesChangedCallback, Pa_RemoveDevicesChangedCallback, 
+ PaDevicesChangedFlags
+ */
+typedef void PaDevicesChangedCallback( PaDevicesChangedFlags flags, 
+                                       void *userData );
+
+/** Add a devices changed callback function which will be called when the 
+ the state of installed devices changes.
+
+ @note If no callback is registered the traditional behavior of ignoring devices
+ changes is maintained. However, if a callback is registered then PortAudio 
+ will automatically rescan the set of available devices when a change is detected.
+ This is done in an attempt to maintain backwards compatibility for older 
+ clients.
+
+ @param userData A client supplied pointer which is passed to the stream callback
+ function. It could for example, contain a pointer to instance data necessary for
+ processing device change notifications.
+*/
+PaError Pa_AddDevicesChangedCallback( PaDevicesChangedCallback* devicesCallback,
+                                      void *userData );
+
+/** Remove a devices changed callback function which will be called when the 
+ the state of installed devices changes.
+
+ @param userData A client supplied pointer which is passed to the stream callback
+ function. It could for example, contain a pointer to instance data necessary for
+ processing device change notifications.
+*/
+PaError Pa_RemoveDevicesChangedCallback( PaDevicesChangedCallback* devicesCallback,
+                                      void *userData );
+
+/** Rescans the system in order to detect new devices.
+
+ It will call the registered PaDevicesChangedCallback with paDevicesListChanged
+ when it is done.
+
+ @note This will free all PaDeviceInfo structures that may have been returned
+ previously.
+*/
+PaError Pa_RescanDevices( void );
+
 /** Retrieve the number of available devices. The number of available devices
  may be zero.
 
@@ -470,7 +557,8 @@
 
  @note PortAudio manages the memory referenced by the returned pointer,
  the client must not manipulate or free the memory. The pointer is only
- guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate().
+ guaranteed to be valid between calls to Pa_Initialize(), Pa_RescanDevices(),
+ and Pa_Terminate().
 
  @see PaDeviceInfo, PaDeviceIndex
 */
Index: src/hostapi/alsa/pa_linux_alsa.c
===================================================================
--- src/hostapi/alsa/pa_linux_alsa.c	(revision 1296)
+++ src/hostapi/alsa/pa_linux_alsa.c	(working copy)
@@ -254,6 +254,7 @@
     (*hostApi)->info.name = "ALSA";
 
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = NULL;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
Index: src/hostapi/oss/pa_unix_oss.c
===================================================================
--- src/hostapi/oss/pa_unix_oss.c	(revision 1296)
+++ src/hostapi/oss/pa_unix_oss.c	(working copy)
@@ -250,6 +250,7 @@
     (*hostApi)->info.type = paOSS;
     (*hostApi)->info.name = "OSS";
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = NULL;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
Index: src/hostapi/wasapi/pa_win_wasapi.cpp
===================================================================
--- src/hostapi/wasapi/pa_win_wasapi.cpp	(revision 1296)
+++ src/hostapi/wasapi/pa_win_wasapi.cpp	(working copy)
@@ -635,6 +635,7 @@
     spEndpoints->Release();
 
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = NULL;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
@@ -1908,4 +1909,4 @@
 				stream->outVol->SetMute(FALSE, NULL);
 				bFirst = false;
 			}
-#endif
\ No newline at end of file
+#endif
Index: src/hostapi/wdmks/pa_win_wdmks.c
===================================================================
--- src/hostapi/wdmks/pa_win_wdmks.c	(revision 1296)
+++ src/hostapi/wdmks/pa_win_wdmks.c	(working copy)
@@ -1876,6 +1876,7 @@
     (*hostApi)->info.deviceCount = deviceCount;
 
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = NULL;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
@@ -3280,4 +3281,4 @@
     /* IMPLEMENT ME, see portaudio.h for required behavior*/
     PA_LOGL_;
     return 0;
-}
\ No newline at end of file
+}
Index: src/hostapi/wmme/pa_win_wmme.c
===================================================================
--- src/hostapi/wmme/pa_win_wmme.c	(revision 1296)
+++ src/hostapi/wmme/pa_win_wmme.c	(working copy)
@@ -1040,6 +1040,7 @@
     InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
 
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = NULL;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
Index: src/hostapi/jack/pa_jack.c
===================================================================
--- src/hostapi/jack/pa_jack.c	(revision 1296)
+++ src/hostapi/jack/pa_jack.c	(working copy)
@@ -746,6 +746,7 @@
     /* Register functions */
 
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = NULL;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
Index: src/hostapi/coreaudio/pa_mac_core.c
===================================================================
--- src/hostapi/coreaudio/pa_mac_core.c	(revision 1296)
+++ src/hostapi/coreaudio/pa_mac_core.c	(working copy)
@@ -70,6 +70,7 @@
 #include "pa_mac_core.h"
 #include "pa_mac_core_utilities.h"
 #include "pa_mac_core_blocking.h"
+#include "pa_debugprint.h"
 
 
 #ifdef __cplusplus
@@ -227,6 +228,7 @@
 #define RING_BUFFER_ADVANCE_DENOMINATOR (4)
 
 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError RescanDevices( struct PaUtilHostApiRepresentation *hostApi );
 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
                                   const PaStreamParameters *inputParameters,
                                   const PaStreamParameters *outputParameters,
@@ -324,47 +326,13 @@
       sfc( stream->streamRepresentation.userData );
 }
 
-
-/*currently, this is only used in initialization, but it might be modified
-  to be used when the list of devices changes.*/
-static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
+/* This is used during initialization, rescan, and default device changes to 
+ gather information about the current default input device. */
+static void gatherDefaultInputDevice(PaMacAUHAL *auhalHostApi)
 {
-    UInt32 size;
-    UInt32 propsize;
-    VVDBUG(("gatherDeviceInfo()\n"));
-    /* -- free any previous allocations -- */
-    if( auhalHostApi->devIds )
-        PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds);
-    auhalHostApi->devIds = NULL;
-
-    /* -- figure out how many devices there are -- */
-    AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
-                                  &propsize,
-                                  NULL );
-    auhalHostApi->devCount = propsize / sizeof( AudioDeviceID );
-
-    VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) );
-
-    /* -- copy the device IDs -- */
-    auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory(
-                             auhalHostApi->allocations,
-                             propsize );
-    if( !auhalHostApi->devIds )
-        return paInsufficientMemory;
-    AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
-                                  &propsize,
-                                  auhalHostApi->devIds );
-#ifdef MAC_CORE_VERBOSE_DEBUG
-    {
-       int i;
-       for( i=0; i<auhalHostApi->devCount; ++i )
-          printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] );
-    }
-#endif
-
-    size = sizeof(AudioDeviceID);
+    UInt32 size = sizeof(AudioDeviceID);
+    VVDBUG(("gatherDefaultInputDevice()\n"));
     auhalHostApi->defaultIn  = kAudioDeviceUnknown;
-    auhalHostApi->defaultOut = kAudioDeviceUnknown;
 
     /* determine the default device. */
     /* I am not sure how these calls to AudioHardwareGetProperty()
@@ -385,8 +353,19 @@
                 auhalHostApi->defaultIn = auhalHostApi->devIds[i];
                 break;
              }
-       }
+      }
     }   
+    VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn  ) );
+}
+
+/* This is used during initialization, rescan, and default device changes to 
+ gather information about the current default output device. */
+static void gatherDefaultOutputDevice(PaMacAUHAL *auhalHostApi)
+{
+    UInt32 size = sizeof(AudioDeviceID);
+    VVDBUG(("gatherDefaultOutputDevice()\n"));
+    auhalHostApi->defaultOut = kAudioDeviceUnknown;
+
     if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
                      &size,
                      &auhalHostApi->defaultOut) ) {
@@ -404,13 +383,119 @@
              }
        }
     }   
-
-    VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn  ) );
     VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) );
+}
 
+/* This is used during initialization and rescan to gather information about 
+ devices. */
+static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
+{
+    UInt32 size;
+    UInt32 propsize;
+    VVDBUG(("gatherDeviceInfo()\n"));
+    /* -- free any previous allocations -- */
+    if( auhalHostApi->devIds )
+        PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds);
+    auhalHostApi->devIds = NULL;
+
+    /* -- figure out how many devices there are -- */
+    AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
+                                  &propsize,
+                                  NULL );
+    auhalHostApi->devCount = propsize / sizeof( AudioDeviceID );
+
+    VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) );
+
+    /* -- copy the device IDs -- */
+    auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory(
+                             auhalHostApi->allocations,
+                             propsize );
+    if( !auhalHostApi->devIds )
+        return paInsufficientMemory;
+    AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
+                                  &propsize,
+                                  auhalHostApi->devIds );
+#ifdef MAC_CORE_VERBOSE_DEBUG
+    {
+       int i;
+       for( i=0; i<auhalHostApi->devCount; ++i )
+          printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] );
+    }
+#endif
+
+    gatherDefaultInputDevice(auhalHostApi);
+    gatherDefaultOutputDevice(auhalHostApi);
+
     return paNoError;
 }
 
+/* Callback for audio hardware property changes. */
+static OSStatus audioPropertyCallback(AudioHardwarePropertyID inPropertyID, 
+                                      void *refCon)
+{
+    struct PaUtilHostApiRepresentation *hostApi = 
+        (struct PaUtilHostApiRepresentation *)refCon;
+    PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
+    int i;
+    switch (inPropertyID)
+    {
+        /*
+         * These are the other types of notifications we might receive, however, they are beyond
+         * the scope of this sample and we ignore them.
+         **/
+    case kAudioHardwarePropertyDefaultInputDevice:
+        PA_DEBUG(("audioPropertyCallback: default input device changed\n"));
+
+        /* default to no device, in case we don't find it in the devIds. */
+        hostApi->info.defaultInputDevice = paNoDevice;
+        
+        gatherDefaultInputDevice(auhalHostApi);
+        
+        /* attempt to find the device amongst the known devIds. */
+        for( i=0; i < auhalHostApi->devCount; ++i )
+        {
+            if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn)
+                hostApi->info.defaultInputDevice = i;
+        }
+
+        PaUtil_DevicesChanged( paDefaultInputDeviceChanged );
+        break;
+        
+    case kAudioHardwarePropertyDefaultOutputDevice:
+        PA_DEBUG(("audioPropertyCallback: default output device changed\n"));
+
+        /* default to no device, in case we don't find it in the devIds. */
+        hostApi->info.defaultOutputDevice = paNoDevice;
+
+        gatherDefaultOutputDevice(auhalHostApi);
+
+        /* attempt to find the device amongst the known devIds. */
+        for( i=0; i < auhalHostApi->devCount; ++i )
+        {
+            if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut)
+                hostApi->info.defaultOutputDevice = i;
+        }
+
+        PaUtil_DevicesChanged( paDefaultOutputDeviceChanged );
+        break;
+        
+    case kAudioHardwarePropertyDefaultSystemOutputDevice:
+        PA_DEBUG(("audioPropertyCallback: default system output device changed\n"));
+        break;
+        
+    case kAudioHardwarePropertyDevices:
+        PA_DEBUG(("audioPropertyCallback: device list changed\n"));
+        PaUtil_DevicesChanged( paUtilHardwareDevicesChanged );
+        break;
+        
+    default:
+        PA_DEBUG(("audioPropertyCallback: unknown message id=%08lx\n", inPropertyID));
+        break;
+    }
+    
+    return noErr;
+}
+
 static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
                                PaDeviceInfo *deviceInfo,
                                AudioDeviceID macCoreDeviceId,
@@ -626,6 +711,7 @@
     }
 
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = RescanDevices;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
@@ -647,6 +733,14 @@
                                       GetStreamReadAvailable,
                                       GetStreamWriteAvailable );
 
+    /* register the audio hardware change listener */
+    AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, 
+                                     audioPropertyCallback, *hostApi);
+    AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultInputDevice, 
+                                     audioPropertyCallback, *hostApi);
+    AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultOutputDevice, 
+                                     audioPropertyCallback, *hostApi);
+    
     return result;
 
 error:
@@ -676,6 +770,14 @@
         TODO: Double check that everything is handled by alloc group
     */
 
+    /* unregister the audio property listener */
+    AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, 
+                                        audioPropertyCallback);
+    AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDefaultInputDevice, 
+                                        audioPropertyCallback);
+    AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDefaultOutputDevice, 
+                                        audioPropertyCallback);
+    
     if( auhalHostApi->allocations )
     {
         PaUtil_FreeAllAllocations( auhalHostApi->allocations );
@@ -685,7 +787,89 @@
     PaUtil_FreeMemory( auhalHostApi );
 }
 
+static PaError RescanDevices( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaError result = paNoError;
+    int i;
+    PaDeviceInfo *deviceInfoArray;
+    PaHostApiIndex hostApiIndex = Pa_HostApiTypeIdToHostApiIndex(hostApi->info.type);
+    PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
 
+    VVDBUG(("RescanDevices()\n"));
+
+    auhalHostApi->devCount = 0;
+    hostApi->info.defaultInputDevice = paNoDevice;
+    hostApi->info.defaultOutputDevice = paNoDevice;
+    hostApi->info.deviceCount = 0;  
+
+    if ( hostApi->deviceInfos )
+        PaUtil_GroupFreeMemory( auhalHostApi->allocations, hostApi->deviceInfos );
+
+    /* get the info we need about the devices */
+    result = gatherDeviceInfo( auhalHostApi );
+    if( result != paNoError )
+       goto error;
+
+    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--;
+            }
+        }
+    }
+
+    return result;
+    
+error:
+    hostApi->info.deviceCount = 0;  
+    if( auhalHostApi )
+    {
+        if( auhalHostApi->allocations && hostApi->deviceInfos )
+        {
+            PaUtil_GroupFreeMemory( auhalHostApi->allocations, hostApi->deviceInfos );
+            hostApi->deviceInfos = 0;
+        }
+    }
+    return result;
+}
+
 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
                                   const PaStreamParameters *inputParameters,
                                   const PaStreamParameters *outputParameters,
Index: src/hostapi/asio/pa_asio.cpp
===================================================================
--- src/hostapi/asio/pa_asio.cpp	(revision 1296)
+++ src/hostapi/asio/pa_asio.cpp	(working copy)
@@ -1286,6 +1286,7 @@
 
 
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = NULL;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
Index: src/hostapi/dsound/pa_win_ds.c
===================================================================
--- src/hostapi/dsound/pa_win_ds.c	(revision 1296)
+++ src/hostapi/dsound/pa_win_ds.c	(working copy)
@@ -1134,6 +1134,7 @@
 
     
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = NULL;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
Index: src/hostapi/asihpi/pa_linux_asihpi.c
===================================================================
--- src/hostapi/asihpi/pa_linux_asihpi.c	(revision 1296)
+++ src/hostapi/asihpi/pa_linux_asihpi.c	(working copy)
@@ -780,6 +780,7 @@
     PA_ENSURE_( PaAsiHpi_BuildDeviceList( hpiHostApi ) );
 
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = NULL;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
Index: src/common/pa_util.h
===================================================================
--- src/common/pa_util.h	(revision 1296)
+++ src/common/pa_util.h	(working copy)
@@ -151,8 +151,23 @@
 
 /* void Pa_Sleep( long msec );  must also be implemented in per-platform .c file */
 
+/** Indicates that the hardware list of devices has changed.
 
+ This is set when the hardware devices have changed.
 
+ It's value matches paDevicesListAboutToBeChanged.
+
+ @see PaDevicesChangedFlags, paDevicesListAboutToBeChanged
+*/
+#define paUtilHardwareDevicesChanged   ((PaDevicesChangedFlags) 0x00000001)
+
+/** Called by host api's when they detect that devices changed.
+
+ @see PaDevicesChangedFlags
+*/
+void PaUtil_DevicesChanged( PaDevicesChangedFlags flag );
+ 
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: src/common/pa_skeleton.c
===================================================================
--- src/common/pa_skeleton.c	(revision 1296)
+++ src/common/pa_skeleton.c	(working copy)
@@ -74,6 +74,7 @@
 
 
 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError RescanDevices( struct PaUtilHostApiRepresentation *hostApi );
 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
                                   const PaStreamParameters *inputParameters,
                                   const PaStreamParameters *outputParameters,
@@ -205,6 +206,7 @@
     }
 
     (*hostApi)->Terminate = Terminate;
+    (*hostApi)->RescanDevices = RescanDevices;
     (*hostApi)->OpenStream = OpenStream;
     (*hostApi)->IsFormatSupported = IsFormatSupported;
 
@@ -254,7 +256,91 @@
     PaUtil_FreeMemory( skeletonHostApi );
 }
 
+static PaError RescanDevices( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaError result = paNoError;
+    int i, deviceCount;
+    PaDeviceInfo *deviceInfoArray;
+    PaHostApiIndex hostApiIndex = Pa_HostApiTypeIdToHostApiIndex(hostApi->info.type);
+		PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation *)hostApi;
 
+		deviceCount = 0;
+    hostApi->info.defaultInputDevice = paNoDevice;
+    hostApi->info.defaultOutputDevice = paNoDevice;
+    hostApi->info.deviceCount = 0;  
+
+    if ( hostApi->deviceInfos )
+        PaUtil_GroupFreeMemory( skeletonHostApi->allocations, hostApi->deviceInfos );
+
+		deviceCount = 0; /* IMPLEMENT ME */
+    
+    if( deviceCount > 0 )
+    {
+        hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+        if( !hostApi->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all device info structs in a contiguous block */
+        deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+                skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( i=0; i < deviceCount; ++i )
+        {
+            PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+            deviceInfo->structVersion = 2;
+            deviceInfo->hostApi = hostApiIndex;
+            deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg:
+                deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 );
+                if( !deviceName )
+                {
+                    result = paInsufficientMemory;
+                    goto error;
+                }
+                strcpy( deviceName, srcName );
+                deviceInfo->name = deviceName;
+            */
+
+            deviceInfo->maxInputChannels = 0;  /* IMPLEMENT ME */
+            deviceInfo->maxOutputChannels = 0;  /* IMPLEMENT ME */
+            
+            deviceInfo->defaultLowInputLatency = 0.;  /* IMPLEMENT ME */
+            deviceInfo->defaultLowOutputLatency = 0.;  /* IMPLEMENT ME */
+            deviceInfo->defaultHighInputLatency = 0.;  /* IMPLEMENT ME */
+            deviceInfo->defaultHighOutputLatency = 0.;  /* IMPLEMENT ME */  
+
+            deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */
+            
+            hostApi->deviceInfos[i] = deviceInfo;
+            ++hostApi->info.deviceCount;
+        }
+    }
+
+		return result;
+    
+error:
+
+    hostApi->info.deviceCount = 0;  
+    if( skeletonHostApi )
+    {
+        if( skeletonHostApi->allocations && hostApi->deviceInfos )
+        {
+            PaUtil_GroupFreeMemory( skeletonHostApi->allocations, hostApi->deviceInfos );
+            hostApi->deviceInfos = 0;
+        }
+    }
+
+    return result;
+}
+
 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
                                   const PaStreamParameters *inputParameters,
                                   const PaStreamParameters *outputParameters,
Index: src/common/pa_front.c
===================================================================
--- src/common/pa_front.c	(revision 1296)
+++ src/common/pa_front.c	(working copy)
@@ -89,9 +89,6 @@
 #define PA_VERSION_  1899
 #define PA_VERSION_TEXT_ "PortAudio V19-devel (built " __DATE__  ")"
 
-
-
-
 int Pa_GetVersion( void )
 {
     return PA_VERSION_;
@@ -121,8 +118,18 @@
     strncpy( lastHostErrorText_, errorText, PA_LAST_HOST_ERROR_TEXT_LENGTH_ );
 }
 
+/* Used to store the list of devices changed callbacks */
+typedef struct DevicesChangedCallbackStorage
+{
+    PaDevicesChangedCallback* devicesCallback;
+    void *userData;
+    struct DevicesChangedCallbackStorage* nextCallback;
+} DevicesChangedCallbackStorage;
 
+/* The linked list of devices changed callbacks */
+static DevicesChangedCallbackStorage* firstDevicesChangedCallback = NULL;
 
+
 static PaUtilHostApiRepresentation **hostApis_ = 0;
 static int hostApisCount_ = 0;
 static int initializationCount_ = 0;
@@ -406,10 +413,10 @@
     case paCanNotReadFromAnOutputOnlyStream:    result = "Can't read from an output only stream"; break;
     case paCanNotWriteToAnInputOnlyStream:      result = "Can't write to an input only stream"; break;
     default:                         
-		if( errorCode > 0 )
-			result = "Invalid error code (value greater than zero)"; 
+        if( errorCode > 0 )
+            result = "Invalid error code (value greater than zero)"; 
         else
-			result = "Invalid error code"; 
+            result = "Invalid error code"; 
         break;
     }
     return result;
@@ -628,6 +635,150 @@
 }
 
 
+PaError Pa_AddDevicesChangedCallback( PaDevicesChangedCallback* devicesCallback,
+                                      void *userData )
+{
+    if ( !devicesCallback )
+        return paNullCallback;
+
+    DevicesChangedCallbackStorage* callback = 
+        (DevicesChangedCallbackStorage*)PaUtil_AllocateMemory( sizeof(DevicesChangedCallbackStorage) );
+    if ( !callback )
+        return paInsufficientMemory;
+
+    callback->devicesCallback = devicesCallback;
+    callback->userData = userData;
+
+    callback->nextCallback = firstDevicesChangedCallback;
+    firstDevicesChangedCallback = callback;
+
+    return paNoError;
+}
+
+
+PaError Pa_RemoveDevicesChangedCallback( PaDevicesChangedCallback* devicesCallback,
+                                         void *userData )
+{
+    DevicesChangedCallbackStorage* previous = NULL;
+    DevicesChangedCallbackStorage* current = firstDevicesChangedCallback;
+
+    while ( current != NULL )
+    {
+        if ( ( current->devicesCallback == devicesCallback ) && 
+             ( current->userData == userData ) )
+        {
+            if ( previous == NULL )
+            {
+                firstDevicesChangedCallback = current->nextCallback;
+            }
+            else
+            {
+                previous->nextCallback = current->nextCallback;
+            }
+            PaUtil_FreeMemory( current );
+            return paNoError;
+        }
+        else
+        {
+            previous = current;
+            current = current->nextCallback;
+        }
+    }
+
+    return paNoError;
+}
+
+void PaUtil_DevicesChanged( PaDevicesChangedFlags flags )
+{
+    PA_DEBUG(( "PaUtil_DevicesChanged(%08x)\n", flags));
+
+    // if no callbacks are registered, then nothing to do
+    if ( !firstDevicesChangedCallback )
+        return;
+
+    /* if the hardware has changed */
+    if ( ( flags & paUtilHardwareDevicesChanged ) != 0 )
+    {
+        /* Rescan devices */
+        Pa_RescanDevices();
+        return;
+    }
+
+    /* call all registered callbacks */
+    DevicesChangedCallbackStorage* current = firstDevicesChangedCallback;
+    while ( current != NULL )
+    {
+        (*current->devicesCallback)( flags, current->userData );
+        current = current->nextCallback;
+    }
+}
+
+PaError Pa_RescanDevices( void )
+{
+    PaError result;
+    int i, baseDeviceIndex;
+
+    PA_LOGAPI_ENTER( "Pa_RescanDevices" );
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        /* call all registered callbacks to let them know the device list 
+           has changed. */
+        DevicesChangedCallbackStorage* current = firstDevicesChangedCallback;
+        while ( current != NULL )
+        {
+            (*current->devicesCallback)( paDevicesListAboutToBeChanged, 
+                                         current->userData );
+            current = current->nextCallback;
+        }
+
+        deviceCount_ = 0;
+        baseDeviceIndex = 0;
+
+        for (i = 0; i < hostApisCount_; i++)
+        {
+            PaUtilHostApiRepresentation* hostApi = hostApis_[i];
+            PA_DEBUG(( "before hostApis_[%d]->RescanDevices=%p.\n",i, hostApi->RescanDevices));
+
+            if ( hostApi->RescanDevices )
+                result = hostApi->RescanDevices( hostApi );
+                
+            PA_DEBUG(( "after hostApis_[%d].\n",i));
+            
+            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;
+        }
+
+        /* call all registered callbacks to let them know the device list 
+           has changed. */
+        current = firstDevicesChangedCallback;
+        while ( current != NULL )
+        {
+            (*current->devicesCallback)( paDevicesListChanged, current->userData );
+            current = current->nextCallback;
+        }
+    }
+
+    PA_LOGAPI_EXIT_PAERROR( "Pa_RescanDevices", result );
+
+    return result;
+}
+
+
 PaDeviceIndex Pa_GetDeviceCount( void )
 {
     PaDeviceIndex result;
Index: src/common/pa_hostapi.h
===================================================================
--- src/common/pa_hostapi.h	(revision 1296)
+++ src/common/pa_hostapi.h	(working copy)
@@ -109,6 +109,21 @@
     void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi );
 
     /**
+        (*RescanDevices)() is guaranteed to be called with a valid <hostApi>
+        parameter, which was previously returned from the same implementation's
+        initializer.
+        
+        If set to 0 the portaudio implementation will skip rescanning this host 
+        api.
+        
+        The host api implementation is allowed to modify the 
+        PaHostApiInfo::deviceCount, and the deviceInfos to reflect the new set
+        of devices.  If an error occurs during processing, the host api 
+        implementation MUST ensure that they are left in a consistant state.
+    */
+    PaError (*RescanDevices)( struct PaUtilHostApiRepresentation *hostApi );
+
+    /**
         The inputParameters and outputParameters pointers should not be saved
         as they will not remain valid after OpenStream is called.
 
