src/audio/esd/SDL_esdaudio.c
author Sam Lantinga
Sat, 06 Oct 2012 11:23:47 -0700
changeset 6565 1f3c0df426dc
parent 6138 4c64952a58fb
child 6885 700f1b25f77f
permissions -rw-r--r--
When using Xinerama, XVidMode always works on screen 0. Otherwise use the real X11 screen.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 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_config.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                 this->enabled = 0;
   133             }
   134         }
   135     }
   136 
   137     /* Use timer for general audio synchronization */
   138     ticks =
   139         ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
   140     if (ticks > 0) {
   141         SDL_Delay(ticks);
   142     }
   143 }
   144 
   145 static void
   146 ESD_PlayDevice(_THIS)
   147 {
   148     int written = 0;
   149 
   150     /* Write the audio data, checking for EAGAIN on broken audio drivers */
   151     do {
   152         written = write(this->hidden->audio_fd,
   153                         this->hidden->mixbuf, this->hidden->mixlen);
   154         if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
   155             SDL_Delay(1);       /* Let a little CPU time go by */
   156         }
   157     } while ((written < 0) &&
   158              ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
   159 
   160     /* Set the next write frame */
   161     this->hidden->next_frame += this->hidden->frame_ticks;
   162 
   163     /* If we couldn't write, assume fatal error for now */
   164     if (written < 0) {
   165         this->enabled = 0;
   166     }
   167 }
   168 
   169 static Uint8 *
   170 ESD_GetDeviceBuf(_THIS)
   171 {
   172     return (this->hidden->mixbuf);
   173 }
   174 
   175 static void
   176 ESD_CloseDevice(_THIS)
   177 {
   178     if (this->hidden != NULL) {
   179         if (this->hidden->mixbuf != NULL) {
   180             SDL_FreeAudioMem(this->hidden->mixbuf);
   181             this->hidden->mixbuf = NULL;
   182         }
   183         if (this->hidden->audio_fd >= 0) {
   184             SDL_NAME(esd_close) (this->hidden->audio_fd);
   185             this->hidden->audio_fd = -1;
   186         }
   187 
   188         SDL_free(this->hidden);
   189         this->hidden = NULL;
   190     }
   191 }
   192 
   193 /* Try to get the name of the program */
   194 static char *
   195 get_progname(void)
   196 {
   197     char *progname = NULL;
   198 #ifdef __LINUX__
   199     FILE *fp;
   200     static char temp[BUFSIZ];
   201 
   202     SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
   203     fp = fopen(temp, "r");
   204     if (fp != NULL) {
   205         if (fgets(temp, sizeof(temp) - 1, fp)) {
   206             progname = SDL_strrchr(temp, '/');
   207             if (progname == NULL) {
   208                 progname = temp;
   209             } else {
   210                 progname = progname + 1;
   211             }
   212         }
   213         fclose(fp);
   214     }
   215 #endif
   216     return (progname);
   217 }
   218 
   219 
   220 static int
   221 ESD_OpenDevice(_THIS, const char *devname, int iscapture)
   222 {
   223     esd_format_t format = (ESD_STREAM | ESD_PLAY);
   224     SDL_AudioFormat test_format = 0;
   225     int found = 0;
   226 
   227     /* Initialize all variables that we clean on shutdown */
   228     this->hidden = (struct SDL_PrivateAudioData *)
   229         SDL_malloc((sizeof *this->hidden));
   230     if (this->hidden == NULL) {
   231         SDL_OutOfMemory();
   232         return 0;
   233     }
   234     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   235     this->hidden->audio_fd = -1;
   236 
   237     /* Convert audio spec to the ESD audio format */
   238     /* Try for a closest match on audio format */
   239     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   240          !found && test_format; test_format = SDL_NextAudioFormat()) {
   241 #ifdef DEBUG_AUDIO
   242         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   243 #endif
   244         found = 1;
   245         switch (test_format) {
   246         case AUDIO_U8:
   247             format |= ESD_BITS8;
   248             break;
   249         case AUDIO_S16SYS:
   250             format |= ESD_BITS16;
   251             break;
   252         default:
   253             found = 0;
   254             break;
   255         }
   256     }
   257 
   258     if (!found) {
   259         ESD_CloseDevice(this);
   260         SDL_SetError("Couldn't find any hardware audio formats");
   261         return 0;
   262     }
   263 
   264     if (this->spec.channels == 1) {
   265         format |= ESD_MONO;
   266     } else {
   267         format |= ESD_STEREO;
   268     }
   269 #if 0
   270     this->spec.samples = ESD_BUF_SIZE;  /* Darn, no way to change this yet */
   271 #endif
   272 
   273     /* Open a connection to the ESD audio server */
   274     this->hidden->audio_fd =
   275         SDL_NAME(esd_play_stream) (format, this->spec.freq, NULL,
   276                                    get_progname());
   277 
   278     if (this->hidden->audio_fd < 0) {
   279         ESD_CloseDevice(this);
   280         SDL_SetError("Couldn't open ESD connection");
   281         return 0;
   282     }
   283 
   284     /* Calculate the final parameters for this audio specification */
   285     SDL_CalculateAudioSpec(&this->spec);
   286     this->hidden->frame_ticks =
   287         (float) (this->spec.samples * 1000) / this->spec.freq;
   288     this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
   289 
   290     /* Allocate mixing buffer */
   291     this->hidden->mixlen = this->spec.size;
   292     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   293     if (this->hidden->mixbuf == NULL) {
   294         ESD_CloseDevice(this);
   295         SDL_OutOfMemory();
   296         return 0;
   297     }
   298     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   299 
   300     /* Get the parent process id (we're the parent of the audio thread) */
   301     this->hidden->parent = getpid();
   302 
   303     /* We're ready to rock and roll. :-) */
   304     return 1;
   305 }
   306 
   307 static void
   308 ESD_Deinitialize(void)
   309 {
   310     UnloadESDLibrary();
   311 }
   312 
   313 static int
   314 ESD_Init(SDL_AudioDriverImpl * impl)
   315 {
   316     if (LoadESDLibrary() < 0) {
   317         return 0;
   318     } else {
   319         int connection = 0;
   320 
   321         /* Don't start ESD if it's not running */
   322         SDL_setenv("ESD_NO_SPAWN", "1", 0);
   323 
   324         connection = SDL_NAME(esd_open_sound) (NULL);
   325         if (connection < 0) {
   326             UnloadESDLibrary();
   327             SDL_SetError("ESD: esd_open_sound failed (no audio server?)");
   328             return 0;
   329         }
   330         SDL_NAME(esd_close) (connection);
   331     }
   332 
   333     /* Set the function pointers */
   334     impl->OpenDevice = ESD_OpenDevice;
   335     impl->PlayDevice = ESD_PlayDevice;
   336     impl->WaitDevice = ESD_WaitDevice;
   337     impl->GetDeviceBuf = ESD_GetDeviceBuf;
   338     impl->CloseDevice = ESD_CloseDevice;
   339     impl->Deinitialize = ESD_Deinitialize;
   340     impl->OnlyHasDefaultOutputDevice = 1;
   341 
   342     return 1;   /* this audio target is available. */
   343 }
   344 
   345 
   346 AudioBootStrap ESD_bootstrap = {
   347     "esd", "Enlightened Sound Daemon", ESD_Init, 0
   348 };
   349 
   350 #endif /* SDL_AUDIO_DRIVER_ESD */
   351 
   352 /* vi: set ts=4 sw=4 expandtab: */