src/audio/alsa/SDL_alsa_audio.c
changeset 10286 3b884985835c
parent 10239 f17581d00c26
parent 10285 9859cda24699
equal deleted inserted replaced
10267:b1f031450499 10286:3b884985835c
   697 
   697 
   698     /* We're ready to rock and roll. :-) */
   698     /* We're ready to rock and roll. :-) */
   699     return 0;
   699     return 0;
   700 }
   700 }
   701 
   701 
       
   702 typedef struct ALSA_Device
       
   703 {
       
   704     char *name;
       
   705     SDL_bool iscapture;
       
   706     struct ALSA_Device *next;
       
   707 } ALSA_Device;
       
   708 
       
   709 static void
       
   710 add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
       
   711 {
       
   712     ALSA_Device *dev = SDL_malloc(sizeof (ALSA_Device));
       
   713     char *desc = ALSA_snd_device_name_get_hint(hint, "DESC");
       
   714     char *handle = NULL;
       
   715     char *ptr;
       
   716 
       
   717     if (!desc) {
       
   718         SDL_free(dev);
       
   719         return;
       
   720     } else if (!dev) {
       
   721         free(desc);
       
   722         return;
       
   723     }
       
   724 
       
   725     SDL_assert(name != NULL);
       
   726 
       
   727     /* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output".
       
   728        just chop the extra lines off, this seems to get a reasonable device
       
   729        name without extra details. */
       
   730     if ((ptr = strchr(desc, '\n')) != NULL) {
       
   731         *ptr = '\0';
       
   732     }
       
   733 
       
   734     /*printf("ALSA: adding %s device '%s' (%s)\n", iscapture ? "capture" : "output", name, desc);*/
       
   735 
       
   736     handle = SDL_strdup(name);
       
   737     if (!handle) {
       
   738         free(desc);
       
   739         SDL_free(dev);
       
   740         return;
       
   741     }
       
   742 
       
   743     SDL_AddAudioDevice(iscapture, desc, handle);
       
   744     free(desc);
       
   745 
       
   746     dev->name = handle;
       
   747     dev->iscapture = iscapture;
       
   748     dev->next = *pSeen;
       
   749     *pSeen = dev;
       
   750 }
       
   751 
       
   752 
       
   753 static SDL_atomic_t ALSA_hotplug_shutdown;
       
   754 static SDL_Thread *ALSA_hotplug_thread;
       
   755 
       
   756 static int SDLCALL
       
   757 ALSA_HotplugThread(void *arg)
       
   758 {
       
   759     SDL_sem *first_run_semaphore = (SDL_sem *) arg;
       
   760     ALSA_Device *devices = NULL;
       
   761     ALSA_Device *next;
       
   762     ALSA_Device *dev;
       
   763     Uint32 ticks;
       
   764 
       
   765     while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
       
   766         void **hints = NULL;
       
   767         if (ALSA_snd_device_name_hint(-1, "pcm", &hints) != -1) {
       
   768             ALSA_Device *unseen = devices;
       
   769             ALSA_Device *seen = NULL;
       
   770             ALSA_Device *prev;
       
   771             int i;
       
   772 
       
   773             for (i = 0; hints[i]; i++) {
       
   774                 char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
       
   775                 if (!name) {
       
   776                     continue;
       
   777                 }
       
   778 
       
   779                 /* only want physical hardware interfaces */
       
   780                 if (SDL_strncmp(name, "hw:", 3) == 0) {
       
   781                     char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
       
   782                     const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
       
   783                     const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
       
   784                     SDL_bool have_output = SDL_FALSE;
       
   785                     SDL_bool have_input = SDL_FALSE;
       
   786 
       
   787                     free(ioid);
       
   788 
       
   789                     if (!isoutput && !isinput) {
       
   790                         free(name);
       
   791                         continue;
       
   792                     }
       
   793 
       
   794                     prev = NULL;
       
   795                     for (dev = unseen; dev; dev = next) {
       
   796                         next = dev->next;
       
   797                         if ( (SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture)) ) {
       
   798                             if (prev) {
       
   799                                 prev->next = next;
       
   800                             } else {
       
   801                                 unseen = next;
       
   802                             }
       
   803                             dev->next = seen;
       
   804                             seen = dev;
       
   805                             if (isinput) have_input = SDL_TRUE;
       
   806                             if (isoutput) have_output = SDL_TRUE;
       
   807                         } else {
       
   808                             prev = dev;
       
   809                         }
       
   810                     }
       
   811 
       
   812                     if (isinput && !have_input) {
       
   813                         add_device(SDL_TRUE, name, hints[i], &seen);
       
   814                     }
       
   815                     if (isoutput && !have_output) {
       
   816                         add_device(SDL_FALSE, name, hints[i], &seen);
       
   817                     }
       
   818                 }
       
   819 
       
   820                 free(name);
       
   821             }
       
   822 
       
   823             ALSA_snd_device_name_free_hint(hints);
       
   824 
       
   825             devices = seen;   /* now we have a known-good list of attached devices. */
       
   826 
       
   827             /* report anything still in unseen as removed. */
       
   828             for (dev = unseen; dev; dev = next) {
       
   829                 /*printf("ALSA: removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
       
   830                 next = dev->next;
       
   831                 SDL_RemoveAudioDevice(dev->iscapture, dev->name);
       
   832                 SDL_free(dev->name);
       
   833                 SDL_free(dev);
       
   834             }
       
   835         }
       
   836 
       
   837         /* On first run, tell ALSA_DetectDevices() that we have a complete device list so it can return. */
       
   838         if (first_run_semaphore) {
       
   839             SDL_SemPost(first_run_semaphore);
       
   840             first_run_semaphore = NULL;  /* let other thread clean it up. */
       
   841         }
       
   842 
       
   843         /* Block awhile before checking again, unless we're told to stop. */
       
   844         ticks = SDL_GetTicks() + 5000;
       
   845         while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks)) {
       
   846             SDL_Delay(100);
       
   847         }
       
   848     }
       
   849 
       
   850     /* Shutting down! Clean up any data we've gathered. */
       
   851     for (dev = devices; dev; dev = next) {
       
   852         /*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
       
   853         next = dev->next;
       
   854         SDL_free(dev->name);
       
   855         SDL_free(dev);
       
   856     }
       
   857 
       
   858     return 0;
       
   859 }
       
   860 
       
   861 static void
       
   862 ALSA_DetectDevices(void)
       
   863 {
       
   864     /* Start the device detection thread here, wait for an initial iteration to complete. */
       
   865     SDL_sem *semaphore = SDL_CreateSemaphore(0);
       
   866     if (!semaphore) {
       
   867         return;  /* oh well. */
       
   868     }
       
   869 
       
   870     SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
       
   871 
       
   872     ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", semaphore);
       
   873     if (ALSA_hotplug_thread) {
       
   874         SDL_SemWait(semaphore);  /* wait for the first iteration to finish. */
       
   875     }
       
   876 
       
   877     SDL_DestroySemaphore(semaphore);
       
   878 }
       
   879 
   702 static void
   880 static void
   703 ALSA_Deinitialize(void)
   881 ALSA_Deinitialize(void)
   704 {
   882 {
       
   883     if (ALSA_hotplug_thread != NULL) {
       
   884         SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
       
   885         SDL_WaitThread(ALSA_hotplug_thread, NULL);
       
   886         ALSA_hotplug_thread = NULL;
       
   887     }
       
   888 
   705     UnloadALSALibrary();
   889     UnloadALSALibrary();
   706 }
   890 }
   707 
       
   708 static void
       
   709 add_device(const int iscapture, const char *name, const char *_desc)
       
   710 {
       
   711     char *desc = NULL;
       
   712     char *handle = NULL;
       
   713     char *ptr = NULL;
       
   714 
       
   715     if (!name || !_desc) {
       
   716         return;  /* nothing we can do with this...? */
       
   717     }
       
   718 
       
   719     desc = SDL_strdup(_desc);
       
   720     if (!desc) {
       
   721         return;  /* oh well, out of memory. Skip it. */
       
   722     }
       
   723 
       
   724     /* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output" */
       
   725     for (ptr = strchr(desc, '\n'); ptr; ptr = strchr(ptr + 1, '\n')) {
       
   726         *ptr = ' ';
       
   727     }
       
   728 
       
   729     handle = SDL_strdup(name);
       
   730     if (handle != NULL) {
       
   731         SDL_AddAudioDevice(iscapture, desc, handle);
       
   732     }
       
   733 
       
   734     SDL_free(desc);
       
   735 }
       
   736 
       
   737 static void
       
   738 ALSA_DetectDevices(void)
       
   739 {
       
   740     void **hints = NULL;
       
   741     int i;
       
   742 
       
   743     /* !!! FIXME: use udev instead. */
       
   744     /* We won't deal with disconnects and hotplugs without udev, but at least
       
   745        you'll get a reasonable device list at startup. */
       
   746 #if 1 /*!SDL_USE_LIBUDEV */
       
   747     if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == -1) {
       
   748         return;  /* oh well. */
       
   749     }
       
   750 
       
   751     for (i = 0; hints[i]; i++) {
       
   752         char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
       
   753         char *desc = ALSA_snd_device_name_get_hint(hints[i], "DESC");
       
   754         char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
       
   755 
       
   756         if ((ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0)) {
       
   757             add_device(SDL_FALSE, name, desc);
       
   758         }
       
   759 
       
   760         if ((ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0)) {
       
   761             add_device(SDL_TRUE, name, desc);
       
   762         }
       
   763 
       
   764         free(name);
       
   765         free(desc);
       
   766         free(ioid);
       
   767     }
       
   768 
       
   769     ALSA_snd_device_name_free_hint(hints);
       
   770 #else
       
   771 #error Fill in udev support here.
       
   772 #endif
       
   773 }
       
   774 
       
   775 static void
       
   776 ALSA_FreeDeviceHandle(void *handle)
       
   777 {
       
   778 #if 1 /*!SDL_USE_LIBUDEV*/
       
   779     SDL_free(handle);
       
   780 #else
       
   781 #error Fill in udev support here.
       
   782 #endif
       
   783 }
       
   784 
       
   785 
   891 
   786 static int
   892 static int
   787 ALSA_Init(SDL_AudioDriverImpl * impl)
   893 ALSA_Init(SDL_AudioDriverImpl * impl)
   788 {
   894 {
   789     if (LoadALSALibrary() < 0) {
   895     if (LoadALSALibrary() < 0) {
   796     impl->WaitDevice = ALSA_WaitDevice;
   902     impl->WaitDevice = ALSA_WaitDevice;
   797     impl->GetDeviceBuf = ALSA_GetDeviceBuf;
   903     impl->GetDeviceBuf = ALSA_GetDeviceBuf;
   798     impl->PlayDevice = ALSA_PlayDevice;
   904     impl->PlayDevice = ALSA_PlayDevice;
   799     impl->CloseDevice = ALSA_CloseDevice;
   905     impl->CloseDevice = ALSA_CloseDevice;
   800     impl->Deinitialize = ALSA_Deinitialize;
   906     impl->Deinitialize = ALSA_Deinitialize;
   801     impl->FreeDeviceHandle = ALSA_FreeDeviceHandle;
       
   802     impl->CaptureFromDevice = ALSA_CaptureFromDevice;
   907     impl->CaptureFromDevice = ALSA_CaptureFromDevice;
   803     impl->FlushCapture = ALSA_FlushCapture;
   908     impl->FlushCapture = ALSA_FlushCapture;
   804 
   909 
   805     impl->HasCaptureSupport = SDL_TRUE;
   910     impl->HasCaptureSupport = SDL_TRUE;
   806 
   911