cscg22-gearboy

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

SDL_bsdaudio.c (10869B)


      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_BSD
     24
     25/*
     26 * Driver for native OpenBSD/NetBSD audio(4).
     27 * vedge@vedge.com.ar.
     28 */
     29
     30#include <errno.h>
     31#include <unistd.h>
     32#include <fcntl.h>
     33#include <sys/time.h>
     34#include <sys/ioctl.h>
     35#include <sys/stat.h>
     36#include <sys/types.h>
     37#include <sys/audioio.h>
     38
     39#include "SDL_timer.h"
     40#include "SDL_audio.h"
     41#include "../SDL_audiomem.h"
     42#include "../SDL_audio_c.h"
     43#include "../SDL_audiodev_c.h"
     44#include "SDL_bsdaudio.h"
     45
     46/* Use timer for synchronization */
     47/* #define USE_TIMER_SYNC */
     48
     49/* #define DEBUG_AUDIO */
     50/* #define DEBUG_AUDIO_STREAM */
     51
     52
     53static void
     54BSDAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
     55{
     56    SDL_EnumUnixAudioDevices(iscapture, 0, NULL, addfn);
     57}
     58
     59
     60static void
     61BSDAUDIO_Status(_THIS)
     62{
     63#ifdef DEBUG_AUDIO
     64    /* *INDENT-OFF* */
     65    audio_info_t info;
     66
     67    if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
     68        fprintf(stderr, "AUDIO_GETINFO failed.\n");
     69        return;
     70    }
     71    fprintf(stderr, "\n"
     72            "[play/record info]\n"
     73            "buffer size	:   %d bytes\n"
     74            "sample rate	:   %i Hz\n"
     75            "channels	:   %i\n"
     76            "precision	:   %i-bit\n"
     77            "encoding	:   0x%x\n"
     78            "seek		:   %i\n"
     79            "sample count	:   %i\n"
     80            "EOF count	:   %i\n"
     81            "paused		:   %s\n"
     82            "error occured	:   %s\n"
     83            "waiting		:   %s\n"
     84            "active		:   %s\n"
     85            "",
     86            info.play.buffer_size,
     87            info.play.sample_rate,
     88            info.play.channels,
     89            info.play.precision,
     90            info.play.encoding,
     91            info.play.seek,
     92            info.play.samples,
     93            info.play.eof,
     94            info.play.pause ? "yes" : "no",
     95            info.play.error ? "yes" : "no",
     96            info.play.waiting ? "yes" : "no",
     97            info.play.active ? "yes" : "no");
     98
     99    fprintf(stderr, "\n"
    100            "[audio info]\n"
    101            "monitor_gain	:   %i\n"
    102            "hw block size	:   %d bytes\n"
    103            "hi watermark	:   %i\n"
    104            "lo watermark	:   %i\n"
    105            "audio mode	:   %s\n"
    106            "",
    107            info.monitor_gain,
    108            info.blocksize,
    109            info.hiwat, info.lowat,
    110            (info.mode == AUMODE_PLAY) ? "PLAY"
    111            : (info.mode = AUMODE_RECORD) ? "RECORD"
    112            : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
    113    /* *INDENT-ON* */
    114#endif /* DEBUG_AUDIO */
    115}
    116
    117
    118/* This function waits until it is possible to write a full sound buffer */
    119static void
    120BSDAUDIO_WaitDevice(_THIS)
    121{
    122#ifndef USE_BLOCKING_WRITES     /* Not necessary when using blocking writes */
    123    /* See if we need to use timed audio synchronization */
    124    if (this->hidden->frame_ticks) {
    125        /* Use timer for general audio synchronization */
    126        Sint32 ticks;
    127
    128        ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
    129        if (ticks > 0) {
    130            SDL_Delay(ticks);
    131        }
    132    } else {
    133        /* Use select() for audio synchronization */
    134        fd_set fdset;
    135        struct timeval timeout;
    136
    137        FD_ZERO(&fdset);
    138        FD_SET(this->hidden->audio_fd, &fdset);
    139        timeout.tv_sec = 10;
    140        timeout.tv_usec = 0;
    141#ifdef DEBUG_AUDIO
    142        fprintf(stderr, "Waiting for audio to get ready\n");
    143#endif
    144        if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
    145            <= 0) {
    146            const char *message =
    147                "Audio timeout - buggy audio driver? (disabled)";
    148            /* In general we should never print to the screen,
    149               but in this case we have no other way of letting
    150               the user know what happened.
    151             */
    152            fprintf(stderr, "SDL: %s\n", message);
    153            this->enabled = 0;
    154            /* Don't try to close - may hang */
    155            this->hidden->audio_fd = -1;
    156#ifdef DEBUG_AUDIO
    157            fprintf(stderr, "Done disabling audio\n");
    158#endif
    159        }
    160#ifdef DEBUG_AUDIO
    161        fprintf(stderr, "Ready!\n");
    162#endif
    163    }
    164#endif /* !USE_BLOCKING_WRITES */
    165}
    166
    167static void
    168BSDAUDIO_PlayDevice(_THIS)
    169{
    170    int written, p = 0;
    171
    172    /* Write the audio data, checking for EAGAIN on broken audio drivers */
    173    do {
    174        written = write(this->hidden->audio_fd,
    175                        &this->hidden->mixbuf[p], this->hidden->mixlen - p);
    176
    177        if (written > 0)
    178            p += written;
    179        if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
    180            /* Non recoverable error has occurred. It should be reported!!! */
    181            perror("audio");
    182            break;
    183        }
    184
    185        if (p < written
    186            || ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) {
    187            SDL_Delay(1);       /* Let a little CPU time go by */
    188        }
    189    } while (p < written);
    190
    191    /* If timer synchronization is enabled, set the next write frame */
    192    if (this->hidden->frame_ticks) {
    193        this->hidden->next_frame += this->hidden->frame_ticks;
    194    }
    195
    196    /* If we couldn't write, assume fatal error for now */
    197    if (written < 0) {
    198        this->enabled = 0;
    199    }
    200#ifdef DEBUG_AUDIO
    201    fprintf(stderr, "Wrote %d bytes of audio data\n", written);
    202#endif
    203}
    204
    205static Uint8 *
    206BSDAUDIO_GetDeviceBuf(_THIS)
    207{
    208    return (this->hidden->mixbuf);
    209}
    210
    211static void
    212BSDAUDIO_CloseDevice(_THIS)
    213{
    214    if (this->hidden != NULL) {
    215        SDL_FreeAudioMem(this->hidden->mixbuf);
    216        this->hidden->mixbuf = NULL;
    217        if (this->hidden->audio_fd >= 0) {
    218            close(this->hidden->audio_fd);
    219            this->hidden->audio_fd = -1;
    220        }
    221        SDL_free(this->hidden);
    222        this->hidden = NULL;
    223    }
    224}
    225
    226static int
    227BSDAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
    228{
    229    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
    230    SDL_AudioFormat format = 0;
    231    audio_info_t info;
    232
    233    /* We don't care what the devname is...we'll try to open anything. */
    234    /*  ...but default to first name in the list... */
    235    if (devname == NULL) {
    236        devname = SDL_GetAudioDeviceName(0, iscapture);
    237        if (devname == NULL) {
    238            return SDL_SetError("No such audio device");
    239        }
    240    }
    241
    242    /* Initialize all variables that we clean on shutdown */
    243    this->hidden = (struct SDL_PrivateAudioData *)
    244        SDL_malloc((sizeof *this->hidden));
    245    if (this->hidden == NULL) {
    246        return SDL_OutOfMemory();
    247    }
    248    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    249
    250    /* Open the audio device */
    251    this->hidden->audio_fd = open(devname, flags, 0);
    252    if (this->hidden->audio_fd < 0) {
    253        return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
    254    }
    255
    256    AUDIO_INITINFO(&info);
    257
    258    /* Calculate the final parameters for this audio specification */
    259    SDL_CalculateAudioSpec(&this->spec);
    260
    261    /* Set to play mode */
    262    info.mode = AUMODE_PLAY;
    263    if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
    264        BSDAUDIO_CloseDevice(this);
    265        return SDL_SetError("Couldn't put device into play mode");
    266    }
    267
    268    AUDIO_INITINFO(&info);
    269    for (format = SDL_FirstAudioFormat(this->spec.format);
    270         format; format = SDL_NextAudioFormat()) {
    271        switch (format) {
    272        case AUDIO_U8:
    273            info.play.encoding = AUDIO_ENCODING_ULINEAR;
    274            info.play.precision = 8;
    275            break;
    276        case AUDIO_S8:
    277            info.play.encoding = AUDIO_ENCODING_SLINEAR;
    278            info.play.precision = 8;
    279            break;
    280        case AUDIO_S16LSB:
    281            info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
    282            info.play.precision = 16;
    283            break;
    284        case AUDIO_S16MSB:
    285            info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
    286            info.play.precision = 16;
    287            break;
    288        case AUDIO_U16LSB:
    289            info.play.encoding = AUDIO_ENCODING_ULINEAR_LE;
    290            info.play.precision = 16;
    291            break;
    292        case AUDIO_U16MSB:
    293            info.play.encoding = AUDIO_ENCODING_ULINEAR_BE;
    294            info.play.precision = 16;
    295            break;
    296        default:
    297            continue;
    298        }
    299
    300        if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
    301            break;
    302        }
    303    }
    304
    305    if (!format) {
    306        BSDAUDIO_CloseDevice(this);
    307        return SDL_SetError("No supported encoding for 0x%x", this->spec.format);
    308    }
    309
    310    this->spec.format = format;
    311
    312    AUDIO_INITINFO(&info);
    313    info.play.channels = this->spec.channels;
    314    if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
    315        this->spec.channels = 1;
    316    }
    317    AUDIO_INITINFO(&info);
    318    info.play.sample_rate = this->spec.freq;
    319    info.blocksize = this->spec.size;
    320    info.hiwat = 5;
    321    info.lowat = 3;
    322    (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
    323    (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
    324    this->spec.freq = info.play.sample_rate;
    325    /* Allocate mixing buffer */
    326    this->hidden->mixlen = this->spec.size;
    327    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
    328    if (this->hidden->mixbuf == NULL) {
    329        BSDAUDIO_CloseDevice(this);
    330        return SDL_OutOfMemory();
    331    }
    332    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
    333
    334    BSDAUDIO_Status(this);
    335
    336    /* We're ready to rock and roll. :-) */
    337    return 0;
    338}
    339
    340static int
    341BSDAUDIO_Init(SDL_AudioDriverImpl * impl)
    342{
    343    /* Set the function pointers */
    344    impl->DetectDevices = BSDAUDIO_DetectDevices;
    345    impl->OpenDevice = BSDAUDIO_OpenDevice;
    346    impl->PlayDevice = BSDAUDIO_PlayDevice;
    347    impl->WaitDevice = BSDAUDIO_WaitDevice;
    348    impl->GetDeviceBuf = BSDAUDIO_GetDeviceBuf;
    349    impl->CloseDevice = BSDAUDIO_CloseDevice;
    350
    351    return 1;   /* this audio target is available. */
    352}
    353
    354
    355AudioBootStrap BSD_AUDIO_bootstrap = {
    356    "bsd", "BSD audio", BSDAUDIO_Init, 0
    357};
    358
    359#endif /* SDL_AUDIO_DRIVER_BSD */
    360
    361/* vi: set ts=4 sw=4 expandtab: */