mirror of
				https://github.com/Ryujinx/SDL.git
				synced 2025-11-04 13:44:50 +00:00 
			
		
		
		
	alsa: Implemented hotplug support, cleaned up device names.
This commit is contained in:
		
							parent
							
								
									35e564a694
								
							
						
					
					
						commit
						850da32f30
					
				| 
						 | 
				
			
			@ -29,6 +29,7 @@
 | 
			
		|||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "SDL_assert.h"
 | 
			
		||||
#include "SDL_timer.h"
 | 
			
		||||
#include "SDL_audio.h"
 | 
			
		||||
#include "../SDL_audiomem.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -660,92 +661,195 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 | 
			
		|||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
ALSA_Deinitialize(void)
 | 
			
		||||
typedef struct ALSA_Device
 | 
			
		||||
{
 | 
			
		||||
    UnloadALSALibrary();
 | 
			
		||||
}
 | 
			
		||||
    char *name;
 | 
			
		||||
    SDL_bool iscapture;
 | 
			
		||||
    struct ALSA_Device *next;
 | 
			
		||||
} ALSA_Device;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
add_device(const int iscapture, const char *name, const char *_desc)
 | 
			
		||||
add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
 | 
			
		||||
{
 | 
			
		||||
    char *desc = NULL;
 | 
			
		||||
    ALSA_Device *dev = SDL_malloc(sizeof (ALSA_Device));
 | 
			
		||||
    char *desc = ALSA_snd_device_name_get_hint(hint, "DESC");
 | 
			
		||||
    char *handle = NULL;
 | 
			
		||||
    char *ptr = NULL;
 | 
			
		||||
    char *ptr;
 | 
			
		||||
 | 
			
		||||
    if (!name || !_desc) {
 | 
			
		||||
        return;  /* nothing we can do with this...? */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    desc = SDL_strdup(_desc);
 | 
			
		||||
    if (!desc) {
 | 
			
		||||
        return;  /* oh well, out of memory. Skip it. */
 | 
			
		||||
        SDL_free(dev);
 | 
			
		||||
        return;
 | 
			
		||||
    } else if (!dev) {
 | 
			
		||||
        free(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 = ' ';
 | 
			
		||||
    SDL_assert(name != NULL);
 | 
			
		||||
 | 
			
		||||
    /* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output".
 | 
			
		||||
       just chop the extra lines off, this seems to get a reasonable device
 | 
			
		||||
       name without extra details. */
 | 
			
		||||
    if ((ptr = strchr(desc, '\n')) != NULL) {
 | 
			
		||||
        *ptr = '\0';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*printf("ALSA: adding %s device '%s' (%s)\n", iscapture ? "capture" : "output", name, desc);*/
 | 
			
		||||
 | 
			
		||||
    handle = SDL_strdup(name);
 | 
			
		||||
    if (handle != NULL) {
 | 
			
		||||
        SDL_AddAudioDevice(iscapture, desc, handle);
 | 
			
		||||
    if (!handle) {
 | 
			
		||||
        free(desc);
 | 
			
		||||
        SDL_free(dev);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SDL_free(desc);
 | 
			
		||||
    SDL_AddAudioDevice(iscapture, desc, handle);
 | 
			
		||||
    free(desc);
 | 
			
		||||
 | 
			
		||||
    dev->name = handle;
 | 
			
		||||
    dev->iscapture = iscapture;
 | 
			
		||||
    dev->next = *pSeen;
 | 
			
		||||
    *pSeen = dev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static SDL_atomic_t ALSA_hotplug_shutdown;
 | 
			
		||||
static SDL_Thread *ALSA_hotplug_thread;
 | 
			
		||||
 | 
			
		||||
static int SDLCALL
 | 
			
		||||
ALSA_HotplugThread(void *arg)
 | 
			
		||||
{
 | 
			
		||||
    SDL_sem *first_run_semaphore = (SDL_sem *) arg;
 | 
			
		||||
    ALSA_Device *devices = NULL;
 | 
			
		||||
    ALSA_Device *next;
 | 
			
		||||
    ALSA_Device *dev;
 | 
			
		||||
    Uint32 ticks;
 | 
			
		||||
 | 
			
		||||
    while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
 | 
			
		||||
        void **hints = NULL;
 | 
			
		||||
        if (ALSA_snd_device_name_hint(-1, "pcm", &hints) != -1) {
 | 
			
		||||
            ALSA_Device *unseen = devices;
 | 
			
		||||
            ALSA_Device *seen = NULL;
 | 
			
		||||
            ALSA_Device *prev;
 | 
			
		||||
            int i;
 | 
			
		||||
 | 
			
		||||
            for (i = 0; hints[i]; i++) {
 | 
			
		||||
                char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
 | 
			
		||||
                if (!name) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                /* only want physical hardware interfaces */
 | 
			
		||||
                if (SDL_strncmp(name, "hw:", 3) == 0) {
 | 
			
		||||
                    char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
 | 
			
		||||
                    const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
 | 
			
		||||
                    const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
 | 
			
		||||
                    SDL_bool have_output = SDL_FALSE;
 | 
			
		||||
                    SDL_bool have_input = SDL_FALSE;
 | 
			
		||||
 | 
			
		||||
                    free(ioid);
 | 
			
		||||
 | 
			
		||||
                    if (!isoutput && !isinput) {
 | 
			
		||||
                        free(name);
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    prev = NULL;
 | 
			
		||||
                    for (dev = unseen; dev; dev = next) {
 | 
			
		||||
                        next = dev->next;
 | 
			
		||||
                        if ( (SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture)) ) {
 | 
			
		||||
                            if (prev) {
 | 
			
		||||
                                prev->next = next;
 | 
			
		||||
                            } else {
 | 
			
		||||
                                unseen = next;
 | 
			
		||||
                            }
 | 
			
		||||
                            dev->next = seen;
 | 
			
		||||
                            seen = dev;
 | 
			
		||||
                            if (isinput) have_input = SDL_TRUE;
 | 
			
		||||
                            if (isoutput) have_output = SDL_TRUE;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            prev = dev;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (isinput && !have_input) {
 | 
			
		||||
                        add_device(SDL_TRUE, name, hints[i], &seen);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (isoutput && !have_output) {
 | 
			
		||||
                        add_device(SDL_FALSE, name, hints[i], &seen);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                free(name);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ALSA_snd_device_name_free_hint(hints);
 | 
			
		||||
 | 
			
		||||
            devices = seen;   /* now we have a known-good list of attached devices. */
 | 
			
		||||
 | 
			
		||||
            /* report anything still in unseen as removed. */
 | 
			
		||||
            for (dev = unseen; dev; dev = next) {
 | 
			
		||||
                /*printf("ALSA: removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
 | 
			
		||||
                next = dev->next;
 | 
			
		||||
                SDL_RemoveAudioDevice(dev->iscapture, dev->name);
 | 
			
		||||
                SDL_free(dev->name);
 | 
			
		||||
                SDL_free(dev);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* On first run, tell ALSA_DetectDevices() that we have a complete device list so it can return. */
 | 
			
		||||
        if (first_run_semaphore) {
 | 
			
		||||
            SDL_SemPost(first_run_semaphore);
 | 
			
		||||
            first_run_semaphore = NULL;  /* let other thread clean it up. */
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Block awhile before checking again, unless we're told to stop. */
 | 
			
		||||
        ticks = SDL_GetTicks() + 5000;
 | 
			
		||||
        while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks))
 | 
			
		||||
            SDL_Delay(100);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Shutting down! Clean up any data we've gathered. */
 | 
			
		||||
    for (dev = devices; dev; dev = next) {
 | 
			
		||||
        /*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
 | 
			
		||||
        next = dev->next;
 | 
			
		||||
        SDL_free(dev->name);
 | 
			
		||||
        SDL_free(dev);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
    /* Start the device detection thread here, wait for an initial iteration to complete. */
 | 
			
		||||
    SDL_sem *semaphore = SDL_CreateSemaphore(0);
 | 
			
		||||
    if (!semaphore) {
 | 
			
		||||
        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");
 | 
			
		||||
    SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
 | 
			
		||||
 | 
			
		||||
        /* only want physical hardware interfaces */
 | 
			
		||||
        if (SDL_strncmp(name, "hw:", 3) == 0) {
 | 
			
		||||
            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_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", semaphore);
 | 
			
		||||
    if (ALSA_hotplug_thread) {
 | 
			
		||||
        SDL_SemWait(semaphore);  /* wait for the first iteration to finish. */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ALSA_snd_device_name_free_hint(hints);
 | 
			
		||||
#else
 | 
			
		||||
#error Fill in udev support here.
 | 
			
		||||
#endif
 | 
			
		||||
    SDL_DestroySemaphore(semaphore);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
ALSA_FreeDeviceHandle(void *handle)
 | 
			
		||||
ALSA_Deinitialize(void)
 | 
			
		||||
{
 | 
			
		||||
#if 1 /*!SDL_USE_LIBUDEV*/
 | 
			
		||||
    SDL_free(handle);
 | 
			
		||||
#else
 | 
			
		||||
#error Fill in udev support here.
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
    if (ALSA_hotplug_thread != NULL) {
 | 
			
		||||
        SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
 | 
			
		||||
        SDL_WaitThread(ALSA_hotplug_thread, NULL);
 | 
			
		||||
        ALSA_hotplug_thread = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    UnloadALSALibrary();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
ALSA_Init(SDL_AudioDriverImpl * impl)
 | 
			
		||||
| 
						 | 
				
			
			@ -762,7 +866,6 @@ ALSA_Init(SDL_AudioDriverImpl * impl)
 | 
			
		|||
    impl->PlayDevice = ALSA_PlayDevice;
 | 
			
		||||
    impl->CloseDevice = ALSA_CloseDevice;
 | 
			
		||||
    impl->Deinitialize = ALSA_Deinitialize;
 | 
			
		||||
    impl->FreeDeviceHandle = ALSA_FreeDeviceHandle;
 | 
			
		||||
 | 
			
		||||
    return 1;   /* this audio target is available. */
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue