cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_esdaudio.c (9184B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 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
     48static const char *esd_library = SDL_AUDIO_DRIVER_ESD_DYNAMIC;
     49static void *esd_handle = NULL;
     50
     51static int (*SDL_NAME(esd_open_sound)) (const char *host);
     52static int (*SDL_NAME(esd_close)) (int esd);
     53static 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) }
     57static 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
     68static void
     69UnloadESDLibrary()
     70{
     71    if (esd_handle != NULL) {
     72        SDL_UnloadObject(esd_handle);
     73        esd_handle = NULL;
     74    }
     75}
     76
     77static int
     78LoadESDLibrary(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
    102static void
    103UnloadESDLibrary()
    104{
    105    return;
    106}
    107
    108static int
    109LoadESDLibrary(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 */
    118static void
    119ESD_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 = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
    139    if (ticks > 0) {
    140        SDL_Delay(ticks);
    141    }
    142}
    143
    144static void
    145ESD_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        this->enabled = 0;
    165    }
    166}
    167
    168static Uint8 *
    169ESD_GetDeviceBuf(_THIS)
    170{
    171    return (this->hidden->mixbuf);
    172}
    173
    174static void
    175ESD_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 */
    191static char *
    192get_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
    217static int
    218ESD_OpenDevice(_THIS, 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
    300static void
    301ESD_Deinitialize(void)
    302{
    303    UnloadESDLibrary();
    304}
    305
    306static int
    307ESD_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
    339AudioBootStrap 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: */