From 9698068d3eeb2f05c812aaff1db297f3e5581549 Mon Sep 17 00:00:00 2001 From: David Reid Date: Wed, 19 Oct 2016 21:14:20 +1000 Subject: [PATCH] ALSA: Have the id of enumerated devices be returned in "hw:%d,%d" format. The rationale for this change is that during testing I noticed that snd_pcm_open() would sometimes fail with "Invalid value for card" when using the "NAME" hint returned by snd_device_name_get_hint() verbatim. Using "hw:%d,%d" seems to fix this problem. --- mini_al.h | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/mini_al.h b/mini_al.h index 7994f54c..f62823bf 100644 --- a/mini_al.h +++ b/mini_al.h @@ -485,7 +485,7 @@ mal_uint32 mal_get_sample_size_in_bytes(mal_format format); #endif #ifdef MAL_ENABLE_ALSA -#include // Needed for printf() which is used for "hw:%d,%d" formatting. TODO: Remove this later. +#include // Needed for sprintf() which is used for "hw:%d,%d" formatting. TODO: Remove this later. #endif #if !defined(MAL_64BIT) && !defined(MAL_32BIT) @@ -2121,6 +2121,12 @@ static mal_result mal_enumerate_devices__alsa(mal_device_type type, mal_uint32* // // An alternative enumeration technique is to use snd_card_next() and family. The problem with this // one, which is significant, is that it does _not_ include user-space devices. + // + // --- + // + // During my testing I have discovered that snd_pcm_open() can fail on names returned by the "NAME" + // hint returned by snd_device_name_get_hint(). To resolve this I have needed to parse the NAME + // string and convert it to "hw:%d,%d" format. char** ppDeviceHints; if (snd_device_name_hint(-1, "pcm", (void***)&ppDeviceHints) < 0) { @@ -2148,11 +2154,56 @@ static mal_result mal_enumerate_devices__alsa(mal_device_type type, mal_uint32* // NAME is the ID. mal_strncpy_s(pInfo->id.str, sizeof(pInfo->id.str), NAME ? NAME : "", (size_t)-1); + + // NAME -> "hw:%d,%d" + if (colonPos != -1 && NAME != NULL) { + // We need to convert the NAME string to "hw:%d,%d" format. + char* cardStr = NAME + 3; + for (;;) { + if (cardStr[0] == '\0') { + cardStr = NULL; + break; + } + if (cardStr[0] == 'C' && cardStr[1] == 'A' && cardStr[2] == 'R' && cardStr[3] == 'D' && cardStr[4] == '=') { + cardStr = cardStr + 5; + break; + } + + cardStr += 1; + } + + if (cardStr != NULL) { + char* deviceStr = cardStr + 1; + for (;;) { + if (deviceStr[0] == '\0') { + deviceStr = NULL; + break; + } + if (deviceStr[0] == ',') { + deviceStr[0] = '\0'; // This is the comma after the "CARD=###" part. + } else { + if (deviceStr[0] == 'D' && deviceStr[1] == 'E' && deviceStr[2] == 'V' && deviceStr[3] == '=') { + deviceStr = deviceStr + 4; + break; + } + } + + deviceStr += 1; + } + + if (deviceStr != NULL) { + int cardIndex = snd_card_get_index(cardStr); + if (cardIndex >= 0) { + sprintf(pInfo->id.str, "hw:%d,%s", cardIndex, deviceStr); + } + } + } + } + // DESC is the name, followed by the description on a new line. int lfPos = 0; mal_find_char(DESC, '\n', &lfPos); - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), DESC ? DESC : "", (lfPos != -1) ? (size_t)lfPos : (size_t)-1); pInfo += 1; @@ -2225,7 +2276,7 @@ static mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type typ if (snd_pcm_open((snd_pcm_t**)&pDevice->alsa.pPCM, deviceName, (type == mal_device_type_playback) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, 0) < 0) { if (mal_strcmp(deviceName, "default") == 0 || mal_strcmp(deviceName, "pulse") == 0) { // We may have failed to open the "default" or "pulse" device, in which case try falling back to "plughw:0,0". - mal_strncpy_s(deviceName, "plughw:0,0"); + mal_strncpy_s(deviceName, sizeof(deviceName), "plughw:0,0", (size_t)-1); if (snd_pcm_open((snd_pcm_t**)&pDevice->alsa.pPCM, deviceName, (type == mal_device_type_playback) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, 0) < 0) { mal_device_uninit__alsa(pDevice); return mal_post_error(pDevice, "[ALSA] snd_pcm_open() failed.", MAL_ALSA_FAILED_TO_OPEN_DEVICE);