sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_sndioaudio.c (10426B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 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 
     22 #include "../../SDL_internal.h"
     23 
     24 #if SDL_AUDIO_DRIVER_SNDIO
     25 
     26 /* OpenBSD sndio target */
     27 
     28 #if HAVE_STDIO_H
     29 #include <stdio.h>
     30 #endif
     31 
     32 #ifdef HAVE_SIGNAL_H
     33 #include <signal.h>
     34 #endif
     35 
     36 #include <poll.h>
     37 #include <unistd.h>
     38 
     39 #include "SDL_audio.h"
     40 #include "../SDL_audio_c.h"
     41 #include "SDL_sndioaudio.h"
     42 
     43 #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
     44 #include "SDL_loadso.h"
     45 #endif
     46 
     47 #ifndef INFTIM
     48 #define INFTIM -1
     49 #endif
     50 
     51 #ifndef SIO_DEVANY
     52 #define SIO_DEVANY "default"
     53 #endif
     54 
     55 static struct sio_hdl * (*SNDIO_sio_open)(const char *, unsigned int, int);
     56 static void (*SNDIO_sio_close)(struct sio_hdl *);
     57 static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *);
     58 static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *);
     59 static int (*SNDIO_sio_start)(struct sio_hdl *);
     60 static int (*SNDIO_sio_stop)(struct sio_hdl *);
     61 static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t);
     62 static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t);
     63 static int (*SNDIO_sio_nfds)(struct sio_hdl *);
     64 static int (*SNDIO_sio_pollfd)(struct sio_hdl *, struct pollfd *, int);
     65 static int (*SNDIO_sio_revents)(struct sio_hdl *, struct pollfd *);
     66 static int (*SNDIO_sio_eof)(struct sio_hdl *);
     67 static void (*SNDIO_sio_initpar)(struct sio_par *);
     68 
     69 #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
     70 static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC;
     71 static void *sndio_handle = NULL;
     72 
     73 static int
     74 load_sndio_sym(const char *fn, void **addr)
     75 {
     76     *addr = SDL_LoadFunction(sndio_handle, fn);
     77     if (*addr == NULL) {
     78         /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
     79         return 0;
     80     }
     81 
     82     return 1;
     83 }
     84 
     85 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
     86 #define SDL_SNDIO_SYM(x) \
     87     if (!load_sndio_sym(#x, (void **) (char *) &SNDIO_##x)) return -1
     88 #else
     89 #define SDL_SNDIO_SYM(x) SNDIO_##x = x
     90 #endif
     91 
     92 static int
     93 load_sndio_syms(void)
     94 {
     95     SDL_SNDIO_SYM(sio_open);
     96     SDL_SNDIO_SYM(sio_close);
     97     SDL_SNDIO_SYM(sio_setpar);
     98     SDL_SNDIO_SYM(sio_getpar);
     99     SDL_SNDIO_SYM(sio_start);
    100     SDL_SNDIO_SYM(sio_stop);
    101     SDL_SNDIO_SYM(sio_read);
    102     SDL_SNDIO_SYM(sio_write);
    103     SDL_SNDIO_SYM(sio_nfds);
    104     SDL_SNDIO_SYM(sio_pollfd);
    105     SDL_SNDIO_SYM(sio_revents);
    106     SDL_SNDIO_SYM(sio_eof);
    107     SDL_SNDIO_SYM(sio_initpar);
    108     return 0;
    109 }
    110 
    111 #undef SDL_SNDIO_SYM
    112 
    113 #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
    114 
    115 static void
    116 UnloadSNDIOLibrary(void)
    117 {
    118     if (sndio_handle != NULL) {
    119         SDL_UnloadObject(sndio_handle);
    120         sndio_handle = NULL;
    121     }
    122 }
    123 
    124 static int
    125 LoadSNDIOLibrary(void)
    126 {
    127     int retval = 0;
    128     if (sndio_handle == NULL) {
    129         sndio_handle = SDL_LoadObject(sndio_library);
    130         if (sndio_handle == NULL) {
    131             retval = -1;
    132             /* Don't call SDL_SetError(): SDL_LoadObject already did. */
    133         } else {
    134             retval = load_sndio_syms();
    135             if (retval < 0) {
    136                 UnloadSNDIOLibrary();
    137             }
    138         }
    139     }
    140     return retval;
    141 }
    142 
    143 #else
    144 
    145 static void
    146 UnloadSNDIOLibrary(void)
    147 {
    148 }
    149 
    150 static int
    151 LoadSNDIOLibrary(void)
    152 {
    153     load_sndio_syms();
    154     return 0;
    155 }
    156 
    157 #endif /* SDL_AUDIO_DRIVER_SNDIO_DYNAMIC */
    158 
    159 
    160 
    161 
    162 static void
    163 SNDIO_WaitDevice(_THIS)
    164 {
    165     /* no-op; SNDIO_sio_write() blocks if necessary. */
    166 }
    167 
    168 static void
    169 SNDIO_PlayDevice(_THIS)
    170 {
    171     const int written = SNDIO_sio_write(this->hidden->dev,
    172                                         this->hidden->mixbuf,
    173                                         this->hidden->mixlen);
    174 
    175     /* If we couldn't write, assume fatal error for now */
    176     if ( written == 0 ) {
    177         SDL_OpenedAudioDeviceDisconnected(this);
    178     }
    179 #ifdef DEBUG_AUDIO
    180     fprintf(stderr, "Wrote %d bytes of audio data\n", written);
    181 #endif
    182 }
    183 
    184 static int
    185 SNDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
    186 {
    187     size_t r;
    188     int revents;
    189     int nfds;
    190 
    191     /* Emulate a blocking read */
    192     r = SNDIO_sio_read(this->hidden->dev, buffer, buflen);
    193     while (r == 0 && !SNDIO_sio_eof(this->hidden->dev)) {
    194         if ((nfds = SNDIO_sio_pollfd(this->hidden->dev, this->hidden->pfd, POLLIN)) <= 0
    195             || poll(this->hidden->pfd, nfds, INFTIM) < 0) {
    196             return -1;
    197         }
    198         revents = SNDIO_sio_revents(this->hidden->dev, this->hidden->pfd);
    199         if (revents & POLLIN) {
    200             r = SNDIO_sio_read(this->hidden->dev, buffer, buflen);
    201         }
    202         if (revents & POLLHUP) {
    203             break;
    204         }
    205     }
    206     return (int) r;
    207 }
    208 
    209 static void
    210 SNDIO_FlushCapture(_THIS)
    211 {
    212     char buf[512];
    213 
    214     while (SNDIO_sio_read(this->hidden->dev, buf, sizeof(buf)) != 0) {
    215         /* do nothing */;
    216     }
    217 }
    218 
    219 static Uint8 *
    220 SNDIO_GetDeviceBuf(_THIS)
    221 {
    222     return this->hidden->mixbuf;
    223 }
    224 
    225 static void
    226 SNDIO_CloseDevice(_THIS)
    227 {
    228     if ( this->hidden->pfd != NULL ) {
    229         SDL_free(this->hidden->pfd);
    230     }
    231     if ( this->hidden->dev != NULL ) {
    232         SNDIO_sio_stop(this->hidden->dev);
    233         SNDIO_sio_close(this->hidden->dev);
    234     }
    235     SDL_free(this->hidden->mixbuf);
    236     SDL_free(this->hidden);
    237 }
    238 
    239 static int
    240 SNDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
    241 {
    242     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
    243     struct sio_par par;
    244     int status;
    245 
    246     this->hidden = (struct SDL_PrivateAudioData *)
    247         SDL_malloc(sizeof(*this->hidden));
    248     if (this->hidden == NULL) {
    249         return SDL_OutOfMemory();
    250     }
    251     SDL_zerop(this->hidden);
    252 
    253     this->hidden->mixlen = this->spec.size;
    254 
    255     /* Capture devices must be non-blocking for SNDIO_FlushCapture */
    256     if ((this->hidden->dev =
    257         SNDIO_sio_open(devname != NULL ? devname : SIO_DEVANY,
    258                        iscapture ? SIO_REC : SIO_PLAY, iscapture)) == NULL) {
    259         return SDL_SetError("sio_open() failed");
    260     }
    261 
    262     /* Allocate the pollfd array for capture devices */
    263     if (iscapture && (this->hidden->pfd =
    264         SDL_malloc(sizeof(struct pollfd) * SNDIO_sio_nfds(this->hidden->dev))) == NULL) {
    265         return SDL_OutOfMemory();
    266     }
    267 
    268     SNDIO_sio_initpar(&par);
    269 
    270     par.rate = this->spec.freq;
    271     par.pchan = this->spec.channels;
    272     par.round = this->spec.samples;
    273     par.appbufsz = par.round * 2;
    274 
    275     /* Try for a closest match on audio format */
    276     status = -1;
    277     while (test_format && (status < 0)) {
    278         if (!SDL_AUDIO_ISFLOAT(test_format)) {
    279             par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0;
    280             par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0;
    281             par.bits = SDL_AUDIO_BITSIZE(test_format);
    282 
    283             if (SNDIO_sio_setpar(this->hidden->dev, &par) == 0) {
    284                 continue;
    285             }
    286             if (SNDIO_sio_getpar(this->hidden->dev, &par) == 0) {
    287                 return SDL_SetError("sio_getpar() failed");
    288             }
    289             if (par.bps != SIO_BPS(par.bits)) {
    290                 continue;
    291             }
    292             if ((par.bits == 8 * par.bps) || (par.msb)) {
    293                 status = 0;
    294                 break;
    295             }
    296         }
    297         test_format = SDL_NextAudioFormat();
    298     }
    299 
    300     if (status < 0) {
    301         return SDL_SetError("sndio: Couldn't find any hardware audio formats");
    302     }
    303 
    304     if ((par.bps == 4) && (par.sig) && (par.le))
    305         this->spec.format = AUDIO_S32LSB;
    306     else if ((par.bps == 4) && (par.sig) && (!par.le))
    307         this->spec.format = AUDIO_S32MSB;
    308     else if ((par.bps == 2) && (par.sig) && (par.le))
    309         this->spec.format = AUDIO_S16LSB;
    310     else if ((par.bps == 2) && (par.sig) && (!par.le))
    311         this->spec.format = AUDIO_S16MSB;
    312     else if ((par.bps == 2) && (!par.sig) && (par.le))
    313         this->spec.format = AUDIO_U16LSB;
    314     else if ((par.bps == 2) && (!par.sig) && (!par.le))
    315         this->spec.format = AUDIO_U16MSB;
    316     else if ((par.bps == 1) && (par.sig))
    317         this->spec.format = AUDIO_S8;
    318     else if ((par.bps == 1) && (!par.sig))
    319         this->spec.format = AUDIO_U8;
    320     else {
    321         return SDL_SetError("sndio: Got unsupported hardware audio format.");
    322     }
    323 
    324     this->spec.freq = par.rate;
    325     this->spec.channels = par.pchan;
    326     this->spec.samples = par.round;
    327 
    328     /* Calculate the final parameters for this audio specification */
    329     SDL_CalculateAudioSpec(&this->spec);
    330 
    331     /* Allocate mixing buffer */
    332     this->hidden->mixlen = this->spec.size;
    333     this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
    334     if (this->hidden->mixbuf == NULL) {
    335         return SDL_OutOfMemory();
    336     }
    337     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
    338 
    339     if (!SNDIO_sio_start(this->hidden->dev)) {
    340         return SDL_SetError("sio_start() failed");
    341     }
    342 
    343     /* We're ready to rock and roll. :-) */
    344     return 0;
    345 }
    346 
    347 static void
    348 SNDIO_Deinitialize(void)
    349 {
    350     UnloadSNDIOLibrary();
    351 }
    352 
    353 static int
    354 SNDIO_Init(SDL_AudioDriverImpl * impl)
    355 {
    356     if (LoadSNDIOLibrary() < 0) {
    357         return 0;
    358     }
    359 
    360     /* Set the function pointers */
    361     impl->OpenDevice = SNDIO_OpenDevice;
    362     impl->WaitDevice = SNDIO_WaitDevice;
    363     impl->PlayDevice = SNDIO_PlayDevice;
    364     impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
    365     impl->CloseDevice = SNDIO_CloseDevice;
    366     impl->CaptureFromDevice = SNDIO_CaptureFromDevice;
    367     impl->FlushCapture = SNDIO_FlushCapture;
    368     impl->Deinitialize = SNDIO_Deinitialize;
    369 
    370     impl->AllowsArbitraryDeviceNames = 1;
    371     impl->HasCaptureSupport = SDL_TRUE;
    372 
    373     return 1;   /* this audio target is available. */
    374 }
    375 
    376 AudioBootStrap SNDIO_bootstrap = {
    377     "sndio", "OpenBSD sndio", SNDIO_Init, 0
    378 };
    379 
    380 #endif /* SDL_AUDIO_DRIVER_SNDIO */
    381 
    382 /* vi: set ts=4 sw=4 expandtab: */