alsa: Implemented basic device detection. This is kind of nasty, because ALSA reports dozens of "devices" that aren't really things you'd ever want, or things that should be listed this way, but the default path still works as before, and it at least allows these devices to be available to apps. This does not handle hotplugging yet. You get a device list at init time and that's it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c
index af95237..d073595 100644
--- a/src/audio/alsa/SDL_alsa_audio.c
+++ b/src/audio/alsa/SDL_alsa_audio.c
@@ -85,6 +85,9 @@ static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int);
static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
static int (*ALSA_snd_pcm_sw_params_set_avail_min)
(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
+static int (*ALSA_snd_device_name_hint) (int, const char *, void ***);
+static char* (*ALSA_snd_device_name_get_hint) (const void *, const char *);
+static int (*ALSA_snd_device_name_free_hint) (void **);
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
@@ -144,6 +147,10 @@ load_alsa_syms(void)
SDL_ALSA_SYM(snd_pcm_nonblock);
SDL_ALSA_SYM(snd_pcm_wait);
SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
+ SDL_ALSA_SYM(snd_device_name_hint);
+ SDL_ALSA_SYM(snd_device_name_get_hint);
+ SDL_ALSA_SYM(snd_device_name_free_hint);
+
return 0;
}
@@ -196,25 +203,27 @@ LoadALSALibrary(void)
#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
static const char *
-get_audio_device(int channels)
+get_audio_device(void *handle, const int channels)
{
const char *device;
+ if (handle != NULL) {
+ return (const char *) handle;
+ }
+
+ /* !!! FIXME: we also check "SDL_AUDIO_DEVICE_NAME" at the higher level. */
device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
- if (device == NULL) {
- switch (channels) {
- case 6:
- device = "plug:surround51";
- break;
- case 4:
- device = "plug:surround40";
- break;
- default:
- device = "default";
- break;
- }
+ if (device != NULL) {
+ return device;
+ }
+
+ if (channels == 6) {
+ return "plug:surround51";
+ } else if (channels == 4) {
+ return "plug:surround40";
}
- return device;
+
+ return "default";
}
@@ -487,7 +496,7 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
/* Open the audio device */
/* Name of device should depend on # channels in spec */
status = ALSA_snd_pcm_open(&pcm_handle,
- get_audio_device(this->spec.channels),
+ get_audio_device(handle, this->spec.channels),
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (status < 0) {
@@ -656,6 +665,79 @@ ALSA_Deinitialize(void)
UnloadALSALibrary();
}
+static void
+add_device(const int iscapture, const char *name, const char *_desc)
+{
+ char *desc = SDL_strdup(_desc);
+ char *handle = NULL;
+ char *ptr;
+
+ if (!desc) {
+ return;
+ }
+
+ /* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output" */
+ for (ptr = strchr(desc, '\n'); ptr; ptr = strchr(ptr + 1, '\n')) {
+ *ptr = ' ';
+ }
+
+ handle = SDL_strdup(name);
+ if (handle != NULL) {
+ SDL_AddAudioDevice(iscapture, desc, handle);
+ }
+
+ SDL_free(desc);
+}
+
+static void
+ALSA_DetectDevices(void)
+{
+ void **hints = NULL;
+ int i;
+
+ /* !!! FIXME: use udev instead. */
+ /* We won't deal with disconnects and hotplugs without udev, but at least
+ you'll get a reasonable device list at startup. */
+#if 1 /*!SDL_USE_LIBUDEV */
+ if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == -1) {
+ return; /* oh well. */
+ }
+
+ for (i = 0; hints[i]; i++) {
+ char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
+ char *desc = ALSA_snd_device_name_get_hint(hints[i], "DESC");
+ char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
+
+ if ((ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0)) {
+ add_device(SDL_FALSE, name, desc);
+ }
+
+ if ((ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0)) {
+ add_device(SDL_TRUE, name, desc);
+ }
+
+ free(name);
+ free(desc);
+ free(ioid);
+ }
+
+ ALSA_snd_device_name_free_hint(hints);
+#else
+#error Fill in udev support here.
+#endif
+}
+
+static void
+ALSA_FreeDeviceHandle(void *handle)
+{
+#if 1 /*!SDL_USE_LIBUDEV*/
+ SDL_free(handle);
+#else
+#error Fill in udev support here.
+#endif
+}
+
+
static int
ALSA_Init(SDL_AudioDriverImpl * impl)
{
@@ -664,13 +746,14 @@ ALSA_Init(SDL_AudioDriverImpl * impl)
}
/* Set the function pointers */
+ impl->DetectDevices = ALSA_DetectDevices;
impl->OpenDevice = ALSA_OpenDevice;
impl->WaitDevice = ALSA_WaitDevice;
impl->GetDeviceBuf = ALSA_GetDeviceBuf;
impl->PlayDevice = ALSA_PlayDevice;
impl->CloseDevice = ALSA_CloseDevice;
impl->Deinitialize = ALSA_Deinitialize;
- impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: Add device enum! */
+ impl->FreeDeviceHandle = ALSA_FreeDeviceHandle;
return 1; /* this audio target is available. */
}