cscg22-gearboy

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

SDL_sunaudio.c (13322B)


      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_SUNAUDIO
     24
     25/* Allow access to a raw mixing buffer */
     26
     27#include <fcntl.h>
     28#include <errno.h>
     29#ifdef __NETBSD__
     30#include <sys/ioctl.h>
     31#include <sys/audioio.h>
     32#endif
     33#ifdef __SVR4
     34#include <sys/audioio.h>
     35#else
     36#include <sys/time.h>
     37#include <sys/types.h>
     38#endif
     39#include <unistd.h>
     40
     41#include "SDL_timer.h"
     42#include "SDL_audio.h"
     43#include "../SDL_audiomem.h"
     44#include "../SDL_audio_c.h"
     45#include "../SDL_audiodev_c.h"
     46#include "SDL_sunaudio.h"
     47
     48/* Open the audio device for playback, and don't block if busy */
     49
     50#if defined(AUDIO_GETINFO) && !defined(AUDIO_GETBUFINFO)
     51#define AUDIO_GETBUFINFO AUDIO_GETINFO
     52#endif
     53
     54/* Audio driver functions */
     55static Uint8 snd2au(int sample);
     56
     57/* Audio driver bootstrap functions */
     58static void
     59SUNAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
     60{
     61    SDL_EnumUnixAudioDevices(iscapture, 1, (int (*)(int fd)) NULL, addfn);
     62}
     63
     64#ifdef DEBUG_AUDIO
     65void
     66CheckUnderflow(_THIS)
     67{
     68#ifdef AUDIO_GETBUFINFO
     69    audio_info_t info;
     70    int left;
     71
     72    ioctl(this->hidden->audio_fd, AUDIO_GETBUFINFO, &info);
     73    left = (this->hidden->written - info.play.samples);
     74    if (this->hidden->written && (left == 0)) {
     75        fprintf(stderr, "audio underflow!\n");
     76    }
     77#endif
     78}
     79#endif
     80
     81static void
     82SUNAUDIO_WaitDevice(_THIS)
     83{
     84#ifdef AUDIO_GETBUFINFO
     85#define SLEEP_FUDGE 10      /* 10 ms scheduling fudge factor */
     86    audio_info_t info;
     87    Sint32 left;
     88
     89    ioctl(this->hidden->audio_fd, AUDIO_GETBUFINFO, &info);
     90    left = (this->hidden->written - info.play.samples);
     91    if (left > this->hidden->fragsize) {
     92        Sint32 sleepy;
     93
     94        sleepy = ((left - this->hidden->fragsize) / this->hidden->frequency);
     95        sleepy -= SLEEP_FUDGE;
     96        if (sleepy > 0) {
     97            SDL_Delay(sleepy);
     98        }
     99    }
    100#else
    101    fd_set fdset;
    102
    103    FD_ZERO(&fdset);
    104    FD_SET(this->hidden->audio_fd, &fdset);
    105    select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, NULL);
    106#endif
    107}
    108
    109static void
    110SUNAUDIO_PlayDevice(_THIS)
    111{
    112    /* Write the audio data */
    113    if (this->hidden->ulaw_only) {
    114        /* Assuming that this->spec.freq >= 8000 Hz */
    115        int accum, incr, pos;
    116        Uint8 *aubuf;
    117
    118        accum = 0;
    119        incr = this->spec.freq / 8;
    120        aubuf = this->hidden->ulaw_buf;
    121        switch (this->hidden->audio_fmt & 0xFF) {
    122        case 8:
    123            {
    124                Uint8 *sndbuf;
    125
    126                sndbuf = this->hidden->mixbuf;
    127                for (pos = 0; pos < this->hidden->fragsize; ++pos) {
    128                    *aubuf = snd2au((0x80 - *sndbuf) * 64);
    129                    accum += incr;
    130                    while (accum > 0) {
    131                        accum -= 1000;
    132                        sndbuf += 1;
    133                    }
    134                    aubuf += 1;
    135                }
    136            }
    137            break;
    138        case 16:
    139            {
    140                Sint16 *sndbuf;
    141
    142                sndbuf = (Sint16 *) this->hidden->mixbuf;
    143                for (pos = 0; pos < this->hidden->fragsize; ++pos) {
    144                    *aubuf = snd2au(*sndbuf / 4);
    145                    accum += incr;
    146                    while (accum > 0) {
    147                        accum -= 1000;
    148                        sndbuf += 1;
    149                    }
    150                    aubuf += 1;
    151                }
    152            }
    153            break;
    154        }
    155#ifdef DEBUG_AUDIO
    156        CheckUnderflow(this);
    157#endif
    158        if (write(this->hidden->audio_fd, this->hidden->ulaw_buf,
    159            this->hidden->fragsize) < 0) {
    160            /* Assume fatal error, for now */
    161            this->enabled = 0;
    162        }
    163        this->hidden->written += this->hidden->fragsize;
    164    } else {
    165#ifdef DEBUG_AUDIO
    166        CheckUnderflow(this);
    167#endif
    168        if (write(this->hidden->audio_fd, this->hidden->mixbuf,
    169            this->spec.size) < 0) {
    170            /* Assume fatal error, for now */
    171            this->enabled = 0;
    172        }
    173        this->hidden->written += this->hidden->fragsize;
    174    }
    175}
    176
    177static Uint8 *
    178SUNAUDIO_GetDeviceBuf(_THIS)
    179{
    180    return (this->hidden->mixbuf);
    181}
    182
    183static void
    184SUNAUDIO_CloseDevice(_THIS)
    185{
    186    if (this->hidden != NULL) {
    187        SDL_FreeAudioMem(this->hidden->mixbuf);
    188        this->hidden->mixbuf = NULL;
    189        SDL_free(this->hidden->ulaw_buf);
    190        this->hidden->ulaw_buf = NULL;
    191        if (this->hidden->audio_fd >= 0) {
    192            close(this->hidden->audio_fd);
    193            this->hidden->audio_fd = -1;
    194        }
    195        SDL_free(this->hidden);
    196        this->hidden = NULL;
    197    }
    198}
    199
    200static int
    201SUNAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
    202{
    203    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
    204    SDL_AudioFormat format = 0;
    205    audio_info_t info;
    206
    207    /* We don't care what the devname is...we'll try to open anything. */
    208    /*  ...but default to first name in the list... */
    209    if (devname == NULL) {
    210        devname = SDL_GetAudioDeviceName(0, iscapture);
    211        if (devname == NULL) {
    212            return SDL_SetError("No such audio device");
    213        }
    214    }
    215
    216    /* Initialize all variables that we clean on shutdown */
    217    this->hidden = (struct SDL_PrivateAudioData *)
    218        SDL_malloc((sizeof *this->hidden));
    219    if (this->hidden == NULL) {
    220        return SDL_OutOfMemory();
    221    }
    222    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    223
    224    /* Open the audio device */
    225    this->hidden->audio_fd = open(devname, flags, 0);
    226    if (this->hidden->audio_fd < 0) {
    227        return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
    228    }
    229
    230#ifdef AUDIO_SETINFO
    231    int enc;
    232#endif
    233    int desired_freq = this->spec.freq;
    234
    235    /* Determine the audio parameters from the AudioSpec */
    236    switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
    237
    238    case 8:
    239        {                       /* Unsigned 8 bit audio data */
    240            this->spec.format = AUDIO_U8;
    241#ifdef AUDIO_SETINFO
    242            enc = AUDIO_ENCODING_LINEAR8;
    243#endif
    244        }
    245        break;
    246
    247    case 16:
    248        {                       /* Signed 16 bit audio data */
    249            this->spec.format = AUDIO_S16SYS;
    250#ifdef AUDIO_SETINFO
    251            enc = AUDIO_ENCODING_LINEAR;
    252#endif
    253        }
    254        break;
    255
    256    default:
    257        {
    258            /* !!! FIXME: fallback to conversion on unsupported types! */
    259            return SDL_SetError("Unsupported audio format");
    260        }
    261    }
    262    this->hidden->audio_fmt = this->spec.format;
    263
    264    this->hidden->ulaw_only = 0;    /* modern Suns do support linear audio */
    265#ifdef AUDIO_SETINFO
    266    for (;;) {
    267        audio_info_t info;
    268        AUDIO_INITINFO(&info);  /* init all fields to "no change" */
    269
    270        /* Try to set the requested settings */
    271        info.play.sample_rate = this->spec.freq;
    272        info.play.channels = this->spec.channels;
    273        info.play.precision = (enc == AUDIO_ENCODING_ULAW)
    274            ? 8 : this->spec.format & 0xff;
    275        info.play.encoding = enc;
    276        if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
    277
    278            /* Check to be sure we got what we wanted */
    279            if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
    280                return SDL_SetError("Error getting audio parameters: %s",
    281                                    strerror(errno));
    282            }
    283            if (info.play.encoding == enc
    284                && info.play.precision == (this->spec.format & 0xff)
    285                && info.play.channels == this->spec.channels) {
    286                /* Yow! All seems to be well! */
    287                this->spec.freq = info.play.sample_rate;
    288                break;
    289            }
    290        }
    291
    292        switch (enc) {
    293        case AUDIO_ENCODING_LINEAR8:
    294            /* unsigned 8bit apparently not supported here */
    295            enc = AUDIO_ENCODING_LINEAR;
    296            this->spec.format = AUDIO_S16SYS;
    297            break;              /* try again */
    298
    299        case AUDIO_ENCODING_LINEAR:
    300            /* linear 16bit didn't work either, resort to µ-law */
    301            enc = AUDIO_ENCODING_ULAW;
    302            this->spec.channels = 1;
    303            this->spec.freq = 8000;
    304            this->spec.format = AUDIO_U8;
    305            this->hidden->ulaw_only = 1;
    306            break;
    307
    308        default:
    309            /* oh well... */
    310            return SDL_SetError("Error setting audio parameters: %s",
    311                                strerror(errno));
    312        }
    313    }
    314#endif /* AUDIO_SETINFO */
    315    this->hidden->written = 0;
    316
    317    /* We can actually convert on-the-fly to U-Law */
    318    if (this->hidden->ulaw_only) {
    319        this->spec.freq = desired_freq;
    320        this->hidden->fragsize = (this->spec.samples * 1000) /
    321            (this->spec.freq / 8);
    322        this->hidden->frequency = 8;
    323        this->hidden->ulaw_buf = (Uint8 *) SDL_malloc(this->hidden->fragsize);
    324        if (this->hidden->ulaw_buf == NULL) {
    325            return SDL_OutOfMemory();
    326        }
    327        this->spec.channels = 1;
    328    } else {
    329        this->hidden->fragsize = this->spec.samples;
    330        this->hidden->frequency = this->spec.freq / 1000;
    331    }
    332#ifdef DEBUG_AUDIO
    333    fprintf(stderr, "Audio device %s U-Law only\n",
    334            this->hidden->ulaw_only ? "is" : "is not");
    335    fprintf(stderr, "format=0x%x chan=%d freq=%d\n",
    336            this->spec.format, this->spec.channels, this->spec.freq);
    337#endif
    338
    339    /* Update the fragment size as size in bytes */
    340    SDL_CalculateAudioSpec(&this->spec);
    341
    342    /* Allocate mixing buffer */
    343    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->spec.size);
    344    if (this->hidden->mixbuf == NULL) {
    345        return SDL_OutOfMemory();
    346    }
    347    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
    348
    349    /* We're ready to rock and roll. :-) */
    350    return 0;
    351}
    352
    353/************************************************************************/
    354/* This function (snd2au()) copyrighted:                                */
    355/************************************************************************/
    356/*      Copyright 1989 by Rich Gopstein and Harris Corporation          */
    357/*                                                                      */
    358/*      Permission to use, copy, modify, and distribute this software   */
    359/*      and its documentation for any purpose and without fee is        */
    360/*      hereby granted, provided that the above copyright notice        */
    361/*      appears in all copies and that both that copyright notice and   */
    362/*      this permission notice appear in supporting documentation, and  */
    363/*      that the name of Rich Gopstein and Harris Corporation not be    */
    364/*      used in advertising or publicity pertaining to distribution     */
    365/*      of the software without specific, written prior permission.     */
    366/*      Rich Gopstein and Harris Corporation make no representations    */
    367/*      about the suitability of this software for any purpose.  It     */
    368/*      provided "as is" without express or implied warranty.           */
    369/************************************************************************/
    370
    371static Uint8
    372snd2au(int sample)
    373{
    374
    375    int mask;
    376
    377    if (sample < 0) {
    378        sample = -sample;
    379        mask = 0x7f;
    380    } else {
    381        mask = 0xff;
    382    }
    383
    384    if (sample < 32) {
    385        sample = 0xF0 | (15 - sample / 2);
    386    } else if (sample < 96) {
    387        sample = 0xE0 | (15 - (sample - 32) / 4);
    388    } else if (sample < 224) {
    389        sample = 0xD0 | (15 - (sample - 96) / 8);
    390    } else if (sample < 480) {
    391        sample = 0xC0 | (15 - (sample - 224) / 16);
    392    } else if (sample < 992) {
    393        sample = 0xB0 | (15 - (sample - 480) / 32);
    394    } else if (sample < 2016) {
    395        sample = 0xA0 | (15 - (sample - 992) / 64);
    396    } else if (sample < 4064) {
    397        sample = 0x90 | (15 - (sample - 2016) / 128);
    398    } else if (sample < 8160) {
    399        sample = 0x80 | (15 - (sample - 4064) / 256);
    400    } else {
    401        sample = 0x80;
    402    }
    403    return (mask & sample);
    404}
    405
    406static int
    407SUNAUDIO_Init(SDL_AudioDriverImpl * impl)
    408{
    409    /* Set the function pointers */
    410    impl->DetectDevices = SUNAUDIO_DetectDevices;
    411    impl->OpenDevice = SUNAUDIO_OpenDevice;
    412    impl->PlayDevice = SUNAUDIO_PlayDevice;
    413    impl->WaitDevice = SUNAUDIO_WaitDevice;
    414    impl->GetDeviceBuf = SUNAUDIO_GetDeviceBuf;
    415    impl->CloseDevice = SUNAUDIO_CloseDevice;
    416
    417    return 1; /* this audio target is available. */
    418}
    419
    420AudioBootStrap SUNAUDIO_bootstrap = {
    421    "audio", "UNIX /dev/audio interface", SUNAUDIO_Init, 0
    422};
    423
    424#endif /* SDL_AUDIO_DRIVER_SUNAUDIO */
    425
    426/* vi: set ts=4 sw=4 expandtab: */