cscg22-gearboy

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

SDL_artsaudio.c (11094B)


      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_ARTS
     24
     25/* Allow access to a raw mixing buffer */
     26
     27#ifdef HAVE_SIGNAL_H
     28#include <signal.h>
     29#endif
     30#include <unistd.h>
     31#include <errno.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_artsaudio.h"
     38
     39#ifdef SDL_AUDIO_DRIVER_ARTS_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_ARTS_DYNAMIC
     47
     48static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC;
     49static void *arts_handle = NULL;
     50
     51/* !!! FIXME: I hate this SDL_NAME clutter...it makes everything so messy! */
     52static int (*SDL_NAME(arts_init)) (void);
     53static void (*SDL_NAME(arts_free)) (void);
     54static arts_stream_t(*SDL_NAME(arts_play_stream)) (int rate, int bits,
     55                                                   int channels,
     56                                                   const char *name);
     57static int (*SDL_NAME(arts_stream_set)) (arts_stream_t s,
     58                                         arts_parameter_t param, int value);
     59static int (*SDL_NAME(arts_stream_get)) (arts_stream_t s,
     60                                         arts_parameter_t param);
     61static int (*SDL_NAME(arts_write)) (arts_stream_t s, const void *buffer,
     62                                    int count);
     63static void (*SDL_NAME(arts_close_stream)) (arts_stream_t s);
     64static int (*SDL_NAME(arts_suspend))(void);
     65static int (*SDL_NAME(arts_suspended)) (void);
     66static const char *(*SDL_NAME(arts_error_text)) (int errorcode);
     67
     68#define SDL_ARTS_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
     69static struct
     70{
     71    const char *name;
     72    void **func;
     73} arts_functions[] = {
     74/* *INDENT-OFF* */
     75    SDL_ARTS_SYM(arts_init),
     76    SDL_ARTS_SYM(arts_free),
     77    SDL_ARTS_SYM(arts_play_stream),
     78    SDL_ARTS_SYM(arts_stream_set),
     79    SDL_ARTS_SYM(arts_stream_get),
     80    SDL_ARTS_SYM(arts_write),
     81    SDL_ARTS_SYM(arts_close_stream),
     82    SDL_ARTS_SYM(arts_suspend),
     83    SDL_ARTS_SYM(arts_suspended),
     84    SDL_ARTS_SYM(arts_error_text),
     85/* *INDENT-ON* */
     86};
     87
     88#undef SDL_ARTS_SYM
     89
     90static void
     91UnloadARTSLibrary()
     92{
     93    if (arts_handle != NULL) {
     94        SDL_UnloadObject(arts_handle);
     95        arts_handle = NULL;
     96    }
     97}
     98
     99static int
    100LoadARTSLibrary(void)
    101{
    102    int i, retval = -1;
    103
    104    if (arts_handle == NULL) {
    105        arts_handle = SDL_LoadObject(arts_library);
    106        if (arts_handle != NULL) {
    107            retval = 0;
    108            for (i = 0; i < SDL_arraysize(arts_functions); ++i) {
    109                *arts_functions[i].func =
    110                    SDL_LoadFunction(arts_handle, arts_functions[i].name);
    111                if (!*arts_functions[i].func) {
    112                    retval = -1;
    113                    UnloadARTSLibrary();
    114                    break;
    115                }
    116            }
    117        }
    118    }
    119
    120    return retval;
    121}
    122
    123#else
    124
    125static void
    126UnloadARTSLibrary()
    127{
    128    return;
    129}
    130
    131static int
    132LoadARTSLibrary(void)
    133{
    134    return 0;
    135}
    136
    137#endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */
    138
    139/* This function waits until it is possible to write a full sound buffer */
    140static void
    141ARTS_WaitDevice(_THIS)
    142{
    143    Sint32 ticks;
    144
    145    /* Check to see if the thread-parent process is still alive */
    146    {
    147        static int cnt = 0;
    148        /* Note that this only works with thread implementations
    149           that use a different process id for each thread.
    150         */
    151        /* Check every 10 loops */
    152        if (this->hidden->parent && (((++cnt) % 10) == 0)) {
    153            if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
    154                this->enabled = 0;
    155            }
    156        }
    157    }
    158
    159    /* Use timer for general audio synchronization */
    160    ticks =
    161        ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
    162    if (ticks > 0) {
    163        SDL_Delay(ticks);
    164    }
    165}
    166
    167static void
    168ARTS_PlayDevice(_THIS)
    169{
    170    /* Write the audio data */
    171    int written = SDL_NAME(arts_write) (this->hidden->stream,
    172                                        this->hidden->mixbuf,
    173                                        this->hidden->mixlen);
    174
    175    /* If timer synchronization is enabled, set the next write frame */
    176    if (this->hidden->frame_ticks) {
    177        this->hidden->next_frame += this->hidden->frame_ticks;
    178    }
    179
    180    /* If we couldn't write, assume fatal error for now */
    181    if (written < 0) {
    182        this->enabled = 0;
    183    }
    184#ifdef DEBUG_AUDIO
    185    fprintf(stderr, "Wrote %d bytes of audio data\n", written);
    186#endif
    187}
    188
    189static void
    190ARTS_WaitDone(_THIS)
    191{
    192    /* !!! FIXME: camp here until buffer drains... SDL_Delay(???); */
    193}
    194
    195
    196static Uint8 *
    197ARTS_GetDeviceBuf(_THIS)
    198{
    199    return (this->hidden->mixbuf);
    200}
    201
    202
    203static void
    204ARTS_CloseDevice(_THIS)
    205{
    206    if (this->hidden != NULL) {
    207        SDL_FreeAudioMem(this->hidden->mixbuf);
    208        this->hidden->mixbuf = NULL;
    209        if (this->hidden->stream) {
    210            SDL_NAME(arts_close_stream) (this->hidden->stream);
    211            this->hidden->stream = 0;
    212        }
    213        SDL_NAME(arts_free) ();
    214        SDL_free(this->hidden);
    215        this->hidden = NULL;
    216    }
    217}
    218
    219static int
    220ARTS_Suspend(void)
    221{
    222    const Uint32 abortms = SDL_GetTicks() + 3000; /* give up after 3 secs */
    223    while ( (!SDL_NAME(arts_suspended)()) && !SDL_TICKS_PASSED(SDL_GetTicks(), abortms) ) {
    224        if ( SDL_NAME(arts_suspend)() ) {
    225            break;
    226        }
    227    }
    228    return SDL_NAME(arts_suspended)();
    229}
    230
    231static int
    232ARTS_OpenDevice(_THIS, const char *devname, int iscapture)
    233{
    234    int rc = 0;
    235    int bits = 0, frag_spec = 0;
    236    SDL_AudioFormat test_format = 0, format = 0;
    237
    238    /* Initialize all variables that we clean on shutdown */
    239    this->hidden = (struct SDL_PrivateAudioData *)
    240        SDL_malloc((sizeof *this->hidden));
    241    if (this->hidden == NULL) {
    242        return SDL_OutOfMemory();
    243    }
    244    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    245
    246    /* Try for a closest match on audio format */
    247    for (test_format = SDL_FirstAudioFormat(this->spec.format);
    248         !format && test_format;) {
    249#ifdef DEBUG_AUDIO
    250        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
    251#endif
    252        switch (test_format) {
    253        case AUDIO_U8:
    254            bits = 8;
    255            format = 1;
    256            break;
    257        case AUDIO_S16LSB:
    258            bits = 16;
    259            format = 1;
    260            break;
    261        default:
    262            format = 0;
    263            break;
    264        }
    265        if (!format) {
    266            test_format = SDL_NextAudioFormat();
    267        }
    268    }
    269    if (format == 0) {
    270        ARTS_CloseDevice(this);
    271        return SDL_SetError("Couldn't find any hardware audio formats");
    272    }
    273    this->spec.format = test_format;
    274
    275    if ((rc = SDL_NAME(arts_init) ()) != 0) {
    276        ARTS_CloseDevice(this);
    277        return SDL_SetError("Unable to initialize ARTS: %s",
    278                            SDL_NAME(arts_error_text) (rc));
    279    }
    280
    281    if (!ARTS_Suspend()) {
    282        ARTS_CloseDevice(this);
    283        return SDL_SetError("ARTS can not open audio device");
    284    }
    285
    286    this->hidden->stream = SDL_NAME(arts_play_stream) (this->spec.freq,
    287                                                       bits,
    288                                                       this->spec.channels,
    289                                                       "SDL");
    290
    291    /* Play nothing so we have at least one write (server bug workaround). */
    292    SDL_NAME(arts_write) (this->hidden->stream, "", 0);
    293
    294    /* Calculate the final parameters for this audio specification */
    295    SDL_CalculateAudioSpec(&this->spec);
    296
    297    /* Determine the power of two of the fragment size */
    298    for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec);
    299    if ((0x01 << frag_spec) != this->spec.size) {
    300        ARTS_CloseDevice(this);
    301        return SDL_SetError("Fragment size must be a power of two");
    302    }
    303    frag_spec |= 0x00020000;    /* two fragments, for low latency */
    304
    305#ifdef ARTS_P_PACKET_SETTINGS
    306    SDL_NAME(arts_stream_set) (this->hidden->stream,
    307                               ARTS_P_PACKET_SETTINGS, frag_spec);
    308#else
    309    SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_SIZE,
    310                               frag_spec & 0xffff);
    311    SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_COUNT,
    312                               frag_spec >> 16);
    313#endif
    314    this->spec.size = SDL_NAME(arts_stream_get) (this->hidden->stream,
    315                                                 ARTS_P_PACKET_SIZE);
    316
    317    /* Allocate mixing buffer */
    318    this->hidden->mixlen = this->spec.size;
    319    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
    320    if (this->hidden->mixbuf == NULL) {
    321        ARTS_CloseDevice(this);
    322        return SDL_OutOfMemory();
    323    }
    324    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
    325
    326    /* Get the parent process id (we're the parent of the audio thread) */
    327    this->hidden->parent = getpid();
    328
    329    /* We're ready to rock and roll. :-) */
    330    return 0;
    331}
    332
    333
    334static void
    335ARTS_Deinitialize(void)
    336{
    337    UnloadARTSLibrary();
    338}
    339
    340
    341static int
    342ARTS_Init(SDL_AudioDriverImpl * impl)
    343{
    344    if (LoadARTSLibrary() < 0) {
    345        return 0;
    346    } else {
    347        if (SDL_NAME(arts_init) () != 0) {
    348            UnloadARTSLibrary();
    349            SDL_SetError("ARTS: arts_init failed (no audio server?)");
    350            return 0;
    351        }
    352
    353        /* Play a stream so aRts doesn't crash */
    354        if (ARTS_Suspend()) {
    355            arts_stream_t stream;
    356            stream = SDL_NAME(arts_play_stream) (44100, 16, 2, "SDL");
    357            SDL_NAME(arts_write) (stream, "", 0);
    358            SDL_NAME(arts_close_stream) (stream);
    359        }
    360
    361        SDL_NAME(arts_free) ();
    362    }
    363
    364    /* Set the function pointers */
    365    impl->OpenDevice = ARTS_OpenDevice;
    366    impl->PlayDevice = ARTS_PlayDevice;
    367    impl->WaitDevice = ARTS_WaitDevice;
    368    impl->GetDeviceBuf = ARTS_GetDeviceBuf;
    369    impl->CloseDevice = ARTS_CloseDevice;
    370    impl->WaitDone = ARTS_WaitDone;
    371    impl->Deinitialize = ARTS_Deinitialize;
    372    impl->OnlyHasDefaultOutputDevice = 1;
    373
    374    return 1;   /* this audio target is available. */
    375}
    376
    377
    378AudioBootStrap ARTS_bootstrap = {
    379    "arts", "Analog RealTime Synthesizer", ARTS_Init, 0
    380};
    381
    382#endif /* SDL_AUDIO_DRIVER_ARTS */
    383
    384/* vi: set ts=4 sw=4 expandtab: */