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;
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 */
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)*/
@ -678,45 +681,45 @@ static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int w
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
static char QueryWaveInKSFilterMaxChannels( int waveInDeviceId, int *maxChannels )
/* On error, *resultInterfaceName will be NULL */
static PaError QueryInputDeviceInterfaceName( PaUtilAllocationGroup *allocations, UINT waveInDeviceId, LPWSTR *resultInterfaceName )
{
void *devicePath;
DWORD devicePathSize;
char result = 0;
LPWSTR resultString = NULL;
DWORD stringSize = 0;
if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
return 0;
*resultInterfaceName = NULL;
devicePath = PaUtil_AllocateMemory( devicePathSize );
if( !devicePath )
return 0;
if( waveInMessage( (HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&stringSize, 0 ) != MMSYSERR_NOERROR )
return paUnanticipatedHostError;
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACE,
(DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
if( stringSize == 0 )
return paNoError;
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 );
if( count > 0 )
{
*maxChannels = count;
result = 1;
}
*resultInterfaceName = resultString;
return paNoError;
}
else
{
PaUtil_GroupFreeMemory( allocations, resultString );
return paUnanticipatedHostError;
}
PaUtil_FreeMemory( devicePath );
return result;
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
{
PaError result = paNoError;
char *deviceName; /* non-const ptr */
char *deviceName = NULL; /* non-const ptr */
LPWSTR deviceInterfaceName = NULL;
MMRESULT mmresult;
WAVEINCAPS wic;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
@ -772,6 +775,10 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
}
deviceInfo->name = deviceName;
QueryInputDeviceInterfaceName( winMmeHostApi->allocations,
winMmeInputDeviceId, &deviceInterfaceName );
winMmeDeviceInfo->deviceInterfaceName = deviceInterfaceName;
if( wic.wChannels == 0xFFFF || wic.wChannels < 1 || wic.wChannels > 255 ){
/* For Windows versions using WDM (possibly Windows 98 ME and later)
* 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
winMmeDeviceInfo->deviceInputChannelCountIsKnown =
QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels );
if( deviceInterfaceName != NULL )
{
/* 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 */
winMmeDeviceInfo->dwFormats = wic.dwFormats;
@ -802,62 +818,60 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
winMmeDeviceInfo->winWmmeDeviceId = winMmeInputDeviceId;
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
*success = 1;
*success = 1;
return result;
error:
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, deviceName );
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, deviceInterfaceName );
return result;
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
static int QueryWaveOutKSFilterMaxChannels( int waveOutDeviceId, int *maxChannels )
/* resultInterfaceName will be NULL if there's an error */
static PaError QueryOutputDeviceInterfaceName( PaUtilAllocationGroup *allocations, UINT waveOutDeviceId, LPWSTR *resultInterfaceName )
{
void *devicePath;
DWORD devicePathSize;
int result = 0;
LPWSTR resultString = NULL;
DWORD stringSize = 0;
if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
return 0;
*resultInterfaceName = NULL;
devicePath = PaUtil_AllocateMemory( devicePathSize );
if( !devicePath )
return 0;
if( waveOutMessage( (HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&stringSize, 0 ) != MMSYSERR_NOERROR )
return paInternalError;
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACE,
(DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
if( stringSize == 0 )
return paNoError;
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 );
if( count > 0 )
{
*maxChannels = count;
result = 1;
}
*resultInterfaceName = resultString;
return paNoError;
}
else
{
PaUtil_GroupFreeMemory( allocations, resultString );
return paInternalError;
}
PaUtil_FreeMemory( devicePath );
return result;
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
{
PaError result = paNoError;
char *deviceName; /* non-const ptr */
char *deviceName = NULL; /* non-const ptr */
LPWSTR deviceInterfaceName = NULL;
MMRESULT mmresult;
WAVEOUTCAPS woc;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
size_t len;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
int wdmksDeviceOutputChannelCountIsKnown;
#endif
*success = 0;
@ -909,6 +923,10 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
}
deviceInfo->name = deviceName;
QueryOutputDeviceInterfaceName( winMmeHostApi->allocations,
winMmeOutputDeviceId, &deviceInterfaceName );
winMmeDeviceInfo->deviceInterfaceName = deviceInterfaceName;
if( woc.wChannels == 0xFFFF || woc.wChannels < 1 || woc.wChannels > 255 ){
/* For Windows versions using WDM (possibly Windows 98 ME and later)
* 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
wdmksDeviceOutputChannelCountIsKnown = QueryWaveOutKSFilterMaxChannels(
winMmeOutputDeviceId, &deviceInfo->maxOutputChannels );
if( wdmksDeviceOutputChannelCountIsKnown && !winMmeDeviceInfo->deviceOutputChannelCountIsKnown )
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
if( deviceInterfaceName != NULL )
{
/* 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;
}
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
winMmeDeviceInfo->dwFormats = woc.dwFormats;
@ -942,11 +967,12 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
winMmeDeviceInfo->winWmmeDeviceId = winMmeOutputDeviceId;
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
*success = 1;
return result;
error:
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, deviceName );
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, deviceInterfaceName );
return result;
}
@ -1274,14 +1300,101 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
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[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 */
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. */
if( deviceInfoInitializationSucceeded ){
/* NOTE: wmmeDeviceInfo->deviceInterfaceName is now allocated and would need to be freed on error. */
if( outArgument->defaultInputDevice == paNoDevice )
{
/* 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;
}
deviceInfo->connectionId = AssignDeviceConnectionId(
hostApi->deviceInfos, hostApi->info.deviceCount,
outArgument->deviceInfos, *newDeviceCount,
wmmeDeviceInfo->deviceInterfaceName, /* isInput= */ 1 );
outArgument->deviceInfos[ *newDeviceCount ] = deviceInfo;
outArgument->inputDeviceCount++;
(*newDeviceCount)++;
@ -1465,6 +1585,8 @@ static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaH
/* N.B. Prior WMME versions failed scanning with an error here. */
if( deviceInfoInitializationSucceeded ){
/* NOTE: wmmeDeviceInfo->deviceInterfaceName is now allocated and would need to be freed on error. */
if( outArgument->defaultOutputDevice == paNoDevice )
{
/* 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;
}
deviceInfo->connectionId = AssignDeviceConnectionId(
hostApi->deviceInfos, hostApi->info.deviceCount,
outArgument->deviceInfos, *newDeviceCount,
wmmeDeviceInfo->deviceInterfaceName, /* isInput= */ 0 );
outArgument->deviceInfos[ *newDeviceCount ] = deviceInfo;
outArgument->outputDeviceCount++;
(*newDeviceCount)++;
@ -1493,8 +1620,10 @@ static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaH
error:
if( outArgument )
{
FreeDeviceInfos( winMmeHostApi->allocations, outArgument->deviceInfos );
FreeDeviceInfos( winMmeHostApi->allocations, outArgument->deviceInfos, *newDeviceCount );
}
*newDeviceCount = 0;
return result;
}
@ -1505,16 +1634,15 @@ static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, P
(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 */
if( hostApi->deviceInfos )
{
FreeDeviceInfos( winMmeHostApi->allocations, hostApi->deviceInfos );
FreeDeviceInfos( winMmeHostApi->allocations, hostApi->deviceInfos, hostApi->info.deviceCount );
hostApi->deviceInfos = NULL;
}
hostApi->info.deviceCount = 0;
hostApi->info.defaultInputDevice = paNoDevice;
hostApi->info.defaultOutputDevice = paNoDevice;
if( scanResults != NULL )
{
@ -1550,7 +1678,7 @@ static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi,
if( scanDeviceInfosResults->deviceInfos )
{
FreeDeviceInfos( winMmeHostApi->allocations, hostApi->deviceInfos );
FreeDeviceInfos( winMmeHostApi->allocations, hostApi->deviceInfos, deviceCount );
}
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, scanDeviceInfosResults );
@ -1559,7 +1687,7 @@ static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi,
return paNoError;
}
/**************************************************************************************************************************/
/***********************************************************************************/
static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames(
unsigned long suggestedLatencyFrames,