1/* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 Sam Lantinga 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Library General Public 7 License as published by the Free Software Foundation; either 8 version 2 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Library General Public License for more details. 14 15 You should have received a copy of the GNU Library General Public 16 License along with this library; if not, write to the Free 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 19 Sam Lantinga 20 slouken@libsdl.org 21*/ 22#include "SDL_config.h" 23 24/* 25 * Driver for native OpenBSD/NetBSD audio(4). 26 * vedge@vedge.com.ar. 27 */ 28 29#include <errno.h> 30#include <unistd.h> 31#include <fcntl.h> 32#include <sys/time.h> 33#include <sys/ioctl.h> 34#include <sys/stat.h> 35#include <sys/types.h> 36#include <sys/audioio.h> 37 38#include "SDL_timer.h" 39#include "SDL_audio.h" 40#include "../SDL_audiomem.h" 41#include "../SDL_audio_c.h" 42#include "../SDL_audiodev_c.h" 43#include "SDL_bsdaudio.h" 44 45/* The tag name used by NetBSD/OpenBSD audio */ 46#ifdef __NetBSD__ 47#define BSD_AUDIO_DRIVER_NAME "netbsd" 48#define BSD_AUDIO_DRIVER_DESC "Native NetBSD audio" 49#else 50#define BSD_AUDIO_DRIVER_NAME "openbsd" 51#define BSD_AUDIO_DRIVER_DESC "Native OpenBSD audio" 52#endif 53 54/* Open the audio device for playback, and don't block if busy */ 55/* #define USE_BLOCKING_WRITES */ 56 57/* Use timer for synchronization */ 58/* #define USE_TIMER_SYNC */ 59 60/* #define DEBUG_AUDIO */ 61/* #define DEBUG_AUDIO_STREAM */ 62 63#ifdef USE_BLOCKING_WRITES 64#define OPEN_FLAGS O_WRONLY 65#else 66#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) 67#endif 68 69/* Audio driver functions */ 70static void OBSD_WaitAudio(_THIS); 71static int OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec); 72static void OBSD_PlayAudio(_THIS); 73static Uint8 *OBSD_GetAudioBuf(_THIS); 74static void OBSD_CloseAudio(_THIS); 75 76#ifdef DEBUG_AUDIO 77static void OBSD_Status(_THIS); 78#endif 79 80/* Audio driver bootstrap functions */ 81 82static int 83Audio_Available(void) 84{ 85 int fd; 86 int available; 87 88 available = 0; 89 fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); 90 if(fd >= 0) { 91 available = 1; 92 close(fd); 93 } 94 return(available); 95} 96 97static void 98Audio_DeleteDevice(SDL_AudioDevice *device) 99{ 100 SDL_free(device->hidden); 101 SDL_free(device); 102} 103 104static SDL_AudioDevice 105*Audio_CreateDevice(int devindex) 106{ 107 SDL_AudioDevice *this; 108 109 /* Initialize all variables that we clean on shutdown */ 110 this = (SDL_AudioDevice*)SDL_malloc(sizeof(SDL_AudioDevice)); 111 if(this) { 112 SDL_memset(this, 0, (sizeof *this)); 113 this->hidden = 114 (struct SDL_PrivateAudioData*)SDL_malloc((sizeof *this->hidden)); 115 } 116 if((this == NULL) || (this->hidden == NULL)) { 117 SDL_OutOfMemory(); 118 if(this) SDL_free(this); 119 return(0); 120 } 121 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 122 audio_fd = -1; 123 124 /* Set the function pointers */ 125 this->OpenAudio = OBSD_OpenAudio; 126 this->WaitAudio = OBSD_WaitAudio; 127 this->PlayAudio = OBSD_PlayAudio; 128 this->GetAudioBuf = OBSD_GetAudioBuf; 129 this->CloseAudio = OBSD_CloseAudio; 130 131 this->free = Audio_DeleteDevice; 132 133 return this; 134} 135 136AudioBootStrap BSD_AUDIO_bootstrap = { 137 BSD_AUDIO_DRIVER_NAME, BSD_AUDIO_DRIVER_DESC, 138 Audio_Available, Audio_CreateDevice 139}; 140 141/* This function waits until it is possible to write a full sound buffer */ 142static void 143OBSD_WaitAudio(_THIS) 144{ 145#ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */ 146 /* See if we need to use timed audio synchronization */ 147 if ( frame_ticks ) { 148 /* Use timer for general audio synchronization */ 149 Sint32 ticks; 150 151 ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; 152 if ( ticks > 0 ) { 153 SDL_Delay(ticks); 154 } 155 } else { 156 /* Use select() for audio synchronization */ 157 fd_set fdset; 158 struct timeval timeout; 159 160 FD_ZERO(&fdset); 161 FD_SET(audio_fd, &fdset); 162 timeout.tv_sec = 10; 163 timeout.tv_usec = 0; 164#ifdef DEBUG_AUDIO 165 fprintf(stderr, "Waiting for audio to get ready\n"); 166#endif 167 if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) { 168 const char *message = 169 "Audio timeout - buggy audio driver? (disabled)"; 170 /* In general we should never print to the screen, 171 but in this case we have no other way of letting 172 the user know what happened. 173 */ 174 fprintf(stderr, "SDL: %s\n", message); 175 this->enabled = 0; 176 /* Don't try to close - may hang */ 177 audio_fd = -1; 178#ifdef DEBUG_AUDIO 179 fprintf(stderr, "Done disabling audio\n"); 180#endif 181 } 182#ifdef DEBUG_AUDIO 183 fprintf(stderr, "Ready!\n"); 184#endif 185 } 186#endif /* !USE_BLOCKING_WRITES */ 187} 188 189static void 190OBSD_PlayAudio(_THIS) 191{ 192 int written, p=0; 193 194 /* Write the audio data, checking for EAGAIN on broken audio drivers */ 195 do { 196 written = write(audio_fd, &mixbuf[p], mixlen-p); 197 if (written>0) 198 p += written; 199 if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) 200 { 201 /* Non recoverable error has occurred. It should be reported!!! */ 202 perror("audio"); 203 break; 204 } 205 206 if ( p < written || ((written < 0) && ((errno == 0) || (errno == EAGAIN))) ) { 207 SDL_Delay(1); /* Let a little CPU time go by */ 208 } 209 } while ( p < written ); 210 211 /* If timer synchronization is enabled, set the next write frame */ 212 if ( frame_ticks ) { 213 next_frame += frame_ticks; 214 } 215 216 /* If we couldn't write, assume fatal error for now */ 217 if ( written < 0 ) { 218 this->enabled = 0; 219 } 220#ifdef DEBUG_AUDIO 221 fprintf(stderr, "Wrote %d bytes of audio data\n", written); 222#endif 223} 224 225static Uint8 226*OBSD_GetAudioBuf(_THIS) 227{ 228 return(mixbuf); 229} 230 231static void 232OBSD_CloseAudio(_THIS) 233{ 234 if(mixbuf != NULL) { 235 SDL_FreeAudioMem(mixbuf); 236 mixbuf = NULL; 237 } 238 if(audio_fd >= 0) { 239 close(audio_fd); 240 audio_fd = -1; 241 } 242} 243 244#ifdef DEBUG_AUDIO 245void 246OBSD_Status(_THIS) 247{ 248 audio_info_t info; 249 250 if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { 251 fprintf(stderr,"AUDIO_GETINFO failed.\n"); 252 return; 253 } 254 255 fprintf(stderr,"\n" 256"[play/record info]\n" 257"buffer size : %d bytes\n" 258"sample rate : %i Hz\n" 259"channels : %i\n" 260"precision : %i-bit\n" 261"encoding : 0x%x\n" 262"seek : %i\n" 263"sample count : %i\n" 264"EOF count : %i\n" 265"paused : %s\n" 266"error occured : %s\n" 267"waiting : %s\n" 268"active : %s\n" 269"", 270 info.play.buffer_size, 271 info.play.sample_rate, 272 info.play.channels, 273 info.play.precision, 274 info.play.encoding, 275 info.play.seek, 276 info.play.samples, 277 info.play.eof, 278 info.play.pause ? "yes" : "no", 279 info.play.error ? "yes" : "no", 280 info.play.waiting ? "yes" : "no", 281 info.play.active ? "yes": "no"); 282 283 fprintf(stderr,"\n" 284"[audio info]\n" 285"monitor_gain : %i\n" 286"hw block size : %d bytes\n" 287"hi watermark : %i\n" 288"lo watermark : %i\n" 289"audio mode : %s\n" 290"", 291 info.monitor_gain, 292 info.blocksize, 293 info.hiwat, info.lowat, 294 (info.mode == AUMODE_PLAY) ? "PLAY" 295 : (info.mode = AUMODE_RECORD) ? "RECORD" 296 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" 297 : "?")); 298} 299#endif /* DEBUG_AUDIO */ 300 301static int 302OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec) 303{ 304 char audiodev[64]; 305 Uint16 format; 306 audio_info_t info; 307 308 AUDIO_INITINFO(&info); 309 310 /* Calculate the final parameters for this audio specification */ 311 SDL_CalculateAudioSpec(spec); 312 313#ifdef USE_TIMER_SYNC 314 frame_ticks = 0.0; 315#endif 316 317 /* Open the audio device */ 318 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); 319 if(audio_fd < 0) { 320 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); 321 return(-1); 322 } 323 324 /* Set to play mode */ 325 info.mode = AUMODE_PLAY; 326 if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) { 327 SDL_SetError("Couldn't put device into play mode"); 328 return(-1); 329 } 330 331 mixbuf = NULL; 332 AUDIO_INITINFO(&info); 333 for (format = SDL_FirstAudioFormat(spec->format); 334 format; format = SDL_NextAudioFormat()) 335 { 336 switch(format) { 337 case AUDIO_U8: 338 info.play.encoding = AUDIO_ENCODING_ULINEAR; 339 info.play.precision = 8; 340 break; 341 case AUDIO_S8: 342 info.play.encoding = AUDIO_ENCODING_SLINEAR; 343 info.play.precision = 8; 344 break; 345 case AUDIO_S16LSB: 346 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE; 347 info.play.precision = 16; 348 break; 349 case AUDIO_S16MSB: 350 info.play.encoding = AUDIO_ENCODING_SLINEAR_BE; 351 info.play.precision = 16; 352 break; 353 case AUDIO_U16LSB: 354 info.play.encoding = AUDIO_ENCODING_ULINEAR_LE; 355 info.play.precision = 16; 356 break; 357 case AUDIO_U16MSB: 358 info.play.encoding = AUDIO_ENCODING_ULINEAR_BE; 359 info.play.precision = 16; 360 break; 361 default: 362 continue; 363 } 364 if (ioctl(audio_fd, AUDIO_SETINFO, &info) == 0) 365 break; 366 } 367 368 if(!format) { 369 SDL_SetError("No supported encoding for 0x%x", spec->format); 370 return(-1); 371 } 372 373 spec->format = format; 374 375 AUDIO_INITINFO(&info); 376 info.play.channels = spec->channels; 377 if (ioctl(audio_fd, AUDIO_SETINFO, &info) == -1) 378 spec->channels = 1; 379 AUDIO_INITINFO(&info); 380 info.play.sample_rate = spec->freq; 381 info.blocksize = spec->size; 382 info.hiwat = 5; 383 info.lowat = 3; 384 (void)ioctl(audio_fd, AUDIO_SETINFO, &info); 385 (void)ioctl(audio_fd, AUDIO_GETINFO, &info); 386 spec->freq = info.play.sample_rate; 387 /* Allocate mixing buffer */ 388 mixlen = spec->size; 389 mixbuf = (Uint8*)SDL_AllocAudioMem(mixlen); 390 if(mixbuf == NULL) { 391 return(-1); 392 } 393 SDL_memset(mixbuf, spec->silence, spec->size); 394 395 /* Get the parent process id (we're the parent of the audio thread) */ 396 parent = getpid(); 397 398#ifdef DEBUG_AUDIO 399 OBSD_Status(this); 400#endif 401 402 /* We're ready to rock and roll. :-) */ 403 return(0); 404} 405