src/audio/sndio/SDL_sndioaudio.c
author Ryan C. Gordon
Sun, 03 Jan 2016 20:52:44 -0500
changeset 10004 8f2f519d1e61
parent 9998 f67cf37e9cd4
child 10255 9530fc07da6c
permissions -rw-r--r--
CMake: Don't make a libSDL2.so symlink on Mac OS X (do .dylib instead).
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 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 <unistd.h>
    37 
    38 #include "SDL_audio.h"
    39 #include "../SDL_audiomem.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 static struct sio_hdl * (*SNDIO_sio_open)(const char *, unsigned int, int);
    48 static void (*SNDIO_sio_close)(struct sio_hdl *);
    49 static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *);
    50 static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *);
    51 static int (*SNDIO_sio_start)(struct sio_hdl *);
    52 static int (*SNDIO_sio_stop)(struct sio_hdl *);
    53 static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t);
    54 static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t);
    55 static void (*SNDIO_sio_initpar)(struct sio_par *);
    56 
    57 #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
    58 static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC;
    59 static void *sndio_handle = NULL;
    60 
    61 static int
    62 load_sndio_sym(const char *fn, void **addr)
    63 {
    64     *addr = SDL_LoadFunction(sndio_handle, fn);
    65     if (*addr == NULL) {
    66         /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
    67         return 0;
    68     }
    69 
    70     return 1;
    71 }
    72 
    73 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
    74 #define SDL_SNDIO_SYM(x) \
    75     if (!load_sndio_sym(#x, (void **) (char *) &SNDIO_##x)) return -1
    76 #else
    77 #define SDL_SNDIO_SYM(x) SNDIO_##x = x
    78 #endif
    79 
    80 static int
    81 load_sndio_syms(void)
    82 {
    83     SDL_SNDIO_SYM(sio_open);
    84     SDL_SNDIO_SYM(sio_close);
    85     SDL_SNDIO_SYM(sio_setpar);
    86     SDL_SNDIO_SYM(sio_getpar);
    87     SDL_SNDIO_SYM(sio_start);
    88     SDL_SNDIO_SYM(sio_stop);
    89     SDL_SNDIO_SYM(sio_read);
    90     SDL_SNDIO_SYM(sio_write);
    91     SDL_SNDIO_SYM(sio_initpar);
    92     return 0;
    93 }
    94 
    95 #undef SDL_SNDIO_SYM
    96 
    97 #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
    98 
    99 static void
   100 UnloadSNDIOLibrary(void)
   101 {
   102     if (sndio_handle != NULL) {
   103         SDL_UnloadObject(sndio_handle);
   104         sndio_handle = NULL;
   105     }
   106 }
   107 
   108 static int
   109 LoadSNDIOLibrary(void)
   110 {
   111     int retval = 0;
   112     if (sndio_handle == NULL) {
   113         sndio_handle = SDL_LoadObject(sndio_library);
   114         if (sndio_handle == NULL) {
   115             retval = -1;
   116             /* Don't call SDL_SetError(): SDL_LoadObject already did. */
   117         } else {
   118             retval = load_sndio_syms();
   119             if (retval < 0) {
   120                 UnloadSNDIOLibrary();
   121             }
   122         }
   123     }
   124     return retval;
   125 }
   126 
   127 #else
   128 
   129 static void
   130 UnloadSNDIOLibrary(void)
   131 {
   132 }
   133 
   134 static int
   135 LoadSNDIOLibrary(void)
   136 {
   137     load_sndio_syms();
   138     return 0;
   139 }
   140 
   141 #endif /* SDL_AUDIO_DRIVER_SNDIO_DYNAMIC */
   142 
   143 
   144 
   145 
   146 static void
   147 SNDIO_WaitDevice(_THIS)
   148 {
   149     /* no-op; SNDIO_sio_write() blocks if necessary. */
   150 }
   151 
   152 static void
   153 SNDIO_PlayDevice(_THIS)
   154 {
   155     const int written = SNDIO_sio_write(this->hidden->dev,
   156                                         this->hidden->mixbuf,
   157                                         this->hidden->mixlen);
   158 
   159     /* If we couldn't write, assume fatal error for now */
   160     if ( written == 0 ) {
   161         SDL_OpenedAudioDeviceDisconnected(this);
   162     }
   163 #ifdef DEBUG_AUDIO
   164     fprintf(stderr, "Wrote %d bytes of audio data\n", written);
   165 #endif
   166 }
   167 
   168 static Uint8 *
   169 SNDIO_GetDeviceBuf(_THIS)
   170 {
   171     return this->hidden->mixbuf;
   172 }
   173 
   174 static void
   175 SNDIO_WaitDone(_THIS)
   176 {
   177     SNDIO_sio_stop(this->hidden->dev);
   178 }
   179 
   180 static void
   181 SNDIO_CloseDevice(_THIS)
   182 {
   183     if (this->hidden != NULL) {
   184         SDL_FreeAudioMem(this->hidden->mixbuf);
   185         this->hidden->mixbuf = NULL;
   186         if ( this->hidden->dev != NULL ) {
   187             SNDIO_sio_close(this->hidden->dev);
   188             this->hidden->dev = NULL;
   189         }
   190         SDL_free(this->hidden);
   191         this->hidden = NULL;
   192     }
   193 }
   194 
   195 static int
   196 SNDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   197 {
   198     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
   199     struct sio_par par;
   200     int status;
   201 
   202     this->hidden = (struct SDL_PrivateAudioData *)
   203         SDL_malloc(sizeof(*this->hidden));
   204     if (this->hidden == NULL) {
   205         return SDL_OutOfMemory();
   206     }
   207     SDL_memset(this->hidden, 0, sizeof(*this->hidden));
   208 
   209     this->hidden->mixlen = this->spec.size;
   210 
   211     /* !!! FIXME: SIO_DEVANY can be a specific device... */
   212     if ((this->hidden->dev = SNDIO_sio_open(SIO_DEVANY, SIO_PLAY, 0)) == NULL) {
   213         SNDIO_CloseDevice(this);
   214         return SDL_SetError("sio_open() failed");
   215     }
   216 
   217     SNDIO_sio_initpar(&par);
   218 
   219     par.rate = this->spec.freq;
   220     par.pchan = this->spec.channels;
   221     par.round = this->spec.samples;
   222     par.appbufsz = par.round * 2;
   223 
   224     /* Try for a closest match on audio format */
   225     status = -1;
   226     while (test_format && (status < 0)) {
   227         if (!SDL_AUDIO_ISFLOAT(test_format)) {
   228             par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0;
   229             par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0;
   230             par.bits = SDL_AUDIO_BITSIZE(test_format);
   231 
   232             if (SNDIO_sio_setpar(this->hidden->dev, &par) == 0) {
   233                 continue;
   234             }
   235             if (SNDIO_sio_getpar(this->hidden->dev, &par) == 0) {
   236                 SNDIO_CloseDevice(this);
   237                 return SDL_SetError("sio_getpar() failed");
   238             }
   239             if (par.bps != SIO_BPS(par.bits)) {
   240                 continue;
   241             }
   242             if ((par.bits == 8 * par.bps) || (par.msb)) {
   243                 status = 0;
   244                 break;
   245             }
   246         }
   247         test_format = SDL_NextAudioFormat();
   248     }
   249 
   250     if (status < 0) {
   251         SNDIO_CloseDevice(this);
   252         return SDL_SetError("sndio: Couldn't find any hardware audio formats");
   253     }
   254 
   255     if ((par.bps == 4) && (par.sig) && (par.le))
   256         this->spec.format = AUDIO_S32LSB;
   257     else if ((par.bps == 4) && (par.sig) && (!par.le))
   258         this->spec.format = AUDIO_S32MSB;
   259     else if ((par.bps == 2) && (par.sig) && (par.le))
   260         this->spec.format = AUDIO_S16LSB;
   261     else if ((par.bps == 2) && (par.sig) && (!par.le))
   262         this->spec.format = AUDIO_S16MSB;
   263     else if ((par.bps == 2) && (!par.sig) && (par.le))
   264         this->spec.format = AUDIO_U16LSB;
   265     else if ((par.bps == 2) && (!par.sig) && (!par.le))
   266         this->spec.format = AUDIO_U16MSB;
   267     else if ((par.bps == 1) && (par.sig))
   268         this->spec.format = AUDIO_S8;
   269     else if ((par.bps == 1) && (!par.sig))
   270         this->spec.format = AUDIO_U8;
   271     else {
   272         SNDIO_CloseDevice(this);
   273         return SDL_SetError("sndio: Got unsupported hardware audio format.");
   274     }
   275 
   276     this->spec.freq = par.rate;
   277     this->spec.channels = par.pchan;
   278     this->spec.samples = par.round;
   279 
   280     /* Calculate the final parameters for this audio specification */
   281     SDL_CalculateAudioSpec(&this->spec);
   282 
   283     /* Allocate mixing buffer */
   284     this->hidden->mixlen = this->spec.size;
   285     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   286     if (this->hidden->mixbuf == NULL) {
   287         SNDIO_CloseDevice(this);
   288         return SDL_OutOfMemory();
   289     }
   290     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
   291 
   292     if (!SNDIO_sio_start(this->hidden->dev)) {
   293         return SDL_SetError("sio_start() failed");
   294     }
   295 
   296     /* We're ready to rock and roll. :-) */
   297     return 0;
   298 }
   299 
   300 static void
   301 SNDIO_Deinitialize(void)
   302 {
   303     UnloadSNDIOLibrary();
   304 }
   305 
   306 static int
   307 SNDIO_Init(SDL_AudioDriverImpl * impl)
   308 {
   309     if (LoadSNDIOLibrary() < 0) {
   310         return 0;
   311     }
   312 
   313     /* Set the function pointers */
   314     impl->OpenDevice = SNDIO_OpenDevice;
   315     impl->WaitDevice = SNDIO_WaitDevice;
   316     impl->PlayDevice = SNDIO_PlayDevice;
   317     impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
   318     impl->WaitDone = SNDIO_WaitDone;
   319     impl->CloseDevice = SNDIO_CloseDevice;
   320     impl->Deinitialize = SNDIO_Deinitialize;
   321     impl->OnlyHasDefaultOutputDevice = 1;  /* !!! FIXME: sndio can handle multiple devices. */
   322 
   323     return 1;   /* this audio target is available. */
   324 }
   325 
   326 AudioBootStrap SNDIO_bootstrap = {
   327     "sndio", "OpenBSD sndio", SNDIO_Init, 0
   328 };
   329 
   330 #endif /* SDL_AUDIO_DRIVER_SNDIO */
   331 
   332 /* vi: set ts=4 sw=4 expandtab: */