Atlas - SDL_paudio.c
Home / ext / SDL2 / src / audio / paudio Lines: 12 | Size: 17463 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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_audio_c.h" 39#include "../../core/unix/SDL_poll.h" 40#include "SDL_paudio.h" 41 42/* #define DEBUG_AUDIO */ 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 int timeoutMS; 142 audio_buffer paud_bufinfo; 143 144 if (ioctl(this->hidden->audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0) { 145#ifdef DEBUG_AUDIO 146 fprintf(stderr, "Couldn't get audio buffer information\n"); 147#endif 148 timeoutMS = 10 * 1000; 149 } else { 150 timeoutMS = paud_bufinfo.write_buf_time; 151#ifdef DEBUG_AUDIO 152 fprintf(stderr, "Waiting for write_buf_time=%d ms\n", timeoutMS); 153#endif 154 } 155 156#ifdef DEBUG_AUDIO 157 fprintf(stderr, "Waiting for audio to get ready\n"); 158#endif 159 if (SDL_IOReady(this->hidden->audio_fd, SDL_TRUE, timeoutMS) <= 0) { 160 /* 161 * In general we should never print to the screen, 162 * but in this case we have no other way of letting 163 * the user know what happened. 164 */ 165 fprintf(stderr, "SDL: %s - Audio timeout - buggy audio driver? (disabled)\n", strerror(errno)); 166 SDL_OpenedAudioDeviceDisconnected(this); 167 /* Don't try to close - may hang */ 168 this->hidden->audio_fd = -1; 169#ifdef DEBUG_AUDIO 170 fprintf(stderr, "Done disabling audio\n"); 171#endif 172 } 173#ifdef DEBUG_AUDIO 174 fprintf(stderr, "Ready!\n"); 175#endif 176 } 177} 178 179static void 180PAUDIO_PlayDevice(_THIS) 181{ 182 int written = 0; 183 const Uint8 *mixbuf = this->hidden->mixbuf; 184 const size_t mixlen = this->hidden->mixlen; 185 186 /* Write the audio data, checking for EAGAIN on broken audio drivers */ 187 do { 188 written = write(this->hidden->audio_fd, mixbuf, mixlen); 189 if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) { 190 SDL_Delay(1); /* Let a little CPU time go by */ 191 } 192 } while ((written < 0) && 193 ((errno == 0) || (errno == EAGAIN) || (errno == EINTR))); 194 195 /* If timer synchronization is enabled, set the next write frame */ 196 if (this->hidden->frame_ticks) { 197 this->hidden->next_frame += this->hidden->frame_ticks; 198 } 199 200 /* If we couldn't write, assume fatal error for now */ 201 if (written < 0) { 202 SDL_OpenedAudioDeviceDisconnected(this); 203 } 204#ifdef DEBUG_AUDIO 205 fprintf(stderr, "Wrote %d bytes of audio data\n", written); 206#endif 207} 208 209static Uint8 * 210PAUDIO_GetDeviceBuf(_THIS) 211{ 212 return this->hidden->mixbuf; 213} 214 215static void 216PAUDIO_CloseDevice(_THIS) 217{ 218 if (this->hidden->audio_fd >= 0) { 219 close(this->hidden->audio_fd); 220 } 221 SDL_free(this->hidden->mixbuf); 222 SDL_free(this->hidden); 223} 224 225static int 226PAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) 227{ 228 const char *workaround = SDL_getenv("SDL_DSP_NOSELECT"); 229 char audiodev[1024]; 230 const char *err = NULL; 231 int format; 232 int bytes_per_sample; 233 SDL_AudioFormat test_format; 234 audio_init paud_init; 235 audio_buffer paud_bufinfo; 236 audio_control paud_control; 237 audio_change paud_change; 238 int fd = -1; 239 240 /* Initialize all variables that we clean on shutdown */ 241 this->hidden = (struct SDL_PrivateAudioData *) 242 SDL_malloc((sizeof *this->hidden)); 243 if (this->hidden == NULL) { 244 return SDL_OutOfMemory(); 245 } 246 SDL_zerop(this->hidden); 247 248 /* Open the audio device */ 249 fd = OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); 250 this->hidden->audio_fd = fd; 251 if (fd < 0) { 252 return SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); 253 } 254 255 /* 256 * We can't set the buffer size - just ask the device for the maximum 257 * that we can have. 258 */ 259 if (ioctl(fd, AUDIO_BUFFER, &paud_bufinfo) < 0) { 260 return SDL_SetError("Couldn't get audio buffer information"); 261 } 262 263 if (this->spec.channels > 1) 264 this->spec.channels = 2; 265 else 266 this->spec.channels = 1; 267 268 /* 269 * Fields in the audio_init structure: 270 * 271 * Ignored by us: 272 * 273 * paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only? 274 * paud.slot_number; * slot number of the adapter 275 * paud.device_id; * adapter identification number 276 * 277 * Input: 278 * 279 * paud.srate; * the sampling rate in Hz 280 * paud.bits_per_sample; * 8, 16, 32, ... 281 * paud.bsize; * block size for this rate 282 * paud.mode; * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX 283 * paud.channels; * 1=mono, 2=stereo 284 * paud.flags; * FIXED - fixed length data 285 * * LEFT_ALIGNED, RIGHT_ALIGNED (var len only) 286 * * TWOS_COMPLEMENT - 2's complement data 287 * * SIGNED - signed? comment seems wrong in sys/audio.h 288 * * BIG_ENDIAN 289 * paud.operation; * PLAY, RECORD 290 * 291 * Output: 292 * 293 * paud.flags; * PITCH - pitch is supported 294 * * INPUT - input is supported 295 * * OUTPUT - output is supported 296 * * MONITOR - monitor is supported 297 * * VOLUME - volume is supported 298 * * VOLUME_DELAY - volume delay is supported 299 * * BALANCE - balance is supported 300 * * BALANCE_DELAY - balance delay is supported 301 * * TREBLE - treble control is supported 302 * * BASS - bass control is supported 303 * * BESTFIT_PROVIDED - best fit returned 304 * * LOAD_CODE - DSP load needed 305 * paud.rc; * NO_PLAY - DSP code can't do play requests 306 * * NO_RECORD - DSP code can't do record requests 307 * * INVALID_REQUEST - request was invalid 308 * * CONFLICT - conflict with open's flags 309 * * OVERLOADED - out of DSP MIPS or memory 310 * paud.position_resolution; * smallest increment for position 311 */ 312 313 paud_init.srate = this->spec.freq; 314 paud_init.mode = PCM; 315 paud_init.operation = PLAY; 316 paud_init.channels = this->spec.channels; 317 318 /* Try for a closest match on audio format */ 319 format = 0; 320 for (test_format = SDL_FirstAudioFormat(this->spec.format); 321 !format && test_format;) { 322#ifdef DEBUG_AUDIO 323 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); 324#endif 325 switch (test_format) { 326 case AUDIO_U8: 327 bytes_per_sample = 1; 328 paud_init.bits_per_sample = 8; 329 paud_init.flags = TWOS_COMPLEMENT | FIXED; 330 format = 1; 331 break; 332 case AUDIO_S8: 333 bytes_per_sample = 1; 334 paud_init.bits_per_sample = 8; 335 paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED; 336 format = 1; 337 break; 338 case AUDIO_S16LSB: 339 bytes_per_sample = 2; 340 paud_init.bits_per_sample = 16; 341 paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED; 342 format = 1; 343 break; 344 case AUDIO_S16MSB: 345 bytes_per_sample = 2; 346 paud_init.bits_per_sample = 16; 347 paud_init.flags = BIG_ENDIAN | SIGNED | TWOS_COMPLEMENT | FIXED; 348 format = 1; 349 break; 350 case AUDIO_U16LSB: 351 bytes_per_sample = 2; 352 paud_init.bits_per_sample = 16; 353 paud_init.flags = TWOS_COMPLEMENT | FIXED; 354 format = 1; 355 break; 356 case AUDIO_U16MSB: 357 bytes_per_sample = 2; 358 paud_init.bits_per_sample = 16; 359 paud_init.flags = BIG_ENDIAN | TWOS_COMPLEMENT | FIXED; 360 format = 1; 361 break; 362 default: 363 break; 364 } 365 if (!format) { 366 test_format = SDL_NextAudioFormat(); 367 } 368 } 369 if (format == 0) { 370#ifdef DEBUG_AUDIO 371 fprintf(stderr, "Couldn't find any hardware audio formats\n"); 372#endif 373 return SDL_SetError("Couldn't find any hardware audio formats"); 374 } 375 this->spec.format = test_format; 376 377 /* 378 * We know the buffer size and the max number of subsequent writes 379 * that can be pending. If more than one can pend, allow the application 380 * to do something like double buffering between our write buffer and 381 * the device's own buffer that we are filling with write() anyway. 382 * 383 * We calculate this->spec.samples like this because 384 * SDL_CalculateAudioSpec() will give put paud_bufinfo.write_buf_cap 385 * (or paud_bufinfo.write_buf_cap/2) into this->spec.size in return. 386 */ 387 if (paud_bufinfo.request_buf_cap == 1) { 388 this->spec.samples = paud_bufinfo.write_buf_cap 389 / bytes_per_sample / this->spec.channels; 390 } else { 391 this->spec.samples = paud_bufinfo.write_buf_cap 392 / bytes_per_sample / this->spec.channels / 2; 393 } 394 paud_init.bsize = bytes_per_sample * this->spec.channels; 395 396 SDL_CalculateAudioSpec(&this->spec); 397 398 /* 399 * The AIX paud device init can't modify the values of the audio_init 400 * structure that we pass to it. So we don't need any recalculation 401 * of this stuff and no reinit call as in linux dsp code. 402 * 403 * /dev/paud supports all of the encoding formats, so we don't need 404 * to do anything like reopening the device, either. 405 */ 406 if (ioctl(fd, AUDIO_INIT, &paud_init) < 0) { 407 switch (paud_init.rc) { 408 case 1: 409 err = "Couldn't set audio format: DSP can't do play requests"; 410 break; 411 case 2: 412 err = "Couldn't set audio format: DSP can't do record requests"; 413 break; 414 case 4: 415 err = "Couldn't set audio format: request was invalid"; 416 break; 417 case 5: 418 err = "Couldn't set audio format: conflict with open's flags"; 419 break; 420 case 6: 421 err = "Couldn't set audio format: out of DSP MIPS or memory"; 422 break; 423 default: 424 err = "Couldn't set audio format: not documented in sys/audio.h"; 425 break; 426 } 427 } 428 429 if (err != NULL) { 430 return SDL_SetError("Paudio: %s", err); 431 } 432 433 /* Allocate mixing buffer */ 434 this->hidden->mixlen = this->spec.size; 435 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen); 436 if (this->hidden->mixbuf == NULL) { 437 return SDL_OutOfMemory(); 438 } 439 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); 440 441 /* 442 * Set some paramters: full volume, first speaker that we can find. 443 * Ignore the other settings for now. 444 */ 445 paud_change.input = AUDIO_IGNORE; /* the new input source */ 446 paud_change.output = OUTPUT_1; /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,OUTPUT_1 */ 447 paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */ 448 paud_change.volume = 0x7fffffff; /* volume level [0-0x7fffffff] */ 449 paud_change.volume_delay = AUDIO_IGNORE; /* the new volume delay */ 450 paud_change.balance = 0x3fffffff; /* the new balance */ 451 paud_change.balance_delay = AUDIO_IGNORE; /* the new balance delay */ 452 paud_change.treble = AUDIO_IGNORE; /* the new treble state */ 453 paud_change.bass = AUDIO_IGNORE; /* the new bass state */ 454 paud_change.pitch = AUDIO_IGNORE; /* the new pitch state */ 455 456 paud_control.ioctl_request = AUDIO_CHANGE; 457 paud_control.request_info = (char *) &paud_change; 458 if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) { 459#ifdef DEBUG_AUDIO 460 fprintf(stderr, "Can't change audio display settings\n"); 461#endif 462 } 463 464 /* 465 * Tell the device to expect data. Actual start will wait for 466 * the first write() call. 467 */ 468 paud_control.ioctl_request = AUDIO_START; 469 paud_control.position = 0; 470 if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) { 471#ifdef DEBUG_AUDIO 472 fprintf(stderr, "Can't start audio play\n"); 473#endif 474 return SDL_SetError("Can't start audio play"); 475 } 476 477 /* Check to see if we need to use SDL_IOReady() workaround */ 478 if (workaround != NULL) { 479 this->hidden->frame_ticks = (float) (this->spec.samples * 1000) / 480 this->spec.freq; 481 this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks; 482 } 483 484 /* We're ready to rock and roll. :-) */ 485 return 0; 486} 487 488static int 489PAUDIO_Init(SDL_AudioDriverImpl * impl) 490{ 491 /* !!! FIXME: not right for device enum? */ 492 int fd = OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); 493 if (fd < 0) { 494 SDL_SetError("PAUDIO: Couldn't open audio device"); 495 return 0; 496 } 497 close(fd); 498 499 /* Set the function pointers */ 500 impl->OpenDevice = PAUDIO_OpenDevice; 501 impl->PlayDevice = PAUDIO_PlayDevice; 502 impl->PlayDevice = PAUDIO_WaitDevice; 503 impl->GetDeviceBuf = PAUDIO_GetDeviceBuf; 504 impl->CloseDevice = PAUDIO_CloseDevice; 505 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: add device enum! */ 506 507 return 1; /* this audio target is available. */ 508} 509 510AudioBootStrap PAUDIO_bootstrap = { 511 "paud", "AIX Paudio", PAUDIO_Init, 0 512}; 513 514#endif /* SDL_AUDIO_DRIVER_PAUDIO */ 515 516/* vi: set ts=4 sw=4 expandtab: */ 517[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.