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 (at) libsdl.org 21 */ 22 #include "SDL_config.h" 23 24 /* 25 * Driver for native OpenBSD/NetBSD audio(4). 26 * vedge (at) 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 */ 70 static void OBSD_WaitAudio(_THIS); 71 static int OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec); 72 static void OBSD_PlayAudio(_THIS); 73 static Uint8 *OBSD_GetAudioBuf(_THIS); 74 static void OBSD_CloseAudio(_THIS); 75 76 #ifdef DEBUG_AUDIO 77 static void OBSD_Status(_THIS); 78 #endif 79 80 /* Audio driver bootstrap functions */ 81 82 static int 83 Audio_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 97 static void 98 Audio_DeleteDevice(SDL_AudioDevice *device) 99 { 100 SDL_free(device->hidden); 101 SDL_free(device); 102 } 103 104 static 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 136 AudioBootStrap 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 */ 142 static void 143 OBSD_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 189 static void 190 OBSD_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 225 static Uint8 226 *OBSD_GetAudioBuf(_THIS) 227 { 228 return(mixbuf); 229 } 230 231 static void 232 OBSD_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 245 void 246 OBSD_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 301 static int 302 OBSD_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