wasapi: get channels count and sample rate from a mix format (IAudioClient::GetMixFormat) for a PaDeviceInfo to circumvent rare cases when WASAPI does not accept format provided PKEY_AudioEngine_DeviceFormat due to a wrong channel count (bug and solution reported by Etienne Dechamps (edechamps), discussion: http://app.assembla.com/spaces/portaudio/git/merge_requests/7024281), new PaWasapi_GetDeviceMixFormat API to get mix format provided by IAudioClient::GetMixFormat
This commit is contained in:
parent
57b98d5a8c
commit
d74ea7b610
2 changed files with 73 additions and 13 deletions
|
|
@ -320,7 +320,7 @@ PaError PaWasapi_GetAudioClient( PaStream *pStream, void **pAudioClient, int bOu
|
||||||
PaError PaWasapi_UpdateDeviceList();
|
PaError PaWasapi_UpdateDeviceList();
|
||||||
|
|
||||||
|
|
||||||
/** Returns current sound format of the device assigned to the opened stream. Format is represented by
|
/** Returns current audio format of the device assigned to the opened stream. Format is represented by
|
||||||
PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure. Use this function to reconfirm format if
|
PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure. Use this function to reconfirm format if
|
||||||
PA's processor is overriden and paWinWasapiRedirectHostProcessor flag is specified.
|
PA's processor is overriden and paWinWasapiRedirectHostProcessor flag is specified.
|
||||||
|
|
||||||
|
|
@ -336,8 +336,9 @@ PaError PaWasapi_UpdateDeviceList();
|
||||||
int PaWasapi_GetDeviceCurrentFormat( PaStream *pStream, void *pFormat, unsigned int nFormatSize, int bOutput );
|
int PaWasapi_GetDeviceCurrentFormat( PaStream *pStream, void *pFormat, unsigned int nFormatSize, int bOutput );
|
||||||
|
|
||||||
|
|
||||||
/** Returns default sound format for device. Format is represented by PaWinWaveFormat or
|
/** Returns default audio format for device in Shared Mode. Format is represented by
|
||||||
WAVEFORMATEXTENSIBLE structure.
|
PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure and obtained by getting
|
||||||
|
the device property with a PKEY_AudioEngine_DeviceFormat key.
|
||||||
|
|
||||||
@param pFormat Pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure.
|
@param pFormat Pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure.
|
||||||
@param nFormatSize Size of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes.
|
@param nFormatSize Size of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes.
|
||||||
|
|
@ -350,6 +351,21 @@ int PaWasapi_GetDeviceCurrentFormat( PaStream *pStream, void *pFormat, unsigned
|
||||||
int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice );
|
int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice );
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns mix audio format for device in Shared Mode. Format is represented by
|
||||||
|
PaWinWaveFormat or WAVEFORMATEXTENSIBLE structureand obtained by
|
||||||
|
IAudioClient::GetMixFormat.
|
||||||
|
|
||||||
|
@param pFormat Pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure.
|
||||||
|
@param nFormatSize Size of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes.
|
||||||
|
@param nDevice Device index.
|
||||||
|
|
||||||
|
@return Non-negative value indicating the number of bytes copied into format decriptor
|
||||||
|
or, a PaErrorCode (which are always negative) if PortAudio is not initialized
|
||||||
|
or an error is encountered.
|
||||||
|
*/
|
||||||
|
int PaWasapi_GetDeviceMixFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice );
|
||||||
|
|
||||||
|
|
||||||
/** Returns device role (PaWasapiDeviceRole enum).
|
/** Returns device role (PaWasapiDeviceRole enum).
|
||||||
|
|
||||||
@param nDevice Device index.
|
@param nDevice Device index.
|
||||||
|
|
|
||||||
|
|
@ -422,6 +422,9 @@ typedef struct PaWasapiDeviceInfo
|
||||||
// Default format (setup through Control Panel by user)
|
// Default format (setup through Control Panel by user)
|
||||||
WAVEFORMATEXTENSIBLE DefaultFormat;
|
WAVEFORMATEXTENSIBLE DefaultFormat;
|
||||||
|
|
||||||
|
// Mix format (internal format used by WASAPI audio engine)
|
||||||
|
WAVEFORMATEXTENSIBLE MixFormat;
|
||||||
|
|
||||||
// Fields filled from IMMEndpoint'sGetDataFlow
|
// Fields filled from IMMEndpoint'sGetDataFlow
|
||||||
EDataFlow flow;
|
EDataFlow flow;
|
||||||
|
|
||||||
|
|
@ -1586,8 +1589,6 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA
|
||||||
#ifndef PA_WINRT
|
#ifndef PA_WINRT
|
||||||
IMMDeviceCollection* pEndPoints = NULL;
|
IMMDeviceCollection* pEndPoints = NULL;
|
||||||
IMMDeviceEnumerator *pEnumerator = NULL;
|
IMMDeviceEnumerator *pEnumerator = NULL;
|
||||||
#else
|
|
||||||
WAVEFORMATEX *mixFormat;
|
|
||||||
#endif
|
#endif
|
||||||
IAudioClient *tmpClient;
|
IAudioClient *tmpClient;
|
||||||
|
|
||||||
|
|
@ -1866,9 +1867,20 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA
|
||||||
hr = S_OK;
|
hr = S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get mix format
|
||||||
|
{
|
||||||
|
WAVEFORMATEX *mixFormat;
|
||||||
|
|
||||||
|
hr = IAudioClient_GetMixFormat(tmpClient, &mixFormat);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
memcpy(&paWasapi->devInfo[i].MixFormat, mixFormat, min(sizeof(paWasapi->devInfo[i].MixFormat), (sizeof(*mixFormat) + mixFormat->cbSize)));
|
||||||
|
CoTaskMemFree(mixFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register WINRT device
|
||||||
#ifdef PA_WINRT
|
#ifdef PA_WINRT
|
||||||
// Get mix format which will treat as default device format
|
|
||||||
hr = IAudioClient_GetMixFormat(tmpClient, &mixFormat);
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
// Default device
|
// Default device
|
||||||
|
|
@ -1880,9 +1892,8 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA
|
||||||
// State
|
// State
|
||||||
paWasapi->devInfo[i].state = DEVICE_STATE_ACTIVE;
|
paWasapi->devInfo[i].state = DEVICE_STATE_ACTIVE;
|
||||||
|
|
||||||
// Default format
|
// Default format is always a mix format
|
||||||
memcpy(&paWasapi->devInfo[i].DefaultFormat, mixFormat, min(sizeof(paWasapi->devInfo[i].DefaultFormat), (sizeof(*mixFormat) + mixFormat->cbSize)));
|
paWasapi->devInfo[i].DefaultFormat = paWasapi->devInfo[i].MixFormat;
|
||||||
CoTaskMemFree(mixFormat);
|
|
||||||
|
|
||||||
// Form-factor
|
// Form-factor
|
||||||
paWasapi->devInfo[i].formFactor = UnknownFormFactor;
|
paWasapi->devInfo[i].formFactor = UnknownFormFactor;
|
||||||
|
|
@ -1917,18 +1928,18 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA
|
||||||
// we can now fill in portaudio device data
|
// we can now fill in portaudio device data
|
||||||
deviceInfo->maxInputChannels = 0;
|
deviceInfo->maxInputChannels = 0;
|
||||||
deviceInfo->maxOutputChannels = 0;
|
deviceInfo->maxOutputChannels = 0;
|
||||||
deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec;
|
deviceInfo->defaultSampleRate = paWasapi->devInfo[i].MixFormat.Format.nSamplesPerSec;
|
||||||
switch (paWasapi->devInfo[i].flow)
|
switch (paWasapi->devInfo[i].flow)
|
||||||
{
|
{
|
||||||
case eRender: {
|
case eRender: {
|
||||||
deviceInfo->maxOutputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;
|
deviceInfo->maxOutputChannels = paWasapi->devInfo[i].MixFormat.Format.nChannels;
|
||||||
deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
|
deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
|
||||||
deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
|
deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
|
||||||
PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
|
PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
|
||||||
deviceInfo->maxOutputChannels, (float)deviceInfo->defaultHighOutputLatency, (float)deviceInfo->defaultLowOutputLatency));
|
deviceInfo->maxOutputChannels, (float)deviceInfo->defaultHighOutputLatency, (float)deviceInfo->defaultLowOutputLatency));
|
||||||
break;}
|
break;}
|
||||||
case eCapture: {
|
case eCapture: {
|
||||||
deviceInfo->maxInputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;
|
deviceInfo->maxInputChannels = paWasapi->devInfo[i].MixFormat.Format.nChannels;
|
||||||
deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
|
deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
|
||||||
deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
|
deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
|
||||||
PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
|
PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
|
||||||
|
|
@ -2223,6 +2234,39 @@ int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, Pa
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------
|
||||||
|
int PaWasapi_GetDeviceMixFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice )
|
||||||
|
{
|
||||||
|
PaError ret;
|
||||||
|
PaWasapiHostApiRepresentation *paWasapi;
|
||||||
|
UINT32 size;
|
||||||
|
PaDeviceIndex index;
|
||||||
|
|
||||||
|
if (pFormat == NULL)
|
||||||
|
return paBadBufferPtr;
|
||||||
|
if (nFormatSize <= 0)
|
||||||
|
return paBufferTooSmall;
|
||||||
|
|
||||||
|
// Get API
|
||||||
|
paWasapi = _GetHostApi(&ret);
|
||||||
|
if (paWasapi == NULL)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
// Get device index
|
||||||
|
ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
|
||||||
|
if (ret != paNoError)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
// Validate index
|
||||||
|
if ((UINT32)index >= paWasapi->deviceCount)
|
||||||
|
return paInvalidDevice;
|
||||||
|
|
||||||
|
size = min(nFormatSize, (UINT32)sizeof(paWasapi->devInfo[ index ].MixFormat));
|
||||||
|
memcpy(pFormat, &paWasapi->devInfo[ index ].MixFormat, size);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------
|
||||||
int PaWasapi_GetDeviceRole( PaDeviceIndex nDevice )
|
int PaWasapi_GetDeviceRole( PaDeviceIndex nDevice )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue