src/audio/alsa/SDL_alsa_audio.c
changeset 10284 bd207addc5ec
parent 10282 9bbe05f2612a
child 10285 9859cda24699
equal deleted inserted replaced
10283:9a277db2806d 10284:bd207addc5ec
    27 #include <sys/types.h>
    27 #include <sys/types.h>
    28 #include <signal.h>             /* For kill() */
    28 #include <signal.h>             /* For kill() */
    29 #include <errno.h>
    29 #include <errno.h>
    30 #include <string.h>
    30 #include <string.h>
    31 
    31 
       
    32 #include "SDL_assert.h"
    32 #include "SDL_timer.h"
    33 #include "SDL_timer.h"
    33 #include "SDL_audio.h"
    34 #include "SDL_audio.h"
    34 #include "../SDL_audiomem.h"
    35 #include "../SDL_audiomem.h"
    35 #include "../SDL_audio_c.h"
    36 #include "../SDL_audio_c.h"
    36 #include "SDL_alsa_audio.h"
    37 #include "SDL_alsa_audio.h"
   658 
   659 
   659     /* We're ready to rock and roll. :-) */
   660     /* We're ready to rock and roll. :-) */
   660     return 0;
   661     return 0;
   661 }
   662 }
   662 
   663 
       
   664 typedef struct ALSA_Device
       
   665 {
       
   666     char *name;
       
   667     SDL_bool iscapture;
       
   668     struct ALSA_Device *next;
       
   669 } ALSA_Device;
       
   670 
       
   671 static void
       
   672 add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
       
   673 {
       
   674     ALSA_Device *dev = SDL_malloc(sizeof (ALSA_Device));
       
   675     char *desc = ALSA_snd_device_name_get_hint(hint, "DESC");
       
   676     char *handle = NULL;
       
   677     char *ptr;
       
   678 
       
   679     if (!desc) {
       
   680         SDL_free(dev);
       
   681         return;
       
   682     } else if (!dev) {
       
   683         free(desc);
       
   684         return;
       
   685     }
       
   686 
       
   687     SDL_assert(name != NULL);
       
   688 
       
   689     /* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output".
       
   690        just chop the extra lines off, this seems to get a reasonable device
       
   691        name without extra details. */
       
   692     if ((ptr = strchr(desc, '\n')) != NULL) {
       
   693         *ptr = '\0';
       
   694     }
       
   695 
       
   696     /*printf("ALSA: adding %s device '%s' (%s)\n", iscapture ? "capture" : "output", name, desc);*/
       
   697 
       
   698     handle = SDL_strdup(name);
       
   699     if (!handle) {
       
   700         free(desc);
       
   701         SDL_free(dev);
       
   702         return;
       
   703     }
       
   704 
       
   705     SDL_AddAudioDevice(iscapture, desc, handle);
       
   706     free(desc);
       
   707 
       
   708     dev->name = handle;
       
   709     dev->iscapture = iscapture;
       
   710     dev->next = *pSeen;
       
   711     *pSeen = dev;
       
   712 }
       
   713 
       
   714 
       
   715 static SDL_atomic_t ALSA_hotplug_shutdown;
       
   716 static SDL_Thread *ALSA_hotplug_thread;
       
   717 
       
   718 static int SDLCALL
       
   719 ALSA_HotplugThread(void *arg)
       
   720 {
       
   721     SDL_sem *first_run_semaphore = (SDL_sem *) arg;
       
   722     ALSA_Device *devices = NULL;
       
   723     ALSA_Device *next;
       
   724     ALSA_Device *dev;
       
   725     Uint32 ticks;
       
   726 
       
   727     while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
       
   728         void **hints = NULL;
       
   729         if (ALSA_snd_device_name_hint(-1, "pcm", &hints) != -1) {
       
   730             ALSA_Device *unseen = devices;
       
   731             ALSA_Device *seen = NULL;
       
   732             ALSA_Device *prev;
       
   733             int i;
       
   734 
       
   735             for (i = 0; hints[i]; i++) {
       
   736                 char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
       
   737                 if (!name) {
       
   738                     continue;
       
   739                 }
       
   740 
       
   741                 /* only want physical hardware interfaces */
       
   742                 if (SDL_strncmp(name, "hw:", 3) == 0) {
       
   743                     char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
       
   744                     const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
       
   745                     const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
       
   746                     SDL_bool have_output = SDL_FALSE;
       
   747                     SDL_bool have_input = SDL_FALSE;
       
   748 
       
   749                     free(ioid);
       
   750 
       
   751                     if (!isoutput && !isinput) {
       
   752                         free(name);
       
   753                         continue;
       
   754                     }
       
   755 
       
   756                     prev = NULL;
       
   757                     for (dev = unseen; dev; dev = next) {
       
   758                         next = dev->next;
       
   759                         if ( (SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture)) ) {
       
   760                             if (prev) {
       
   761                                 prev->next = next;
       
   762                             } else {
       
   763                                 unseen = next;
       
   764                             }
       
   765                             dev->next = seen;
       
   766                             seen = dev;
       
   767                             if (isinput) have_input = SDL_TRUE;
       
   768                             if (isoutput) have_output = SDL_TRUE;
       
   769                         } else {
       
   770                             prev = dev;
       
   771                         }
       
   772                     }
       
   773 
       
   774                     if (isinput && !have_input) {
       
   775                         add_device(SDL_TRUE, name, hints[i], &seen);
       
   776                     }
       
   777                     if (isoutput && !have_output) {
       
   778                         add_device(SDL_FALSE, name, hints[i], &seen);
       
   779                     }
       
   780                 }
       
   781 
       
   782                 free(name);
       
   783             }
       
   784 
       
   785             ALSA_snd_device_name_free_hint(hints);
       
   786 
       
   787             devices = seen;   /* now we have a known-good list of attached devices. */
       
   788 
       
   789             /* report anything still in unseen as removed. */
       
   790             for (dev = unseen; dev; dev = next) {
       
   791                 /*printf("ALSA: removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
       
   792                 next = dev->next;
       
   793                 SDL_RemoveAudioDevice(dev->iscapture, dev->name);
       
   794                 SDL_free(dev->name);
       
   795                 SDL_free(dev);
       
   796             }
       
   797         }
       
   798 
       
   799         /* On first run, tell ALSA_DetectDevices() that we have a complete device list so it can return. */
       
   800         if (first_run_semaphore) {
       
   801             SDL_SemPost(first_run_semaphore);
       
   802             first_run_semaphore = NULL;  /* let other thread clean it up. */
       
   803         }
       
   804 
       
   805         /* Block awhile before checking again, unless we're told to stop. */
       
   806         ticks = SDL_GetTicks() + 5000;
       
   807         while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks))
       
   808             SDL_Delay(100);
       
   809         }
       
   810     }
       
   811 
       
   812     /* Shutting down! Clean up any data we've gathered. */
       
   813     for (dev = devices; dev; dev = next) {
       
   814         /*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
       
   815         next = dev->next;
       
   816         SDL_free(dev->name);
       
   817         SDL_free(dev);
       
   818     }
       
   819 
       
   820     return 0;
       
   821 }
       
   822 
       
   823 static void
       
   824 ALSA_DetectDevices(void)
       
   825 {
       
   826     /* Start the device detection thread here, wait for an initial iteration to complete. */
       
   827     SDL_sem *semaphore = SDL_CreateSemaphore(0);
       
   828     if (!semaphore) {
       
   829         return;  /* oh well. */
       
   830     }
       
   831 
       
   832     SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
       
   833 
       
   834     ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", semaphore);
       
   835     if (ALSA_hotplug_thread) {
       
   836         SDL_SemWait(semaphore);  /* wait for the first iteration to finish. */
       
   837     }
       
   838 
       
   839     SDL_DestroySemaphore(semaphore);
       
   840 }
       
   841 
   663 static void
   842 static void
   664 ALSA_Deinitialize(void)
   843 ALSA_Deinitialize(void)
   665 {
   844 {
       
   845     if (ALSA_hotplug_thread != NULL) {
       
   846         SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
       
   847         SDL_WaitThread(ALSA_hotplug_thread, NULL);
       
   848         ALSA_hotplug_thread = NULL;
       
   849     }
       
   850 
   666     UnloadALSALibrary();
   851     UnloadALSALibrary();
   667 }
   852 }
   668 
       
   669 static void
       
   670 add_device(const int iscapture, const char *name, const char *_desc)
       
   671 {
       
   672     char *desc = NULL;
       
   673     char *handle = NULL;
       
   674     char *ptr = NULL;
       
   675 
       
   676     if (!name || !_desc) {
       
   677         return;  /* nothing we can do with this...? */
       
   678     }
       
   679 
       
   680     desc = SDL_strdup(_desc);
       
   681     if (!desc) {
       
   682         return;  /* oh well, out of memory. Skip it. */
       
   683     }
       
   684 
       
   685     /* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output" */
       
   686     for (ptr = strchr(desc, '\n'); ptr; ptr = strchr(ptr + 1, '\n')) {
       
   687         *ptr = ' ';
       
   688     }
       
   689 
       
   690     handle = SDL_strdup(name);
       
   691     if (handle != NULL) {
       
   692         SDL_AddAudioDevice(iscapture, desc, handle);
       
   693     }
       
   694 
       
   695     SDL_free(desc);
       
   696 }
       
   697 
       
   698 static void
       
   699 ALSA_DetectDevices(void)
       
   700 {
       
   701     void **hints = NULL;
       
   702     int i;
       
   703 
       
   704     /* !!! FIXME: use udev instead. */
       
   705     /* We won't deal with disconnects and hotplugs without udev, but at least
       
   706        you'll get a reasonable device list at startup. */
       
   707 #if 1 /*!SDL_USE_LIBUDEV */
       
   708     if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == -1) {
       
   709         return;  /* oh well. */
       
   710     }
       
   711 
       
   712     for (i = 0; hints[i]; i++) {
       
   713         char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
       
   714         char *desc = ALSA_snd_device_name_get_hint(hints[i], "DESC");
       
   715         char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
       
   716 
       
   717         /* only want physical hardware interfaces */
       
   718         if (SDL_strncmp(name, "hw:", 3) == 0) {
       
   719             if ((ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0)) {
       
   720                 add_device(SDL_FALSE, name, desc);
       
   721             }
       
   722 
       
   723             if ((ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0)) {
       
   724                 add_device(SDL_TRUE, name, desc);
       
   725             }
       
   726         }
       
   727 
       
   728         free(name);
       
   729         free(desc);
       
   730         free(ioid);
       
   731     }
       
   732 
       
   733     ALSA_snd_device_name_free_hint(hints);
       
   734 #else
       
   735 #error Fill in udev support here.
       
   736 #endif
       
   737 }
       
   738 
       
   739 static void
       
   740 ALSA_FreeDeviceHandle(void *handle)
       
   741 {
       
   742 #if 1 /*!SDL_USE_LIBUDEV*/
       
   743     SDL_free(handle);
       
   744 #else
       
   745 #error Fill in udev support here.
       
   746 #endif
       
   747 }
       
   748 
       
   749 
   853 
   750 static int
   854 static int
   751 ALSA_Init(SDL_AudioDriverImpl * impl)
   855 ALSA_Init(SDL_AudioDriverImpl * impl)
   752 {
   856 {
   753     if (LoadALSALibrary() < 0) {
   857     if (LoadALSALibrary() < 0) {
   760     impl->WaitDevice = ALSA_WaitDevice;
   864     impl->WaitDevice = ALSA_WaitDevice;
   761     impl->GetDeviceBuf = ALSA_GetDeviceBuf;
   865     impl->GetDeviceBuf = ALSA_GetDeviceBuf;
   762     impl->PlayDevice = ALSA_PlayDevice;
   866     impl->PlayDevice = ALSA_PlayDevice;
   763     impl->CloseDevice = ALSA_CloseDevice;
   867     impl->CloseDevice = ALSA_CloseDevice;
   764     impl->Deinitialize = ALSA_Deinitialize;
   868     impl->Deinitialize = ALSA_Deinitialize;
   765     impl->FreeDeviceHandle = ALSA_FreeDeviceHandle;
       
   766 
   869 
   767     return 1;   /* this audio target is available. */
   870     return 1;   /* this audio target is available. */
   768 }
   871 }
   769 
   872 
   770 
   873