Mercurial > audlegacy-plugins
comparison src/Output/CoreAudio/audio.c @ 0:13389e613d67 trunk
[svn] - initial import of audacious-plugins tree (lots to do)
| author | nenolod |
|---|---|
| date | Mon, 18 Sep 2006 01:11:49 -0700 |
| parents | |
| children | 088092a52fea |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:13389e613d67 |
|---|---|
| 1 /* XMMS - Cross-platform multimedia player | |
| 2 * Copyright (C) 1998-2001 Peter Alm, Mikael Alm, Olle Hallnas, | |
| 3 * Thomas Nilsson and 4Front Technologies | |
| 4 * Copyright (C) 1999-2001 Haavard Kvaalen | |
| 5 * | |
| 6 * This program is free software; you can redistribute it and/or modify | |
| 7 * it under the terms of the GNU General Public License as published by | |
| 8 * the Free Software Foundation; either version 2 of the License, or | |
| 9 * (at your option) any later version. | |
| 10 * | |
| 11 * This program is distributed in the hope that it will be useful, | |
| 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 * GNU General Public License for more details. | |
| 15 * | |
| 16 * You should have received a copy of the GNU General Public License | |
| 17 * along with this program; if not, write to the Free Software | |
| 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
| 19 */ | |
| 20 #include "coreaudio.h" | |
| 21 #include "libaudacious/util.h" | |
| 22 #include <errno.h> | |
| 23 #include <CoreAudio/CoreAudio.h> | |
| 24 | |
| 25 AudioDeviceID device_id; | |
| 26 AudioStreamBasicDescription device_format; | |
| 27 AudioStreamBasicDescription streamDesc; | |
| 28 | |
| 29 //static gint fd = 0; | |
| 30 static float *buffer; | |
| 31 gboolean playing_flag; | |
| 32 static gboolean prebuffer, unpause, do_pause, remove_prebuffer; | |
| 33 static gint device_buffer_size; | |
| 34 static gint buffer_size, prebuffer_size;//, blk_size; | |
| 35 static gint buffer_index = 0; | |
| 36 static gint output_time_offset = 0; | |
| 37 static guint64 written = 0, output_total = 0; | |
| 38 static gint flush; | |
| 39 static gchar *device_name; | |
| 40 | |
| 41 gint sample_multiplier, sample_size; | |
| 42 | |
| 43 gboolean paused; | |
| 44 | |
| 45 | |
| 46 float left_volume, right_volume; | |
| 47 float base_pitch = 0.0; | |
| 48 float user_pitch = 0.0; | |
| 49 int output_buf_length; // length of data in output buffer | |
| 50 short output_buf[OUTPUT_BUFSIZE]; /* buffer used to hold main output to dbfsd */ | |
| 51 short cue_buf[OUTPUT_BUFSIZE]; /* buffer used to hold cue output to dbfsd */ | |
| 52 short conv_buf[OUTPUT_BUFSIZE]; /* buffer used to hold format converted input */ | |
| 53 | |
| 54 /* | |
| 55 * The format of the data from the input plugin | |
| 56 * This will never change during a song. | |
| 57 */ | |
| 58 struct format_info input; | |
| 59 | |
| 60 | |
| 61 /* | |
| 62 * The format we get from the effect plugin. | |
| 63 * This will be different from input if the effect plugin does | |
| 64 * some kind of format conversion. | |
| 65 */ | |
| 66 struct format_info effect; | |
| 67 | |
| 68 | |
| 69 /* | |
| 70 * The format of the data we actually send to the soundcard. | |
| 71 * This might be different from effect if we need to resample or do | |
| 72 * some other format conversion. | |
| 73 */ | |
| 74 struct format_info output; | |
| 75 | |
| 76 | |
| 77 static int osx_calc_bitrate(int osx_fmt, int rate, int channels) | |
| 78 { | |
| 79 int bitrate = rate * channels; | |
| 80 | |
| 81 // for now we know output is stereo | |
| 82 // fix this later | |
| 83 | |
| 84 if (osx_fmt == FMT_U16_BE || osx_fmt == FMT_U16_LE || | |
| 85 osx_fmt == FMT_S16_BE || osx_fmt == FMT_S16_LE) | |
| 86 { | |
| 87 bitrate *= 2; | |
| 88 } | |
| 89 | |
| 90 //printf("osx_calc_bitrate(): %d\n",bitrate); | |
| 91 | |
| 92 return bitrate; | |
| 93 } | |
| 94 | |
| 95 | |
| 96 static int osx_get_format(AFormat fmt) | |
| 97 { | |
| 98 int format = 0; | |
| 99 | |
| 100 switch (fmt) | |
| 101 { | |
| 102 case FMT_U16_NE: | |
| 103 #ifdef WORDS_BIGENDIAN | |
| 104 format = FMT_U16_BE; | |
| 105 #else | |
| 106 format = FMT_U16_LE; | |
| 107 #endif | |
| 108 break; | |
| 109 case FMT_S16_NE: | |
| 110 #ifdef WORDS_BIGENDIAN | |
| 111 format = FMT_S16_BE; | |
| 112 #else | |
| 113 format = FMT_S16_LE; | |
| 114 #endif | |
| 115 break; | |
| 116 default: | |
| 117 format = fmt; | |
| 118 break; | |
| 119 } | |
| 120 | |
| 121 return format; | |
| 122 } | |
| 123 | |
| 124 | |
| 125 OSStatus play_callback(AudioDeviceID inDevice, const AudioTimeStamp * inNow, const AudioBufferList * inInputData, const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, const AudioTimeStamp * inOutputTime, void * inClientData) | |
| 126 { | |
| 127 int i; | |
| 128 long m, n, o; | |
| 129 float * dest, tempfloat; | |
| 130 float * src; | |
| 131 int src_size_bytes; | |
| 132 int src_size_float; | |
| 133 int num_output_samples; | |
| 134 int used_samples; | |
| 135 | |
| 136 src_size_bytes = outOutputData->mBuffers[0].mDataByteSize; | |
| 137 src_size_float = src_size_bytes / sizeof(float); | |
| 138 | |
| 139 num_output_samples = MIN(buffer_index,src_size_float); | |
| 140 | |
| 141 //printf("play_callback(): num_output_samples %d, index %d\n",num_output_samples,buffer_index); | |
| 142 | |
| 143 // if we are prebuffering, zero the buffer | |
| 144 if (prebuffer && (buffer_index < prebuffer_size)) | |
| 145 { | |
| 146 //printf("prebuffering... %d samples left\n",prebuffer_size-buffer_index); | |
| 147 num_output_samples = 0; | |
| 148 } | |
| 149 else | |
| 150 { | |
| 151 prebuffer = FALSE; | |
| 152 } | |
| 153 | |
| 154 src = buffer; | |
| 155 dest = outOutputData->mBuffers[0].mData; | |
| 156 | |
| 157 // copy available data to buffer and apply volume to each channel | |
| 158 for (i = 0; i < num_output_samples/2; i++) | |
| 159 { | |
| 160 //tempfloat = *src; | |
| 161 *dest = (*src) * left_volume; | |
| 162 src++; | |
| 163 dest++; | |
| 164 | |
| 165 *dest = (*src) * right_volume; | |
| 166 src++; | |
| 167 dest++; | |
| 168 } | |
| 169 | |
| 170 // if less than a buffer's worth of data is ready, zero remainder of output buffer | |
| 171 if (num_output_samples != src_size_float) | |
| 172 { | |
| 173 //printf("zeroing %d samples",(src_size_float - num_output_samples)); | |
| 174 | |
| 175 dest = (float*)outOutputData->mBuffers[0].mData + num_output_samples; | |
| 176 | |
| 177 memset(dest,0,(src_size_float - num_output_samples) * sizeof(float)); | |
| 178 } | |
| 179 | |
| 180 // move unwritten data to beginning of buffer | |
| 181 { | |
| 182 dest = buffer; | |
| 183 | |
| 184 for (i = num_output_samples; i < buffer_index; i++) | |
| 185 { | |
| 186 *dest = *src; | |
| 187 dest++; | |
| 188 src++; | |
| 189 } | |
| 190 | |
| 191 output_total += num_output_samples; | |
| 192 buffer_index -= num_output_samples; | |
| 193 } | |
| 194 | |
| 195 | |
| 196 if (flush != -1) | |
| 197 { | |
| 198 osx_set_audio_params(); | |
| 199 output_time_offset = flush; | |
| 200 written = ((guint64)flush * input.bps) / (1000 * sample_size); | |
| 201 buffer_index = 0; | |
| 202 output_total = 0; | |
| 203 | |
| 204 flush = -1; | |
| 205 prebuffer = TRUE; | |
| 206 } | |
| 207 | |
| 208 //printf("\n"); | |
| 209 | |
| 210 return 0; | |
| 211 } | |
| 212 | |
| 213 | |
| 214 static void osx_setup_format(AFormat fmt, int rate, int nch) | |
| 215 { | |
| 216 //printf("osx_setup_format(): fmt %d, rate %d, nch %d\n",fmt,rate,nch); | |
| 217 | |
| 218 effect.format.xmms = osx_get_format(fmt); | |
| 219 effect.frequency = rate; | |
| 220 effect.channels = nch; | |
| 221 effect.bps = osx_calc_bitrate(fmt, rate, nch); | |
| 222 | |
| 223 output.format.osx = osx_get_format(fmt); | |
| 224 output.frequency = rate; | |
| 225 output.channels = nch; | |
| 226 | |
| 227 osx_set_audio_params(); | |
| 228 | |
| 229 output.bps = osx_calc_bitrate(output.format.osx, output.frequency,output.channels); | |
| 230 } | |
| 231 | |
| 232 | |
| 233 gint osx_get_written_time(void) | |
| 234 { | |
| 235 gint retval; | |
| 236 | |
| 237 if (!playing_flag) | |
| 238 { | |
| 239 retval = 0; | |
| 240 } | |
| 241 else | |
| 242 { | |
| 243 retval = (written * sample_size * 1000) / effect.bps; | |
| 244 retval = (int)((float)retval / user_pitch); | |
| 245 } | |
| 246 | |
| 247 //printf("osx_get_written_time(): written time is %d\n",retval); | |
| 248 | |
| 249 return retval; | |
| 250 } | |
| 251 | |
| 252 | |
| 253 gint osx_get_output_time(void) | |
| 254 { | |
| 255 gint retval; | |
| 256 | |
| 257 retval = output_time_offset + ((output_total * sample_size * 1000) / output.bps); | |
| 258 retval = (int)((float)retval / user_pitch); | |
| 259 | |
| 260 //printf("osx_get_output_time(): time is %d\n",retval); | |
| 261 | |
| 262 return retval; | |
| 263 } | |
| 264 | |
| 265 | |
| 266 gint osx_playing(void) | |
| 267 { | |
| 268 gint retval; | |
| 269 | |
| 270 retval = 0; | |
| 271 | |
| 272 if (!playing_flag) | |
| 273 { | |
| 274 retval = 0; | |
| 275 } | |
| 276 else | |
| 277 { | |
| 278 if (buffer_index == 0) | |
| 279 { | |
| 280 retval = FALSE; | |
| 281 } | |
| 282 else | |
| 283 { | |
| 284 retval = TRUE; | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 //printf("osx_playing(): playing is now %d\n",playing_flag); | |
| 289 | |
| 290 return retval; | |
| 291 } | |
| 292 | |
| 293 | |
| 294 gint osx_free(void) | |
| 295 { | |
| 296 gint bytes_free; | |
| 297 | |
| 298 if (remove_prebuffer && prebuffer) | |
| 299 { | |
| 300 prebuffer = FALSE; | |
| 301 remove_prebuffer = FALSE; | |
| 302 } | |
| 303 | |
| 304 if (prebuffer) | |
| 305 { | |
| 306 remove_prebuffer = TRUE; | |
| 307 } | |
| 308 | |
| 309 // get number of free samples | |
| 310 bytes_free = buffer_size - buffer_index; | |
| 311 | |
| 312 // adjust for mono | |
| 313 if (input.channels == 1) | |
| 314 { | |
| 315 bytes_free /= 2; | |
| 316 } | |
| 317 | |
| 318 // adjust by pitch conversion; | |
| 319 bytes_free = (int)((float)bytes_free * base_pitch * user_pitch); | |
| 320 | |
| 321 // convert from number of samples to number of bytes | |
| 322 bytes_free *= sample_size; | |
| 323 | |
| 324 return bytes_free; | |
| 325 } | |
| 326 | |
| 327 | |
| 328 void osx_write(gpointer ptr, int length) | |
| 329 { | |
| 330 int count, offset = 0; | |
| 331 int error; | |
| 332 float tempfloat; | |
| 333 float * dest; | |
| 334 short * src, * tempbuf; | |
| 335 int i; | |
| 336 int num_samples; | |
| 337 | |
| 338 //printf("oss_write(): lenght: %d \n",length); | |
| 339 | |
| 340 remove_prebuffer = FALSE; | |
| 341 | |
| 342 // //printf("written is now %d\n",(gint)written); | |
| 343 | |
| 344 // get number of samples | |
| 345 num_samples = length / sample_size; | |
| 346 | |
| 347 // update amount of samples received | |
| 348 written += num_samples; | |
| 349 | |
| 350 // step through audio | |
| 351 while (num_samples > 0) | |
| 352 { | |
| 353 // get # of samples to write to the buffer | |
| 354 count = MIN(num_samples, osx_free()/sample_size); | |
| 355 | |
| 356 src = ptr+offset; | |
| 357 | |
| 358 if (dbconvert((char*)src,count * sample_size) == -1) | |
| 359 { | |
| 360 //printf("dbconvert error %d\n",errno); | |
| 361 } | |
| 362 else | |
| 363 { | |
| 364 src = output_buf; | |
| 365 dest = (float*)(buffer + buffer_index); | |
| 366 | |
| 367 //printf("output_buf_length is %d\n",output_buf_length); | |
| 368 | |
| 369 for (i = 0; i < output_buf_length; i++) | |
| 370 { | |
| 371 tempfloat = ((float)*src)/32768.0; | |
| 372 *dest = tempfloat; | |
| 373 dest++; | |
| 374 src++; | |
| 375 } | |
| 376 | |
| 377 buffer_index += output_buf_length; | |
| 378 } | |
| 379 | |
| 380 if (buffer_index > buffer_size) | |
| 381 { | |
| 382 //printf("BUFFER_INDEX > BUFFER_SIZE!!!!\n"); | |
| 383 exit(0); | |
| 384 } | |
| 385 | |
| 386 num_samples -= count; | |
| 387 offset += count; | |
| 388 } | |
| 389 | |
| 390 //printf("buffer_index is now %d\n\n",buffer_index); | |
| 391 } | |
| 392 | |
| 393 | |
| 394 void osx_close(void) | |
| 395 { | |
| 396 //printf("osx_close(): playing_flag is %d\n",playing_flag); | |
| 397 | |
| 398 if (!playing_flag) | |
| 399 { | |
| 400 return; | |
| 401 } | |
| 402 | |
| 403 playing_flag = 0; | |
| 404 | |
| 405 // close audio device | |
| 406 AudioDeviceStop(device_id, play_callback); | |
| 407 AudioDeviceRemoveIOProc(device_id, play_callback); | |
| 408 | |
| 409 g_free(device_name); | |
| 410 | |
| 411 //printf("osx_close(): playing_flag is now %d\n",playing_flag); | |
| 412 } | |
| 413 | |
| 414 | |
| 415 void osx_flush(gint time) | |
| 416 { | |
| 417 //printf("osx_flush(): %d\n",time); | |
| 418 | |
| 419 flush = time; | |
| 420 | |
| 421 while (flush != -1) | |
| 422 { | |
| 423 xmms_usleep(10000); | |
| 424 } | |
| 425 } | |
| 426 | |
| 427 | |
| 428 void osx_pause(short p) | |
| 429 { | |
| 430 //printf("osx_pause(): %d\n",p); | |
| 431 | |
| 432 if (p == TRUE) | |
| 433 { | |
| 434 if (AudioDeviceStop(device_id, play_callback)) | |
| 435 { | |
| 436 //printf("failed to stop audio device.\n"); | |
| 437 } | |
| 438 | |
| 439 //printf("PAUSED!\n"); | |
| 440 } | |
| 441 else | |
| 442 { | |
| 443 if (AudioDeviceStart(device_id, play_callback)) | |
| 444 { | |
| 445 //printf("failed to start audio device.\n"); | |
| 446 } | |
| 447 | |
| 448 //printf("UNPAUSED!\n"); | |
| 449 } | |
| 450 | |
| 451 paused = p; | |
| 452 } | |
| 453 | |
| 454 | |
| 455 void osx_set_audio_params(void) | |
| 456 { | |
| 457 int stereo_multiplier, format_multiplier; | |
| 458 int frag, stereo, ret; | |
| 459 struct timeval tv; | |
| 460 fd_set set; | |
| 461 | |
| 462 //printf("osx_set_audio_params(): fmt %d, freq %d, nch %d\n",output.format.osx,output.frequency,output.channels); | |
| 463 | |
| 464 // set audio format | |
| 465 | |
| 466 // set num channels | |
| 467 | |
| 468 switch (input.channels) | |
| 469 { | |
| 470 case 1: stereo_multiplier = 2; break; | |
| 471 case 2: stereo_multiplier = 1; break; | |
| 472 default: stereo_multiplier = 1; break; | |
| 473 } | |
| 474 | |
| 475 switch (input.format.xmms) | |
| 476 { | |
| 477 case FMT_U8: | |
| 478 case FMT_S8: | |
| 479 format_multiplier = 2; | |
| 480 sample_size = 1; | |
| 481 break; | |
| 482 case FMT_S16_LE: | |
| 483 case FMT_S16_BE: | |
| 484 case FMT_S16_NE: | |
| 485 format_multiplier = 1; | |
| 486 sample_size = 2; | |
| 487 break; | |
| 488 default: format_multiplier = 1; break; | |
| 489 } | |
| 490 | |
| 491 sample_multiplier = stereo_multiplier * format_multiplier; | |
| 492 | |
| 493 base_pitch = input.frequency / device_format.mSampleRate; | |
| 494 | |
| 495 //printf("sample multiplier is now %d, base pitch %.2f\n",sample_multiplier,base_pitch); | |
| 496 } | |
| 497 | |
| 498 | |
| 499 gint osx_open(AFormat fmt, gint rate, gint nch) | |
| 500 { | |
| 501 char s[32]; | |
| 502 long m; | |
| 503 long size; | |
| 504 char device_name[128]; | |
| 505 | |
| 506 //printf("\nosx_open(): fmt %d, rate %d, nch %d\n",fmt,rate,nch); | |
| 507 | |
| 508 // init conversion variables | |
| 509 base_pitch = 1.0; | |
| 510 user_pitch = 1.0; | |
| 511 | |
| 512 // open audio device | |
| 513 | |
| 514 size = sizeof(device_id); | |
| 515 | |
| 516 if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device_id)) | |
| 517 { | |
| 518 //printf("failed to open default audio device"); | |
| 519 return -1; | |
| 520 } | |
| 521 | |
| 522 //printf("opened audio device\n"); | |
| 523 | |
| 524 size = 128; | |
| 525 | |
| 526 if (AudioDeviceGetProperty(device_id,1,0,kAudioDevicePropertyDeviceName,&size,device_name)) | |
| 527 { | |
| 528 //printf("could not get device name\n"); | |
| 529 return -1; | |
| 530 } | |
| 531 | |
| 532 //printf("device name is: \"%s\"\n",device_name); | |
| 533 | |
| 534 size = sizeof(device_format); | |
| 535 | |
| 536 if (AudioDeviceGetProperty(device_id, 0, 0, kAudioDevicePropertyStreamFormat, &size, &device_format)) | |
| 537 { | |
| 538 //printf("failed to get audio format!\n"); | |
| 539 return -1; | |
| 540 } | |
| 541 | |
| 542 //fprintf(stderr, "got format: sample rate %f, %ld channels and %ld-bit sample\n", | |
| 543 // device_format.mSampleRate,device_format.mChannelsPerFrame,device_format.mBitsPerChannel); | |
| 544 | |
| 545 if (device_format.mFormatID != kAudioFormatLinearPCM) | |
| 546 { | |
| 547 //printf("audio format isn't PCM\n"); | |
| 548 return -1; | |
| 549 } | |
| 550 | |
| 551 //printf("format is PCM\n"); | |
| 552 | |
| 553 input.format.xmms = fmt; | |
| 554 input.frequency = rate; | |
| 555 input.channels = nch; | |
| 556 input.bps = osx_calc_bitrate(osx_get_format(fmt),rate,nch); | |
| 557 | |
| 558 osx_setup_format(osx_get_format(fmt),device_format.mSampleRate,device_format.mChannelsPerFrame); | |
| 559 | |
| 560 //set audio buffer size | |
| 561 { | |
| 562 device_buffer_size = 4096 * sizeof(float); | |
| 563 size = sizeof(gint); | |
| 564 | |
| 565 if (AudioDeviceSetProperty(device_id,0,0,0,kAudioDevicePropertyBufferSize,size,&device_buffer_size)) | |
| 566 { | |
| 567 //printf("failed to set device buffer size\n"); | |
| 568 } | |
| 569 | |
| 570 //printf("buffer size set to %d\n",device_buffer_size); | |
| 571 } | |
| 572 | |
| 573 buffer_size = 11 * 4096; | |
| 574 prebuffer_size = 4096; | |
| 575 | |
| 576 buffer = (float *) g_malloc0(buffer_size*sizeof(float)); | |
| 577 | |
| 578 //printf("created buffer of size %d, prebuffer is %d\n",buffer_size,prebuffer_size); | |
| 579 | |
| 580 flush = -1; | |
| 581 prebuffer = TRUE; | |
| 582 | |
| 583 buffer_index = output_time_offset = written = output_total = 0; | |
| 584 | |
| 585 paused = FALSE; | |
| 586 | |
| 587 do_pause = FALSE; | |
| 588 unpause = FALSE; | |
| 589 remove_prebuffer = FALSE; | |
| 590 | |
| 591 playing_flag = 1; | |
| 592 | |
| 593 if (AudioDeviceAddIOProc(device_id, play_callback, NULL)) | |
| 594 { | |
| 595 //printf("failed to add IO Proc callback\n"); | |
| 596 osx_close(); | |
| 597 return -1; | |
| 598 } | |
| 599 | |
| 600 //printf("added callback\n"); | |
| 601 | |
| 602 if (AudioDeviceStart(device_id,play_callback)) | |
| 603 { | |
| 604 osx_close(); | |
| 605 //printf("failed to start audio device.\n"); | |
| 606 exit(0); | |
| 607 } | |
| 608 | |
| 609 //printf("started audio device\n"); | |
| 610 | |
| 611 return 1; | |
| 612 } |
