src/audio/esd/SDL_esdaudio.c
author Ryan C. Gordon
Sun, 03 Jan 2016 20:52:44 -0500
changeset 10004 8f2f519d1e61
parent 9998 f67cf37e9cd4
child 10255 9530fc07da6c
permissions -rw-r--r--
CMake: Don't make a libSDL2.so symlink on Mac OS X (do .dylib instead).
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_AUDIO_DRIVER_ESD
    24 
    25 /* Allow access to an ESD network stream mixing buffer */
    26 
    27 #include <sys/types.h>
    28 #include <unistd.h>
    29 #include <signal.h>
    30 #include <errno.h>
    31 #include <esd.h>
    32 
    33 #include "SDL_timer.h"
    34 #include "SDL_audio.h"
    35 #include "../SDL_audiomem.h"
    36 #include "../SDL_audio_c.h"
    37 #include "SDL_esdaudio.h"
    38 
    39 #ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
    40 #include "SDL_name.h"
    41 #include "SDL_loadso.h"
    42 #else
    43 #define SDL_NAME(X) X
    44 #endif
    45 
    46 #ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
    47 
    48 static const char *esd_library = SDL_AUDIO_DRIVER_ESD_DYNAMIC;
    49 static void *esd_handle = NULL;
    50 
    51 static int (*SDL_NAME(esd_open_sound)) (const char *host);
    52 static int (*SDL_NAME(esd_close)) (int esd);
    53 static int (*SDL_NAME(esd_play_stream)) (esd_format_t format, int rate,
    54                                          const char *host, const char *name);
    55 
    56 #define SDL_ESD_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
    57 static struct
    58 {
    59     const char *name;
    60     void **func;
    61 } const esd_functions[] = {
    62     SDL_ESD_SYM(esd_open_sound),
    63     SDL_ESD_SYM(esd_close), SDL_ESD_SYM(esd_play_stream),
    64 };
    65 
    66 #undef SDL_ESD_SYM
    67 
    68 static void
    69 UnloadESDLibrary()
    70 {
    71     if (esd_handle != NULL) {
    72         SDL_UnloadObject(esd_handle);
    73         esd_handle = NULL;
    74     }
    75 }
    76 
    77 static int
    78 LoadESDLibrary(void)
    79 {
    80     int i, retval = -1;
    81 
    82     if (esd_handle == NULL) {
    83         esd_handle = SDL_LoadObject(esd_library);
    84         if (esd_handle) {
    85             retval = 0;
    86             for (i = 0; i < SDL_arraysize(esd_functions); ++i) {
    87                 *esd_functions[i].func =
    88                     SDL_LoadFunction(esd_handle, esd_functions[i].name);
    89                 if (!*esd_functions[i].func) {
    90                     retval = -1;
    91                     UnloadESDLibrary();
    92                     break;
    93                 }
    94             }
    95         }
    96     }
    97     return retval;
    98 }
    99 
   100 #else
   101 
   102 static void
   103 UnloadESDLibrary()
   104 {
   105     return;
   106 }
   107 
   108 static int
   109 LoadESDLibrary(void)
   110 {
   111     return 0;
   112 }
   113 
   114 #endif /* SDL_AUDIO_DRIVER_ESD_DYNAMIC */
   115 
   116 
   117 /* This function waits until it is possible to write a full sound buffer */
   118 static void
   119 ESD_WaitDevice(_THIS)
   120 {
   121     Sint32 ticks;
   122 
   123     /* Check to see if the thread-parent process is still alive */
   124     {
   125         static int cnt = 0;
   126         /* Note that this only works with thread implementations
   127            that use a different process id for each thread.
   128          */
   129         /* Check every 10 loops */
   130         if (this->hidden->parent && (((++cnt) % 10) == 0)) {
   131             if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
   132                 SDL_OpenedAudioDeviceDisconnected(this);
   133             }
   134         }
   135     }
   136 
   137     /* Use timer for general audio synchronization */
   138     ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
   139     if (ticks > 0) {
   140         SDL_Delay(ticks);
   141     }
   142 }
   143 
   144 static void
   145 ESD_PlayDevice(_THIS)
   146 {
   147     int written = 0;
   148 
   149     /* Write the audio data, checking for EAGAIN on broken audio drivers */
   150     do {
   151         written = write(this->hidden->audio_fd,
   152                         this->hidden->mixbuf, this->hidden->mixlen);
   153         if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
   154             SDL_Delay(1);       /* Let a little CPU time go by */
   155         }
   156     } while ((written < 0) &&
   157              ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
   158 
   159     /* Set the next write frame */
   160     this->hidden->next_frame += this->hidden->frame_ticks;
   161 
   162     /* If we couldn't write, assume fatal error for now */
   163     if (written < 0) {
   164         SDL_OpenedAudioDeviceDisconnected(this);
   165     }
   166 }
   167 
   168 static Uint8 *
   169 ESD_GetDeviceBuf(_THIS)
   170 {
   171     return (this->hidden->mixbuf);
   172 }
   173 
   174 static void
   175 ESD_CloseDevice(_THIS)
   176 {
   177     if (this->hidden != NULL) {
   178         SDL_FreeAudioMem(this->hidden->mixbuf);
   179         this->hidden->mixbuf = NULL;
   180         if (this->hidden->audio_fd >= 0) {
   181             SDL_NAME(esd_close) (this->hidden->audio_fd);
   182             this->hidden->audio_fd = -1;
   183         }
   184 
   185         SDL_free(this->hidden);
   186         this->hidden = NULL;
   187     }
   188 }
   189 
   190 /* Try to get the name of the program */
   191 static char *
   192 get_progname(void)
   193 {
   194     char *progname = NULL;
   195 #ifdef __LINUX__
   196     FILE *fp;
   197     static char temp[BUFSIZ];
   198 
   199     SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
   200     fp = fopen(temp, "r");
   201     if (fp != NULL) {
   202         if (fgets(temp, sizeof(temp) - 1, fp)) {
   203             progname = SDL_strrchr(temp, '/');
   204             if (progname == NULL) {
   205                 progname = temp;
   206             } else {
   207                 progname = progname + 1;
   208             }
   209         }
   210         fclose(fp);
   211     }
   212 #endif
   213     return (progname);
   214 }
   215 
   216 
   217 static int
   218 ESD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   219 {
   220     esd_format_t format = (ESD_STREAM | ESD_PLAY);
   221     SDL_AudioFormat test_format = 0;
   222     int found = 0;
   223 
   224     /* Initialize all variables that we clean on shutdown */
   225     this->hidden = (struct SDL_PrivateAudioData *)
   226         SDL_malloc((sizeof *this->hidden));
   227     if (this->hidden == NULL) {
   228         return SDL_OutOfMemory();
   229     }
   230     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   231     this->hidden->audio_fd = -1;
   232 
   233     /* Convert audio spec to the ESD audio format */
   234     /* Try for a closest match on audio format */
   235     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   236          !found && test_format; test_format = SDL_NextAudioFormat()) {
   237 #ifdef DEBUG_AUDIO
   238         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   239 #endif
   240         found = 1;
   241         switch (test_format) {
   242         case AUDIO_U8:
   243             format |= ESD_BITS8;
   244             break;
   245         case AUDIO_S16SYS:
   246             format |= ESD_BITS16;
   247             break;
   248         default:
   249             found = 0;
   250             break;
   251         }
   252     }
   253 
   254     if (!found) {
   255         ESD_CloseDevice(this);
   256         return SDL_SetError("Couldn't find any hardware audio formats");
   257     }
   258 
   259     if (this->spec.channels == 1) {
   260         format |= ESD_MONO;
   261     } else {
   262         format |= ESD_STEREO;
   263     }
   264 #if 0
   265     this->spec.samples = ESD_BUF_SIZE;  /* Darn, no way to change this yet */
   266 #endif
   267 
   268     /* Open a connection to the ESD audio server */
   269     this->hidden->audio_fd =
   270         SDL_NAME(esd_play_stream) (format, this->spec.freq, NULL,
   271                                    get_progname());
   272 
   273     if (this->hidden->audio_fd < 0) {
   274         ESD_CloseDevice(this);
   275         return SDL_SetError("Couldn't open ESD connection");
   276     }
   277 
   278     /* Calculate the final parameters for this audio specification */
   279     SDL_CalculateAudioSpec(&this->spec);
   280     this->hidden->frame_ticks =
   281         (float) (this->spec.samples * 1000) / this->spec.freq;
   282     this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
   283 
   284     /* Allocate mixing buffer */
   285     this->hidden->mixlen = this->spec.size;
   286     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   287     if (this->hidden->mixbuf == NULL) {
   288         ESD_CloseDevice(this);
   289         return SDL_OutOfMemory();
   290     }
   291     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   292 
   293     /* Get the parent process id (we're the parent of the audio thread) */
   294     this->hidden->parent = getpid();
   295 
   296     /* We're ready to rock and roll. :-) */
   297     return 0;
   298 }
   299 
   300 static void
   301 ESD_Deinitialize(void)
   302 {
   303     UnloadESDLibrary();
   304 }
   305 
   306 static int
   307 ESD_Init(SDL_AudioDriverImpl * impl)
   308 {
   309     if (LoadESDLibrary() < 0) {
   310         return 0;
   311     } else {
   312         int connection = 0;
   313 
   314         /* Don't start ESD if it's not running */
   315         SDL_setenv("ESD_NO_SPAWN", "1", 0);
   316 
   317         connection = SDL_NAME(esd_open_sound) (NULL);
   318         if (connection < 0) {
   319             UnloadESDLibrary();
   320             SDL_SetError("ESD: esd_open_sound failed (no audio server?)");
   321             return 0;
   322         }
   323         SDL_NAME(esd_close) (connection);
   324     }
   325 
   326     /* Set the function pointers */
   327     impl->OpenDevice = ESD_OpenDevice;
   328     impl->PlayDevice = ESD_PlayDevice;
   329     impl->WaitDevice = ESD_WaitDevice;
   330     impl->GetDeviceBuf = ESD_GetDeviceBuf;
   331     impl->CloseDevice = ESD_CloseDevice;
   332     impl->Deinitialize = ESD_Deinitialize;
   333     impl->OnlyHasDefaultOutputDevice = 1;
   334 
   335     return 1;   /* this audio target is available. */
   336 }
   337 
   338 
   339 AudioBootStrap ESD_bootstrap = {
   340     "esd", "Enlightened Sound Daemon", ESD_Init, 0
   341 };
   342 
   343 #endif /* SDL_AUDIO_DRIVER_ESD */
   344 
   345 /* vi: set ts=4 sw=4 expandtab: */