src/audio/sndio/SDL_sndioaudio.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 21 May 2018 12:00:21 -0400
changeset 11994 8e094f91ddab
parent 11811 5d94cb6b24d3
child 12503 806492103856
permissions -rw-r--r--
thread: fixed compiler warnings on non-Linux systems that use pthread.

(static function rtkit_setpriority was unused, moved it in with rest of
__LINUX__ section.)
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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: */