SDL_bsdaudio.c (10869B)
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#include "../../SDL_internal.h" 22 23#if SDL_AUDIO_DRIVER_BSD 24 25/* 26 * Driver for native OpenBSD/NetBSD audio(4). 27 * vedge@vedge.com.ar. 28 */ 29 30#include <errno.h> 31#include <unistd.h> 32#include <fcntl.h> 33#include <sys/time.h> 34#include <sys/ioctl.h> 35#include <sys/stat.h> 36#include <sys/types.h> 37#include <sys/audioio.h> 38 39#include "SDL_timer.h" 40#include "SDL_audio.h" 41#include "../SDL_audiomem.h" 42#include "../SDL_audio_c.h" 43#include "../SDL_audiodev_c.h" 44#include "SDL_bsdaudio.h" 45 46/* Use timer for synchronization */ 47/* #define USE_TIMER_SYNC */ 48 49/* #define DEBUG_AUDIO */ 50/* #define DEBUG_AUDIO_STREAM */ 51 52 53static void 54BSDAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn) 55{ 56 SDL_EnumUnixAudioDevices(iscapture, 0, NULL, addfn); 57} 58 59 60static void 61BSDAUDIO_Status(_THIS) 62{ 63#ifdef DEBUG_AUDIO 64 /* *INDENT-OFF* */ 65 audio_info_t info; 66 67 if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { 68 fprintf(stderr, "AUDIO_GETINFO failed.\n"); 69 return; 70 } 71 fprintf(stderr, "\n" 72 "[play/record info]\n" 73 "buffer size : %d bytes\n" 74 "sample rate : %i Hz\n" 75 "channels : %i\n" 76 "precision : %i-bit\n" 77 "encoding : 0x%x\n" 78 "seek : %i\n" 79 "sample count : %i\n" 80 "EOF count : %i\n" 81 "paused : %s\n" 82 "error occured : %s\n" 83 "waiting : %s\n" 84 "active : %s\n" 85 "", 86 info.play.buffer_size, 87 info.play.sample_rate, 88 info.play.channels, 89 info.play.precision, 90 info.play.encoding, 91 info.play.seek, 92 info.play.samples, 93 info.play.eof, 94 info.play.pause ? "yes" : "no", 95 info.play.error ? "yes" : "no", 96 info.play.waiting ? "yes" : "no", 97 info.play.active ? "yes" : "no"); 98 99 fprintf(stderr, "\n" 100 "[audio info]\n" 101 "monitor_gain : %i\n" 102 "hw block size : %d bytes\n" 103 "hi watermark : %i\n" 104 "lo watermark : %i\n" 105 "audio mode : %s\n" 106 "", 107 info.monitor_gain, 108 info.blocksize, 109 info.hiwat, info.lowat, 110 (info.mode == AUMODE_PLAY) ? "PLAY" 111 : (info.mode = AUMODE_RECORD) ? "RECORD" 112 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?")); 113 /* *INDENT-ON* */ 114#endif /* DEBUG_AUDIO */ 115} 116 117 118/* This function waits until it is possible to write a full sound buffer */ 119static void 120BSDAUDIO_WaitDevice(_THIS) 121{ 122#ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */ 123 /* See if we need to use timed audio synchronization */ 124 if (this->hidden->frame_ticks) { 125 /* Use timer for general audio synchronization */ 126 Sint32 ticks; 127 128 ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS; 129 if (ticks > 0) { 130 SDL_Delay(ticks); 131 } 132 } else { 133 /* Use select() for audio synchronization */ 134 fd_set fdset; 135 struct timeval timeout; 136 137 FD_ZERO(&fdset); 138 FD_SET(this->hidden->audio_fd, &fdset); 139 timeout.tv_sec = 10; 140 timeout.tv_usec = 0; 141#ifdef DEBUG_AUDIO 142 fprintf(stderr, "Waiting for audio to get ready\n"); 143#endif 144 if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout) 145 <= 0) { 146 const char *message = 147 "Audio timeout - buggy audio driver? (disabled)"; 148 /* In general we should never print to the screen, 149 but in this case we have no other way of letting 150 the user know what happened. 151 */ 152 fprintf(stderr, "SDL: %s\n", message); 153 this->enabled = 0; 154 /* Don't try to close - may hang */ 155 this->hidden->audio_fd = -1; 156#ifdef DEBUG_AUDIO 157 fprintf(stderr, "Done disabling audio\n"); 158#endif 159 } 160#ifdef DEBUG_AUDIO 161 fprintf(stderr, "Ready!\n"); 162#endif 163 } 164#endif /* !USE_BLOCKING_WRITES */ 165} 166 167static void 168BSDAUDIO_PlayDevice(_THIS) 169{ 170 int written, p = 0; 171 172 /* Write the audio data, checking for EAGAIN on broken audio drivers */ 173 do { 174 written = write(this->hidden->audio_fd, 175 &this->hidden->mixbuf[p], this->hidden->mixlen - p); 176 177 if (written > 0) 178 p += written; 179 if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) { 180 /* Non recoverable error has occurred. It should be reported!!! */ 181 perror("audio"); 182 break; 183 } 184 185 if (p < written 186 || ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) { 187 SDL_Delay(1); /* Let a little CPU time go by */ 188 } 189 } while (p < written); 190 191 /* If timer synchronization is enabled, set the next write frame */ 192 if (this->hidden->frame_ticks) { 193 this->hidden->next_frame += this->hidden->frame_ticks; 194 } 195 196 /* If we couldn't write, assume fatal error for now */ 197 if (written < 0) { 198 this->enabled = 0; 199 } 200#ifdef DEBUG_AUDIO 201 fprintf(stderr, "Wrote %d bytes of audio data\n", written); 202#endif 203} 204 205static Uint8 * 206BSDAUDIO_GetDeviceBuf(_THIS) 207{ 208 return (this->hidden->mixbuf); 209} 210 211static void 212BSDAUDIO_CloseDevice(_THIS) 213{ 214 if (this->hidden != NULL) { 215 SDL_FreeAudioMem(this->hidden->mixbuf); 216 this->hidden->mixbuf = NULL; 217 if (this->hidden->audio_fd >= 0) { 218 close(this->hidden->audio_fd); 219 this->hidden->audio_fd = -1; 220 } 221 SDL_free(this->hidden); 222 this->hidden = NULL; 223 } 224} 225 226static int 227BSDAUDIO_OpenDevice(_THIS, const char *devname, int iscapture) 228{ 229 const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT); 230 SDL_AudioFormat format = 0; 231 audio_info_t info; 232 233 /* We don't care what the devname is...we'll try to open anything. */ 234 /* ...but default to first name in the list... */ 235 if (devname == NULL) { 236 devname = SDL_GetAudioDeviceName(0, iscapture); 237 if (devname == NULL) { 238 return SDL_SetError("No such audio device"); 239 } 240 } 241 242 /* Initialize all variables that we clean on shutdown */ 243 this->hidden = (struct SDL_PrivateAudioData *) 244 SDL_malloc((sizeof *this->hidden)); 245 if (this->hidden == NULL) { 246 return SDL_OutOfMemory(); 247 } 248 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 249 250 /* Open the audio device */ 251 this->hidden->audio_fd = open(devname, flags, 0); 252 if (this->hidden->audio_fd < 0) { 253 return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno)); 254 } 255 256 AUDIO_INITINFO(&info); 257 258 /* Calculate the final parameters for this audio specification */ 259 SDL_CalculateAudioSpec(&this->spec); 260 261 /* Set to play mode */ 262 info.mode = AUMODE_PLAY; 263 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) { 264 BSDAUDIO_CloseDevice(this); 265 return SDL_SetError("Couldn't put device into play mode"); 266 } 267 268 AUDIO_INITINFO(&info); 269 for (format = SDL_FirstAudioFormat(this->spec.format); 270 format; format = SDL_NextAudioFormat()) { 271 switch (format) { 272 case AUDIO_U8: 273 info.play.encoding = AUDIO_ENCODING_ULINEAR; 274 info.play.precision = 8; 275 break; 276 case AUDIO_S8: 277 info.play.encoding = AUDIO_ENCODING_SLINEAR; 278 info.play.precision = 8; 279 break; 280 case AUDIO_S16LSB: 281 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE; 282 info.play.precision = 16; 283 break; 284 case AUDIO_S16MSB: 285 info.play.encoding = AUDIO_ENCODING_SLINEAR_BE; 286 info.play.precision = 16; 287 break; 288 case AUDIO_U16LSB: 289 info.play.encoding = AUDIO_ENCODING_ULINEAR_LE; 290 info.play.precision = 16; 291 break; 292 case AUDIO_U16MSB: 293 info.play.encoding = AUDIO_ENCODING_ULINEAR_BE; 294 info.play.precision = 16; 295 break; 296 default: 297 continue; 298 } 299 300 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) { 301 break; 302 } 303 } 304 305 if (!format) { 306 BSDAUDIO_CloseDevice(this); 307 return SDL_SetError("No supported encoding for 0x%x", this->spec.format); 308 } 309 310 this->spec.format = format; 311 312 AUDIO_INITINFO(&info); 313 info.play.channels = this->spec.channels; 314 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) { 315 this->spec.channels = 1; 316 } 317 AUDIO_INITINFO(&info); 318 info.play.sample_rate = this->spec.freq; 319 info.blocksize = this->spec.size; 320 info.hiwat = 5; 321 info.lowat = 3; 322 (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info); 323 (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info); 324 this->spec.freq = info.play.sample_rate; 325 /* Allocate mixing buffer */ 326 this->hidden->mixlen = this->spec.size; 327 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); 328 if (this->hidden->mixbuf == NULL) { 329 BSDAUDIO_CloseDevice(this); 330 return SDL_OutOfMemory(); 331 } 332 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); 333 334 BSDAUDIO_Status(this); 335 336 /* We're ready to rock and roll. :-) */ 337 return 0; 338} 339 340static int 341BSDAUDIO_Init(SDL_AudioDriverImpl * impl) 342{ 343 /* Set the function pointers */ 344 impl->DetectDevices = BSDAUDIO_DetectDevices; 345 impl->OpenDevice = BSDAUDIO_OpenDevice; 346 impl->PlayDevice = BSDAUDIO_PlayDevice; 347 impl->WaitDevice = BSDAUDIO_WaitDevice; 348 impl->GetDeviceBuf = BSDAUDIO_GetDeviceBuf; 349 impl->CloseDevice = BSDAUDIO_CloseDevice; 350 351 return 1; /* this audio target is available. */ 352} 353 354 355AudioBootStrap BSD_AUDIO_bootstrap = { 356 "bsd", "BSD audio", BSDAUDIO_Init, 0 357}; 358 359#endif /* SDL_AUDIO_DRIVER_BSD */ 360 361/* vi: set ts=4 sw=4 expandtab: */