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 Carsten Griwodz 20 griff (at) kom.tu-darmstadt.de 21 22 based on linux/SDL_dspaudio.c by Sam Lantinga 23 */ 24 #include "SDL_config.h" 25 26 /* Allow access to a raw mixing buffer */ 27 28 #include <errno.h> 29 #include <unistd.h> 30 #include <fcntl.h> 31 #include <sys/types.h> 32 #include <sys/time.h> 33 #include <sys/ioctl.h> 34 #include <sys/stat.h> 35 #include <sys/mman.h> 36 37 #include "SDL_audio.h" 38 #include "../SDL_audio_c.h" 39 #include "../SDL_audiodev_c.h" 40 #include "SDL_umsaudio.h" 41 42 /* The tag name used by UMS audio */ 43 #define UMS_DRIVER_NAME "ums" 44 45 #define DEBUG_AUDIO 1 46 47 /* Audio driver functions */ 48 static int UMS_OpenAudio(_THIS, SDL_AudioSpec *spec); 49 static void UMS_PlayAudio(_THIS); 50 static Uint8 *UMS_GetAudioBuf(_THIS); 51 static void UMS_CloseAudio(_THIS); 52 53 static UMSAudioDevice_ReturnCode UADOpen(_THIS, string device, string mode, long flags); 54 static UMSAudioDevice_ReturnCode UADClose(_THIS); 55 static UMSAudioDevice_ReturnCode UADGetBitsPerSample(_THIS, long* bits); 56 static UMSAudioDevice_ReturnCode UADSetBitsPerSample(_THIS, long bits); 57 static UMSAudioDevice_ReturnCode UADSetSampleRate(_THIS, long rate, long* set_rate); 58 static UMSAudioDevice_ReturnCode UADSetByteOrder(_THIS, string byte_order); 59 static UMSAudioDevice_ReturnCode UADSetAudioFormatType(_THIS, string fmt); 60 static UMSAudioDevice_ReturnCode UADSetNumberFormat(_THIS, string fmt); 61 static UMSAudioDevice_ReturnCode UADInitialize(_THIS); 62 static UMSAudioDevice_ReturnCode UADStart(_THIS); 63 static UMSAudioDevice_ReturnCode UADStop(_THIS); 64 static UMSAudioDevice_ReturnCode UADSetTimeFormat(_THIS, UMSAudioTypes_TimeFormat fmt ); 65 static UMSAudioDevice_ReturnCode UADWriteBuffSize(_THIS, long* buff_size ); 66 static UMSAudioDevice_ReturnCode UADWriteBuffRemain(_THIS, long* buff_size ); 67 static UMSAudioDevice_ReturnCode UADWriteBuffUsed(_THIS, long* buff_size ); 68 static UMSAudioDevice_ReturnCode UADSetDMABufferSize(_THIS, long bytes, long* bytes_ret ); 69 static UMSAudioDevice_ReturnCode UADSetVolume(_THIS, long volume ); 70 static UMSAudioDevice_ReturnCode UADSetBalance(_THIS, long balance ); 71 static UMSAudioDevice_ReturnCode UADSetChannels(_THIS, long channels ); 72 static UMSAudioDevice_ReturnCode UADPlayRemainingData(_THIS, boolean block ); 73 static UMSAudioDevice_ReturnCode UADEnableOutput(_THIS, string output, long* left_gain, long* right_gain); 74 static UMSAudioDevice_ReturnCode UADWrite(_THIS, UMSAudioTypes_Buffer* buff, long samples, long* samples_written); 75 76 /* Audio driver bootstrap functions */ 77 static int Audio_Available(void) 78 { 79 return 1; 80 } 81 82 static void Audio_DeleteDevice(_THIS) 83 { 84 if(this->hidden->playbuf._buffer) SDL_free(this->hidden->playbuf._buffer); 85 if(this->hidden->fillbuf._buffer) SDL_free(this->hidden->fillbuf._buffer); 86 _somFree( this->hidden->umsdev ); 87 SDL_free(this->hidden); 88 SDL_free(this); 89 } 90 91 static SDL_AudioDevice *Audio_CreateDevice(int devindex) 92 { 93 SDL_AudioDevice *this; 94 95 /* 96 * Allocate and initialize management storage and private management 97 * storage for this SDL-using library. 98 */ 99 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); 100 if ( this ) { 101 SDL_memset(this, 0, (sizeof *this)); 102 this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc((sizeof *this->hidden)); 103 } 104 if ( (this == NULL) || (this->hidden == NULL) ) { 105 SDL_OutOfMemory(); 106 if ( this ) { 107 SDL_free(this); 108 } 109 return(0); 110 } 111 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 112 #ifdef DEBUG_AUDIO 113 fprintf(stderr, "Creating UMS Audio device\n"); 114 #endif 115 116 /* 117 * Calls for UMS env initialization and audio object construction. 118 */ 119 this->hidden->ev = somGetGlobalEnvironment(); 120 this->hidden->umsdev = UMSAudioDeviceNew(); 121 122 /* 123 * Set the function pointers. 124 */ 125 this->OpenAudio = UMS_OpenAudio; 126 this->WaitAudio = NULL; /* we do blocking output */ 127 this->PlayAudio = UMS_PlayAudio; 128 this->GetAudioBuf = UMS_GetAudioBuf; 129 this->CloseAudio = UMS_CloseAudio; 130 this->free = Audio_DeleteDevice; 131 132 #ifdef DEBUG_AUDIO 133 fprintf(stderr, "done\n"); 134 #endif 135 return this; 136 } 137 138 AudioBootStrap UMS_bootstrap = { 139 UMS_DRIVER_NAME, "AIX UMS audio", 140 Audio_Available, Audio_CreateDevice 141 }; 142 143 static Uint8 *UMS_GetAudioBuf(_THIS) 144 { 145 #ifdef DEBUG_AUDIO 146 fprintf(stderr, "enter UMS_GetAudioBuf\n"); 147 #endif 148 return this->hidden->fillbuf._buffer; 149 /* 150 long bufSize; 151 UMSAudioDevice_ReturnCode rc; 152 153 rc = UADSetTimeFormat(this, UMSAudioTypes_Bytes ); 154 rc = UADWriteBuffSize(this, bufSize ); 155 */ 156 } 157 158 static void UMS_CloseAudio(_THIS) 159 { 160 UMSAudioDevice_ReturnCode rc; 161 162 #ifdef DEBUG_AUDIO 163 fprintf(stderr, "enter UMS_CloseAudio\n"); 164 #endif 165 rc = UADPlayRemainingData(this, TRUE); 166 rc = UADStop(this); 167 rc = UADClose(this); 168 } 169 170 static void UMS_PlayAudio(_THIS) 171 { 172 UMSAudioDevice_ReturnCode rc; 173 long samplesToWrite; 174 long samplesWritten; 175 UMSAudioTypes_Buffer swpbuf; 176 177 #ifdef DEBUG_AUDIO 178 fprintf(stderr, "enter UMS_PlayAudio\n"); 179 #endif 180 samplesToWrite = this->hidden->playbuf._length/this->hidden->bytesPerSample; 181 do 182 { 183 rc = UADWrite(this, &this->hidden->playbuf, 184 samplesToWrite, 185 &samplesWritten ); 186 samplesToWrite -= samplesWritten; 187 188 /* rc values: UMSAudioDevice_Success 189 * UMSAudioDevice_Failure 190 * UMSAudioDevice_Preempted 191 * UMSAudioDevice_Interrupted 192 * UMSAudioDevice_DeviceError 193 */ 194 if ( rc == UMSAudioDevice_DeviceError ) { 195 #ifdef DEBUG_AUDIO 196 fprintf(stderr, "Returning from PlayAudio with devices error\n"); 197 #endif 198 return; 199 } 200 } 201 while(samplesToWrite>0); 202 203 SDL_LockAudio(); 204 SDL_memcpy( &swpbuf, &this->hidden->playbuf, sizeof(UMSAudioTypes_Buffer) ); 205 SDL_memcpy( &this->hidden->playbuf, &this->hidden->fillbuf, sizeof(UMSAudioTypes_Buffer) ); 206 SDL_memcpy( &this->hidden->fillbuf, &swpbuf, sizeof(UMSAudioTypes_Buffer) ); 207 SDL_UnlockAudio(); 208 209 #ifdef DEBUG_AUDIO 210 fprintf(stderr, "Wrote audio data and swapped buffer\n"); 211 #endif 212 } 213 214 #if 0 215 // /* Set the DSP frequency */ 216 // value = spec->freq; 217 // if ( ioctl(this->hidden->audio_fd, SOUND_PCM_WRITE_RATE, &value) < 0 ) { 218 // SDL_SetError("Couldn't set audio frequency"); 219 // return(-1); 220 // } 221 // spec->freq = value; 222 #endif 223 224 static int UMS_OpenAudio(_THIS, SDL_AudioSpec *spec) 225 { 226 char* audiodev = "/dev/paud0"; 227 long lgain; 228 long rgain; 229 long outRate; 230 long outBufSize; 231 long bitsPerSample; 232 long samplesPerSec; 233 long success; 234 Uint16 test_format; 235 int frag_spec; 236 UMSAudioDevice_ReturnCode rc; 237 238 #ifdef DEBUG_AUDIO 239 fprintf(stderr, "enter UMS_OpenAudio\n"); 240 #endif 241 rc = UADOpen(this, audiodev,"PLAY", UMSAudioDevice_BlockingIO); 242 if ( rc != UMSAudioDevice_Success ) { 243 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); 244 return -1; 245 } 246 247 rc = UADSetAudioFormatType(this, "PCM"); 248 249 success = 0; 250 test_format = SDL_FirstAudioFormat(spec->format); 251 do 252 { 253 #ifdef DEBUG_AUDIO 254 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); 255 #endif 256 switch ( test_format ) 257 { 258 case AUDIO_U8: 259 /* from the mac code: better ? */ 260 /* sample_bits = spec->size / spec->samples / spec->channels * 8; */ 261 success = 1; 262 bitsPerSample = 8; 263 rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); 264 rc = UADSetByteOrder(this, "MSB"); /* irrelevant */ 265 rc = UADSetNumberFormat(this, "UNSIGNED"); 266 break; 267 case AUDIO_S8: 268 success = 1; 269 bitsPerSample = 8; 270 rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); 271 rc = UADSetByteOrder(this, "MSB"); /* irrelevant */ 272 rc = UADSetNumberFormat(this, "SIGNED"); 273 break; 274 case AUDIO_S16LSB: 275 success = 1; 276 bitsPerSample = 16; 277 rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); 278 rc = UADSetByteOrder(this, "LSB"); 279 rc = UADSetNumberFormat(this, "SIGNED"); 280 break; 281 case AUDIO_S16MSB: 282 success = 1; 283 bitsPerSample = 16; 284 rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); 285 rc = UADSetByteOrder(this, "MSB"); 286 rc = UADSetNumberFormat(this, "SIGNED"); 287 break; 288 case AUDIO_U16LSB: 289 success = 1; 290 bitsPerSample = 16; 291 rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); 292 rc = UADSetByteOrder(this, "LSB"); 293 rc = UADSetNumberFormat(this, "UNSIGNED"); 294 break; 295 case AUDIO_U16MSB: 296 success = 1; 297 bitsPerSample = 16; 298 rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); 299 rc = UADSetByteOrder(this, "MSB"); 300 rc = UADSetNumberFormat(this, "UNSIGNED"); 301 break; 302 default: 303 break; 304 } 305 if ( ! success ) { 306 test_format = SDL_NextAudioFormat(); 307 } 308 } 309 while ( ! success && test_format ); 310 311 if ( success == 0 ) { 312 SDL_SetError("Couldn't find any hardware audio formats"); 313 return -1; 314 } 315 316 spec->format = test_format; 317 318 for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec ); 319 if ( (0x01<<frag_spec) != spec->size ) { 320 SDL_SetError("Fragment size must be a power of two"); 321 return -1; 322 } 323 if ( frag_spec > 2048 ) frag_spec = 2048; 324 325 this->hidden->bytesPerSample = (bitsPerSample / 8) * spec->channels; 326 samplesPerSec = this->hidden->bytesPerSample * outRate; 327 328 this->hidden->playbuf._length = 0; 329 this->hidden->playbuf._maximum = spec->size; 330 this->hidden->playbuf._buffer = (unsigned char*)SDL_malloc(spec->size); 331 this->hidden->fillbuf._length = 0; 332 this->hidden->fillbuf._maximum = spec->size; 333 this->hidden->fillbuf._buffer = (unsigned char*)SDL_malloc(spec->size); 334 335 rc = UADSetBitsPerSample(this, bitsPerSample ); 336 rc = UADSetDMABufferSize(this, frag_spec, &outBufSize ); 337 rc = UADSetChannels(this, spec->channels); /* functions reduces to mono or stereo */ 338 339 lgain = 100; /*maximum left input gain*/ 340 rgain = 100; /*maimum right input gain*/ 341 rc = UADEnableOutput(this, "LINE_OUT",&lgain,&rgain); 342 rc = UADInitialize(this); 343 rc = UADStart(this); 344 rc = UADSetVolume(this, 100); 345 rc = UADSetBalance(this, 0); 346 347 /* We're ready to rock and roll. :-) */ 348 return 0; 349 } 350 351 352 static UMSAudioDevice_ReturnCode UADGetBitsPerSample(_THIS, long* bits) 354 { 355 return UMSAudioDevice_get_bits_per_sample( this->hidden->umsdev, 356 this->hidden->ev, 357 bits ); 358 } 359 360 static UMSAudioDevice_ReturnCode UADSetBitsPerSample(_THIS, long bits) 361 { 362 return UMSAudioDevice_set_bits_per_sample( this->hidden->umsdev, 363 this->hidden->ev, 364 bits ); 365 } 366 367 static UMSAudioDevice_ReturnCode UADSetSampleRate(_THIS, long rate, long* set_rate) 368 { 369 /* from the mac code: sample rate = spec->freq << 16; */ 370 return UMSAudioDevice_set_sample_rate( this->hidden->umsdev, 371 this->hidden->ev, 372 rate, 373 set_rate ); 374 } 375 376 static UMSAudioDevice_ReturnCode UADSetByteOrder(_THIS, string byte_order) 377 { 378 return UMSAudioDevice_set_byte_order( this->hidden->umsdev, 379 this->hidden->ev, 380 byte_order ); 381 } 382 383 static UMSAudioDevice_ReturnCode UADSetAudioFormatType(_THIS, string fmt) 384 { 385 /* possible PCM, A_LAW or MU_LAW */ 386 return UMSAudioDevice_set_audio_format_type( this->hidden->umsdev, 387 this->hidden->ev, 388 fmt ); 389 } 390 391 static UMSAudioDevice_ReturnCode UADSetNumberFormat(_THIS, string fmt) 392 { 393 /* possible SIGNED, UNSIGNED, or TWOS_COMPLEMENT */ 394 return UMSAudioDevice_set_number_format( this->hidden->umsdev, 395 this->hidden->ev, 396 fmt ); 397 } 398 399 static UMSAudioDevice_ReturnCode UADInitialize(_THIS) 400 { 401 return UMSAudioDevice_initialize( this->hidden->umsdev, 402 this->hidden->ev ); 403 } 404 405 static UMSAudioDevice_ReturnCode UADStart(_THIS) 406 { 407 return UMSAudioDevice_start( this->hidden->umsdev, 408 this->hidden->ev ); 409 } 410 411 static UMSAudioDevice_ReturnCode UADSetTimeFormat(_THIS, UMSAudioTypes_TimeFormat fmt ) 412 { 413 /* 414 * Switches the time format to the new format, immediately. 415 * possible UMSAudioTypes_Msecs, UMSAudioTypes_Bytes or UMSAudioTypes_Samples 416 */ 417 return UMSAudioDevice_set_time_format( this->hidden->umsdev, 418 this->hidden->ev, 419 fmt ); 420 } 421 422 static UMSAudioDevice_ReturnCode UADWriteBuffSize(_THIS, long* buff_size ) 423 { 424 /* 425 * returns write buffer size in the current time format 426 */ 427 return UMSAudioDevice_write_buff_size( this->hidden->umsdev, 428 this->hidden->ev, 429 buff_size ); 430 } 431 432 static UMSAudioDevice_ReturnCode UADWriteBuffRemain(_THIS, long* buff_size ) 433 { 434 /* 435 * returns amount of available space in the write buffer 436 * in the current time format 437 */ 438 return UMSAudioDevice_write_buff_remain( this->hidden->umsdev, 439 this->hidden->ev, 440 buff_size ); 441 } 442 443 static UMSAudioDevice_ReturnCode UADWriteBuffUsed(_THIS, long* buff_size ) 444 { 445 /* 446 * returns amount of filled space in the write buffer 447 * in the current time format 448 */ 449 return UMSAudioDevice_write_buff_used( this->hidden->umsdev, 450 this->hidden->ev, 451 buff_size ); 452 } 453 454 static UMSAudioDevice_ReturnCode UADSetDMABufferSize(_THIS, long bytes, long* bytes_ret ) 455 { 456 /* 457 * Request a new DMA buffer size, maximum requested size 2048. 458 * Takes effect with next initialize() call. 459 * Devices may or may not support DMA. 460 */ 461 return UMSAudioDevice_set_DMA_buffer_size( this->hidden->umsdev, 462 this->hidden->ev, 463 bytes, 464 bytes_ret ); 465 } 466 467 static UMSAudioDevice_ReturnCode UADSetVolume(_THIS, long volume ) 468 { 469 /* 470 * Set the volume. 471 * Takes effect immediately. 472 */ 473 return UMSAudioDevice_set_volume( this->hidden->umsdev, 474 this->hidden->ev, 475 volume ); 476 } 477 478 static UMSAudioDevice_ReturnCode UADSetBalance(_THIS, long balance ) 479 { 480 /* 481 * Set the balance. 482 * Takes effect immediately. 483 */ 484 return UMSAudioDevice_set_balance( this->hidden->umsdev, 485 this->hidden->ev, 486 balance ); 487 } 488 489 static UMSAudioDevice_ReturnCode UADSetChannels(_THIS, long channels ) 490 { 491 /* 492 * Set mono or stereo. 493 * Takes effect with next initialize() call. 494 */ 495 if ( channels != 1 ) channels = 2; 496 return UMSAudioDevice_set_number_of_channels( this->hidden->umsdev, 497 this->hidden->ev, 498 channels ); 499 } 500 501 static UMSAudioDevice_ReturnCode UADOpen(_THIS, string device, string mode, long flags) 502 { 503 return UMSAudioDevice_open( this->hidden->umsdev, 504 this->hidden->ev, 505 device, 506 mode, 507 flags ); 508 } 509 510 static UMSAudioDevice_ReturnCode UADWrite(_THIS, UMSAudioTypes_Buffer* buff, 511 long samples, 512 long* samples_written) 513 { 514 return UMSAudioDevice_write( this->hidden->umsdev, 515 this->hidden->ev, 516 buff, 517 samples, 518 samples_written ); 519 } 520 521 static UMSAudioDevice_ReturnCode UADPlayRemainingData(_THIS, boolean block ) 522 { 523 return UMSAudioDevice_play_remaining_data( this->hidden->umsdev, 524 this->hidden->ev, 525 block); 526 } 527 528 static UMSAudioDevice_ReturnCode UADStop(_THIS) 529 { 530 return UMSAudioDevice_stop( this->hidden->umsdev, 531 this->hidden->ev ); 532 } 533 534 static UMSAudioDevice_ReturnCode UADClose(_THIS) 535 { 536 return UMSAudioDevice_close( this->hidden->umsdev, 537 this->hidden->ev ); 538 } 539 540 static UMSAudioDevice_ReturnCode UADEnableOutput(_THIS, string output, long* left_gain, long* right_gain) 541 { 542 return UMSAudioDevice_enable_output( this->hidden->umsdev, 543 this->hidden->ev, 544 output, 545 left_gain, 546 right_gain ); 547 } 548 549