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 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 (at) 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 */ 51 static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec); 52 static void DSP_WaitAudio(_THIS); 53 static void DSP_PlayAudio(_THIS); 54 static Uint8 *DSP_GetAudioBuf(_THIS); 55 static void DSP_CloseAudio(_THIS); 56 57 static Uint8 snd2au(int sample); 58 59 /* Audio driver bootstrap functions */ 60 61 static 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 75 static void Audio_DeleteDevice(SDL_AudioDevice *device) 76 { 77 SDL_free(device->hidden); 78 SDL_free(device); 79 } 80 81 static 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 114 AudioBootStrap SUNAUDIO_bootstrap = { 115 "audio", "UNIX /dev/audio interface", 116 Audio_Available, Audio_CreateDevice 117 }; 118 119 #ifdef DEBUG_AUDIO 120 void 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 135 void 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 162 void 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 225 Uint8 *DSP_GetAudioBuf(_THIS) 226 { 227 return(mixbuf); 228 } 229 230 void 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 243 int 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 400 static 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