sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_esdaudio.c (8911B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 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: */