SDL_sndioaudio.c (9010B)
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 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 47static struct sio_hdl * (*SNDIO_sio_open)(const char *, unsigned int, int); 48static void (*SNDIO_sio_close)(struct sio_hdl *); 49static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *); 50static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *); 51static int (*SNDIO_sio_start)(struct sio_hdl *); 52static int (*SNDIO_sio_stop)(struct sio_hdl *); 53static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t); 54static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t); 55static void (*SNDIO_sio_initpar)(struct sio_par *); 56 57#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC 58static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC; 59static void *sndio_handle = NULL; 60 61static int 62load_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 80static int 81load_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 99static void 100UnloadSNDIOLibrary(void) 101{ 102 if (sndio_handle != NULL) { 103 SDL_UnloadObject(sndio_handle); 104 sndio_handle = NULL; 105 } 106} 107 108static int 109LoadSNDIOLibrary(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 129static void 130UnloadSNDIOLibrary(void) 131{ 132} 133 134static int 135LoadSNDIOLibrary(void) 136{ 137 load_sndio_syms(); 138 return 0; 139} 140 141#endif /* SDL_AUDIO_DRIVER_SNDIO_DYNAMIC */ 142 143 144 145 146static void 147SNDIO_WaitDevice(_THIS) 148{ 149 /* no-op; SNDIO_sio_write() blocks if necessary. */ 150} 151 152static void 153SNDIO_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 this->enabled = 0; 162 } 163#ifdef DEBUG_AUDIO 164 fprintf(stderr, "Wrote %d bytes of audio data\n", written); 165#endif 166} 167 168static Uint8 * 169SNDIO_GetDeviceBuf(_THIS) 170{ 171 return this->hidden->mixbuf; 172} 173 174static void 175SNDIO_WaitDone(_THIS) 176{ 177 SNDIO_sio_stop(this->hidden->dev); 178} 179 180static void 181SNDIO_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 195static int 196SNDIO_OpenDevice(_THIS, 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 300static void 301SNDIO_Deinitialize(void) 302{ 303 UnloadSNDIOLibrary(); 304} 305 306static int 307SNDIO_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 326AudioBootStrap 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: */