mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 00:34:03 +02:00
ALSA: Change to device enumeration.
In order to get detailed information about a device, that is supported formats, channels and sample rates, the PCM needs to be opened. This can fail sometimes, in which case enumeration would previously just not enumerate the device. This is OK, but it creates issues. The first is that the enumerated devices will not be consistent with what's reported by `aplay`. The other is that when a hardware device is opened, iteration will not include that device because it'll be opened in exclusive mode. This creates a practical issue when trying to get the name of an already opened device. This commit makes it so that these devices will still be enumerated, only they'll be missing detailed format, channels and rate information.
This commit is contained in:
+86
-80
@@ -28912,110 +28912,116 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu
|
|||||||
ma_uint32 iChannel;
|
ma_uint32 iChannel;
|
||||||
int openMode = 0; /*MA_SND_PCM_NO_AUTO_RESAMPLE | MA_SND_PCM_NO_AUTO_CHANNELS | MA_SND_PCM_NO_AUTO_FORMAT;*/
|
int openMode = 0; /*MA_SND_PCM_NO_AUTO_RESAMPLE | MA_SND_PCM_NO_AUTO_CHANNELS | MA_SND_PCM_NO_AUTO_FORMAT;*/
|
||||||
|
|
||||||
/* For detailed info we need to open the device. */
|
|
||||||
if (pContextStateALSA->snd_pcm_open(&pPCM, deviceInfo.id.alsa, (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE, openMode) != 0) {
|
|
||||||
goto next_device;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We need to initialize a HW parameters object in order to know what formats are supported. */
|
|
||||||
pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(pContextStateALSA->snd_pcm_hw_params_sizeof(), ma_context_get_allocation_callbacks(pContext));
|
|
||||||
if (pHWParams == NULL) {
|
|
||||||
pContextStateALSA->snd_pcm_close(pPCM);
|
|
||||||
goto next_device; /* Out of memory. */
|
|
||||||
}
|
|
||||||
|
|
||||||
resultALSA = pContextStateALSA->snd_pcm_hw_params_any(pPCM, pHWParams);
|
|
||||||
if (resultALSA < 0) {
|
|
||||||
ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext));
|
|
||||||
pContextStateALSA->snd_pcm_close(pPCM);
|
|
||||||
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters for device \"%s\". snd_pcm_hw_params_any() failed. %s.", deviceInfo.id.alsa, pContextStateALSA->snd_strerror(resultALSA));
|
|
||||||
goto next_device;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Some ALSA devices can support many permutations of formats, channels and rates. We only support
|
For detailed info we need to open the device. I can think of two reasons why opening might fail:
|
||||||
a fixed number of permutations which means we need to employ some strategies to ensure the best
|
|
||||||
combinations are returned. An example is the "pulse" device which can do its own data conversion
|
|
||||||
in software and as a result can support any combination of format, channels and rate.
|
|
||||||
|
|
||||||
We want to ensure that the first data formats are the best. We have a list of favored sample
|
1) The device is disabled or physically unplugged.
|
||||||
formats and sample rates, so these will be the basis of our iteration.
|
2) The device is a hardware device and is already opened in exclusive mode
|
||||||
|
|
||||||
|
When this happens we're still going to enumerate the device, but we're going to just not fill out
|
||||||
|
the detailed info.
|
||||||
*/
|
*/
|
||||||
|
if (pContextStateALSA->snd_pcm_open(&pPCM, deviceInfo.id.alsa, (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE, openMode) == 0) {
|
||||||
|
/* We need to initialize a HW parameters object in order to know what formats are supported. */
|
||||||
|
pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(pContextStateALSA->snd_pcm_hw_params_sizeof(), ma_context_get_allocation_callbacks(pContext));
|
||||||
|
if (pHWParams == NULL) {
|
||||||
|
pContextStateALSA->snd_pcm_close(pPCM);
|
||||||
|
goto next_device; /* Out of memory. */
|
||||||
|
}
|
||||||
|
|
||||||
/* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */
|
resultALSA = pContextStateALSA->snd_pcm_hw_params_any(pPCM, pHWParams);
|
||||||
for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
|
if (resultALSA < 0) {
|
||||||
ma_format format = g_maFormatPriorities[iFormat];
|
ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext));
|
||||||
|
pContextStateALSA->snd_pcm_close(pPCM);
|
||||||
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters for device \"%s\". snd_pcm_hw_params_any() failed. %s.", deviceInfo.id.alsa, pContextStateALSA->snd_strerror(resultALSA));
|
||||||
|
goto next_device;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For each format we need to make sure we reset the configuration space so we don't return
|
Some ALSA devices can support many permutations of formats, channels and rates. We only support
|
||||||
channel counts and rates that aren't compatible with a format.
|
a fixed number of permutations which means we need to employ some strategies to ensure the best
|
||||||
|
combinations are returned. An example is the "pulse" device which can do its own data conversion
|
||||||
|
in software and as a result can support any combination of format, channels and rate.
|
||||||
|
|
||||||
|
We want to ensure that the first data formats are the best. We have a list of favored sample
|
||||||
|
formats and sample rates, so these will be the basis of our iteration.
|
||||||
*/
|
*/
|
||||||
pContextStateALSA->snd_pcm_hw_params_any(pPCM, pHWParams);
|
|
||||||
|
|
||||||
/* Test the format first. If this fails it means the format is not supported and we can skip it. */
|
/* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */
|
||||||
if (pContextStateALSA->snd_pcm_hw_params_test_format(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) {
|
for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
|
||||||
/* The format is supported. */
|
ma_format format = g_maFormatPriorities[iFormat];
|
||||||
unsigned int minChannels;
|
|
||||||
unsigned int maxChannels;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The configuration space needs to be restricted to this format so we can get an accurate
|
For each format we need to make sure we reset the configuration space so we don't return
|
||||||
picture of which sample rates and channel counts are support with this format.
|
channel counts and rates that aren't compatible with a format.
|
||||||
*/
|
*/
|
||||||
pContextStateALSA->snd_pcm_hw_params_set_format(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format));
|
pContextStateALSA->snd_pcm_hw_params_any(pPCM, pHWParams);
|
||||||
|
|
||||||
/* Now we need to check for supported channels. */
|
/* Test the format first. If this fails it means the format is not supported and we can skip it. */
|
||||||
pContextStateALSA->snd_pcm_hw_params_get_channels_min(pHWParams, &minChannels);
|
if (pContextStateALSA->snd_pcm_hw_params_test_format(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) {
|
||||||
pContextStateALSA->snd_pcm_hw_params_get_channels_max(pHWParams, &maxChannels);
|
/* The format is supported. */
|
||||||
|
unsigned int minChannels;
|
||||||
|
unsigned int maxChannels;
|
||||||
|
|
||||||
if (minChannels > MA_MAX_CHANNELS) {
|
/*
|
||||||
continue; /* Too many channels. */
|
The configuration space needs to be restricted to this format so we can get an accurate
|
||||||
}
|
picture of which sample rates and channel counts are support with this format.
|
||||||
if (maxChannels < MA_MIN_CHANNELS) {
|
*/
|
||||||
continue; /* Not enough channels. */
|
pContextStateALSA->snd_pcm_hw_params_set_format(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format));
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/* Now we need to check for supported channels. */
|
||||||
Make sure the channel count is clamped. This is mainly intended for the max channels
|
pContextStateALSA->snd_pcm_hw_params_get_channels_min(pHWParams, &minChannels);
|
||||||
because some devices can report an unbound maximum.
|
pContextStateALSA->snd_pcm_hw_params_get_channels_max(pHWParams, &maxChannels);
|
||||||
*/
|
|
||||||
minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
|
|
||||||
maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
|
|
||||||
|
|
||||||
if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
|
if (minChannels > MA_MAX_CHANNELS) {
|
||||||
/* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */
|
continue; /* Too many channels. */
|
||||||
ma_context_iterate_rates_and_add_native_data_format__alsa(pContextStateALSA, pPCM, pHWParams, format, 0, 0, &deviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */
|
}
|
||||||
} else {
|
if (maxChannels < MA_MIN_CHANNELS) {
|
||||||
/* The device only supports a specific set of channels. We need to iterate over all of them. */
|
continue; /* Not enough channels. */
|
||||||
for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
|
}
|
||||||
/* Test the channel before applying it to the configuration space. */
|
|
||||||
unsigned int channels = iChannel;
|
|
||||||
|
|
||||||
/* Make sure our channel range is reset before testing again or else we'll always fail the test. */
|
/*
|
||||||
pContextStateALSA->snd_pcm_hw_params_any(pPCM, pHWParams);
|
Make sure the channel count is clamped. This is mainly intended for the max channels
|
||||||
pContextStateALSA->snd_pcm_hw_params_set_format(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format));
|
because some devices can report an unbound maximum.
|
||||||
|
*/
|
||||||
|
minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
|
||||||
|
maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
|
||||||
|
|
||||||
if (pContextStateALSA->snd_pcm_hw_params_test_channels(pPCM, pHWParams, channels) == 0) {
|
if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
|
||||||
/* The channel count is supported. */
|
/* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */
|
||||||
|
ma_context_iterate_rates_and_add_native_data_format__alsa(pContextStateALSA, pPCM, pHWParams, format, 0, 0, &deviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */
|
||||||
|
} else {
|
||||||
|
/* The device only supports a specific set of channels. We need to iterate over all of them. */
|
||||||
|
for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
|
||||||
|
/* Test the channel before applying it to the configuration space. */
|
||||||
|
unsigned int channels = iChannel;
|
||||||
|
|
||||||
/* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */
|
/* Make sure our channel range is reset before testing again or else we'll always fail the test. */
|
||||||
pContextStateALSA->snd_pcm_hw_params_set_channels(pPCM, pHWParams, channels);
|
pContextStateALSA->snd_pcm_hw_params_any(pPCM, pHWParams);
|
||||||
|
pContextStateALSA->snd_pcm_hw_params_set_format(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format));
|
||||||
|
|
||||||
/* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */
|
if (pContextStateALSA->snd_pcm_hw_params_test_channels(pPCM, pHWParams, channels) == 0) {
|
||||||
ma_context_iterate_rates_and_add_native_data_format__alsa(pContextStateALSA, pPCM, pHWParams, format, channels, 0, &deviceInfo);
|
/* The channel count is supported. */
|
||||||
} else {
|
|
||||||
/* The channel count is not supported. Skip. */
|
/* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */
|
||||||
|
pContextStateALSA->snd_pcm_hw_params_set_channels(pPCM, pHWParams, channels);
|
||||||
|
|
||||||
|
/* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */
|
||||||
|
ma_context_iterate_rates_and_add_native_data_format__alsa(pContextStateALSA, pPCM, pHWParams, format, channels, 0, &deviceInfo);
|
||||||
|
} else {
|
||||||
|
/* The channel count is not supported. Skip. */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/* The format is not supported. Skip. */
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
/* The format is not supported. Skip. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext));
|
||||||
|
|
||||||
|
pContextStateALSA->snd_pcm_close(pPCM);
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext));
|
|
||||||
|
|
||||||
pContextStateALSA->snd_pcm_close(pPCM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
|
if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user