cscg22-gearboy

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

SDL_dspaudio.c (9169B)


      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_OSS
     24
     25/* Allow access to a raw mixing buffer */
     26
     27#include <stdio.h>              /* For perror() */
     28#include <string.h>             /* For strerror() */
     29#include <errno.h>
     30#include <unistd.h>
     31#include <fcntl.h>
     32#include <signal.h>
     33#include <sys/time.h>
     34#include <sys/ioctl.h>
     35#include <sys/stat.h>
     36
     37#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
     38/* This is installed on some systems */
     39#include <soundcard.h>
     40#else
     41/* This is recommended by OSS */
     42#include <sys/soundcard.h>
     43#endif
     44
     45#include "SDL_timer.h"
     46#include "SDL_audio.h"
     47#include "../SDL_audiomem.h"
     48#include "../SDL_audio_c.h"
     49#include "../SDL_audiodev_c.h"
     50#include "SDL_dspaudio.h"
     51
     52
     53static void
     54DSP_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
     55{
     56    SDL_EnumUnixAudioDevices(iscapture, 0, NULL, addfn);
     57}
     58
     59
     60static void
     61DSP_CloseDevice(_THIS)
     62{
     63    if (this->hidden != NULL) {
     64        SDL_FreeAudioMem(this->hidden->mixbuf);
     65        this->hidden->mixbuf = NULL;
     66        if (this->hidden->audio_fd >= 0) {
     67            close(this->hidden->audio_fd);
     68            this->hidden->audio_fd = -1;
     69        }
     70        SDL_free(this->hidden);
     71        this->hidden = NULL;
     72    }
     73}
     74
     75
     76static int
     77DSP_OpenDevice(_THIS, const char *devname, int iscapture)
     78{
     79    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
     80    int format;
     81    int value;
     82    int frag_spec;
     83    SDL_AudioFormat test_format;
     84
     85    /* We don't care what the devname is...we'll try to open anything. */
     86    /*  ...but default to first name in the list... */
     87    if (devname == NULL) {
     88        devname = SDL_GetAudioDeviceName(0, iscapture);
     89        if (devname == NULL) {
     90            return SDL_SetError("No such audio device");
     91        }
     92    }
     93
     94    /* Make sure fragment size stays a power of 2, or OSS fails. */
     95    /* I don't know which of these are actually legal values, though... */
     96    if (this->spec.channels > 8)
     97        this->spec.channels = 8;
     98    else if (this->spec.channels > 4)
     99        this->spec.channels = 4;
    100    else if (this->spec.channels > 2)
    101        this->spec.channels = 2;
    102
    103    /* Initialize all variables that we clean on shutdown */
    104    this->hidden = (struct SDL_PrivateAudioData *)
    105        SDL_malloc((sizeof *this->hidden));
    106    if (this->hidden == NULL) {
    107        return SDL_OutOfMemory();
    108    }
    109    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    110
    111    /* Open the audio device */
    112    this->hidden->audio_fd = open(devname, flags, 0);
    113    if (this->hidden->audio_fd < 0) {
    114        DSP_CloseDevice(this);
    115        return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
    116    }
    117    this->hidden->mixbuf = NULL;
    118
    119    /* Make the file descriptor use blocking writes with fcntl() */
    120    {
    121        long ctlflags;
    122        ctlflags = fcntl(this->hidden->audio_fd, F_GETFL);
    123        ctlflags &= ~O_NONBLOCK;
    124        if (fcntl(this->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
    125            DSP_CloseDevice(this);
    126            return SDL_SetError("Couldn't set audio blocking mode");
    127        }
    128    }
    129
    130    /* Get a list of supported hardware formats */
    131    if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
    132        perror("SNDCTL_DSP_GETFMTS");
    133        DSP_CloseDevice(this);
    134        return SDL_SetError("Couldn't get audio format list");
    135    }
    136
    137    /* Try for a closest match on audio format */
    138    format = 0;
    139    for (test_format = SDL_FirstAudioFormat(this->spec.format);
    140         !format && test_format;) {
    141#ifdef DEBUG_AUDIO
    142        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
    143#endif
    144        switch (test_format) {
    145        case AUDIO_U8:
    146            if (value & AFMT_U8) {
    147                format = AFMT_U8;
    148            }
    149            break;
    150        case AUDIO_S16LSB:
    151            if (value & AFMT_S16_LE) {
    152                format = AFMT_S16_LE;
    153            }
    154            break;
    155        case AUDIO_S16MSB:
    156            if (value & AFMT_S16_BE) {
    157                format = AFMT_S16_BE;
    158            }
    159            break;
    160#if 0
    161/*
    162 * These formats are not used by any real life systems so they are not
    163 * needed here.
    164 */
    165        case AUDIO_S8:
    166            if (value & AFMT_S8) {
    167                format = AFMT_S8;
    168            }
    169            break;
    170        case AUDIO_U16LSB:
    171            if (value & AFMT_U16_LE) {
    172                format = AFMT_U16_LE;
    173            }
    174            break;
    175        case AUDIO_U16MSB:
    176            if (value & AFMT_U16_BE) {
    177                format = AFMT_U16_BE;
    178            }
    179            break;
    180#endif
    181        default:
    182            format = 0;
    183            break;
    184        }
    185        if (!format) {
    186            test_format = SDL_NextAudioFormat();
    187        }
    188    }
    189    if (format == 0) {
    190        DSP_CloseDevice(this);
    191        return SDL_SetError("Couldn't find any hardware audio formats");
    192    }
    193    this->spec.format = test_format;
    194
    195    /* Set the audio format */
    196    value = format;
    197    if ((ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
    198        (value != format)) {
    199        perror("SNDCTL_DSP_SETFMT");
    200        DSP_CloseDevice(this);
    201        return SDL_SetError("Couldn't set audio format");
    202    }
    203
    204    /* Set the number of channels of output */
    205    value = this->spec.channels;
    206    if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
    207        perror("SNDCTL_DSP_CHANNELS");
    208        DSP_CloseDevice(this);
    209        return SDL_SetError("Cannot set the number of channels");
    210    }
    211    this->spec.channels = value;
    212
    213    /* Set the DSP frequency */
    214    value = this->spec.freq;
    215    if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
    216        perror("SNDCTL_DSP_SPEED");
    217        DSP_CloseDevice(this);
    218        return SDL_SetError("Couldn't set audio frequency");
    219    }
    220    this->spec.freq = value;
    221
    222    /* Calculate the final parameters for this audio specification */
    223    SDL_CalculateAudioSpec(&this->spec);
    224
    225    /* Determine the power of two of the fragment size */
    226    for (frag_spec = 0; (0x01U << frag_spec) < this->spec.size; ++frag_spec);
    227    if ((0x01U << frag_spec) != this->spec.size) {
    228        DSP_CloseDevice(this);
    229        return SDL_SetError("Fragment size must be a power of two");
    230    }
    231    frag_spec |= 0x00020000;    /* two fragments, for low latency */
    232
    233    /* Set the audio buffering parameters */
    234#ifdef DEBUG_AUDIO
    235    fprintf(stderr, "Requesting %d fragments of size %d\n",
    236            (frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
    237#endif
    238    if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
    239        perror("SNDCTL_DSP_SETFRAGMENT");
    240    }
    241#ifdef DEBUG_AUDIO
    242    {
    243        audio_buf_info info;
    244        ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
    245        fprintf(stderr, "fragments = %d\n", info.fragments);
    246        fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
    247        fprintf(stderr, "fragsize = %d\n", info.fragsize);
    248        fprintf(stderr, "bytes = %d\n", info.bytes);
    249    }
    250#endif
    251
    252    /* Allocate mixing buffer */
    253    this->hidden->mixlen = this->spec.size;
    254    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
    255    if (this->hidden->mixbuf == NULL) {
    256        DSP_CloseDevice(this);
    257        return SDL_OutOfMemory();
    258    }
    259    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
    260
    261    /* We're ready to rock and roll. :-) */
    262    return 0;
    263}
    264
    265
    266static void
    267DSP_PlayDevice(_THIS)
    268{
    269    const Uint8 *mixbuf = this->hidden->mixbuf;
    270    const int mixlen = this->hidden->mixlen;
    271    if (write(this->hidden->audio_fd, mixbuf, mixlen) == -1) {
    272        perror("Audio write");
    273        this->enabled = 0;
    274    }
    275#ifdef DEBUG_AUDIO
    276    fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
    277#endif
    278}
    279
    280static Uint8 *
    281DSP_GetDeviceBuf(_THIS)
    282{
    283    return (this->hidden->mixbuf);
    284}
    285
    286static int
    287DSP_Init(SDL_AudioDriverImpl * impl)
    288{
    289    /* Set the function pointers */
    290    impl->DetectDevices = DSP_DetectDevices;
    291    impl->OpenDevice = DSP_OpenDevice;
    292    impl->PlayDevice = DSP_PlayDevice;
    293    impl->GetDeviceBuf = DSP_GetDeviceBuf;
    294    impl->CloseDevice = DSP_CloseDevice;
    295
    296    return 1;   /* this audio target is available. */
    297}
    298
    299
    300AudioBootStrap DSP_bootstrap = {
    301    "dsp", "OSS /dev/dsp standard audio", DSP_Init, 0
    302};
    303
    304#endif /* SDL_AUDIO_DRIVER_OSS */
    305
    306/* vi: set ts=4 sw=4 expandtab: */