1 /* 2 Copyright (C) 2013 Paul Brossier <piem@aubio.org> 3 4 This file is part of aubio. 5 6 aubio 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 3 of the License, or 9 (at your option) any later version. 10 11 aubio 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 aubio. If not, see <http://www.gnu.org/licenses/>. 18 19 */ 20 21 #include "aubio_priv.h" 22 #ifdef HAVE_AUDIO_UNIT 23 24 #include "fvec.h" 25 #include "fmat.h" 26 #include "io/audio_unit.h" 27 28 #include <AudioToolbox/AudioToolbox.h> 29 30 #define AU_IOS_MAX_OUT 64 31 #define AU_IOS_MAX_FRAMES AU_IOS_MAX_OUT * 16 * 2 32 #define PREFERRED_LATENCY 0.010 33 #define MAX_FPS 4096 34 35 #define INT_TO_FLOAT 3.0517578125e-05 // 1. / 32768. 36 37 struct _aubio_audio_unit_t { 38 AudioUnit audio_unit; 39 uint_t samplerate; 40 uint_t blocksize; 41 uint_t sw_input_channels; 42 uint_t sw_output_channels; 43 uint_t hw_output_channels; 44 uint_t hw_input_channels; 45 Float32 latency; 46 sint_t total_frames; 47 fmat_t *input_frames; 48 fmat_t *output_frames; 49 SInt16 *au_ios_inbuf; 50 SInt16 *au_ios_outbuf; 51 aubio_device_callback_t callback; 52 void *callback_closure; 53 int dio_error; // flag to check if we had a read error 54 bool input_enabled; 55 bool prevent_feedback; 56 bool verbose; 57 int au_ios_start; 58 int au_ios_end; 59 AURenderCallbackStruct au_ios_cb_struct; 60 }; 61 62 63 static OSStatus 64 aubio_audio_unit_process(void *closure, AudioUnitRenderActionFlags * action_flags, 65 const AudioTimeStamp * time_stamp, UInt32 bus_number, UInt32 inNumber_frames, 66 AudioBufferList * input_output); 67 68 static int aubio_audio_unit_blocking(aubio_audio_unit_t *o); 69 70 static void audio_unit_check_audio_route(aubio_audio_unit_t *o); 71 72 static void audio_unit_interruption_listener(void *closure, UInt32 inInterruptionState); 73 static void audio_unit_route_change_listener(void *closure, AudioSessionPropertyID 74 inID, UInt32 dataSize, const void *inData); 75 static OSStatus audio_unit_set_audio_session_category(bool has_input, bool verbose); 76 static UInt32 audio_unit_get_audio_session_category (); 77 78 aubio_audio_unit_t * new_aubio_audio_unit(uint_t samplerate, 79 uint_t sw_input_channels, uint_t sw_output_channels, 80 uint_t blocksize) 81 { 82 aubio_audio_unit_t * o = AUBIO_NEW(aubio_audio_unit_t); 83 o->hw_output_channels = 2; 84 o->hw_input_channels = 2; 85 o->sw_output_channels = sw_output_channels; 86 o->sw_input_channels = sw_input_channels; 87 o->samplerate = samplerate; 88 o->latency = PREFERRED_LATENCY; 89 o->blocksize = blocksize; 90 91 o->au_ios_start = 0; 92 o->au_ios_end = 0; 93 94 o->verbose = 0; 95 o->input_enabled = true; 96 o->prevent_feedback = 1; 97 o->dio_error = 0; 98 99 o->total_frames = 0; 100 101 /* the integers coming from and to the audio unit */ 102 o->au_ios_outbuf = AUBIO_ARRAY(SInt16, AU_IOS_MAX_FRAMES * o->hw_output_channels); 103 o->au_ios_inbuf = AUBIO_ARRAY(SInt16, AU_IOS_MAX_FRAMES * o->hw_input_channels); 104 105 /* the floats coming from and to the device callback */ 106 o->output_frames = new_fmat(sw_output_channels, blocksize); 107 o->input_frames = new_fmat(sw_input_channels, blocksize); 108 109 /* check for some sizes */ 110 if ( o->hw_output_channels != o->output_frames->height ) { 111 AUBIO_ERR ("got hw_output_channels = %d, but output_frames has %d rows\n", 112 o->hw_output_channels, o->output_frames->height); 113 } 114 if ( o->blocksize != o->output_frames->length ) { 115 AUBIO_ERR ("got blocksize = %d, but output_frames has length %d\n", 116 o->blocksize, o->output_frames->length); 117 } 118 if ( o->hw_input_channels != o->input_frames->height ) { 119 AUBIO_ERR ("got hw_input_channels = %d, but input_frames has %d rows\n", 120 o->hw_input_channels, o->input_frames->height); 121 } 122 if ( o->blocksize != o->input_frames->length ) { 123 AUBIO_ERR ("got blocksize = %d, but input_frames has length %d\n", 124 o->blocksize, o->input_frames->length); 125 } 126 127 return o; 128 } 129 130 sint_t aubio_audio_unit_set_preferred_latency (aubio_audio_unit_t *o, smpl_t latency) 131 { 132 o->latency = latency; 133 return 0; 134 } 135 136 sint_t aubio_audio_unit_set_prevent_feedback (aubio_audio_unit_t *o, uint_t prevent_feedback) 137 { 138 o->prevent_feedback = prevent_feedback; 139 return 0; 140 } 141 142 sint_t aubio_audio_unit_set_verbose (aubio_audio_unit_t *o, uint_t verbose) 143 { 144 o->verbose = verbose; 145 return 0; 146 } 147 148 149 sint_t aubio_audio_unit_init (aubio_audio_unit_t *o) 150 { 151 OSStatus err = noErr; 152 Float32 latency = o->latency; 153 Float64 samplerate = (Float64)o->samplerate; 154 155 o->au_ios_cb_struct.inputProc = aubio_audio_unit_process; 156 o->au_ios_cb_struct.inputProcRefCon = o; 157 158 /* setting up audio session with interruption listener */ 159 err = AudioSessionInitialize(NULL, NULL, audio_unit_interruption_listener, o); 160 if (err) { AUBIO_ERR("audio_unit: could not initialize audio session (%d)\n", (int)err); goto fail; } 161 162 audio_unit_set_audio_session_category(o->input_enabled, o->verbose); 163 audio_unit_check_audio_route(o); 164 165 /* add route change listener */ 166 err = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, 167 audio_unit_route_change_listener, o); 168 if (err) { AUBIO_ERR("audio_unit: could not set route change listener (%d)\n", (int)err); goto fail; } 169 170 /* set latency */ 171 err = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, 172 sizeof(latency), &latency); 173 if (err) { AUBIO_ERR("audio_unit: could not set preferred latency (%d)\n", (int)err); goto fail; } 174 175 #if 0 // only for iphone OS >= 3.1 176 UInt32 val = 1; // set to 0 (default) to use ear speaker in voice application 177 err = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, 178 sizeof(UInt32), &val); 179 if (err) { AUBIO_ERR("audio_unit: could not set session property to default to speaker\n"); } 180 #endif 181 182 /* setting up audio unit */ 183 AudioComponentDescription desc; 184 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 185 desc.componentSubType = kAudioUnitSubType_RemoteIO; 186 desc.componentType = kAudioUnitType_Output; 187 desc.componentFlags = 0; 188 desc.componentFlagsMask = 0; 189 190 AudioStreamBasicDescription audioFormat; 191 192 /* look for a component that match the description */ 193 AudioComponent comp = AudioComponentFindNext(NULL, &desc); 194 195 /* create the audio component */ 196 AudioUnit *audio_unit = &(o->audio_unit); 197 198 err = AudioComponentInstanceNew(comp, &(o->audio_unit)); 199 if (err) { AUBIO_ERR("audio_unit: failed creating the audio unit\n"); goto fail; } 200 201 /* enable IO */ 202 UInt32 enabled = 1; 203 err = AudioUnitSetProperty (*audio_unit, kAudioOutputUnitProperty_EnableIO, 204 kAudioUnitScope_Input, 1, &enabled, sizeof(enabled)); 205 if (err) { 206 AUBIO_ERR("audio_unit: failed enabling input of audio unit\n"); 207 goto fail; 208 } 209 210 /* set max fps */ 211 UInt32 max_fps = MIN(o->blocksize, MAX_FPS); 212 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_MaximumFramesPerSlice, 213 kAudioUnitScope_Global, 0, &max_fps, sizeof(max_fps)); 214 if (err) { 215 AUBIO_ERR("audio_unit: could not set maximum frames per slice property (%d)\n", (int)err); 216 goto fail; 217 } 218 219 AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_SetRenderCallback, 220 kAudioUnitScope_Input, 0, &(o->au_ios_cb_struct), sizeof(o->au_ios_cb_struct)); 221 if (err) { AUBIO_ERR("audio_unit: failed setting audio unit render callback\n"); goto fail; } 222 223 #if 0 224 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_SampleRate, 225 kAudioUnitScope_Input, 0, &samplerate, sizeof(Float64)); 226 if (err) { AUBIO_ERR("audio_unit: could not set audio input sample rate\n"); goto fail; } 227 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_SampleRate, 228 kAudioUnitScope_Output, 1, &samplerate, sizeof(Float64)); 229 if (err) { AUBIO_ERR("audio_unit: could not set audio input sample rate\n"); goto fail; } 230 #endif 231 232 audioFormat.mSampleRate = (Float64)samplerate; 233 audioFormat.mChannelsPerFrame = 2; 234 audioFormat.mFormatID = kAudioFormatLinearPCM; 235 audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; 236 audioFormat.mFramesPerPacket = 1; 237 audioFormat.mBitsPerChannel = 8 * sizeof(SInt16); 238 #if 1 // interleaving 239 audioFormat.mBytesPerFrame = 2 * sizeof(SInt16); 240 audioFormat.mBytesPerPacket = 2 * sizeof(SInt16); 241 #else 242 audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame = sizeof(SInt32); 243 audioFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 244 #endif 245 246 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_StreamFormat, 247 kAudioUnitScope_Input, 0, &audioFormat, sizeof(audioFormat)); 248 if (err) { AUBIO_ERR("audio_unit: could not set audio output format\n"); goto fail; } 249 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_StreamFormat, 250 kAudioUnitScope_Output, 1, &audioFormat, sizeof(audioFormat)); 251 if (err) { AUBIO_ERR("audio_unit: could not set audio input format\n"); goto fail; } 252 253 #if 0 254 AudioStreamBasicDescription thruFormat; 255 thissize = sizeof(thruFormat); 256 err = AudioUnitGetProperty (*audio_unit, kAudioUnitProperty_StreamFormat, 257 kAudioUnitScope_Input, 0, &thruFormat, &thissize); 258 if (err) { AUBIO_ERR("audio_unit: could not get speaker output format, err: %d\n", (int)err); goto fail; } 259 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_StreamFormat, 260 kAudioUnitScope_Output, 1, &thruFormat, sizeof(thruFormat)); 261 if (err) { AUBIO_ERR("audio_unit: could not set input audio format, err: %d\n", (int)err); goto fail; } 262 #endif 263 264 /* time to initialize the unit */ 265 err = AudioUnitInitialize(*audio_unit); 266 if (err) { AUBIO_ERR("audio_unit: failed initializing audio, err: %d\n", (int)err); goto fail; } 267 268 return 0; 269 270 fail: 271 return err; 272 } 273 274 /* perform function */ 275 OSStatus 276 aubio_audio_unit_process(void *closure, AudioUnitRenderActionFlags * action_flags, 277 const AudioTimeStamp * time_stamp, UNUSED UInt32 bus_number, UInt32 inNumber_frames, 278 AudioBufferList * input_output) 279 { 280 UInt32 b; int err = 0; 281 aubio_audio_unit_t *o = (aubio_audio_unit_t *)closure; 282 AudioUnit thisUnit = o->audio_unit; 283 284 if (o->input_enabled) { 285 err = AudioUnitRender(thisUnit, action_flags, time_stamp, 1, 286 inNumber_frames, input_output); 287 if (err) { 288 AUBIO_ERR("audio_unit: error performing AudioUnitRender (%d)\n", err); 289 return err; 290 } 291 } 292 293 // get the number of frames from the audio buffer list, NOT inNumber_frames 294 UInt32 number_frames = input_output->mBuffers[0].mDataByteSize/ sizeof(SInt16) / 2; 295 296 // FIXME find out why this happens 297 if (number_frames < 10) { 298 AUBIO_ERR("audio_unit: got number_frames %d\n", (int)number_frames); 299 return -1; 300 } 301 302 if (o->total_frames >= (signed)number_frames) { 303 304 SInt16 *data; 305 if (o->au_ios_start + number_frames > AU_IOS_MAX_FRAMES) { 306 // start reminder samples writing at reminder 307 int reminder = AU_IOS_MAX_FRAMES - o->au_ios_start; 308 int starter = (o->au_ios_start + number_frames) - AU_IOS_MAX_FRAMES; 309 for (b = 0; b < input_output->mNumberBuffers; b++) { 310 data = (SInt16 *)(input_output->mBuffers[b].mData); 311 /* copy microphone output to input buffer */ 312 memcpy (o->au_ios_inbuf + o->au_ios_start * 2, data, reminder * 2 * sizeof(SInt16)); 313 memcpy (o->au_ios_inbuf, data + reminder * 2, starter * 2 * sizeof(SInt16)); 314 /* silence data before copying from output */ 315 //memset (data, 0, input_output->mBuffers[b].mDataByteSize); 316 /* copy output buffer to speakers */ 317 memcpy (data, o->au_ios_outbuf + o->au_ios_start * 2, reminder * 2 * sizeof(SInt16)); 318 memcpy (data + reminder * 2, o->au_ios_outbuf, starter * 2 * sizeof(SInt16)); 319 } 320 } else { 321 for (b = 0; b < input_output->mNumberBuffers; b++) { 322 data = (SInt16 *)(input_output->mBuffers[b].mData); 323 /* copy microphone samples to au_ios_inbuf */ 324 memcpy(o->au_ios_inbuf + o->au_ios_start * 2, data, number_frames * 2 * sizeof(SInt16)); 325 /* silence data before copying from output */ 326 //memset (data, 0, input_output->mBuffers[b].mDataByteSize); 327 /* copy output buffer to speakers */ 328 memcpy(data, o->au_ios_outbuf + o->au_ios_start * 2, number_frames * 2 * sizeof(SInt16)); 329 } 330 } 331 o->au_ios_start += number_frames; 332 o->au_ios_start %= AU_IOS_MAX_FRAMES; 333 o->total_frames -= number_frames; 334 335 #if 1 336 } else { 337 if (o->total_frames > 0) o->dio_error = 1; 338 for (b = 0; b < input_output->mNumberBuffers; b++) { 339 memset (input_output->mBuffers[b].mData, 0, 340 input_output->mBuffers[b].mDataByteSize); 341 } 342 //total_frames = 0; 343 #endif 344 } 345 346 // now call callback 347 while ( o->total_frames < (signed)number_frames ) { 348 //AUBIO_DBG ("audio_unit: total_frames = %d, number_frames = %d, o->au_ios_start = %d, o->au_ios_end = %d", 349 // o->total_frames, number_frames, o->au_ios_start, o->au_ios_end); 350 aubio_audio_unit_blocking(o); 351 } 352 353 return err; 354 } 355 356 int aubio_audio_unit_blocking(aubio_audio_unit_t *o) 357 { 358 uint_t sw_output_channels, sw_input_channels, 359 hw_output_channels, hw_input_channels, 360 i, j, blocksize; 361 if (! o->callback) return -1; 362 363 smpl_t ** tbuf; 364 365 sw_output_channels = o->sw_output_channels; 366 sw_input_channels = o->sw_input_channels; 367 hw_output_channels = o->hw_output_channels; 368 hw_input_channels = o->hw_input_channels; 369 blocksize = o->blocksize; 370 371 if (!sw_input_channels && !sw_output_channels) goto fail; 372 373 if (o->dio_error) { 374 AUBIO_WRN("audio_unit: dio error %d\n", o->total_frames); 375 o->dio_error = 0; 376 } 377 378 if (o->au_ios_inbuf) { 379 /* copy samples from input buffer */ 380 tbuf = o->input_frames->data; 381 if (o->input_enabled) { 382 for (j = 0; j < blocksize;j++) { 383 for (i = 0; i < sw_input_channels && i < hw_input_channels; i++) { 384 //tbuf[i][j] = 385 // (smpl_t)(o->au_ios_inbuf[i + (j + o->au_ios_end) * sw_input_channels] / 32768.); 386 // on iphone, input is mono, copy right to left channel 387 tbuf[i][j] = 388 (smpl_t) o->au_ios_inbuf[0 + (j + o->au_ios_end) * hw_input_channels] 389 * INT_TO_FLOAT; 390 } 391 } 392 } else { 393 // input is disabled, fill with zeroes 394 for (j = 0; j < blocksize; j++) { 395 for (i = 0; i < sw_input_channels && i < hw_input_channels; i++) { 396 tbuf[i][j] = 0; 397 } 398 } 399 } 400 } 401 402 o->callback(o->callback_closure, o->input_frames, o->output_frames); 403 404 /* copy samples to output buffer */ 405 tbuf = o->output_frames->data; 406 for (i = 0; i < o->output_frames->height; i++) { 407 for (j = 0; j < o->output_frames->length; j++) { 408 smpl_t val = tbuf[i][j]; 409 if (val < -1.0) val = -1.0; 410 if (val > 1.0) val = 1.0; 411 o->au_ios_outbuf[i + (j + o->au_ios_end) * hw_output_channels ] = (SInt16)(val * 32767); 412 } 413 } 414 415 o->au_ios_end += blocksize; 416 o->au_ios_end %= AU_IOS_MAX_FRAMES; 417 o->total_frames += blocksize; 418 419 return 0; 420 421 fail: 422 AUBIO_ERR("audio_unit: callback() failed\n"); 423 o->total_frames += AU_IOS_MAX_FRAMES; 424 return 1; 425 } 426 427 sint_t aubio_audio_unit_get_info (aubio_audio_unit_t *o) 428 { 429 UInt32 thissize, input_hw_channels, output_hw_channels, max_fps; 430 Float32 latency, input_latency, output_latency, input_hw_volume, output_hw_volume; 431 Float64 samplerate; 432 OSStatus err = 0; 433 434 // Show some info about the opened unit 435 436 /* get sampling rate */ 437 thissize = sizeof(samplerate); 438 err = AudioUnitGetProperty (o->audio_unit, kAudioUnitProperty_SampleRate, 439 kAudioUnitScope_Output, 1, &samplerate, &thissize); 440 if (err) { AUBIO_ERR("audio_unit: could not get audio unit sample rate (%d)\n", 441 (int)err); goto fail; } 442 443 /* get hardware input channels */ 444 thissize = sizeof(input_hw_channels); 445 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, 446 &thissize, &input_hw_channels); 447 if (err) { AUBIO_ERR("audio_unit: could not get hardware input channels (%d)\n", 448 (int)err); goto fail; } 449 450 /* get hardware output channels */ 451 thissize = sizeof(output_hw_channels); 452 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputNumberChannels, 453 &thissize, &output_hw_channels); 454 if (err) { AUBIO_ERR("audio_unit: could not get hardware output channels (%d)\n", 455 (int)err); goto fail; } 456 457 /* get hardware input volume */ 458 thissize = sizeof(input_hw_volume); 459 err = AudioSessionGetProperty(kAudioSessionProperty_InputGainScalar, 460 &thissize, &input_hw_volume); 461 if (err) { AUBIO_ERR("audio_unit: could not get hardware input volume (%d)\n", 462 (int)err); goto fail; } 463 464 /* get hardware output volume */ 465 thissize = sizeof(output_hw_volume); 466 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputVolume, 467 &thissize, &output_hw_volume); 468 if (err) { AUBIO_ERR("audio_unit: could not get hardware output volume (%d)\n", 469 (int)err); goto fail; } 470 471 AUBIO_MSG("audio_unit: opened at %.0fHz, sw channels %din/%dout, hw channels %din/%dout, hw vol %.2fin/%.2fout\n", 472 samplerate, 473 o->sw_input_channels, o->sw_output_channels, 474 (unsigned int)input_hw_channels, (unsigned int)output_hw_channels, 475 input_hw_volume, output_hw_volume); 476 477 /* get max frames per slice */ 478 thissize = sizeof(max_fps); 479 err = AudioUnitGetProperty (o->audio_unit, kAudioUnitProperty_MaximumFramesPerSlice, 480 kAudioUnitScope_Global, 0, &max_fps, &thissize); 481 if (err) { AUBIO_ERR("audio_unit: could not get maximum frames per slice property %d\n", 482 (int)err); goto fail; } 483 484 /* get hardware latency */ 485 thissize = sizeof(latency); 486 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration, 487 &thissize, &latency); 488 if (err) { AUBIO_ERR("audio_unit: could not get hardware latency %d\n", 489 (int)err); goto fail; } 490 491 /* get input latency */ 492 thissize = sizeof(input_latency); 493 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputLatency, 494 &thissize, &input_latency); 495 if (err) { AUBIO_ERR("audio_unit: could not get input latency %d\n", 496 (int)err); goto fail; } 497 498 /* get output harlatency */ 499 thissize = sizeof(output_latency); 500 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency, 501 &thissize, &output_latency); 502 if (err) { AUBIO_ERR("audio_unit: could not get output latency %d\n", 503 (int)err); goto fail; } 504 505 AUBIO_MSG("audio_unit: I/O latency: %.2fms, %d frames, (%.2fms, %d frames in, %.2fms %d frames out)\n", 506 latency*1000., (sint_t)round(latency*samplerate), 507 input_latency*1000., (sint_t)ROUND(input_latency*samplerate), 508 output_latency*1000., (sint_t)ROUND(output_latency*samplerate)); 509 510 fail: 511 return err; 512 } 513 514 sint_t aubio_audio_unit_start(aubio_audio_unit_t *o) { 515 OSStatus err = 0; 516 517 if (o->verbose) { 518 // print some info about the current settings 519 aubio_audio_unit_get_info (o); 520 } 521 522 /* time to start the unit */ 523 err = AudioOutputUnitStart (o->audio_unit); 524 if (err) { AUBIO_ERR("audio_unit: could not start unit (%d)\n", (int)err); } 525 return err; 526 } 527 528 sint_t aubio_audio_unit_stop(aubio_audio_unit_t *o) 529 { 530 if (o->audio_unit == NULL) return -1; 531 OSStatus err = AudioOutputUnitStop (o->audio_unit); 532 if (err) { AUBIO_WRN("audio_unit: failed stopping audio unit (%d)\n", (int)err); } 533 err = AudioUnitUninitialize (o->audio_unit); 534 if (err) { AUBIO_WRN("audio_unit: failed unitializing audio unit (%d)\n", (int)err); } 535 err = AudioSessionSetActive(false); 536 if (err) { AUBIO_WRN("audio_unit: failed stopping audio session (%d)\n", (int)err); } 537 return err; 538 } 539 540 uint_t aubio_audio_unit_set_callback(aubio_audio_unit_t *o, 541 aubio_device_callback_t callback, void *closure) { 542 o->callback = callback; 543 o->callback_closure = closure; 544 return 0; 545 } 546 547 /* interruption listeners */ 548 void audio_unit_interruption_listener(void *closure, UInt32 inInterruptionState) 549 { 550 OSStatus err = 0; 551 aubio_audio_unit_t *o = (aubio_audio_unit_t *) closure; 552 AudioUnit this_unit = o->audio_unit; 553 554 if (inInterruptionState == kAudioSessionEndInterruption) { 555 AUBIO_WRN("audio_unit: session interruption ended\n"); 556 err = AudioSessionSetActive(true); 557 if (err) { 558 AUBIO_ERR("audio_unit: could not make session active after interruption (%d)\n", (int)err); 559 goto fail; 560 } 561 err = AudioOutputUnitStart(this_unit); 562 if (err) { 563 AUBIO_ERR("audio_unit: failed starting unit (%d)\n", (int)err); 564 goto fail; 565 } 566 } 567 if (inInterruptionState == kAudioSessionBeginInterruption) { 568 AUBIO_WRN("audio_unit: session interruption started\n"); 569 err = AudioOutputUnitStop(this_unit); 570 if (err) { 571 AUBIO_ERR("audio_unit: could not stop unit at interruption (%d)\n", (int)err); 572 goto fail; 573 } 574 err = AudioSessionSetActive(false); 575 if (err) { 576 AUBIO_ERR("audio_unit: could not make session inactive after interruption (%d)\n", (int)err); 577 goto fail; 578 } 579 } 580 fail: 581 return; 582 } 583 584 UInt32 audio_unit_get_audio_session_category () { 585 UInt32 category, thissize; 586 thissize = sizeof(category); 587 OSStatus err = AudioSessionGetProperty(kAudioSessionProperty_AudioCategory, 588 &thissize, &category); 589 if (err) { 590 AUBIO_ERR("audio_unit: could not get audio category (%d)\n", (int)err); 591 return err; 592 } 593 if (category == kAudioSessionCategory_AmbientSound) { 594 AUBIO_MSG("audio_unit: session category is AmbiantSound\n"); 595 } else if (category == kAudioSessionCategory_SoloAmbientSound) { 596 AUBIO_MSG("audio_unit: session category is SoloAmbiantSound\n"); 597 } else if (category == kAudioSessionCategory_MediaPlayback) { 598 AUBIO_MSG("audio_unit: session category is MediaPlayback\n"); 599 } else if (category == kAudioSessionCategory_RecordAudio) { 600 AUBIO_MSG("audio_unit: session category is RecordAudio\n"); 601 } else if (category == kAudioSessionCategory_PlayAndRecord) { 602 AUBIO_MSG("audio_unit: session category is PlayAndRecord\n"); 603 } else if (category == kAudioSessionCategory_AudioProcessing) { 604 AUBIO_MSG("audio_unit: session category is AudioProcessing\n"); 605 } 606 return category; 607 } 608 609 OSStatus audio_unit_set_audio_session_category(bool has_input, bool verbose) 610 { 611 //if we have input, set the session category accordingly 612 OSStatus err = 0; 613 UInt32 category; 614 if (has_input) { 615 category = kAudioSessionCategory_PlayAndRecord; 616 if (verbose) AUBIO_MSG("audio_unit: setting category to PlayAndRecord\n"); 617 } else { 618 category = kAudioSessionCategory_MediaPlayback; 619 if (verbose) AUBIO_MSG("audio_unit: setting category to MediaPlayback\n"); 620 } 621 err = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, 622 sizeof(category), &category); 623 if (err) { 624 AUBIO_ERR("audio_unit: could not set audio category\n"); 625 } 626 627 // Audiob.us style 628 UInt32 allowMixing = 1; 629 AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, 630 sizeof (allowMixing), &allowMixing); 631 if (err) { 632 AUBIO_ERR("audio_unit: could not set audio session to mix with others\n"); 633 } 634 635 return err; 636 } 637 638 void audio_unit_check_audio_route(aubio_audio_unit_t *o) { 639 CFStringRef currentRoute; 640 UInt32 val, thissize = sizeof(currentRoute); 641 OSStatus err = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &thissize, ¤tRoute); 642 if (err) { AUBIO_ERR("audio_unit: could not get current route\n"); goto fail; } 643 else { 644 char *route = (char *)CFStringGetCStringPtr ( currentRoute, kCFStringEncodingUTF8); 645 if (route == NULL) { 646 int bufferSize = 25; 647 route = calloc(bufferSize, sizeof(char)); 648 CFStringGetCString ( currentRoute, route, bufferSize, 649 kCFStringEncodingUTF8); 650 } 651 if (o->verbose) { 652 AUBIO_MSG ("audio_unit: current route is %s\n", route); 653 } 654 //free(route); 655 } 656 if( currentRoute ) { 657 if( CFStringCompare( currentRoute, CFSTR("Headset"), 0 ) == kCFCompareEqualTo ) { 658 val = kAudioSessionOverrideAudioRoute_None; 659 } else if( CFStringCompare( currentRoute, CFSTR("Receiver" ), 0 ) == kCFCompareEqualTo ) { 660 val = kAudioSessionOverrideAudioRoute_Speaker; 661 } else if( CFStringCompare( currentRoute, CFSTR("ReceiverAndMicrophone" ), 0 ) == kCFCompareEqualTo ) { 662 val = kAudioSessionOverrideAudioRoute_Speaker; 663 } else if( CFStringCompare( currentRoute, CFSTR("SpeakerAndMicrophone" ), 0 ) == kCFCompareEqualTo ) { 664 val = kAudioSessionOverrideAudioRoute_Speaker; 665 } else if( CFStringCompare( currentRoute, CFSTR("HeadphonesAndMicrophone" ), 0 ) == kCFCompareEqualTo ) { 666 val = kAudioSessionOverrideAudioRoute_None; 667 } else if( CFStringCompare( currentRoute, CFSTR("HeadsetInOut" ), 0 ) == kCFCompareEqualTo ) { 668 val = kAudioSessionOverrideAudioRoute_None; 669 } else { 670 val = kAudioSessionOverrideAudioRoute_None; 671 } 672 673 o->input_enabled = true; 674 if (val == kAudioSessionOverrideAudioRoute_Speaker) { 675 if (o->prevent_feedback) { 676 o->input_enabled = false; 677 if (o->verbose) { 678 AUBIO_MSG ("audio_unit: disabling input to avoid feedback\n"); 679 } 680 } else { 681 AUBIO_WRN ("audio_unit: input not disabled as prevent_feedback set to 0, risking feedback\n"); 682 } 683 } 684 685 err = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, 686 sizeof(UInt32), &val); 687 if (err) { AUBIO_ERR("audio_unit: could not set session OverrideAudioRoute to Speaker\n"); } 688 689 } 690 691 fail: 692 if ( currentRoute ) free((void*)currentRoute); 693 return; 694 695 } 696 697 SInt32 698 audio_unit_get_route_change_reason(CFDictionaryRef routeChangeDic) { 699 CFNumberRef routeChangeReasonRef = (CFNumberRef)CFDictionaryGetValue(routeChangeDic, 700 CFSTR(kAudioSession_AudioRouteChangeKey_Reason)); 701 SInt32 change_reason_number; 702 CFNumberGetValue ( routeChangeReasonRef, kCFNumberSInt32Type, &change_reason_number); 703 switch (change_reason_number) { 704 case kAudioSessionRouteChangeReason_NewDeviceAvailable: 705 AUBIO_MSG("audio_unit: route changed to NewDeviceAvailable\n"); 706 break; 707 case kAudioSessionRouteChangeReason_OldDeviceUnavailable: 708 AUBIO_MSG("audio_unit: route changed to OldDeviceUnavailable\n"); 709 break; 710 case kAudioSessionRouteChangeReason_CategoryChange: 711 AUBIO_MSG("audio_unit: route changed to CategoryChange\n"); 712 audio_unit_get_audio_session_category(); 713 break; 714 case kAudioSessionRouteChangeReason_Override: 715 AUBIO_MSG("audio_unit: route changed to Override\n"); 716 break; 717 case kAudioSessionRouteChangeReason_WakeFromSleep: 718 AUBIO_MSG("audio_unit: route changed to WakeFromSleep\n"); 719 break; 720 case kAudioSessionRouteChangeReason_NoSuitableRouteForCategory: 721 AUBIO_MSG("audio_unit: route changed to NoSuitableRouteForCategory\n"); 722 break; 723 case kAudioSessionRouteChangeReason_Unknown: 724 default: 725 AUBIO_ERR("audio_unit: route changed for an unknown reason!?\n"); 726 break; 727 } 728 return change_reason_number; 729 } 730 731 /* route change listeners */ 732 void 733 audio_unit_route_change_listener(void *closure, AudioSessionPropertyID inID, 734 UInt32 dataSize, const void *inData) 735 { 736 737 UNUSED aubio_audio_unit_t *o = (aubio_audio_unit_t *)closure; 738 UNUSED UInt32 size = dataSize; 739 if (inID == kAudioSessionProperty_AudioRouteChange) { 740 741 // OSStatus err = 0; 742 //AudioUnit audio_unit = o->audio_unit; 743 744 if (o->verbose) { 745 // show route change reason 746 audio_unit_get_route_change_reason((CFDictionaryRef)inData); 747 } 748 749 // check current audio route, changing it to prevent feedback as needed 750 audio_unit_check_audio_route(o); 751 752 if (o->verbose) { 753 // print some info about the current settings 754 aubio_audio_unit_get_info(o); 755 } 756 757 } 758 759 } 760 761 /* delete object */ 762 uint_t del_aubio_audio_unit(aubio_audio_unit_t *o) 763 { 764 int err = 0; 765 err = aubio_audio_unit_stop(o); 766 if (o->au_ios_inbuf) free(o->au_ios_inbuf); 767 o->au_ios_inbuf = NULL; 768 if (o->au_ios_outbuf) free(o->au_ios_outbuf); 769 o->au_ios_outbuf = NULL; 770 del_fmat (o->input_frames); 771 del_fmat (o->output_frames); 772 o->audio_unit = NULL; 773 return (int)err; 774 } 775 776 #endif /* HAVE_AUDIO_UNIT */ 777