hotplug wmme: Factor out new Query{Input, Output} DeviceInterfaceName() functions to wrap DRV_QUERYDEVICEINTERFACE. Store the device interface name in PaWinMmeDeviceInfo::deviceInterfaceName, free the associated allocation in FreeDeviceInfos() (required adding deviceCount parameter). Rework WDM/KS-based numChannels query to use the stored deviceInterfaceName. Free name memory on error in InitializeInputDeviceInfo, InitializeOutputDeviceInfo. Implement device connectionId correlation by matching deviceInterfaceName using new functions: FindDeviceByConnectionId(), FindDeviceInfoByInterfaceName(), AssignDeviceConnectionId().

This commit is contained in:
Ross Bencina 2016-09-11 20:52:02 +10:00
commit def5763094

View file

@ -445,6 +445,9 @@ typedef struct
{ {
PaDeviceInfo inheritedDeviceInfo; PaDeviceInfo inheritedDeviceInfo;
UINT winWmmeDeviceId; /*<< Windows MME device id for this device */ UINT winWmmeDeviceId; /*<< Windows MME device id for this device */
LPWSTR deviceInterfaceName; /*<< A null-terminated Unicode string containing the device-interface name returned by DRV_QUERYDEVICEINTERFACE.
see: https://msdn.microsoft.com/en-us/library/windows/hardware/ff536363(v=vs.85).aspx
We use this for connectionId impl because it works on Windows XP (DRV_QUERYFUNCTIONINSTANCEID is supported only on Vista and later) */
DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */ DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/ char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/ char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
@ -678,45 +681,45 @@ static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int w
} }
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO /* On error, *resultInterfaceName will be NULL */
static char QueryWaveInKSFilterMaxChannels( int waveInDeviceId, int *maxChannels ) static PaError QueryInputDeviceInterfaceName( PaUtilAllocationGroup *allocations, UINT waveInDeviceId, LPWSTR *resultInterfaceName )
{ {
void *devicePath; LPWSTR resultString = NULL;
DWORD devicePathSize; DWORD stringSize = 0;
char result = 0;
if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE, *resultInterfaceName = NULL;
(DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
return 0;
devicePath = PaUtil_AllocateMemory( devicePathSize ); if( waveInMessage( (HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
if( !devicePath ) (DWORD_PTR)&stringSize, 0 ) != MMSYSERR_NOERROR )
return 0; return paUnanticipatedHostError;
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */ if( stringSize == 0 )
if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACE, return paNoError;
(DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
resultString = (LPWSTR)PaUtil_GroupAllocateMemory( allocations, stringSize );
if( !resultString )
return paInsufficientMemory;
if( waveInMessage( (HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACE,
(DWORD_PTR)resultString, stringSize ) == MMSYSERR_NOERROR )
{ {
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 1 ); *resultInterfaceName = resultString;
if( count > 0 ) return paNoError;
}
else
{ {
*maxChannels = count; PaUtil_GroupFreeMemory( allocations, resultString );
result = 1; return paUnanticipatedHostError;
} }
}
PaUtil_FreeMemory( devicePath );
return result;
} }
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success ) PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
{ {
PaError result = paNoError; PaError result = paNoError;
char *deviceName; /* non-const ptr */ char *deviceName = NULL; /* non-const ptr */
LPWSTR deviceInterfaceName = NULL;
MMRESULT mmresult; MMRESULT mmresult;
WAVEINCAPS wic; WAVEINCAPS wic;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
@ -772,6 +775,10 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
} }
deviceInfo->name = deviceName; deviceInfo->name = deviceName;
QueryInputDeviceInterfaceName( winMmeHostApi->allocations,
winMmeInputDeviceId, &deviceInterfaceName );
winMmeDeviceInfo->deviceInterfaceName = deviceInterfaceName;
if( wic.wChannels == 0xFFFF || wic.wChannels < 1 || wic.wChannels > 255 ){ if( wic.wChannels == 0xFFFF || wic.wChannels < 1 || wic.wChannels > 255 ){
/* For Windows versions using WDM (possibly Windows 98 ME and later) /* For Windows versions using WDM (possibly Windows 98 ME and later)
* the kernel mixer sits between the application and the driver. As a result, * the kernel mixer sits between the application and the driver. As a result,
@ -792,8 +799,17 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
} }
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
winMmeDeviceInfo->deviceInputChannelCountIsKnown = if( deviceInterfaceName != NULL )
QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels ); {
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path
that we can use with WDM/KS, although this seems to be undocumented */
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( deviceInterfaceName, /* isInput= */ 1 );
if( count > 0 )
{
deviceInfo->maxInputChannels = count;
winMmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
}
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
winMmeDeviceInfo->dwFormats = wic.dwFormats; winMmeDeviceInfo->dwFormats = wic.dwFormats;
@ -803,61 +819,59 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
winMmeDeviceInfo->winWmmeDeviceId = winMmeInputDeviceId; winMmeDeviceInfo->winWmmeDeviceId = winMmeInputDeviceId;
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
*success = 1; *success = 1;
return result;
error: error:
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, deviceName );
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, deviceInterfaceName );
return result; return result;
} }
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO /* resultInterfaceName will be NULL if there's an error */
static int QueryWaveOutKSFilterMaxChannels( int waveOutDeviceId, int *maxChannels ) static PaError QueryOutputDeviceInterfaceName( PaUtilAllocationGroup *allocations, UINT waveOutDeviceId, LPWSTR *resultInterfaceName )
{ {
void *devicePath; LPWSTR resultString = NULL;
DWORD devicePathSize; DWORD stringSize = 0;
int result = 0;
if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE, *resultInterfaceName = NULL;
(DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
return 0;
devicePath = PaUtil_AllocateMemory( devicePathSize ); if( waveOutMessage( (HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
if( !devicePath ) (DWORD_PTR)&stringSize, 0 ) != MMSYSERR_NOERROR )
return 0; return paInternalError;
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */ if( stringSize == 0 )
if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACE, return paNoError;
(DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
resultString = (LPWSTR)PaUtil_GroupAllocateMemory( allocations, stringSize );
if( !resultString )
return paInsufficientMemory;
if( waveOutMessage( (HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACE,
(DWORD_PTR)resultString, stringSize ) == MMSYSERR_NOERROR )
{ {
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 0 ); *resultInterfaceName = resultString;
if( count > 0 ) return paNoError;
}
else
{ {
*maxChannels = count; PaUtil_GroupFreeMemory( allocations, resultString );
result = 1; return paInternalError;
} }
}
PaUtil_FreeMemory( devicePath );
return result;
} }
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success ) PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
{ {
PaError result = paNoError; PaError result = paNoError;
char *deviceName; /* non-const ptr */ char *deviceName = NULL; /* non-const ptr */
LPWSTR deviceInterfaceName = NULL;
MMRESULT mmresult; MMRESULT mmresult;
WAVEOUTCAPS woc; WAVEOUTCAPS woc;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
size_t len; size_t len;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
int wdmksDeviceOutputChannelCountIsKnown;
#endif
*success = 0; *success = 0;
@ -909,6 +923,10 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
} }
deviceInfo->name = deviceName; deviceInfo->name = deviceName;
QueryOutputDeviceInterfaceName( winMmeHostApi->allocations,
winMmeOutputDeviceId, &deviceInterfaceName );
winMmeDeviceInfo->deviceInterfaceName = deviceInterfaceName;
if( woc.wChannels == 0xFFFF || woc.wChannels < 1 || woc.wChannels > 255 ){ if( woc.wChannels == 0xFFFF || woc.wChannels < 1 || woc.wChannels > 255 ){
/* For Windows versions using WDM (possibly Windows 98 ME and later) /* For Windows versions using WDM (possibly Windows 98 ME and later)
* the kernel mixer sits between the application and the driver. As a result, * the kernel mixer sits between the application and the driver. As a result,
@ -929,10 +947,17 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
} }
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
wdmksDeviceOutputChannelCountIsKnown = QueryWaveOutKSFilterMaxChannels( if( deviceInterfaceName != NULL )
winMmeOutputDeviceId, &deviceInfo->maxOutputChannels ); {
if( wdmksDeviceOutputChannelCountIsKnown && !winMmeDeviceInfo->deviceOutputChannelCountIsKnown ) /* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path
that we can use with WDM/KS, although this seems to be undocumented */
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( deviceInterfaceName, /* isInput= */ 0 );
if( count > 0 )
{
deviceInfo->maxOutputChannels = count;
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1; winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
}
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
winMmeDeviceInfo->dwFormats = woc.dwFormats; winMmeDeviceInfo->dwFormats = woc.dwFormats;
@ -942,11 +967,12 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
winMmeDeviceInfo->winWmmeDeviceId = winMmeOutputDeviceId; winMmeDeviceInfo->winWmmeDeviceId = winMmeOutputDeviceId;
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
*success = 1; *success = 1;
return result;
error: error:
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, deviceName );
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, deviceInterfaceName );
return result; return result;
} }
@ -1274,14 +1300,101 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paFormatIsSupported; return paFormatIsSupported;
} }
/**************************************************************************************************************************/ /***********************************************************************************/
static void FreeDeviceInfos( PaUtilAllocationGroup *allocations, PaDeviceInfo **deviceInfos ) static const PaDeviceInfo* FindDeviceByConnectionId(
const PaDeviceInfo **deviceInfos, int deviceCount, PaDeviceConnectionId connectionId )
{
int i;
#ifndef NDEBUG
if( deviceCount )
{
assert( deviceInfos != NULL );
assert( deviceInfos[0] != NULL );
}
#endif
for( i = 0; i < deviceCount; ++i )
{
const PaDeviceInfo *deviceInfo = deviceInfos[i];
if( deviceInfo->connectionId == connectionId )
return deviceInfo;
}
return NULL;
}
/* used for looking up existing devices to reuse their connectionId */
static const PaDeviceInfo* FindDeviceInfoByInterfaceName(
const PaDeviceInfo **deviceInfos, int deviceCount,
LPCWSTR deviceInterfaceName, int isInput )
{
int i;
#ifndef NDEBUG
if( deviceCount )
{
assert( deviceInfos != NULL );
assert( deviceInfos[0] != NULL );
}
#endif
for( i = 0; i < deviceCount; ++i )
{
const PaDeviceInfo *deviceInfo = deviceInfos[i];
const PaWinMmeDeviceInfo *wmmeDeviceInfo = (const PaWinMmeDeviceInfo*)deviceInfo;
if( ((isInput && deviceInfo->maxInputChannels > 0)
|| (!isInput && deviceInfo->maxOutputChannels > 0))
&&
/* require both interface names to be non-NULL, because they can be NULL due to errors. */
(deviceInterfaceName != NULL && wmmeDeviceInfo->deviceInterfaceName != NULL
&& (wcscmp(deviceInterfaceName, wmmeDeviceInfo->deviceInterfaceName) == 0)) )
{
return deviceInfo;
}
}
return NULL;
}
/* correlate or allocate device connection id */
static PaDeviceConnectionId AssignDeviceConnectionId(
const PaDeviceInfo **activeDeviceInfos, int activeDeviceCount,
const PaDeviceInfo **newDeviceInfos, int newDeviceCount,
LPCWSTR deviceInterfaceName, int isInput )
{
const PaDeviceInfo *currentDeviceInfo;
currentDeviceInfo = FindDeviceInfoByInterfaceName(
activeDeviceInfos, activeDeviceCount, /* search active device list for deviceInterfaceName */
deviceInterfaceName, isInput );
if( currentDeviceInfo && FindDeviceByConnectionId(
newDeviceInfos, newDeviceCount, /* search new device list connectionId */
currentDeviceInfo->connectionId ) == NULL )
{
/* deviceInterfaceName matches, and its connectionId hasn't already been reused */
return currentDeviceInfo->connectionId;
}
return PaUtil_MakeDeviceConnectionId();
}
static void FreeDeviceInfos( PaUtilAllocationGroup *allocations, PaDeviceInfo **deviceInfos, int deviceCount )
{ {
if( deviceInfos ) if( deviceInfos )
{ {
if( deviceInfos[0] ) if( deviceInfos[0] )
{ {
int i;
for( i = 0; i < deviceCount; ++i )
{
PaWinMmeDeviceInfo *wmmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfos[ i ];
PaUtil_GroupFreeMemory( allocations, wmmeDeviceInfo->deviceInterfaceName );
}
/* all device info structs are allocated in a block so we can destroy them like this */ /* all device info structs are allocated in a block so we can destroy them like this */
PaUtil_GroupFreeMemory( allocations, deviceInfos[0] ); PaUtil_GroupFreeMemory( allocations, deviceInfos[0] );
} }
@ -1416,6 +1529,8 @@ static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaH
/* N.B. Prior WMME versions failed scanning with an error here. */ /* N.B. Prior WMME versions failed scanning with an error here. */
if( deviceInfoInitializationSucceeded ){ if( deviceInfoInitializationSucceeded ){
/* NOTE: wmmeDeviceInfo->deviceInterfaceName is now allocated and would need to be freed on error. */
if( outArgument->defaultInputDevice == paNoDevice ) if( outArgument->defaultInputDevice == paNoDevice )
{ {
/* if there is currently no default device, use the first one available */ /* if there is currently no default device, use the first one available */
@ -1427,6 +1542,11 @@ static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaH
outArgument->defaultInputDevice = *newDeviceCount; outArgument->defaultInputDevice = *newDeviceCount;
} }
deviceInfo->connectionId = AssignDeviceConnectionId(
hostApi->deviceInfos, hostApi->info.deviceCount,
outArgument->deviceInfos, *newDeviceCount,
wmmeDeviceInfo->deviceInterfaceName, /* isInput= */ 1 );
outArgument->deviceInfos[ *newDeviceCount ] = deviceInfo; outArgument->deviceInfos[ *newDeviceCount ] = deviceInfo;
outArgument->inputDeviceCount++; outArgument->inputDeviceCount++;
(*newDeviceCount)++; (*newDeviceCount)++;
@ -1465,6 +1585,8 @@ static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaH
/* N.B. Prior WMME versions failed scanning with an error here. */ /* N.B. Prior WMME versions failed scanning with an error here. */
if( deviceInfoInitializationSucceeded ){ if( deviceInfoInitializationSucceeded ){
/* NOTE: wmmeDeviceInfo->deviceInterfaceName is now allocated and would need to be freed on error. */
if( outArgument->defaultOutputDevice == paNoDevice ) if( outArgument->defaultOutputDevice == paNoDevice )
{ {
/* if there is currently no default device, use the first one available */ /* if there is currently no default device, use the first one available */
@ -1477,6 +1599,11 @@ static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaH
outArgument->defaultOutputDevice = *newDeviceCount; outArgument->defaultOutputDevice = *newDeviceCount;
} }
deviceInfo->connectionId = AssignDeviceConnectionId(
hostApi->deviceInfos, hostApi->info.deviceCount,
outArgument->deviceInfos, *newDeviceCount,
wmmeDeviceInfo->deviceInterfaceName, /* isInput= */ 0 );
outArgument->deviceInfos[ *newDeviceCount ] = deviceInfo; outArgument->deviceInfos[ *newDeviceCount ] = deviceInfo;
outArgument->outputDeviceCount++; outArgument->outputDeviceCount++;
(*newDeviceCount)++; (*newDeviceCount)++;
@ -1493,8 +1620,10 @@ static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaH
error: error:
if( outArgument ) if( outArgument )
{ {
FreeDeviceInfos( winMmeHostApi->allocations, outArgument->deviceInfos ); FreeDeviceInfos( winMmeHostApi->allocations, outArgument->deviceInfos, *newDeviceCount );
} }
*newDeviceCount = 0;
return result; return result;
} }
@ -1505,16 +1634,15 @@ static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, P
(void)index; /* unused parameter */ (void)index; /* unused parameter */
hostApi->info.deviceCount = 0;
hostApi->info.defaultInputDevice = paNoDevice;
hostApi->info.defaultOutputDevice = paNoDevice;
/* Free any old memory which might be in the device info */ /* Free any old memory which might be in the device info */
if( hostApi->deviceInfos ) if( hostApi->deviceInfos )
{ {
FreeDeviceInfos( winMmeHostApi->allocations, hostApi->deviceInfos ); FreeDeviceInfos( winMmeHostApi->allocations, hostApi->deviceInfos, hostApi->info.deviceCount );
hostApi->deviceInfos = NULL; hostApi->deviceInfos = NULL;
} }
hostApi->info.deviceCount = 0;
hostApi->info.defaultInputDevice = paNoDevice;
hostApi->info.defaultOutputDevice = paNoDevice;
if( scanResults != NULL ) if( scanResults != NULL )
{ {
@ -1550,7 +1678,7 @@ static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi,
if( scanDeviceInfosResults->deviceInfos ) if( scanDeviceInfosResults->deviceInfos )
{ {
FreeDeviceInfos( winMmeHostApi->allocations, hostApi->deviceInfos ); FreeDeviceInfos( winMmeHostApi->allocations, hostApi->deviceInfos, deviceCount );
} }
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, scanDeviceInfosResults ); PaUtil_GroupFreeMemory( winMmeHostApi->allocations, scanDeviceInfosResults );
@ -1559,7 +1687,7 @@ static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi,
return paNoError; return paNoError;
} }
/**************************************************************************************************************************/ /***********************************************************************************/
static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames( static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames(
unsigned long suggestedLatencyFrames, unsigned long suggestedLatencyFrames,