cscg22-gearboy

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

SDL_nasaudio.c (11799B)


      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_NAS
     24
     25/* Allow access to a raw mixing buffer */
     26
     27#include <signal.h>
     28#include <unistd.h>
     29
     30#include "SDL_timer.h"
     31#include "SDL_audio.h"
     32#include "SDL_loadso.h"
     33#include "../SDL_audiomem.h"
     34#include "../SDL_audio_c.h"
     35#include "SDL_nasaudio.h"
     36
     37static struct SDL_PrivateAudioData *this2 = NULL;
     38
     39
     40static void (*NAS_AuCloseServer) (AuServer *);
     41static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
     42static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
     43static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
     44static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
     45static void (*NAS_AuSetElements)
     46  (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
     47static void (*NAS_AuWriteElement)
     48  (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
     49static AuServer *(*NAS_AuOpenServer)
     50  (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
     51static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
     52  (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
     53
     54
     55#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
     56
     57static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
     58static void *nas_handle = NULL;
     59
     60static int
     61load_nas_sym(const char *fn, void **addr)
     62{
     63    *addr = SDL_LoadFunction(nas_handle, fn);
     64    if (*addr == NULL) {
     65        return 0;
     66    }
     67    return 1;
     68}
     69
     70/* cast funcs to char* first, to please GCC's strict aliasing rules. */
     71#define SDL_NAS_SYM(x) \
     72    if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
     73#else
     74#define SDL_NAS_SYM(x) NAS_##x = x
     75#endif
     76
     77static int
     78load_nas_syms(void)
     79{
     80    SDL_NAS_SYM(AuCloseServer);
     81    SDL_NAS_SYM(AuNextEvent);
     82    SDL_NAS_SYM(AuDispatchEvent);
     83    SDL_NAS_SYM(AuCreateFlow);
     84    SDL_NAS_SYM(AuStartFlow);
     85    SDL_NAS_SYM(AuSetElements);
     86    SDL_NAS_SYM(AuWriteElement);
     87    SDL_NAS_SYM(AuOpenServer);
     88    SDL_NAS_SYM(AuRegisterEventHandler);
     89    return 0;
     90}
     91
     92#undef SDL_NAS_SYM
     93
     94#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
     95
     96static void
     97UnloadNASLibrary(void)
     98{
     99    if (nas_handle != NULL) {
    100        SDL_UnloadObject(nas_handle);
    101        nas_handle = NULL;
    102    }
    103}
    104
    105static int
    106LoadNASLibrary(void)
    107{
    108    int retval = 0;
    109    if (nas_handle == NULL) {
    110        nas_handle = SDL_LoadObject(nas_library);
    111        if (nas_handle == NULL) {
    112            /* Copy error string so we can use it in a new SDL_SetError(). */
    113            const char *origerr = SDL_GetError();
    114            const size_t len = SDL_strlen(origerr) + 1;
    115            char *err = (char *) alloca(len);
    116            SDL_strlcpy(err, origerr, len);
    117            retval = -1;
    118            SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
    119                         nas_library, err);
    120        } else {
    121            retval = load_nas_syms();
    122            if (retval < 0) {
    123                UnloadNASLibrary();
    124            }
    125        }
    126    }
    127    return retval;
    128}
    129
    130#else
    131
    132static void
    133UnloadNASLibrary(void)
    134{
    135}
    136
    137static int
    138LoadNASLibrary(void)
    139{
    140    load_nas_syms();
    141    return 0;
    142}
    143
    144#endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
    145
    146/* This function waits until it is possible to write a full sound buffer */
    147static void
    148NAS_WaitDevice(_THIS)
    149{
    150    while (this->hidden->buf_free < this->hidden->mixlen) {
    151        AuEvent ev;
    152        NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
    153        NAS_AuDispatchEvent(this->hidden->aud, &ev);
    154    }
    155}
    156
    157static void
    158NAS_PlayDevice(_THIS)
    159{
    160    while (this->hidden->mixlen > this->hidden->buf_free) {
    161        /*
    162         * We think the buffer is full? Yikes! Ask the server for events,
    163         *  in the hope that some of them is LowWater events telling us more
    164         *  of the buffer is free now than what we think.
    165         */
    166        AuEvent ev;
    167        NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
    168        NAS_AuDispatchEvent(this->hidden->aud, &ev);
    169    }
    170    this->hidden->buf_free -= this->hidden->mixlen;
    171
    172    /* Write the audio data */
    173    NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
    174                       this->hidden->mixlen, this->hidden->mixbuf, AuFalse,
    175                       NULL);
    176
    177    this->hidden->written += this->hidden->mixlen;
    178
    179#ifdef DEBUG_AUDIO
    180    fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
    181#endif
    182}
    183
    184static Uint8 *
    185NAS_GetDeviceBuf(_THIS)
    186{
    187    return (this->hidden->mixbuf);
    188}
    189
    190static void
    191NAS_CloseDevice(_THIS)
    192{
    193    if (this->hidden != NULL) {
    194        SDL_FreeAudioMem(this->hidden->mixbuf);
    195        this->hidden->mixbuf = NULL;
    196        if (this->hidden->aud) {
    197            NAS_AuCloseServer(this->hidden->aud);
    198            this->hidden->aud = 0;
    199        }
    200        SDL_free(this->hidden);
    201        this2 = this->hidden = NULL;
    202    }
    203}
    204
    205static unsigned char
    206sdlformat_to_auformat(unsigned int fmt)
    207{
    208    switch (fmt) {
    209    case AUDIO_U8:
    210        return AuFormatLinearUnsigned8;
    211    case AUDIO_S8:
    212        return AuFormatLinearSigned8;
    213    case AUDIO_U16LSB:
    214        return AuFormatLinearUnsigned16LSB;
    215    case AUDIO_U16MSB:
    216        return AuFormatLinearUnsigned16MSB;
    217    case AUDIO_S16LSB:
    218        return AuFormatLinearSigned16LSB;
    219    case AUDIO_S16MSB:
    220        return AuFormatLinearSigned16MSB;
    221    }
    222    return AuNone;
    223}
    224
    225static AuBool
    226event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
    227{
    228    switch (ev->type) {
    229    case AuEventTypeElementNotify:
    230        {
    231            AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
    232
    233            switch (event->kind) {
    234            case AuElementNotifyKindLowWater:
    235                if (this2->buf_free >= 0) {
    236                    this2->really += event->num_bytes;
    237                    gettimeofday(&this2->last_tv, 0);
    238                    this2->buf_free += event->num_bytes;
    239                } else {
    240                    this2->buf_free = event->num_bytes;
    241                }
    242                break;
    243            case AuElementNotifyKindState:
    244                switch (event->cur_state) {
    245                case AuStatePause:
    246                    if (event->reason != AuReasonUser) {
    247                        if (this2->buf_free >= 0) {
    248                            this2->really += event->num_bytes;
    249                            gettimeofday(&this2->last_tv, 0);
    250                            this2->buf_free += event->num_bytes;
    251                        } else {
    252                            this2->buf_free = event->num_bytes;
    253                        }
    254                    }
    255                    break;
    256                }
    257            }
    258        }
    259    }
    260    return AuTrue;
    261}
    262
    263static AuDeviceID
    264find_device(_THIS, int nch)
    265{
    266    /* These "Au" things are all macros, not functions... */
    267    int i;
    268    for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
    269        if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
    270             AuComponentKindPhysicalOutput) &&
    271            AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
    272            return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
    273        }
    274    }
    275    return AuNone;
    276}
    277
    278static int
    279NAS_OpenDevice(_THIS, const char *devname, int iscapture)
    280{
    281    AuElement elms[3];
    282    int buffer_size;
    283    SDL_AudioFormat test_format, format;
    284
    285    /* Initialize all variables that we clean on shutdown */
    286    this->hidden = (struct SDL_PrivateAudioData *)
    287        SDL_malloc((sizeof *this->hidden));
    288    if (this->hidden == NULL) {
    289        return SDL_OutOfMemory();
    290    }
    291    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    292
    293    /* Try for a closest match on audio format */
    294    format = 0;
    295    for (test_format = SDL_FirstAudioFormat(this->spec.format);
    296         !format && test_format;) {
    297        format = sdlformat_to_auformat(test_format);
    298        if (format == AuNone) {
    299            test_format = SDL_NextAudioFormat();
    300        }
    301    }
    302    if (format == 0) {
    303        NAS_CloseDevice(this);
    304        return SDL_SetError("NAS: Couldn't find any hardware audio formats");
    305    }
    306    this->spec.format = test_format;
    307
    308    this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
    309    if (this->hidden->aud == 0) {
    310        NAS_CloseDevice(this);
    311        return SDL_SetError("NAS: Couldn't open connection to NAS server");
    312    }
    313
    314    this->hidden->dev = find_device(this, this->spec.channels);
    315    if ((this->hidden->dev == AuNone)
    316        || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
    317        NAS_CloseDevice(this);
    318        return SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
    319    }
    320
    321    buffer_size = this->spec.freq;
    322    if (buffer_size < 4096)
    323        buffer_size = 4096;
    324
    325    if (buffer_size > 32768)
    326        buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */
    327
    328    /* Calculate the final parameters for this audio specification */
    329    SDL_CalculateAudioSpec(&this->spec);
    330
    331    this2 = this->hidden;
    332
    333    AuMakeElementImportClient(elms, this->spec.freq, format,
    334                              this->spec.channels, AuTrue, buffer_size,
    335                              buffer_size / 4, 0, NULL);
    336    AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
    337                              AuUnlimitedSamples, 0, NULL);
    338    NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
    339                      NULL);
    340    NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
    341                               this->hidden->flow, event_handler,
    342                               (AuPointer) NULL);
    343
    344    NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
    345
    346    /* Allocate mixing buffer */
    347    this->hidden->mixlen = this->spec.size;
    348    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
    349    if (this->hidden->mixbuf == NULL) {
    350        NAS_CloseDevice(this);
    351        return SDL_OutOfMemory();
    352    }
    353    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
    354
    355    /* We're ready to rock and roll. :-) */
    356    return 0;
    357}
    358
    359static void
    360NAS_Deinitialize(void)
    361{
    362    UnloadNASLibrary();
    363}
    364
    365static int
    366NAS_Init(SDL_AudioDriverImpl * impl)
    367{
    368    if (LoadNASLibrary() < 0) {
    369        return 0;
    370    } else {
    371        AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
    372        if (aud == NULL) {
    373            SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
    374            return 0;
    375        }
    376        NAS_AuCloseServer(aud);
    377    }
    378
    379    /* Set the function pointers */
    380    impl->OpenDevice = NAS_OpenDevice;
    381    impl->PlayDevice = NAS_PlayDevice;
    382    impl->WaitDevice = NAS_WaitDevice;
    383    impl->GetDeviceBuf = NAS_GetDeviceBuf;
    384    impl->CloseDevice = NAS_CloseDevice;
    385    impl->Deinitialize = NAS_Deinitialize;
    386    impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: is this true? */
    387
    388    return 1;   /* this audio target is available. */
    389}
    390
    391AudioBootStrap NAS_bootstrap = {
    392    "nas", "Network Audio System", NAS_Init, 0
    393};
    394
    395#endif /* SDL_AUDIO_DRIVER_NAS */
    396
    397/* vi: set ts=4 sw=4 expandtab: */