1/* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2006 Sam Lantinga 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 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 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19 Sam Lantinga 20 slouken@libsdl.org 21*/ 22#include "SDL_config.h" 23 24/* Allow access to a raw mixing buffer */ 25 26#include <fcntl.h> 27#include <errno.h> 28#ifdef __NETBSD__ 29#include <sys/ioctl.h> 30#include <sys/audioio.h> 31#endif 32#ifdef __SVR4 33#include <sys/audioio.h> 34#else 35#include <sys/time.h> 36#include <sys/types.h> 37#endif 38#include <unistd.h> 39 40#include "SDL_timer.h" 41#include "SDL_audio.h" 42#include "../SDL_audiomem.h" 43#include "../SDL_audio_c.h" 44#include "../SDL_audiodev_c.h" 45#include "SDL_sunaudio.h" 46 47/* Open the audio device for playback, and don't block if busy */ 48#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) 49 50/* Audio driver functions */ 51static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec); 52static void DSP_WaitAudio(_THIS); 53static void DSP_PlayAudio(_THIS); 54static Uint8 *DSP_GetAudioBuf(_THIS); 55static void DSP_CloseAudio(_THIS); 56 57static Uint8 snd2au(int sample); 58 59/* Audio driver bootstrap functions */ 60 61static int Audio_Available(void) 62{ 63 int fd; 64 int available; 65 66 available = 0; 67 fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 1); 68 if ( fd >= 0 ) { 69 available = 1; 70 close(fd); 71 } 72 return(available); 73} 74 75static void Audio_DeleteDevice(SDL_AudioDevice *device) 76{ 77 SDL_free(device->hidden); 78 SDL_free(device); 79} 80 81static SDL_AudioDevice *Audio_CreateDevice(int devindex) 82{ 83 SDL_AudioDevice *this; 84 85 /* Initialize all variables that we clean on shutdown */ 86 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); 87 if ( this ) { 88 SDL_memset(this, 0, (sizeof *this)); 89 this->hidden = (struct SDL_PrivateAudioData *) 90 SDL_malloc((sizeof *this->hidden)); 91 } 92 if ( (this == NULL) || (this->hidden == NULL) ) { 93 SDL_OutOfMemory(); 94 if ( this ) { 95 SDL_free(this); 96 } 97 return(0); 98 } 99 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 100 audio_fd = -1; 101 102 /* Set the function pointers */ 103 this->OpenAudio = DSP_OpenAudio; 104 this->WaitAudio = DSP_WaitAudio; 105 this->PlayAudio = DSP_PlayAudio; 106 this->GetAudioBuf = DSP_GetAudioBuf; 107 this->CloseAudio = DSP_CloseAudio; 108 109 this->free = Audio_DeleteDevice; 110 111 return this; 112} 113 114AudioBootStrap SUNAUDIO_bootstrap = { 115 "audio", "UNIX /dev/audio interface", 116 Audio_Available, Audio_CreateDevice 117}; 118 119#ifdef DEBUG_AUDIO 120void CheckUnderflow(_THIS) 121{ 122#ifdef AUDIO_GETINFO 123 audio_info_t info; 124 int left; 125 126 ioctl(audio_fd, AUDIO_GETINFO, &info); 127 left = (written - info.play.samples); 128 if ( written && (left == 0) ) { 129 fprintf(stderr, "audio underflow!\n"); 130 } 131#endif 132} 133#endif 134 135void DSP_WaitAudio(_THIS) 136{ 137#ifdef AUDIO_GETINFO 138#define SLEEP_FUDGE 10 /* 10 ms scheduling fudge factor */ 139 audio_info_t info; 140 Sint32 left; 141 142 ioctl(audio_fd, AUDIO_GETINFO, &info); 143 left = (written - info.play.samples); 144 if ( left > fragsize ) { 145 Sint32 sleepy; 146 147 sleepy = ((left - fragsize)/frequency); 148 sleepy -= SLEEP_FUDGE; 149 if ( sleepy > 0 ) { 150 SDL_Delay(sleepy); 151 } 152 } 153#else 154 fd_set fdset; 155 156 FD_ZERO(&fdset); 157 FD_SET(audio_fd, &fdset); 158 select(audio_fd+1, NULL, &fdset, NULL, NULL); 159#endif 160} 161 162void DSP_PlayAudio(_THIS) 163{ 164 /* Write the audio data */ 165 if ( ulaw_only ) { 166 /* Assuming that this->spec.freq >= 8000 Hz */ 167 int accum, incr, pos; 168 Uint8 *aubuf; 169 170 accum = 0; 171 incr = this->spec.freq/8; 172 aubuf = ulaw_buf; 173 switch (audio_fmt & 0xFF) { 174 case 8: { 175 Uint8 *sndbuf; 176 177 sndbuf = mixbuf; 178 for ( pos=0; pos < fragsize; ++pos ) { 179 *aubuf = snd2au((0x80-*sndbuf)*64); 180 accum += incr; 181 while ( accum > 0 ) { 182 accum -= 1000; 183 sndbuf += 1; 184 } 185 aubuf += 1; 186 } 187 } 188 break; 189 case 16: { 190 Sint16 *sndbuf; 191 192 sndbuf = (Sint16 *)mixbuf; 193 for ( pos=0; pos < fragsize; ++pos ) { 194 *aubuf = snd2au(*sndbuf/4); 195 accum += incr; 196 while ( accum > 0 ) { 197 accum -= 1000; 198 sndbuf += 1; 199 } 200 aubuf += 1; 201 } 202 } 203 break; 204 } 205#ifdef DEBUG_AUDIO 206 CheckUnderflow(this); 207#endif 208 if ( write(audio_fd, ulaw_buf, fragsize) < 0 ) { 209 /* Assume fatal error, for now */ 210 this->enabled = 0; 211 } 212 written += fragsize; 213 } else { 214#ifdef DEBUG_AUDIO 215 CheckUnderflow(this); 216#endif 217 if ( write(audio_fd, mixbuf, this->spec.size) < 0 ) { 218 /* Assume fatal error, for now */ 219 this->enabled = 0; 220 } 221 written += fragsize; 222 } 223} 224 225Uint8 *DSP_GetAudioBuf(_THIS) 226{ 227 return(mixbuf); 228} 229 230void DSP_CloseAudio(_THIS) 231{ 232 if ( mixbuf != NULL ) { 233 SDL_FreeAudioMem(mixbuf); 234 mixbuf = NULL; 235 } 236 if ( ulaw_buf != NULL ) { 237 SDL_free(ulaw_buf); 238 ulaw_buf = NULL; 239 } 240 close(audio_fd); 241} 242 243int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec) 244{ 245 char audiodev[1024]; 246#ifdef AUDIO_SETINFO 247 int enc; 248#endif 249 int desired_freq = spec->freq; 250 251 /* Initialize our freeable variables, in case we fail*/ 252 audio_fd = -1; 253 mixbuf = NULL; 254 ulaw_buf = NULL; 255 256 /* Determine the audio parameters from the AudioSpec */ 257 switch ( spec->format & 0xFF ) { 258 259 case 8: { /* Unsigned 8 bit audio data */ 260 spec->format = AUDIO_U8; 261#ifdef AUDIO_SETINFO 262 enc = AUDIO_ENCODING_LINEAR8; 263#endif 264 } 265 break; 266 267 case 16: { /* Signed 16 bit audio data */ 268 spec->format = AUDIO_S16SYS; 269#ifdef AUDIO_SETINFO 270 enc = AUDIO_ENCODING_LINEAR; 271#endif 272 } 273 break; 274 275 default: { 276 SDL_SetError("Unsupported audio format"); 277 return(-1); 278 } 279 } 280 audio_fmt = spec->format; 281 282 /* Open the audio device */ 283 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 1); 284 if ( audio_fd < 0 ) { 285 SDL_SetError("Couldn't open %s: %s", audiodev, 286 strerror(errno)); 287 return(-1); 288 } 289 290 ulaw_only = 0; /* modern Suns do support linear audio */ 291#ifdef AUDIO_SETINFO 292 for(;;) { 293 audio_info_t info; 294 AUDIO_INITINFO(&info); /* init all fields to "no change" */ 295 296 /* Try to set the requested settings */ 297 info.play.sample_rate = spec->freq; 298 info.play.channels = spec->channels; 299 info.play.precision = (enc == AUDIO_ENCODING_ULAW) 300 ? 8 : spec->format & 0xff; 301 info.play.encoding = enc; 302 if( ioctl(audio_fd, AUDIO_SETINFO, &info) == 0 ) { 303 304 /* Check to be sure we got what we wanted */ 305 if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { 306 SDL_SetError("Error getting audio parameters: %s", 307 strerror(errno)); 308 return -1; 309 } 310 if(info.play.encoding == enc 311 && info.play.precision == (spec->format & 0xff) 312 && info.play.channels == spec->channels) { 313 /* Yow! All seems to be well! */ 314 spec->freq = info.play.sample_rate; 315 break; 316 } 317 } 318 319 switch(enc) { 320 case AUDIO_ENCODING_LINEAR8: 321 /* unsigned 8bit apparently not supported here */ 322 enc = AUDIO_ENCODING_LINEAR; 323 spec->format = AUDIO_S16SYS; 324 break; /* try again */ 325 326 case AUDIO_ENCODING_LINEAR: 327 /* linear 16bit didn't work either, resort to µ-law */ 328 enc = AUDIO_ENCODING_ULAW; 329 spec->channels = 1; 330 spec->freq = 8000; 331 spec->format = AUDIO_U8; 332 ulaw_only = 1; 333 break; 334 335 default: 336 /* oh well... */ 337 SDL_SetError("Error setting audio parameters: %s", 338 strerror(errno)); 339 return -1; 340 } 341 } 342#endif /* AUDIO_SETINFO */ 343 written = 0; 344 345 /* We can actually convert on-the-fly to U-Law */ 346 if ( ulaw_only ) { 347 spec->freq = desired_freq; 348 fragsize = (spec->samples*1000)/(spec->freq/8); 349 frequency = 8; 350 ulaw_buf = (Uint8 *)SDL_malloc(fragsize); 351 if ( ulaw_buf == NULL ) { 352 SDL_OutOfMemory(); 353 return(-1); 354 } 355 spec->channels = 1; 356 } else { 357 fragsize = spec->samples; 358 frequency = spec->freq/1000; 359 } 360#ifdef DEBUG_AUDIO 361 fprintf(stderr, "Audio device %s U-Law only\n", 362 ulaw_only ? "is" : "is not"); 363 fprintf(stderr, "format=0x%x chan=%d freq=%d\n", 364 spec->format, spec->channels, spec->freq); 365#endif 366 367 /* Update the fragment size as size in bytes */ 368 SDL_CalculateAudioSpec(spec); 369 370 /* Allocate mixing buffer */ 371 mixbuf = (Uint8 *)SDL_AllocAudioMem(spec->size); 372 if ( mixbuf == NULL ) { 373 SDL_OutOfMemory(); 374 return(-1); 375 } 376 SDL_memset(mixbuf, spec->silence, spec->size); 377 378 /* We're ready to rock and roll. :-) */ 379 return(0); 380} 381 382/************************************************************************/ 383/* This function (snd2au()) copyrighted: */ 384/************************************************************************/ 385/* Copyright 1989 by Rich Gopstein and Harris Corporation */ 386/* */ 387/* Permission to use, copy, modify, and distribute this software */ 388/* and its documentation for any purpose and without fee is */ 389/* hereby granted, provided that the above copyright notice */ 390/* appears in all copies and that both that copyright notice and */ 391/* this permission notice appear in supporting documentation, and */ 392/* that the name of Rich Gopstein and Harris Corporation not be */ 393/* used in advertising or publicity pertaining to distribution */ 394/* of the software without specific, written prior permission. */ 395/* Rich Gopstein and Harris Corporation make no representations */ 396/* about the suitability of this software for any purpose. It */ 397/* provided "as is" without express or implied warranty. */ 398/************************************************************************/ 399 400static Uint8 snd2au(int sample) 401{ 402 403 int mask; 404 405 if (sample < 0) { 406 sample = -sample; 407 mask = 0x7f; 408 } else { 409 mask = 0xff; 410 } 411 412 if (sample < 32) { 413 sample = 0xF0 | (15 - sample / 2); 414 } else if (sample < 96) { 415 sample = 0xE0 | (15 - (sample - 32) / 4); 416 } else if (sample < 224) { 417 sample = 0xD0 | (15 - (sample - 96) / 8); 418 } else if (sample < 480) { 419 sample = 0xC0 | (15 - (sample - 224) / 16); 420 } else if (sample < 992) { 421 sample = 0xB0 | (15 - (sample - 480) / 32); 422 } else if (sample < 2016) { 423 sample = 0xA0 | (15 - (sample - 992) / 64); 424 } else if (sample < 4064) { 425 sample = 0x90 | (15 - (sample - 2016) / 128); 426 } else if (sample < 8160) { 427 sample = 0x80 | (15 - (sample - 4064) / 256); 428 } else { 429 sample = 0x80; 430 } 431 return (mask & sample); 432} 433