SDL_paudio.c (18288B)
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_PAUDIO 24 25/* Allow access to a raw mixing buffer */ 26 27#include <errno.h> 28#include <unistd.h> 29#include <fcntl.h> 30#include <sys/time.h> 31#include <sys/ioctl.h> 32#include <sys/types.h> 33#include <sys/stat.h> 34 35#include "SDL_timer.h" 36#include "SDL_audio.h" 37#include "SDL_stdinc.h" 38#include "../SDL_audiomem.h" 39#include "../SDL_audio_c.h" 40#include "SDL_paudio.h" 41 42#define DEBUG_AUDIO 0 43 44/* A conflict within AIX 4.3.3 <sys/> headers and probably others as well. 45 * I guess nobody ever uses audio... Shame over AIX header files. */ 46#include <sys/machine.h> 47#undef BIG_ENDIAN 48#include <sys/audio.h> 49 50/* Open the audio device for playback, and don't block if busy */ 51/* #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) */ 52#define OPEN_FLAGS O_WRONLY 53 54/* Get the name of the audio device we use for output */ 55 56#ifndef _PATH_DEV_DSP 57#define _PATH_DEV_DSP "/dev/%caud%c/%c" 58#endif 59 60static char devsettings[][3] = { 61 {'p', '0', '1'}, {'p', '0', '2'}, {'p', '0', '3'}, {'p', '0', '4'}, 62 {'p', '1', '1'}, {'p', '1', '2'}, {'p', '1', '3'}, {'p', '1', '4'}, 63 {'p', '2', '1'}, {'p', '2', '2'}, {'p', '2', '3'}, {'p', '2', '4'}, 64 {'p', '3', '1'}, {'p', '3', '2'}, {'p', '3', '3'}, {'p', '3', '4'}, 65 {'b', '0', '1'}, {'b', '0', '2'}, {'b', '0', '3'}, {'b', '0', '4'}, 66 {'b', '1', '1'}, {'b', '1', '2'}, {'b', '1', '3'}, {'b', '1', '4'}, 67 {'b', '2', '1'}, {'b', '2', '2'}, {'b', '2', '3'}, {'b', '2', '4'}, 68 {'b', '3', '1'}, {'b', '3', '2'}, {'b', '3', '3'}, {'b', '3', '4'}, 69 {'\0', '\0', '\0'} 70}; 71 72static int 73OpenUserDefinedDevice(char *path, int maxlen, int flags) 74{ 75 const char *audiodev; 76 int fd; 77 78 /* Figure out what our audio device is */ 79 if ((audiodev = SDL_getenv("SDL_PATH_DSP")) == NULL) { 80 audiodev = SDL_getenv("AUDIODEV"); 81 } 82 if (audiodev == NULL) { 83 return -1; 84 } 85 fd = open(audiodev, flags, 0); 86 if (path != NULL) { 87 SDL_strlcpy(path, audiodev, maxlen); 88 path[maxlen - 1] = '\0'; 89 } 90 return fd; 91} 92 93static int 94OpenAudioPath(char *path, int maxlen, int flags, int classic) 95{ 96 struct stat sb; 97 int cycle = 0; 98 int fd = OpenUserDefinedDevice(path, maxlen, flags); 99 100 if (fd != -1) { 101 return fd; 102 } 103 104 /* !!! FIXME: do we really need a table here? */ 105 while (devsettings[cycle][0] != '\0') { 106 char audiopath[1024]; 107 SDL_snprintf(audiopath, SDL_arraysize(audiopath), 108 _PATH_DEV_DSP, 109 devsettings[cycle][0], 110 devsettings[cycle][1], devsettings[cycle][2]); 111 112 if (stat(audiopath, &sb) == 0) { 113 fd = open(audiopath, flags, 0); 114 if (fd > 0) { 115 if (path != NULL) { 116 SDL_strlcpy(path, audiopath, maxlen); 117 } 118 return fd; 119 } 120 } 121 } 122 return -1; 123} 124 125/* This function waits until it is possible to write a full sound buffer */ 126static void 127PAUDIO_WaitDevice(_THIS) 128{ 129 fd_set fdset; 130 131 /* See if we need to use timed audio synchronization */ 132 if (this->hidden->frame_ticks) { 133 /* Use timer for general audio synchronization */ 134 Sint32 ticks; 135 136 ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS; 137 if (ticks > 0) { 138 SDL_Delay(ticks); 139 } 140 } else { 141 audio_buffer paud_bufinfo; 142 143 /* Use select() for audio synchronization */ 144 struct timeval timeout; 145 FD_ZERO(&fdset); 146 FD_SET(this->hidden->audio_fd, &fdset); 147 148 if (ioctl(this->hidden->audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0) { 149#ifdef DEBUG_AUDIO 150 fprintf(stderr, "Couldn't get audio buffer information\n"); 151#endif 152 timeout.tv_sec = 10; 153 timeout.tv_usec = 0; 154 } else { 155 long ms_in_buf = paud_bufinfo.write_buf_time; 156 timeout.tv_sec = ms_in_buf / 1000; 157 ms_in_buf = ms_in_buf - timeout.tv_sec * 1000; 158 timeout.tv_usec = ms_in_buf * 1000; 159#ifdef DEBUG_AUDIO 160 fprintf(stderr, 161 "Waiting for write_buf_time=%ld,%ld\n", 162 timeout.tv_sec, timeout.tv_usec); 163#endif 164 } 165 166#ifdef DEBUG_AUDIO 167 fprintf(stderr, "Waiting for audio to get ready\n"); 168#endif 169 if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout) 170 <= 0) { 171 const char *message = 172 "Audio timeout - buggy audio driver? (disabled)"; 173 /* 174 * In general we should never print to the screen, 175 * but in this case we have no other way of letting 176 * the user know what happened. 177 */ 178 fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message); 179 this->enabled = 0; 180 /* Don't try to close - may hang */ 181 this->hidden->audio_fd = -1; 182#ifdef DEBUG_AUDIO 183 fprintf(stderr, "Done disabling audio\n"); 184#endif 185 } 186#ifdef DEBUG_AUDIO 187 fprintf(stderr, "Ready!\n"); 188#endif 189 } 190} 191 192static void 193PAUDIO_PlayDevice(_THIS) 194{ 195 int written = 0; 196 const Uint8 *mixbuf = this->hidden->mixbuf; 197 const size_t mixlen = this->hidden->mixlen; 198 199 /* Write the audio data, checking for EAGAIN on broken audio drivers */ 200 do { 201 written = write(this->hidden->audio_fd, mixbuf, mixlen); 202 if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) { 203 SDL_Delay(1); /* Let a little CPU time go by */ 204 } 205 } while ((written < 0) && 206 ((errno == 0) || (errno == EAGAIN) || (errno == EINTR))); 207 208 /* If timer synchronization is enabled, set the next write frame */ 209 if (this->hidden->frame_ticks) { 210 this->hidden->next_frame += this->hidden->frame_ticks; 211 } 212 213 /* If we couldn't write, assume fatal error for now */ 214 if (written < 0) { 215 this->enabled = 0; 216 } 217#ifdef DEBUG_AUDIO 218 fprintf(stderr, "Wrote %d bytes of audio data\n", written); 219#endif 220} 221 222static Uint8 * 223PAUDIO_GetDeviceBuf(_THIS) 224{ 225 return this->hidden->mixbuf; 226} 227 228static void 229PAUDIO_CloseDevice(_THIS) 230{ 231 if (this->hidden != NULL) { 232 SDL_FreeAudioMem(this->hidden->mixbuf); 233 this->hidden->mixbuf = NULL; 234 if (this->hidden->audio_fd >= 0) { 235 close(this->hidden->audio_fd); 236 this->hidden->audio_fd = -1; 237 } 238 SDL_free(this->hidden); 239 this->hidden = NULL; 240 } 241} 242 243static int 244PAUDIO_OpenDevice(_THIS, const char *devname, int iscapture) 245{ 246 const char *workaround = SDL_getenv("SDL_DSP_NOSELECT"); 247 char audiodev[1024]; 248 const char *err = NULL; 249 int format; 250 int bytes_per_sample; 251 SDL_AudioFormat test_format; 252 audio_init paud_init; 253 audio_buffer paud_bufinfo; 254 audio_status paud_status; 255 audio_control paud_control; 256 audio_change paud_change; 257 int fd = -1; 258 259 /* Initialize all variables that we clean on shutdown */ 260 this->hidden = (struct SDL_PrivateAudioData *) 261 SDL_malloc((sizeof *this->hidden)); 262 if (this->hidden == NULL) { 263 return SDL_OutOfMemory(); 264 } 265 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 266 267 /* Open the audio device */ 268 fd = OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); 269 this->hidden->audio_fd = fd; 270 if (fd < 0) { 271 PAUDIO_CloseDevice(this); 272 return SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); 273 } 274 275 /* 276 * We can't set the buffer size - just ask the device for the maximum 277 * that we can have. 278 */ 279 if (ioctl(fd, AUDIO_BUFFER, &paud_bufinfo) < 0) { 280 PAUDIO_CloseDevice(this); 281 return SDL_SetError("Couldn't get audio buffer information"); 282 } 283 284 if (this->spec.channels > 1) 285 this->spec.channels = 2; 286 else 287 this->spec.channels = 1; 288 289 /* 290 * Fields in the audio_init structure: 291 * 292 * Ignored by us: 293 * 294 * paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only? 295 * paud.slot_number; * slot number of the adapter 296 * paud.device_id; * adapter identification number 297 * 298 * Input: 299 * 300 * paud.srate; * the sampling rate in Hz 301 * paud.bits_per_sample; * 8, 16, 32, ... 302 * paud.bsize; * block size for this rate 303 * paud.mode; * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX 304 * paud.channels; * 1=mono, 2=stereo 305 * paud.flags; * FIXED - fixed length data 306 * * LEFT_ALIGNED, RIGHT_ALIGNED (var len only) 307 * * TWOS_COMPLEMENT - 2's complement data 308 * * SIGNED - signed? comment seems wrong in sys/audio.h 309 * * BIG_ENDIAN 310 * paud.operation; * PLAY, RECORD 311 * 312 * Output: 313 * 314 * paud.flags; * PITCH - pitch is supported 315 * * INPUT - input is supported 316 * * OUTPUT - output is supported 317 * * MONITOR - monitor is supported 318 * * VOLUME - volume is supported 319 * * VOLUME_DELAY - volume delay is supported 320 * * BALANCE - balance is supported 321 * * BALANCE_DELAY - balance delay is supported 322 * * TREBLE - treble control is supported 323 * * BASS - bass control is supported 324 * * BESTFIT_PROVIDED - best fit returned 325 * * LOAD_CODE - DSP load needed 326 * paud.rc; * NO_PLAY - DSP code can't do play requests 327 * * NO_RECORD - DSP code can't do record requests 328 * * INVALID_REQUEST - request was invalid 329 * * CONFLICT - conflict with open's flags 330 * * OVERLOADED - out of DSP MIPS or memory 331 * paud.position_resolution; * smallest increment for position 332 */ 333 334 paud_init.srate = this->spec.freq; 335 paud_init.mode = PCM; 336 paud_init.operation = PLAY; 337 paud_init.channels = this->spec.channels; 338 339 /* Try for a closest match on audio format */ 340 format = 0; 341 for (test_format = SDL_FirstAudioFormat(this->spec.format); 342 !format && test_format;) { 343#ifdef DEBUG_AUDIO 344 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); 345#endif 346 switch (test_format) { 347 case AUDIO_U8: 348 bytes_per_sample = 1; 349 paud_init.bits_per_sample = 8; 350 paud_init.flags = TWOS_COMPLEMENT | FIXED; 351 format = 1; 352 break; 353 case AUDIO_S8: 354 bytes_per_sample = 1; 355 paud_init.bits_per_sample = 8; 356 paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED; 357 format = 1; 358 break; 359 case AUDIO_S16LSB: 360 bytes_per_sample = 2; 361 paud_init.bits_per_sample = 16; 362 paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED; 363 format = 1; 364 break; 365 case AUDIO_S16MSB: 366 bytes_per_sample = 2; 367 paud_init.bits_per_sample = 16; 368 paud_init.flags = BIG_ENDIAN | SIGNED | TWOS_COMPLEMENT | FIXED; 369 format = 1; 370 break; 371 case AUDIO_U16LSB: 372 bytes_per_sample = 2; 373 paud_init.bits_per_sample = 16; 374 paud_init.flags = TWOS_COMPLEMENT | FIXED; 375 format = 1; 376 break; 377 case AUDIO_U16MSB: 378 bytes_per_sample = 2; 379 paud_init.bits_per_sample = 16; 380 paud_init.flags = BIG_ENDIAN | TWOS_COMPLEMENT | FIXED; 381 format = 1; 382 break; 383 default: 384 break; 385 } 386 if (!format) { 387 test_format = SDL_NextAudioFormat(); 388 } 389 } 390 if (format == 0) { 391#ifdef DEBUG_AUDIO 392 fprintf(stderr, "Couldn't find any hardware audio formats\n"); 393#endif 394 PAUDIO_CloseDevice(this); 395 return SDL_SetError("Couldn't find any hardware audio formats"); 396 } 397 this->spec.format = test_format; 398 399 /* 400 * We know the buffer size and the max number of subsequent writes 401 * that can be pending. If more than one can pend, allow the application 402 * to do something like double buffering between our write buffer and 403 * the device's own buffer that we are filling with write() anyway. 404 * 405 * We calculate this->spec.samples like this because 406 * SDL_CalculateAudioSpec() will give put paud_bufinfo.write_buf_cap 407 * (or paud_bufinfo.write_buf_cap/2) into this->spec.size in return. 408 */ 409 if (paud_bufinfo.request_buf_cap == 1) { 410 this->spec.samples = paud_bufinfo.write_buf_cap 411 / bytes_per_sample / this->spec.channels; 412 } else { 413 this->spec.samples = paud_bufinfo.write_buf_cap 414 / bytes_per_sample / this->spec.channels / 2; 415 } 416 paud_init.bsize = bytes_per_sample * this->spec.channels; 417 418 SDL_CalculateAudioSpec(&this->spec); 419 420 /* 421 * The AIX paud device init can't modify the values of the audio_init 422 * structure that we pass to it. So we don't need any recalculation 423 * of this stuff and no reinit call as in linux dsp code. 424 * 425 * /dev/paud supports all of the encoding formats, so we don't need 426 * to do anything like reopening the device, either. 427 */ 428 if (ioctl(fd, AUDIO_INIT, &paud_init) < 0) { 429 switch (paud_init.rc) { 430 case 1: 431 err = "Couldn't set audio format: DSP can't do play requests"; 432 break; 433 case 2: 434 err = "Couldn't set audio format: DSP can't do record requests"; 435 break; 436 case 4: 437 err = "Couldn't set audio format: request was invalid"; 438 break; 439 case 5: 440 err = "Couldn't set audio format: conflict with open's flags"; 441 break; 442 case 6: 443 err = "Couldn't set audio format: out of DSP MIPS or memory"; 444 break; 445 default: 446 err = "Couldn't set audio format: not documented in sys/audio.h"; 447 break; 448 } 449 } 450 451 if (err != NULL) { 452 PAUDIO_CloseDevice(this); 453 return SDL_SetError("Paudio: %s", err); 454 } 455 456 /* Allocate mixing buffer */ 457 this->hidden->mixlen = this->spec.size; 458 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); 459 if (this->hidden->mixbuf == NULL) { 460 PAUDIO_CloseDevice(this); 461 return SDL_OutOfMemory(); 462 } 463 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); 464 465 /* 466 * Set some paramters: full volume, first speaker that we can find. 467 * Ignore the other settings for now. 468 */ 469 paud_change.input = AUDIO_IGNORE; /* the new input source */ 470 paud_change.output = OUTPUT_1; /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,OUTPUT_1 */ 471 paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */ 472 paud_change.volume = 0x7fffffff; /* volume level [0-0x7fffffff] */ 473 paud_change.volume_delay = AUDIO_IGNORE; /* the new volume delay */ 474 paud_change.balance = 0x3fffffff; /* the new balance */ 475 paud_change.balance_delay = AUDIO_IGNORE; /* the new balance delay */ 476 paud_change.treble = AUDIO_IGNORE; /* the new treble state */ 477 paud_change.bass = AUDIO_IGNORE; /* the new bass state */ 478 paud_change.pitch = AUDIO_IGNORE; /* the new pitch state */ 479 480 paud_control.ioctl_request = AUDIO_CHANGE; 481 paud_control.request_info = (char *) &paud_change; 482 if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) { 483#ifdef DEBUG_AUDIO 484 fprintf(stderr, "Can't change audio display settings\n"); 485#endif 486 } 487 488 /* 489 * Tell the device to expect data. Actual start will wait for 490 * the first write() call. 491 */ 492 paud_control.ioctl_request = AUDIO_START; 493 paud_control.position = 0; 494 if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) { 495 PAUDIO_CloseDevice(this); 496#ifdef DEBUG_AUDIO 497 fprintf(stderr, "Can't start audio play\n"); 498#endif 499 return SDL_SetError("Can't start audio play"); 500 } 501 502 /* Check to see if we need to use select() workaround */ 503 if (workaround != NULL) { 504 this->hidden->frame_ticks = (float) (this->spec.samples * 1000) / 505 this->spec.freq; 506 this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks; 507 } 508 509 /* We're ready to rock and roll. :-) */ 510 return 0; 511} 512 513static int 514PAUDIO_Init(SDL_AudioDriverImpl * impl) 515{ 516 /* !!! FIXME: not right for device enum? */ 517 int fd = OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); 518 if (fd < 0) { 519 SDL_SetError("PAUDIO: Couldn't open audio device"); 520 return 0; 521 } 522 close(fd); 523 524 /* Set the function pointers */ 525 impl->OpenDevice = DSP_OpenDevice; 526 impl->PlayDevice = DSP_PlayDevice; 527 impl->PlayDevice = DSP_WaitDevice; 528 impl->GetDeviceBuf = DSP_GetDeviceBuf; 529 impl->CloseDevice = DSP_CloseDevice; 530 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: add device enum! */ 531 532 return 1; /* this audio target is available. */ 533} 534 535AudioBootStrap PAUDIO_bootstrap = { 536 "paud", "AIX Paudio", PAUDIO_Init, 0 537}; 538 539#endif /* SDL_AUDIO_DRIVER_PAUDIO */ 540 541/* vi: set ts=4 sw=4 expandtab: */