src/audio/esd/SDL_esdaudio.c
author Sam Lantinga
Mon, 09 Jan 2017 11:58:01 -0800
changeset 10802 6afc9b833867
parent 10737 3406a0f8b041
child 11811 5d94cb6b24d3
permissions -rw-r--r--
We only need the first few keymaps corresponding to the following constants:
K_NORMTAB, K_SHIFTTAB, K_ALTTAB, K_ALTSHIFTTAB

In the normal case we'll load all the keymaps from the kernel, but this reduces the size of the SDL library for the fallback case when we can't get to the tty.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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_audio_c.h"
    36 #include "SDL_esdaudio.h"
    37 
    38 #ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
    39 #include "SDL_name.h"
    40 #include "SDL_loadso.h"
    41 #else
    42 #define SDL_NAME(X) X
    43 #endif
    44 
    45 #ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
    46 
    47 static const char *esd_library = SDL_AUDIO_DRIVER_ESD_DYNAMIC;
    48 static void *esd_handle = NULL;
    49 
    50 static int (*SDL_NAME(esd_open_sound)) (const char *host);
    51 static int (*SDL_NAME(esd_close)) (int esd);
    52 static int (*SDL_NAME(esd_play_stream)) (esd_format_t format, int rate,
    53                                          const char *host, const char *name);
    54 
    55 #define SDL_ESD_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
    56 static struct
    57 {
    58     const char *name;
    59     void **func;
    60 } const esd_functions[] = {
    61     SDL_ESD_SYM(esd_open_sound),
    62     SDL_ESD_SYM(esd_close), SDL_ESD_SYM(esd_play_stream),
    63 };
    64 
    65 #undef SDL_ESD_SYM
    66 
    67 static void
    68 UnloadESDLibrary()
    69 {
    70     if (esd_handle != NULL) {
    71         SDL_UnloadObject(esd_handle);
    72         esd_handle = NULL;
    73     }
    74 }
    75 
    76 static int
    77 LoadESDLibrary(void)
    78 {
    79     int i, retval = -1;
    80 
    81     if (esd_handle == NULL) {
    82         esd_handle = SDL_LoadObject(esd_library);
    83         if (esd_handle) {
    84             retval = 0;
    85             for (i = 0; i < SDL_arraysize(esd_functions); ++i) {
    86                 *esd_functions[i].func =
    87                     SDL_LoadFunction(esd_handle, esd_functions[i].name);
    88                 if (!*esd_functions[i].func) {
    89                     retval = -1;
    90                     UnloadESDLibrary();
    91                     break;
    92                 }
    93             }
    94         }
    95     }
    96     return retval;
    97 }
    98 
    99 #else
   100 
   101 static void
   102 UnloadESDLibrary()
   103 {
   104     return;
   105 }
   106 
   107 static int
   108 LoadESDLibrary(void)
   109 {
   110     return 0;
   111 }
   112 
   113 #endif /* SDL_AUDIO_DRIVER_ESD_DYNAMIC */
   114 
   115 
   116 /* This function waits until it is possible to write a full sound buffer */
   117 static void
   118 ESD_WaitDevice(_THIS)
   119 {
   120     Sint32 ticks;
   121 
   122     /* Check to see if the thread-parent process is still alive */
   123     {
   124         static int cnt = 0;
   125         /* Note that this only works with thread implementations
   126            that use a different process id for each thread.
   127          */
   128         /* Check every 10 loops */
   129         if (this->hidden->parent && (((++cnt) % 10) == 0)) {
   130             if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
   131                 SDL_OpenedAudioDeviceDisconnected(this);
   132             }
   133         }
   134     }
   135 
   136     /* Use timer for general audio synchronization */
   137     ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
   138     if (ticks > 0) {
   139         SDL_Delay(ticks);
   140     }
   141 }
   142 
   143 static void
   144 ESD_PlayDevice(_THIS)
   145 {
   146     int written = 0;
   147 
   148     /* Write the audio data, checking for EAGAIN on broken audio drivers */
   149     do {
   150         written = write(this->hidden->audio_fd,
   151                         this->hidden->mixbuf, this->hidden->mixlen);
   152         if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
   153             SDL_Delay(1);       /* Let a little CPU time go by */
   154         }
   155     } while ((written < 0) &&
   156              ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
   157 
   158     /* Set the next write frame */
   159     this->hidden->next_frame += this->hidden->frame_ticks;
   160 
   161     /* If we couldn't write, assume fatal error for now */
   162     if (written < 0) {
   163         SDL_OpenedAudioDeviceDisconnected(this);
   164     }
   165 }
   166 
   167 static Uint8 *
   168 ESD_GetDeviceBuf(_THIS)
   169 {
   170     return (this->hidden->mixbuf);
   171 }
   172 
   173 static void
   174 ESD_CloseDevice(_THIS)
   175 {
   176     if (this->hidden->audio_fd >= 0) {
   177         SDL_NAME(esd_close) (this->hidden->audio_fd);
   178     }
   179     SDL_free(this->hidden->mixbuf);
   180     SDL_free(this->hidden);
   181 }
   182 
   183 /* Try to get the name of the program */
   184 static char *
   185 get_progname(void)
   186 {
   187     char *progname = NULL;
   188 #ifdef __LINUX__
   189     FILE *fp;
   190     static char temp[BUFSIZ];
   191 
   192     SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
   193     fp = fopen(temp, "r");
   194     if (fp != NULL) {
   195         if (fgets(temp, sizeof(temp) - 1, fp)) {
   196             progname = SDL_strrchr(temp, '/');
   197             if (progname == NULL) {
   198                 progname = temp;
   199             } else {
   200                 progname = progname + 1;
   201             }
   202         }
   203         fclose(fp);
   204     }
   205 #endif
   206     return (progname);
   207 }
   208 
   209 
   210 static int
   211 ESD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   212 {
   213     esd_format_t format = (ESD_STREAM | ESD_PLAY);
   214     SDL_AudioFormat test_format = 0;
   215     int found = 0;
   216 
   217     /* Initialize all variables that we clean on shutdown */
   218     this->hidden = (struct SDL_PrivateAudioData *)
   219         SDL_malloc((sizeof *this->hidden));
   220     if (this->hidden == NULL) {
   221         return SDL_OutOfMemory();
   222     }
   223     SDL_zerop(this->hidden);
   224     this->hidden->audio_fd = -1;
   225 
   226     /* Convert audio spec to the ESD audio format */
   227     /* Try for a closest match on audio format */
   228     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   229          !found && test_format; test_format = SDL_NextAudioFormat()) {
   230 #ifdef DEBUG_AUDIO
   231         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   232 #endif
   233         found = 1;
   234         switch (test_format) {
   235         case AUDIO_U8:
   236             format |= ESD_BITS8;
   237             break;
   238         case AUDIO_S16SYS:
   239             format |= ESD_BITS16;
   240             break;
   241         default:
   242             found = 0;
   243             break;
   244         }
   245     }
   246 
   247     if (!found) {
   248         return SDL_SetError("Couldn't find any hardware audio formats");
   249     }
   250 
   251     if (this->spec.channels == 1) {
   252         format |= ESD_MONO;
   253     } else {
   254         format |= ESD_STEREO;
   255     }
   256 #if 0
   257     this->spec.samples = ESD_BUF_SIZE;  /* Darn, no way to change this yet */
   258 #endif
   259 
   260     /* Open a connection to the ESD audio server */
   261     this->hidden->audio_fd =
   262         SDL_NAME(esd_play_stream) (format, this->spec.freq, NULL,
   263                                    get_progname());
   264 
   265     if (this->hidden->audio_fd < 0) {
   266         return SDL_SetError("Couldn't open ESD connection");
   267     }
   268 
   269     /* Calculate the final parameters for this audio specification */
   270     SDL_CalculateAudioSpec(&this->spec);
   271     this->hidden->frame_ticks =
   272         (float) (this->spec.samples * 1000) / this->spec.freq;
   273     this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
   274 
   275     /* Allocate mixing buffer */
   276     this->hidden->mixlen = this->spec.size;
   277     this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
   278     if (this->hidden->mixbuf == NULL) {
   279         return SDL_OutOfMemory();
   280     }
   281     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   282 
   283     /* Get the parent process id (we're the parent of the audio thread) */
   284     this->hidden->parent = getpid();
   285 
   286     /* We're ready to rock and roll. :-) */
   287     return 0;
   288 }
   289 
   290 static void
   291 ESD_Deinitialize(void)
   292 {
   293     UnloadESDLibrary();
   294 }
   295 
   296 static int
   297 ESD_Init(SDL_AudioDriverImpl * impl)
   298 {
   299     if (LoadESDLibrary() < 0) {
   300         return 0;
   301     } else {
   302         int connection = 0;
   303 
   304         /* Don't start ESD if it's not running */
   305         SDL_setenv("ESD_NO_SPAWN", "1", 0);
   306 
   307         connection = SDL_NAME(esd_open_sound) (NULL);
   308         if (connection < 0) {
   309             UnloadESDLibrary();
   310             SDL_SetError("ESD: esd_open_sound failed (no audio server?)");
   311             return 0;
   312         }
   313         SDL_NAME(esd_close) (connection);
   314     }
   315 
   316     /* Set the function pointers */
   317     impl->OpenDevice = ESD_OpenDevice;
   318     impl->PlayDevice = ESD_PlayDevice;
   319     impl->WaitDevice = ESD_WaitDevice;
   320     impl->GetDeviceBuf = ESD_GetDeviceBuf;
   321     impl->CloseDevice = ESD_CloseDevice;
   322     impl->Deinitialize = ESD_Deinitialize;
   323     impl->OnlyHasDefaultOutputDevice = 1;
   324 
   325     return 1;   /* this audio target is available. */
   326 }
   327 
   328 
   329 AudioBootStrap ESD_bootstrap = {
   330     "esd", "Enlightened Sound Daemon", ESD_Init, 0
   331 };
   332 
   333 #endif /* SDL_AUDIO_DRIVER_ESD */
   334 
   335 /* vi: set ts=4 sw=4 expandtab: */