diff --git a/build/msvc/portaudio.def b/build/msvc/portaudio.def
index bdaa8ee..2534daf 100644
--- a/build/msvc/portaudio.def
+++ b/build/msvc/portaudio.def
@@ -3,7 +3,7 @@ EXPORTS
;
Pa_GetVersion @1
Pa_GetVersionText @2
-Pa_GetErrorText @3
+Pa_GetErrorText @3
Pa_Initialize @4
Pa_Terminate @5
Pa_GetHostApiCount @6
@@ -35,6 +35,8 @@ Pa_GetStreamReadAvailable @31
Pa_GetStreamWriteAvailable @32
Pa_GetSampleSize @33
Pa_Sleep @34
+Pa_UpdateAvailableDeviceList @35
+Pa_SetDevicesChangedCallback @36
PaAsio_GetAvailableBufferSizes @50
PaAsio_ShowControlPanel @51
PaUtil_InitializeX86PlainConverters @52
diff --git a/build/msvc/portaudio.vcproj b/build/msvc/portaudio.vcproj
index e5a648b..e9f3078 100644
--- a/build/msvc/portaudio.vcproj
+++ b/build/msvc/portaudio.vcproj
@@ -1827,6 +1827,10 @@
RelativePath="..\..\src\os\win\pa_win_wdmks_utils.c"
>
+
+
diff --git a/include/portaudio.h b/include/portaudio.h
index 3bb3b31..09d6aa8 100644
--- a/include/portaudio.h
+++ b/include/portaudio.h
@@ -155,9 +155,17 @@ const char *Pa_GetErrorText( PaError errorCode );
Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not
required to be fully nested.
- Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
+ If Pa_Initialize() returns an error code, Pa_Terminate() should
NOT be called.
+ @note The device list returned by Pa_GetDeviceCount() et al. is frozen
+ at the time Pa_Initialize() is called, and not updated dynamically, even if
+ hardware devices (e.g. USB or firewire devices) are connected or disconnected
+ during the course of the program's lifetime. If it is required to re-scan the
+ list of devices, one must call Pa_UpdateAvailableDeviceList() or, completely
+ uninitialize PortAudio using Pa_Terminate(), and then reinitialize it by calling
+ Pa_Initialize().
+
@return paNoError if successful, otherwise an error code indicating the cause
of failure.
@@ -431,6 +439,20 @@ PaDeviceIndex Pa_GetDefaultInputDevice( void );
*/
PaDeviceIndex Pa_GetDefaultOutputDevice( void );
+/** Update the list of available devices to match currently available hardware devices.
+
+ PortAudio's list of available devices is usually frozen at the time Pa_Initialize() is
+ called. Pa_UpdateAvailableDeviceList() may be called to refresh PortAudio's list
+ of available devices at any time while PortAudio is initialized.
+
+ @note Open streams will not be affected by calls to this function, but
+ any previously returned PaDeviceInfo pointers are invalidated once this
+ function is called.
+
+ @return an error code indicating whether the device refresh was successful.
+ */
+PaError Pa_UpdateAvailableDeviceList( void );
+
/** The type used to represent monotonic time in seconds. PaTime is
used for the fields of the PaStreamCallbackTimeInfo argument to the
@@ -1201,6 +1223,13 @@ PaError Pa_GetSampleSize( PaSampleFormat format );
void Pa_Sleep( long msec );
+/* FIXME: document these and put them with other device functions */
+typedef void PaDevicesChangedCallback( void *userData );
+
+PaError Pa_SetDevicesChangedCallback( void *userData, PaStreamFinishedCallback* devicesChangedCallback );
+
+
+
#ifdef __cplusplus
}
diff --git a/src/common/pa_front.c b/src/common/pa_front.c
index 0632710..f530bd9 100644
--- a/src/common/pa_front.c
+++ b/src/common/pa_front.c
@@ -81,6 +81,10 @@
#include "pa_svnrevision.h"
#endif
+extern void PaUtil_InitializeHotPlug();
+extern void PaUtil_TerminateHotPlug();
+
+
/**
* This is incremented if we make incompatible API changes.
* This version scheme is based loosely on http://semver.org/
@@ -161,6 +165,8 @@ static int deviceCount_ = 0;
PaUtilStreamRepresentation *firstOpenStream_ = NULL;
+PaDevicesChangedCallback* devicesChangedCallback_ = NULL;
+void* devicesChangedCallbackUserData_ = NULL;
#define PA_IS_INITIALISED_ (initializationCount_ != 0)
@@ -371,6 +377,9 @@ PaError Pa_Initialize( void )
PaUtil_InitializeClock();
PaUtil_ResetTraceMessages();
+ /* Initialize hot plug here, so all its internal info is setup */
+ PaUtil_InitializeHotPlug();
+
result = InitializeHostApis();
if( result == paNoError )
++initializationCount_;
@@ -396,6 +405,8 @@ PaError Pa_Terminate( void )
TerminateHostApis();
+ PaUtil_TerminateHotPlug();
+
PaUtil_DumpTraceMessages();
}
result = paNoError;
@@ -744,6 +755,117 @@ PaDeviceIndex Pa_GetDefaultOutputDevice( void )
}
+PaError Pa_UpdateAvailableDeviceList( void )
+{
+ PaError result = paNoError;
+ void **scanResults = NULL;
+ int *deviceCounts = NULL;
+ int i = 0;
+
+ PA_LOGAPI_ENTER( "Pa_UpdateAvailableDeviceList" );
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ goto done;
+ }
+
+ /* Allocate data structures used in 2-stage commit */
+ scanResults = (void **) PaUtil_AllocateMemory( sizeof(void*) * hostApisCount_ );
+ if( !scanResults )
+ {
+ result = paInsufficientMemory;
+ goto done;
+ }
+
+ deviceCounts = ( int * ) PaUtil_AllocateMemory( sizeof( int ) * hostApisCount_ );
+ if( !deviceCounts )
+ {
+ result = paInsufficientMemory;
+ goto done;
+ }
+
+ /* Phase 1: Perform a scan of new devices */
+ for( i = 0 ; i < hostApisCount_ ; ++i )
+ {
+ PaUtilHostApiRepresentation *hostApi = hostApis_[i];
+ if( hostApi->ScanDeviceInfos == NULL )
+ continue;
+
+ PA_DEBUG(( "Scanning new device list for host api %d.\n",i));
+ if( hostApi->ScanDeviceInfos( hostApi, i, &scanResults[ i ], &deviceCounts[ i ] ) != paNoError )
+ break;
+
+ }
+
+ /* Check the result of the scan operation */
+ if( i < hostApisCount_ )
+ {
+ /* If failure, rollback the scan changes back to original state */
+ int j = 0;
+ for( j = 0 ; j < i ; ++j )
+ {
+ PaUtilHostApiRepresentation *hostApi = hostApis_[j];
+ if( hostApi->DisposeDeviceInfos == NULL )
+ continue;
+
+ PA_DEBUG(( "Performing rollback for device list scan for host api %d.\n",i));
+ hostApi->DisposeDeviceInfos( hostApi, scanResults[ j ], deviceCounts[ j ] );
+ }
+ }
+ else
+ {
+ int baseDeviceIndex = 0;
+ deviceCount_ = 0;
+
+ /* Otherwise, commit the scan changes to each back-end */
+ for( i = 0 ; i < hostApisCount_ ; ++i )
+ {
+ PaUtilHostApiRepresentation *hostApi = hostApis_[i];
+ if( hostApi->CommitDeviceInfos == NULL )
+ {
+ /* 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(( "Committing device list scan for host api %d.\n",i));
+ if( hostApi->CommitDeviceInfos( hostApi, i, scanResults[ i ], deviceCounts[ i ] ) != paNoError )
+ {
+ PA_DEBUG(( "Committing failed (shouldn't happen) %d.\n",i));
+ result = paInternalError;
+ goto done;
+ }
+
+ 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;
+ }
+ }
+
+done:
+
+ if( scanResults )
+ PaUtil_FreeMemory( scanResults );
+
+ if( deviceCounts )
+ PaUtil_FreeMemory( deviceCounts );
+
+ return result;
+}
+
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
{
int hostSpecificDeviceIndex;
@@ -1807,3 +1929,29 @@ PaError Pa_GetSampleSize( PaSampleFormat format )
return (PaError) result;
}
+extern void PaUtil_LockHotPlug();
+extern void PaUtil_UnlockHotPlug();
+
+
+PaError Pa_SetDevicesChangedCallback( void *userData, PaStreamFinishedCallback* devicesChangedCallback )
+{
+ PaUtil_LockHotPlug();
+ devicesChangedCallback_ = devicesChangedCallback;
+ devicesChangedCallbackUserData_ = userData;
+ PaUtil_UnlockHotPlug();
+ return paNoError;
+}
+
+/* Called whenever a OS audio device change has been detected */
+void PaUtil_DevicesChanged(unsigned state, void* pData)
+{
+ (void)state;
+ (void)pData;
+ PaUtil_LockHotPlug();
+ if (devicesChangedCallback_)
+ {
+ (devicesChangedCallback_)(devicesChangedCallbackUserData_);
+ }
+ PaUtil_UnlockHotPlug();
+}
+
diff --git a/src/common/pa_hostapi.h b/src/common/pa_hostapi.h
index 54b527e..79e3e88 100644
--- a/src/common/pa_hostapi.h
+++ b/src/common/pa_hostapi.h
@@ -322,6 +322,37 @@ typedef struct PaUtilHostApiRepresentation {
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate );
+
+ /**
+ Collect current native device information and compute a new device count.
+ This method should not result in the host API making any changes to its
+ internal data structures, instead it is the first step in a two-stage commit
+ process which will be completed only if all host API scans return with no errors.
+
+ Some time later the caller will either (1) pass scanResults and newDeviceCount
+ to CommitDeviceInfos() to complete the transaction, or (2) pass scanResults
+ and newDeviceCount to DisposeDeviceInfos() to abort the transaction.
+ */
+ PaError (*ScanDeviceInfos)( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void **scanResults, int *newDeviceCount );
+
+ /**
+ Replace the previous device information with the device information
+ passed in as an argument via scanResults. This is the second/final stage of
+ the two-stage transaction required for updating the available device information.
+ This function should free any resources associated with the previous device
+ information.
+ @FIXME: this function should probably not return an error code as it should not be
+ permitted to fail.
+ */
+ PaError (*CommitDeviceInfos)( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *scanResults, int deviceCount );
+
+ /**
+ Free device information previously returned by ScanDeviceInfos(). This function
+ will be called in cases where the update available device info transaction
+ failed for some reason.
+ */
+ PaError (*DisposeDeviceInfos)( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount );
+
} PaUtilHostApiRepresentation;
diff --git a/src/hostapi/alsa/pa_linux_alsa.c b/src/hostapi/alsa/pa_linux_alsa.c
index f286591..dbe2d04 100644
--- a/src/hostapi/alsa/pa_linux_alsa.c
+++ b/src/hostapi/alsa/pa_linux_alsa.c
@@ -757,6 +757,9 @@ PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ (*hostApi)->ScanDeviceInfos = 0;
+ (*hostApi)->CommitDeviceInfos = 0;
+ (*hostApi)->DisposeDeviceInfos = 0;
/** If AlsaErrorHandler is to be used, do not forget to unregister callback pointer in
Terminate function.
diff --git a/src/hostapi/asihpi/pa_linux_asihpi.c b/src/hostapi/asihpi/pa_linux_asihpi.c
index f5a5290..ab76d3c 100644
--- a/src/hostapi/asihpi/pa_linux_asihpi.c
+++ b/src/hostapi/asihpi/pa_linux_asihpi.c
@@ -771,6 +771,9 @@ PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ (*hostApi)->ScanDeviceInfos = 0;
+ (*hostApi)->CommitDeviceInfos = 0;
+ (*hostApi)->DisposeDeviceInfos = 0;
PaUtil_InitializeStreamInterface( &hpiHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
diff --git a/src/hostapi/asio/pa_asio.cpp b/src/hostapi/asio/pa_asio.cpp
index f230d87..47c5761 100644
--- a/src/hostapi/asio/pa_asio.cpp
+++ b/src/hostapi/asio/pa_asio.cpp
@@ -1356,6 +1356,9 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ (*hostApi)->ScanDeviceInfos = 0;
+ (*hostApi)->CommitDeviceInfos = 0;
+ (*hostApi)->DisposeDeviceInfos = 0;
PaUtil_InitializeStreamInterface( &asioHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
diff --git a/src/hostapi/coreaudio/pa_mac_core.c b/src/hostapi/coreaudio/pa_mac_core.c
index 4b7b43c..4e35266 100644
--- a/src/hostapi/coreaudio/pa_mac_core.c
+++ b/src/hostapi/coreaudio/pa_mac_core.c
@@ -825,6 +825,9 @@ PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIn
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ (*hostApi)->ScanDeviceInfos = 0;
+ (*hostApi)->CommitDeviceInfos = 0;
+ (*hostApi)->DisposeDeviceInfos = 0;
PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface,
CloseStream, StartStream,
diff --git a/src/hostapi/dsound/pa_win_ds.c b/src/hostapi/dsound/pa_win_ds.c
index 35fac5f..0a40797 100644
--- a/src/hostapi/dsound/pa_win_ds.c
+++ b/src/hostapi/dsound/pa_win_ds.c
@@ -189,6 +189,9 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate );
+static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void **newDeviceInfos, int *newDeviceCount );
+static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *deviceInfos, int deviceCount );
+static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *deviceInfos, int deviceCount );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
@@ -312,6 +315,12 @@ typedef struct PaWinDsStream
} PaWinDsStream;
+typedef struct PaWinDsScanDeviceInfosResults{ /* used for tranferring device infos during scanning / rescanning */
+ PaDeviceInfo **deviceInfos;
+ PaDeviceIndex defaultInputDevice;
+ PaDeviceIndex defaultOutputDevice;
+} PaWinDsScanDeviceInfosResults;
+
/* Set minimal latency based on the current OS version.
* NT has higher latency.
@@ -455,7 +464,7 @@ typedef struct DSDeviceNameAndGUIDVector{
int count;
int free;
- DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
+ DSDeviceNameAndGUID *items; /* Allocated using LocalAlloc() */
} DSDeviceNameAndGUIDVector;
typedef struct DSDeviceNamesAndGUIDs{
@@ -762,38 +771,15 @@ static double defaultSampleRateSearchOrder_[] =
** The device will not be added to the device list if any errors are encountered.
*/
static PaError AddOutputDeviceInfoFromDirectSound(
- PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
+ PaWinDsDeviceInfo *winDsDeviceInfo, char *name, LPGUID lpGUID, char *pnpInterface )
{
- PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
- PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
HRESULT hr;
- LPDIRECTSOUND lpDirectSound;
+ LPDIRECTSOUND lpDirectSound = NULL;
DSCAPS caps;
- int deviceOK = TRUE;
PaError result = paNoError;
int i;
- /* Copy GUID to the device info structure. Set pointer. */
- if( lpGUID == NULL )
- {
- winDsDeviceInfo->lpGUID = NULL;
- }
- else
- {
- memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
- winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
- }
-
- if( lpGUID )
- {
- if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) ||
- IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) )
- {
- PA_DEBUG(("BLACKLISTED: %s \n",name));
- return paNoError;
- }
- }
/* Create a DirectSound object for the specified GUID
Note that using CoCreateInstance doesn't work on windows CE.
@@ -836,7 +822,8 @@ static PaError AddOutputDeviceInfoFromDirectSound(
lpGUID->Data4[6],
lpGUID->Data4[7]));
- deviceOK = FALSE;
+ result = paUnanticipatedHostError;
+ goto error;
}
else
{
@@ -847,7 +834,9 @@ static PaError AddOutputDeviceInfoFromDirectSound(
if( hr != DS_OK )
{
DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
+
+ result = paUnanticipatedHostError;
+ goto error;
}
else
{
@@ -856,153 +845,160 @@ static PaError AddOutputDeviceInfoFromDirectSound(
if( caps.dwFlags & DSCAPS_EMULDRIVER )
{
/* If WMME supported, then reject Emulated drivers because they are lousy. */
- deviceOK = FALSE;
+ result = paInvalidDevice;
+ goto error;
}
#endif
- if( deviceOK )
+ deviceInfo->maxInputChannels = 0;
+ winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
+
+ /* DS output capabilities only indicate supported number of channels
+ using two flags which indicate mono and/or stereo.
+ We assume that stereo devices may support more than 2 channels
+ (as is the case with 5.1 devices for example) and so
+ set deviceOutputChannelCountIsKnown to 0 (unknown).
+ In this case OpenStream will try to open the device
+ when the user requests more than 2 channels, rather than
+ returning an error.
+ */
+ if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
{
- deviceInfo->maxInputChannels = 0;
- winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
+ deviceInfo->maxOutputChannels = 2;
+ winDsDeviceInfo->deviceOutputChannelCountIsKnown = 0;
+ }
+ else
+ {
+ deviceInfo->maxOutputChannels = 1;
+ winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
+ }
- /* DS output capabilities only indicate supported number of channels
- using two flags which indicate mono and/or stereo.
- We assume that stereo devices may support more than 2 channels
- (as is the case with 5.1 devices for example) and so
- set deviceOutputChannelCountIsKnown to 0 (unknown).
- In this case OpenStream will try to open the device
- when the user requests more than 2 channels, rather than
- returning an error.
- */
- if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
- {
- deviceInfo->maxOutputChannels = 2;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 0;
- }
- else
- {
- deviceInfo->maxOutputChannels = 1;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
- }
-
- /* Guess channels count from speaker configuration. We do it only when
- pnpInterface is NULL or when PAWIN_USE_WDMKS_DEVICE_INFO is undefined.
- */
+ /* Guess channels count from speaker configuration. We do it only when
+ pnpInterface is NULL or when PAWIN_USE_WDMKS_DEVICE_INFO is undefined.
+ */
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( !pnpInterface )
+ if( !pnpInterface )
#endif
+ {
+ DWORD spkrcfg;
+ if( SUCCEEDED(IDirectSound_GetSpeakerConfig( lpDirectSound, &spkrcfg )) )
{
- DWORD spkrcfg;
- if( SUCCEEDED(IDirectSound_GetSpeakerConfig( lpDirectSound, &spkrcfg )) )
+ int count = 0;
+ switch (DSSPEAKER_CONFIG(spkrcfg))
{
- int count = 0;
- switch (DSSPEAKER_CONFIG(spkrcfg))
- {
- case DSSPEAKER_HEADPHONE: count = 2; break;
- case DSSPEAKER_MONO: count = 1; break;
- case DSSPEAKER_QUAD: count = 4; break;
- case DSSPEAKER_STEREO: count = 2; break;
- case DSSPEAKER_SURROUND: count = 4; break;
- case DSSPEAKER_5POINT1: count = 6; break;
- case DSSPEAKER_7POINT1: count = 8; break;
+ case DSSPEAKER_HEADPHONE: count = 2; break;
+ case DSSPEAKER_MONO: count = 1; break;
+ case DSSPEAKER_QUAD: count = 4; break;
+ case DSSPEAKER_STEREO: count = 2; break;
+ case DSSPEAKER_SURROUND: count = 4; break;
+ case DSSPEAKER_5POINT1: count = 6; break;
+ case DSSPEAKER_7POINT1: count = 8; break;
#ifndef DSSPEAKER_7POINT1_SURROUND
#define DSSPEAKER_7POINT1_SURROUND 0x00000008
#endif
- case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
+ case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
#ifndef DSSPEAKER_5POINT1_SURROUND
#define DSSPEAKER_5POINT1_SURROUND 0x00000009
#endif
- case DSSPEAKER_5POINT1_SURROUND: count = 6; break;
- }
- if( count )
- {
- deviceInfo->maxOutputChannels = count;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
- }
+ case DSSPEAKER_5POINT1_SURROUND: count = 6; break;
}
- }
-
-#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( pnpInterface )
- {
- int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0 );
- if( count > 0 )
+ if( count )
{
deviceInfo->maxOutputChannels = count;
winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
}
}
+ }
+
+#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
+ if( pnpInterface )
+ {
+ int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0 );
+ if( count > 0 )
+ {
+ deviceInfo->maxOutputChannels = count;
+ winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
+ }
+ }
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
- /* initialize defaultSampleRate */
+ /* initialize defaultSampleRate */
- if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
- {
- /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+ if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
+ {
+ /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
+ deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
- for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
+ for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
+ {
+ if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
+ && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
{
- if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
- && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
- {
- deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
- break;
- }
+ deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
+ break;
}
}
- else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
- {
- if( caps.dwMinSecondarySampleRate == 0 )
- {
- /*
- ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
- ** But it supports continuous sampling.
- ** So fake range of rates, and hope it really supports it.
- */
- deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
-
- DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
- }
- else
- {
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
- }
- }
- else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
- {
- /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
- ** But we know that they really support a range of rates!
- ** So when we see a ridiculous set of rates, assume it is a range.
- */
- deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
- DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
- }
- else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
-
- //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
- // dwFlags | DSCAPS_CONTINUOUSRATE
-
- deviceInfo->defaultLowInputLatency = 0.;
- deviceInfo->defaultHighInputLatency = 0.;
-
- deviceInfo->defaultLowOutputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
- deviceInfo->defaultHighOutputLatency = deviceInfo->defaultLowOutputLatency * 2;
}
+ else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
+ {
+ if( caps.dwMinSecondarySampleRate == 0 )
+ {
+ /*
+ ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
+ ** But it supports continuous sampling.
+ ** So fake range of rates, and hope it really supports it.
+ */
+ deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
+
+ DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
+ }
+ else
+ {
+ deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+ }
+ }
+ else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
+ {
+ /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
+ ** But we know that they really support a range of rates!
+ ** So when we see a ridiculous set of rates, assume it is a range.
+ */
+ deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
+ DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
+ }
+ else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+
+ //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
+ // dwFlags | DSCAPS_CONTINUOUSRATE
+
+ deviceInfo->defaultLowInputLatency = 0.;
+ deviceInfo->defaultHighInputLatency = 0.;
+
+ deviceInfo->defaultLowOutputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
+ deviceInfo->defaultHighOutputLatency = deviceInfo->defaultLowOutputLatency * 2;
}
IDirectSound_Release( lpDirectSound );
}
- if( deviceOK )
+ /* Copy GUID to the device info structure. Set pointer. */
+ if( lpGUID == NULL )
{
- deviceInfo->name = name;
-
- if( lpGUID == NULL )
- hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
-
- hostApi->info.deviceCount++;
+ winDsDeviceInfo->lpGUID = NULL;
}
+ else
+ {
+ memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+ winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+ }
+
+ deviceInfo->name = name;
+
+ return result;
+
+error:
+ if( lpDirectSound )
+ IDirectSound_Release( lpDirectSound );
return result;
}
@@ -1016,27 +1012,14 @@ static PaError AddOutputDeviceInfoFromDirectSound(
** The device will not be added to the device list if any errors are encountered.
*/
static PaError AddInputDeviceInfoFromDirectSoundCapture(
- PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
+ PaWinDsDeviceInfo *winDsDeviceInfo, char *name, LPGUID lpGUID, char *pnpInterface )
{
- PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
- PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
HRESULT hr;
- LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
+ LPDIRECTSOUNDCAPTURE lpDirectSoundCapture = NULL;
DSCCAPS caps;
- int deviceOK = TRUE;
PaError result = paNoError;
- /* Copy GUID to the device info structure. Set pointer. */
- if( lpGUID == NULL )
- {
- winDsDeviceInfo->lpGUID = NULL;
- }
- else
- {
- winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
- memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
- }
hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
@@ -1053,7 +1036,8 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
if( hr != DS_OK )
{
DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
+ result = paUnanticipatedHostError;
+ goto error;
}
else
{
@@ -1064,7 +1048,8 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
if( hr != DS_OK )
{
DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
+ result = paUnanticipatedHostError;
+ goto error;
}
else
{
@@ -1072,45 +1057,44 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
if( caps.dwFlags & DSCAPS_EMULDRIVER )
{
/* If WMME supported, then reject Emulated drivers because they are lousy. */
- deviceOK = FALSE;
+ result = paInvalidDevice;
+ goto error;
}
#endif
- if( deviceOK )
- {
- deviceInfo->maxInputChannels = caps.dwChannels;
- winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
+ deviceInfo->maxInputChannels = caps.dwChannels;
+ winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
- deviceInfo->maxOutputChannels = 0;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
+ deviceInfo->maxOutputChannels = 0;
+ winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( pnpInterface )
+ if( pnpInterface )
+ {
+ int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1 );
+ if( count > 0 )
{
- int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1 );
- if( count > 0 )
- {
- deviceInfo->maxInputChannels = count;
- winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
- }
+ deviceInfo->maxInputChannels = count;
+ winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
}
+ }
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
/* constants from a WINE patch by Francois Gouget, see:
- http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
+http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
- ---
- Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
- From: Francois Gouget
- To: Ross Bencina
- Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
+---
+Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
+From: Francois Gouget
+To: Ross Bencina
+Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
- [snip]
+[snip]
- I give you permission to use the patch below under the BSD license.
- http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
+I give you permission to use the patch below under the BSD license.
+http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
- [snip]
+[snip]
*/
#ifndef WAVE_FORMAT_48M08
#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
@@ -1123,59 +1107,67 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
#endif
- /* defaultSampleRate */
- if( caps.dwChannels == 2 )
- {
- if( caps.dwFormats & WAVE_FORMAT_4S16 )
- deviceInfo->defaultSampleRate = 44100.0;
- else if( caps.dwFormats & WAVE_FORMAT_48S16 )
- deviceInfo->defaultSampleRate = 48000.0;
- else if( caps.dwFormats & WAVE_FORMAT_2S16 )
- deviceInfo->defaultSampleRate = 22050.0;
- else if( caps.dwFormats & WAVE_FORMAT_1S16 )
- deviceInfo->defaultSampleRate = 11025.0;
- else if( caps.dwFormats & WAVE_FORMAT_96S16 )
- deviceInfo->defaultSampleRate = 96000.0;
- else
- deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
- }
- else if( caps.dwChannels == 1 )
- {
- if( caps.dwFormats & WAVE_FORMAT_4M16 )
- deviceInfo->defaultSampleRate = 44100.0;
- else if( caps.dwFormats & WAVE_FORMAT_48M16 )
- deviceInfo->defaultSampleRate = 48000.0;
- else if( caps.dwFormats & WAVE_FORMAT_2M16 )
- deviceInfo->defaultSampleRate = 22050.0;
- else if( caps.dwFormats & WAVE_FORMAT_1M16 )
- deviceInfo->defaultSampleRate = 11025.0;
- else if( caps.dwFormats & WAVE_FORMAT_96M16 )
- deviceInfo->defaultSampleRate = 96000.0;
- else
- deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
- }
- else deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
-
- deviceInfo->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
- deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2;
-
- deviceInfo->defaultLowOutputLatency = 0.;
- deviceInfo->defaultHighOutputLatency = 0.;
+ /* defaultSampleRate */
+ if( caps.dwChannels == 2 )
+ {
+ if( caps.dwFormats & WAVE_FORMAT_4S16 )
+ deviceInfo->defaultSampleRate = 44100.0;
+ else if( caps.dwFormats & WAVE_FORMAT_48S16 )
+ deviceInfo->defaultSampleRate = 48000.0;
+ else if( caps.dwFormats & WAVE_FORMAT_2S16 )
+ deviceInfo->defaultSampleRate = 22050.0;
+ else if( caps.dwFormats & WAVE_FORMAT_1S16 )
+ deviceInfo->defaultSampleRate = 11025.0;
+ else if( caps.dwFormats & WAVE_FORMAT_96S16 )
+ deviceInfo->defaultSampleRate = 96000.0;
+ else
+ deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
}
+ else if( caps.dwChannels == 1 )
+ {
+ if( caps.dwFormats & WAVE_FORMAT_4M16 )
+ deviceInfo->defaultSampleRate = 44100.0;
+ else if( caps.dwFormats & WAVE_FORMAT_48M16 )
+ deviceInfo->defaultSampleRate = 48000.0;
+ else if( caps.dwFormats & WAVE_FORMAT_2M16 )
+ deviceInfo->defaultSampleRate = 22050.0;
+ else if( caps.dwFormats & WAVE_FORMAT_1M16 )
+ deviceInfo->defaultSampleRate = 11025.0;
+ else if( caps.dwFormats & WAVE_FORMAT_96M16 )
+ deviceInfo->defaultSampleRate = 96000.0;
+ else
+ deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
+ }
+ else deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
+
+ deviceInfo->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
+ deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2;
+
+ deviceInfo->defaultLowOutputLatency = 0.;
+ deviceInfo->defaultHighOutputLatency = 0.;
}
IDirectSoundCapture_Release( lpDirectSoundCapture );
}
- if( deviceOK )
+ /* Copy GUID to the device info structure. Set pointer. */
+ if( lpGUID == NULL )
{
- deviceInfo->name = name;
-
- if( lpGUID == NULL )
- hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
-
- hostApi->info.deviceCount++;
+ winDsDeviceInfo->lpGUID = NULL;
}
+ else
+ {
+ winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+ memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+ }
+
+ deviceInfo->name = name;
+
+ return result;
+
+error:
+ if( lpDirectSoundCapture )
+ IDirectSoundCapture_Release( lpDirectSoundCapture );
return result;
}
@@ -1185,18 +1177,12 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
PaError result = paNoError;
- int i, deviceCount;
PaWinDsHostApiRepresentation *winDsHostApi;
- DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
- PaWinDsDeviceInfo *deviceInfoArray;
+ int deviceCount;
+ void *scanResults = 0;
PaWinDs_InitializeDSoundEntryPoints();
- /* initialise guid vectors so they can be safely deleted on error */
- deviceNamesAndGUIDs.winDsHostApi = NULL;
- deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
- deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
-
winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
if( !winDsHostApi )
{
@@ -1224,109 +1210,25 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
(*hostApi)->info.type = paDirectSound;
(*hostApi)->info.name = "Windows DirectSound";
+ /* these are all updated by CommitDeviceInfos() */
(*hostApi)->info.deviceCount = 0;
(*hostApi)->info.defaultInputDevice = paNoDevice;
(*hostApi)->info.defaultOutputDevice = paNoDevice;
+ (*hostApi)->deviceInfos = 0;
-
-/* DSound - enumerate devices to count them and to gather their GUIDs */
-
- result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
+ result = ScanDeviceInfos( &winDsHostApi->inheritedHostApiRep, hostApiIndex, &scanResults, &deviceCount );
if( result != paNoError )
goto error;
- result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
- if( result != paNoError )
- goto error;
+ /* FIXME for now we ignore the result of CommitDeviceInfos(), it should probably be an atomic non-failing operation */
+ CommitDeviceInfos( &winDsHostApi->inheritedHostApiRep, hostApiIndex, scanResults, deviceCount );
- paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateW( (LPDSENUMCALLBACKW)CollectGUIDsProcW, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
-
- paWinDsDSoundEntryPoints.DirectSoundEnumerateW( (LPDSENUMCALLBACKW)CollectGUIDsProcW, (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;
- }
-
- 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;
-
-
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ (*hostApi)->ScanDeviceInfos = ScanDeviceInfos;
+ (*hostApi)->CommitDeviceInfos = CommitDeviceInfos;
+ (*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos;
PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
@@ -1342,9 +1244,6 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
return result;
error:
- TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
- TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
-
Terminate( (struct PaUtilHostApiRepresentation *)winDsHostApi );
return result;
@@ -1489,6 +1388,232 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paFormatIsSupported;
}
+/***********************************************************************************/
+static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, void **scanResults, int *newDeviceCount )
+{
+ PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+ PaWinDsDeviceInfo *deviceInfoArray;
+ PaError result = paNoError;
+ PaWinDsScanDeviceInfosResults *outArgument = 0;
+ DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
+ int i = 0;
+ int maximumNewDeviceCount = 0;
+
+ /* Check preconditions */
+ if( scanResults == NULL || newDeviceCount == NULL )
+ return paInternalError;
+
+ /* initialize the out params */
+ *scanResults = NULL;
+ *newDeviceCount = 0;
+
+ /* initialise guid vectors so they can be safely deleted on error */
+ deviceNamesAndGUIDs.winDsHostApi = NULL;
+ deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
+ deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
+
+ result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
+ if( result != paNoError )
+ goto error;
+
+ result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
+ if( result != paNoError )
+ goto error;
+
+ deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
+
+ /* DSound - enumerate devices to count them and to gather their GUIDs and device names */
+
+ paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateW( (LPDSENUMCALLBACKW)CollectGUIDsProcW, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
+
+ if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
+ {
+ result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
+ goto error;
+ }
+
+ paWinDsDSoundEntryPoints.DirectSoundEnumerateW( (LPDSENUMCALLBACKW)CollectGUIDsProcW, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
+
+ if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
+ {
+ result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
+ goto error;
+ }
+
+ maximumNewDeviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count +
+ deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
+
+#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
+ if( maximumNewDeviceCount > 0 )
+ {
+ FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
+ }
+#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
+
+ if( maximumNewDeviceCount > 0 )
+ {
+ /* Allocate the out param for all the info we need */
+ outArgument = (PaWinDsScanDeviceInfosResults *) PaUtil_GroupAllocateMemory(
+ winDsHostApi->allocations, sizeof(PaWinDsScanDeviceInfosResults) );
+ if( !outArgument )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate array for pointers to PaDeviceInfo structs */
+ outArgument->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ winDsHostApi->allocations, sizeof(PaDeviceInfo*) * maximumNewDeviceCount );
+ if( !outArgument->deviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all PaDeviceInfo structs in a contiguous block */
+ deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
+ winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * maximumNewDeviceCount );
+ if( !deviceInfoArray )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( i = 0 ; i < maximumNewDeviceCount; ++i )
+ {
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->name = 0;
+
+ outArgument->deviceInfos[ i ] = deviceInfo;
+ }
+
+ for( i = 0 ; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count ; ++i )
+ {
+ PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*)outArgument->deviceInfos[*newDeviceCount];
+
+ result = AddInputDeviceInfoFromDirectSoundCapture( winDsDeviceInfo,
+ deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
+ deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
+ deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
+ if( result == paNoError )
+ {
+ if( deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID == NULL )
+ outArgument->defaultInputDevice = *newDeviceCount;
+ (*newDeviceCount)++;
+ }
+ /* ignore error results here and just skip the device */
+ }
+
+ for( i = 0 ; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count ; ++i )
+ {
+ PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*)outArgument->deviceInfos[*newDeviceCount];
+
+ result = AddOutputDeviceInfoFromDirectSound( winDsDeviceInfo,
+ deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
+ deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
+ deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
+ if( result == paNoError )
+ {
+ if( deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID == NULL )
+ outArgument->defaultOutputDevice = *newDeviceCount;
+ (*newDeviceCount)++;
+ }
+ /* ignore error results here and just skip the device */
+ }
+ }
+
+ result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
+ if( result != paNoError )
+ goto error;
+
+ result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
+ if( result != paNoError )
+ goto error;
+
+ *scanResults = outArgument;
+ return result;
+
+error:
+ TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
+ TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
+
+ if( outArgument )
+ {
+ if( outArgument->deviceInfos )
+ {
+ if( outArgument->deviceInfos[0] )
+ {
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, outArgument->deviceInfos[0] );
+ }
+
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, outArgument->deviceInfos );
+ }
+ }
+ return result;
+}
+
+static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *scanResults, int deviceCount )
+{
+ PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+ PaError result = paNoError;
+ int i = 0;
+
+ hostApi->info.deviceCount = 0;
+ hostApi->info.defaultInputDevice = paNoDevice;
+ hostApi->info.defaultOutputDevice = paNoDevice;
+
+ /* Free any old memory which might be in the device info */
+ if( hostApi->deviceInfos )
+ {
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, hostApi->deviceInfos[0] );
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, hostApi->deviceInfos );
+ hostApi->deviceInfos = NULL;
+ }
+
+ if( scanResults != NULL )
+ {
+ PaWinDsScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinDsScanDeviceInfosResults * ) scanResults;
+
+ if( deviceCount > 0 )
+ {
+ /* use the array allocated in ScanDeviceInfos() as our deviceInfos */
+ hostApi->deviceInfos = scanDeviceInfosResults->deviceInfos;
+
+ hostApi->info.defaultInputDevice = scanDeviceInfosResults->defaultInputDevice;
+ hostApi->info.defaultOutputDevice = scanDeviceInfosResults->defaultOutputDevice;
+
+ hostApi->info.deviceCount = deviceCount;
+ }
+
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults );
+ }
+
+ return result;
+}
+
+static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount )
+{
+ PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+
+ if( scanResults != NULL )
+ {
+ PaWinDsScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinDsScanDeviceInfosResults * ) scanResults;
+
+ if( scanDeviceInfosResults->deviceInfos )
+ {
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); /* all device info structs are allocated in a block so we can destroy them here */
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos );
+ }
+
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults );
+ }
+
+ return paNoError;
+}
+
+/***********************************************************************************/
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
diff --git a/src/hostapi/jack/pa_jack.c b/src/hostapi/jack/pa_jack.c
index a800f8e..ab6af78 100644
--- a/src/hostapi/jack/pa_jack.c
+++ b/src/hostapi/jack/pa_jack.c
@@ -756,6 +756,9 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ (*hostApi)->ScanDeviceInfos = 0;
+ (*hostApi)->CommitDeviceInfos = 0;
+ (*hostApi)->DisposeDeviceInfos = 0;
PaUtil_InitializeStreamInterface( &jackHostApi->callbackStreamInterface,
CloseStream, StartStream,
diff --git a/src/hostapi/oss/pa_unix_oss.c b/src/hostapi/oss/pa_unix_oss.c
index 51e9630..b0ce4c3 100644
--- a/src/hostapi/oss/pa_unix_oss.c
+++ b/src/hostapi/oss/pa_unix_oss.c
@@ -256,6 +256,9 @@ PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ (*hostApi)->ScanDeviceInfos = 0;
+ (*hostApi)->CommitDeviceInfos = 0;
+ (*hostApi)->DisposeDeviceInfos = 0;
PA_ENSURE( BuildDeviceList( ossHostApi ) );
diff --git a/src/hostapi/skeleton/pa_hostapi_skeleton.c b/src/hostapi/skeleton/pa_hostapi_skeleton.c
index 6edc22c..dfb3aa9 100644
--- a/src/hostapi/skeleton/pa_hostapi_skeleton.c
+++ b/src/hostapi/skeleton/pa_hostapi_skeleton.c
@@ -209,6 +209,9 @@ PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiI
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ (*hostApi)->ScanDeviceInfos = 0;
+ (*hostApi)->CommitDeviceInfos = 0;
+ (*hostApi)->DisposeDeviceInfos = 0;
PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
diff --git a/src/hostapi/wasapi/pa_win_wasapi.c b/src/hostapi/wasapi/pa_win_wasapi.c
index 6e62a4a..67618c8 100644
--- a/src/hostapi/wasapi/pa_win_wasapi.c
+++ b/src/hostapi/wasapi/pa_win_wasapi.c
@@ -1516,6 +1516,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ (*hostApi)->ScanDeviceInfos = 0;
+ (*hostApi)->CommitDeviceInfos = 0;
+ (*hostApi)->DisposeDeviceInfos = 0;
PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
diff --git a/src/hostapi/wdmks/pa_win_wdmks.c b/src/hostapi/wdmks/pa_win_wdmks.c
index 129b9e2..608c1c3 100644
--- a/src/hostapi/wdmks/pa_win_wdmks.c
+++ b/src/hostapi/wdmks/pa_win_wdmks.c
@@ -3853,11 +3853,10 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
- /* In preparation for hotplug
(*hostApi)->ScanDeviceInfos = ScanDeviceInfos;
(*hostApi)->CommitDeviceInfos = CommitDeviceInfos;
(*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos;
- */
+
PaUtil_InitializeStreamInterface( &wdmHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
GetStreamTime, GetStreamCpuLoad,
diff --git a/src/hostapi/wmme/pa_win_wmme.c b/src/hostapi/wmme/pa_win_wmme.c
index 422c867..4f4c872 100644
--- a/src/hostapi/wmme/pa_win_wmme.c
+++ b/src/hostapi/wmme/pa_win_wmme.c
@@ -1155,6 +1155,9 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ (*hostApi)->ScanDeviceInfos = 0;
+ (*hostApi)->CommitDeviceInfos = 0;
+ (*hostApi)->DisposeDeviceInfos = 0;
PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
diff --git a/src/os/win/pa_win_hotplug.c b/src/os/win/pa_win_hotplug.c
new file mode 100644
index 0000000..9257c0c
--- /dev/null
+++ b/src/os/win/pa_win_hotplug.c
@@ -0,0 +1,315 @@
+
+#include "pa_util.h"
+#include "pa_debugprint.h"
+#include "pa_allocation.h"
+
+#include "pa_win_wdmks_utils.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
+#pragma comment( lib, "setupapi.lib" )
+#endif
+
+
+/* Implemented in pa_front.c
+ @param first 0 = unknown, 1 = insertion, 2 = removal
+ @param second Host specific device change info (in windows it is the (unicode) device path)
+*/
+extern void PaUtil_DevicesChanged(unsigned, void*);
+
+/* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */
+#if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
+#define CREATE_THREAD_FUNCTION (HANDLE)_beginthreadex
+#define PA_THREAD_FUNC static unsigned WINAPI
+#else
+#define CREATE_THREAD_FUNCTION CreateThread
+#define PA_THREAD_FUNC static DWORD WINAPI
+#endif
+
+typedef struct PaHotPlugDeviceInfo
+{
+ wchar_t name[MAX_PATH];
+ struct PaHotPlugDeviceInfo* next;
+} PaHotPlugDeviceInfo;
+
+typedef struct PaHotPlugDeviceEventHandlerInfo
+{
+ HANDLE hWnd;
+ HANDLE hMsgThread;
+ HANDLE hNotify;
+ CRITICAL_SECTION lock;
+ PaUtilAllocationGroup* cacheAllocGroup;
+ PaHotPlugDeviceInfo* cache;
+
+} PaHotPlugDeviceEventHandlerInfo;
+
+
+static BOOL RemoveDeviceFromCache(PaHotPlugDeviceEventHandlerInfo* pInfo, const wchar_t* name)
+{
+ if (pInfo->cache != NULL)
+ {
+ PaHotPlugDeviceInfo* lastEntry = 0;
+ PaHotPlugDeviceInfo* entry = pInfo->cache;
+ while (entry != NULL)
+ {
+ if (_wcsicmp(entry->name, name) == 0)
+ {
+ if (lastEntry)
+ {
+ lastEntry->next = entry->next;
+ }
+ else
+ {
+ pInfo->cache = NULL;
+ }
+ PaUtil_GroupFreeMemory(pInfo->cacheAllocGroup, entry);
+ return TRUE;
+ }
+
+ lastEntry = entry;
+ entry = entry->next;
+ }
+ }
+ return FALSE;
+}
+
+static void InsertDeviceIntoCache(PaHotPlugDeviceEventHandlerInfo* pInfo, const wchar_t* name)
+{
+ PaHotPlugDeviceInfo** ppEntry = NULL;
+
+ /* Remove it first (if possible) so we don't accidentally get duplicates */
+ RemoveDeviceFromCache(pInfo, name);
+
+ if (pInfo->cache == NULL)
+ {
+ ppEntry = &pInfo->cache;
+ }
+ else
+ {
+ PaHotPlugDeviceInfo* entry = pInfo->cache;
+ while (entry->next != NULL)
+ {
+ entry = entry->next;
+ }
+ ppEntry = &entry->next;
+ }
+
+ *ppEntry = (PaHotPlugDeviceInfo*)PaUtil_GroupAllocateMemory(pInfo->cacheAllocGroup, sizeof(PaHotPlugDeviceInfo));
+ wcsncpy((*ppEntry)->name, name, MAX_PATH-1);
+ (*ppEntry)->next = NULL;
+}
+
+static BOOL IsDeviceAudio(const wchar_t* deviceName)
+{
+ int channelCnt = 0;
+ channelCnt += PaWin_WDMKS_QueryFilterMaximumChannelCount((void*)deviceName, 1);
+ channelCnt += PaWin_WDMKS_QueryFilterMaximumChannelCount((void*)deviceName, 0);
+ return (channelCnt > 0);
+}
+
+static void PopulateCacheWithAvailableAudioDevices(PaHotPlugDeviceEventHandlerInfo* pInfo)
+{
+ HDEVINFO handle = NULL;
+ const int sizeInterface = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + (MAX_PATH * sizeof(WCHAR));
+ SP_DEVICE_INTERFACE_DETAIL_DATA_W* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)PaUtil_AllocateMemory(sizeInterface);
+
+ if (devInterfaceDetails)
+ {
+ devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
+
+ /* Open a handle to search for devices (filters) */
+ handle = SetupDiGetClassDevsW(&KSCATEGORY_AUDIO,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if( handle != NULL )
+ {
+ int device;
+
+ /* Iterate through the devices */
+ for( device = 0;;device++ )
+ {
+ SP_DEVICE_INTERFACE_DATA interfaceData;
+ SP_DEVINFO_DATA devInfoData;
+ int noError;
+
+ interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ interfaceData.Reserved = 0;
+ devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+ devInfoData.Reserved = 0;
+
+ noError = SetupDiEnumDeviceInterfaces(handle,NULL,&KSCATEGORY_AUDIO,device,&interfaceData);
+ if( !noError )
+ break; /* No more devices */
+
+ noError = SetupDiGetDeviceInterfaceDetailW(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
+ if( noError )
+ {
+ if (IsDeviceAudio(devInterfaceDetails->DevicePath))
+ {
+ PA_DEBUG(("Hotplug cache populated with: '%S'\n", devInterfaceDetails->DevicePath));
+ InsertDeviceIntoCache(pInfo, devInterfaceDetails->DevicePath);
+ }
+ }
+ }
+ SetupDiDestroyDeviceInfoList(handle);
+ }
+ PaUtil_FreeMemory(devInterfaceDetails);
+ }
+}
+
+static LRESULT CALLBACK PaMsgWinProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ PaHotPlugDeviceEventHandlerInfo* pInfo = (PaHotPlugDeviceEventHandlerInfo*)( GetWindowLongPtr(hWnd, GWLP_USERDATA) );
+ switch(msg)
+ {
+ case WM_DEVICECHANGE:
+ switch(wParam)
+ {
+ case DBT_DEVICEARRIVAL:
+ {
+ PDEV_BROADCAST_DEVICEINTERFACE_W ptr = (PDEV_BROADCAST_DEVICEINTERFACE_W)lParam;
+ if (ptr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
+ break;
+
+ if (!IsEqualGUID(&ptr->dbcc_classguid, &KSCATEGORY_AUDIO))
+ break;
+
+ if (IsDeviceAudio(ptr->dbcc_name))
+ {
+ PA_DEBUG(("Device inserted : %S\n", ptr->dbcc_name));
+ InsertDeviceIntoCache(pInfo, ptr->dbcc_name);
+ PaUtil_DevicesChanged(1, ptr->dbcc_name);
+ }
+ }
+ break;
+ case DBT_DEVICEREMOVECOMPLETE:
+ {
+ PDEV_BROADCAST_DEVICEINTERFACE_W ptr = (PDEV_BROADCAST_DEVICEINTERFACE_W)lParam;
+ if (ptr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
+ break;
+
+ if (!IsEqualGUID(&ptr->dbcc_classguid, &KSCATEGORY_AUDIO))
+ break;
+
+ if (RemoveDeviceFromCache(pInfo, ptr->dbcc_name))
+ {
+ PA_DEBUG(("Device removed : %S\n", ptr->dbcc_name));
+ PaUtil_DevicesChanged(2, ptr->dbcc_name);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ return DefWindowProcW(hWnd, msg, wParam, lParam);
+}
+
+PA_THREAD_FUNC PaRunMessageLoop(void* ptr)
+{
+ PaHotPlugDeviceEventHandlerInfo* pInfo = (PaHotPlugDeviceEventHandlerInfo*)ptr;
+ WNDCLASSW wnd = { 0 };
+ HMODULE hInstance = GetModuleHandleW(NULL);
+
+ wnd.lpfnWndProc = PaMsgWinProcW;
+ wnd.hInstance = hInstance;
+ wnd.lpszClassName = L"{1E0D4F5A-B31F-4dcc-AE3C-4F30A47BD521}"; /* Using a GUID as class name */
+ pInfo->hWnd = CreateWindowW((LPCWSTR)MAKEINTATOM(RegisterClassW(&wnd)), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);
+ if (pInfo->hWnd)
+ {
+ DEV_BROADCAST_DEVICEINTERFACE_W NotificationFilter = { sizeof(DEV_BROADCAST_DEVICEINTERFACE_W) };
+ NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+
+#ifndef DEVICE_NOTIFY_ALL_INTERFACE_CLASSES
+#define DEVICE_NOTIFY_ALL_INTERFACE_CLASSES 0x00000004
+#endif
+
+ pInfo->hNotify = RegisterDeviceNotificationW(
+ pInfo->hWnd,
+ &NotificationFilter,
+ DEVICE_NOTIFY_WINDOW_HANDLE|DEVICE_NOTIFY_ALL_INTERFACE_CLASSES
+ );
+
+ assert(pInfo->hNotify);
+
+ SetWindowLongPtr(pInfo->hWnd, GWLP_USERDATA, (LONG_PTR)pInfo);
+
+ if (pInfo->hNotify)
+ {
+ MSG msg;
+ BOOL result;
+ while((result = GetMessageW(&msg, pInfo->hWnd, 0, 0)) != 0)
+ {
+ if (result == -1)
+ {
+ break;
+ }
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ UnregisterDeviceNotification(pInfo->hNotify);
+ pInfo->hNotify = 0;
+ }
+ DestroyWindow(pInfo->hWnd);
+ pInfo->hWnd = 0;
+ }
+ return 0;
+}
+
+static PaHotPlugDeviceEventHandlerInfo* s_handler = 0;
+
+void PaUtil_InitializeHotPlug()
+{
+ if (s_handler == 0)
+ {
+ s_handler = (PaHotPlugDeviceEventHandlerInfo*)PaUtil_AllocateMemory(sizeof(PaHotPlugDeviceEventHandlerInfo));
+ if (s_handler)
+ {
+ s_handler->cacheAllocGroup = PaUtil_CreateAllocationGroup();
+ InitializeCriticalSection(&s_handler->lock);
+ PopulateCacheWithAvailableAudioDevices(s_handler);
+ /* Start message thread */
+ s_handler->hMsgThread = CREATE_THREAD_FUNCTION(NULL, 0, PaRunMessageLoop, s_handler, 0, NULL);
+ assert(s_handler->hMsgThread != 0);
+ }
+ }
+}
+
+void PaUtil_TerminateHotPlug()
+{
+ if (s_handler != 0)
+ {
+ if (s_handler->hWnd)
+ {
+ PostMessage(s_handler->hWnd, WM_QUIT, 0, 0);
+ if (WaitForSingleObject(s_handler->hMsgThread, 1000) == WAIT_TIMEOUT)
+ {
+ TerminateThread(s_handler->hMsgThread, -1);
+ }
+ }
+ DeleteCriticalSection(&s_handler->lock);
+ PaUtil_FreeAllAllocations( s_handler->cacheAllocGroup );
+ PaUtil_DestroyAllocationGroup( s_handler->cacheAllocGroup );
+ PaUtil_FreeMemory( s_handler );
+ s_handler = 0;
+ }
+}
+
+void PaUtil_LockHotPlug()
+{
+ EnterCriticalSection(&s_handler->lock);
+}
+
+void PaUtil_UnlockHotPlug()
+{
+ LeaveCriticalSection(&s_handler->lock);
+}
diff --git a/test/patest_update_available_device_list.c b/test/patest_update_available_device_list.c
new file mode 100644
index 0000000..2c077f5
--- /dev/null
+++ b/test/patest_update_available_device_list.c
@@ -0,0 +1,48 @@
+#include
+#include
+
+#include "portaudio.h"
+
+void printDevices()
+{
+ int deviceCount = Pa_GetDeviceCount();
+ int i;
+
+ for( i=0; i < deviceCount; ++i ){
+ const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i);
+ const PaHostApiInfo *hostApiInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
+
+ assert( deviceInfo != 0 );
+
+ printf( "%d %s (%s)\n", i, deviceInfo->name, hostApiInfo->name );
+ }
+}
+
+static void devicesChangedCallback(void* p)
+{
+ (void)p;
+
+ printf( "Portaudio device list have changed!\n" );
+}
+
+int main(int argc, char* argv[])
+{
+ Pa_Initialize();
+
+ Pa_SetDevicesChangedCallback(NULL, devicesChangedCallback);
+
+ for(;;){
+ printDevices();
+
+ printf( "press [enter] to update the device list. or q + [enter] to quit.\n" );
+ if( getchar() == 'q' )
+ break;
+
+ Pa_UpdateAvailableDeviceList();
+ }
+
+ Pa_Terminate();
+
+ return 0;
+}
+