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 );
} 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.
int count;
int free;
- DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
+ DSDeviceNameAndGUID *items; /* Allocated using LocalAlloc() */
} DSDeviceNameAndGUIDVector;
typedef struct DSDeviceNamesAndGUIDs{
** 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.
lpGUID->Data4[6],
lpGUID->Data4[7]));
- deviceOK = FALSE;
+ result = paUnanticipatedHostError;
+ goto error;
}
else
{
if( hr != DS_OK )
{
DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
+
+ result = paUnanticipatedHostError;
+ goto error;
}
else
{
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;
-
- /* 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;
- }
+ 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 )
- {
- if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
- && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
- {
- deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
- break;
- }
- }
- }
- else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
+ for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
{
- 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
+ if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
+ && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
{
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+ deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
+ break;
}
}
- else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
+ }
+ else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
+ {
+ if( caps.dwMinSecondarySampleRate == 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.
+ /*
+ ** 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 - Sample rate range used instead of two odd values for device #%s\n", name ));
+ 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
+ {
+ 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
+ //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
+ // dwFlags | DSCAPS_CONTINUOUSRATE
- deviceInfo->defaultLowInputLatency = 0.;
- deviceInfo->defaultHighInputLatency = 0.;
+ deviceInfo->defaultLowInputLatency = 0.;
+ deviceInfo->defaultHighInputLatency = 0.;
- deviceInfo->defaultLowOutputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
- deviceInfo->defaultHighOutputLatency = deviceInfo->defaultLowOutputLatency * 2;
- }
+ 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;
}
** 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 );
if( hr != DS_OK )
{
DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
+ result = paUnanticipatedHostError;
+ goto error;
}
else
{
if( hr != DS_OK )
{
DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
+ result = paUnanticipatedHostError;
+ goto error;
}
else
{
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 <fgouget@ ... .fr>
- To: Ross Bencina <rbencina@ ... .au>
- 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 <fgouget@ ... .fr>
+To: Ross Bencina <rbencina@ ... .au>
+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 */
#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 */
+ /* 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->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
+ deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2;
- deviceInfo->defaultLowOutputLatency = 0.;
- deviceInfo->defaultHighOutputLatency = 0.;
- }
+ 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;
+ winDsDeviceInfo->lpGUID = NULL;
+ }
+ else
+ {
+ winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+ memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+ }
- if( lpGUID == NULL )
- hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
+ deviceInfo->name = name;
+
+ return result;
- hostApi->info.deviceCount++;
- }
+error:
+ if( lpDirectSoundCapture )
+ IDirectSoundCapture_Release( lpDirectSoundCapture );
return result;
}
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 )
{
(*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 );
- if( result != paNoError )
- goto error;
-
- result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
- if( result != paNoError )
- goto error;
-
- 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 );
+ result = ScanDeviceInfos( &winDsHostApi->inheritedHostApiRep, hostApiIndex, &scanResults, &deviceCount );
if( result != paNoError )
goto error;
- result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
- 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 );
-
(*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,
return result;
error:
- TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
- TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
-
Terminate( (struct PaUtilHostApiRepresentation *)winDsHostApi );
return result;
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,
--- /dev/null
+
+#include "pa_util.h"
+#include "pa_debugprint.h"
+#include "pa_allocation.h"
+
+#include "pa_win_wdmks_utils.h"
+
+#include <windows.h>
+#include <dbt.h>
+#include <process.h>
+#include <assert.h>
+#include <ks.h>
+#include <ksmedia.h>
+
+#include <setupapi.h>
+
+#include <stdio.h>
+
+#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);
+}