2 Copyright (C) 2013 Paul Brossier <piem@aubio.org>
4 This file is part of aubio.
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.
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.
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/>.
22 #ifdef TARGET_OS_IPHONE
23 #include "aubio_priv.h"
27 #include "io/audio_unit.h"
29 #include <AudioToolbox/AudioToolbox.h>
31 #define AU_IOS_MAX_OUT 64
32 #define AU_IOS_MAX_FRAMES AU_IOS_MAX_OUT * 16 * 2
33 #define PREFERRED_LATENCY 0.010
36 #define INT_TO_FLOAT 3.0517578125e-05 // 1. / 32768.
38 struct _aubio_audio_unit_t {
42 uint_t sw_input_channels;
43 uint_t sw_output_channels;
44 uint_t hw_output_channels;
45 uint_t hw_input_channels;
49 fmat_t *output_frames;
51 SInt16 *au_ios_outbuf;
52 aubio_device_callback_t callback;
53 void *callback_closure;
54 int dio_error; // flag to check if we had a read error
56 bool prevent_feedback;
60 AURenderCallbackStruct au_ios_cb_struct;
65 aubio_audio_unit_process(void *closure, AudioUnitRenderActionFlags * action_flags,
66 const AudioTimeStamp * time_stamp, UInt32 bus_number, UInt32 inNumber_frames,
67 AudioBufferList * input_output);
69 static int aubio_audio_unit_blocking(aubio_audio_unit_t *o);
71 static void audio_unit_check_audio_route(aubio_audio_unit_t *o);
73 static void audio_unit_interruption_listener(void *closure, UInt32 inInterruptionState);
74 static void audio_unit_route_change_listener(void *closure, AudioSessionPropertyID
75 inID, UInt32 dataSize, const void *inData);
76 static OSStatus audio_unit_set_audio_session_category(bool has_input, bool verbose);
77 static UInt32 audio_unit_get_audio_session_category ();
79 aubio_audio_unit_t * new_aubio_audio_unit(uint_t samplerate,
80 uint_t sw_input_channels, uint_t sw_output_channels,
83 aubio_audio_unit_t * o = AUBIO_NEW(aubio_audio_unit_t);
84 o->hw_output_channels = 2;
85 o->hw_input_channels = 2;
86 o->sw_output_channels = sw_output_channels;
87 o->sw_input_channels = sw_input_channels;
88 o->samplerate = samplerate;
89 o->latency = PREFERRED_LATENCY;
90 o->blocksize = blocksize;
96 o->input_enabled = true;
97 o->prevent_feedback = 1;
102 /* the integers coming from and to the audio unit */
103 o->au_ios_outbuf = AUBIO_ARRAY(SInt16, AU_IOS_MAX_FRAMES * o->hw_output_channels);
104 o->au_ios_inbuf = AUBIO_ARRAY(SInt16, AU_IOS_MAX_FRAMES * o->hw_input_channels);
106 /* the floats coming from and to the device callback */
107 o->output_frames = new_fmat(sw_output_channels, blocksize);
108 o->input_frames = new_fmat(sw_input_channels, blocksize);
110 /* check for some sizes */
111 if ( o->hw_output_channels != o->output_frames->height ) {
112 AUBIO_ERR ("got hw_output_channels = %d, but output_frames has %d rows\n",
113 o->hw_output_channels, o->output_frames->height);
115 if ( o->blocksize != o->output_frames->length ) {
116 AUBIO_ERR ("got blocksize = %d, but output_frames has length %d\n",
117 o->blocksize, o->output_frames->length);
119 if ( o->hw_input_channels != o->input_frames->height ) {
120 AUBIO_ERR ("got hw_input_channels = %d, but input_frames has %d rows\n",
121 o->hw_input_channels, o->input_frames->height);
123 if ( o->blocksize != o->input_frames->length ) {
124 AUBIO_ERR ("got blocksize = %d, but input_frames has length %d\n",
125 o->blocksize, o->input_frames->length);
131 sint_t aubio_audio_unit_set_preferred_latency (aubio_audio_unit_t *o, smpl_t latency)
133 o->latency = latency;
137 sint_t aubio_audio_unit_set_prevent_feedback (aubio_audio_unit_t *o, uint_t prevent_feedback)
139 o->prevent_feedback = prevent_feedback;
143 sint_t aubio_audio_unit_set_verbose (aubio_audio_unit_t *o, uint_t verbose)
145 o->verbose = verbose;
150 sint_t aubio_audio_unit_init (aubio_audio_unit_t *o)
152 OSStatus err = noErr;
153 Float32 latency = o->latency;
154 Float64 samplerate = (Float64)o->samplerate;
156 o->au_ios_cb_struct.inputProc = aubio_audio_unit_process;
157 o->au_ios_cb_struct.inputProcRefCon = o;
159 /* setting up audio session with interruption listener */
160 err = AudioSessionInitialize(NULL, NULL, audio_unit_interruption_listener, o);
161 if (err) { AUBIO_ERR("audio_unit: could not initialize audio session (%d)\n", (int)err); goto fail; }
163 audio_unit_set_audio_session_category(o->input_enabled, o->verbose);
164 audio_unit_check_audio_route(o);
166 /* add route change listener */
167 err = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
168 audio_unit_route_change_listener, o);
169 if (err) { AUBIO_ERR("audio_unit: could not set route change listener (%d)\n", (int)err); goto fail; }
172 err = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
173 sizeof(latency), &latency);
174 if (err) { AUBIO_ERR("audio_unit: could not set preferred latency (%d)\n", (int)err); goto fail; }
176 #if 0 // only for iphone OS >= 3.1
177 UInt32 val = 1; // set to 0 (default) to use ear speaker in voice application
178 err = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,
179 sizeof(UInt32), &val);
180 if (err) { AUBIO_ERR("audio_unit: could not set session property to default to speaker\n"); }
183 /* setting up audio unit */
184 AudioComponentDescription desc;
185 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
186 desc.componentSubType = kAudioUnitSubType_RemoteIO;
187 desc.componentType = kAudioUnitType_Output;
188 desc.componentFlags = 0;
189 desc.componentFlagsMask = 0;
191 AudioStreamBasicDescription audioFormat;
193 /* look for a component that match the description */
194 AudioComponent comp = AudioComponentFindNext(NULL, &desc);
196 /* create the audio component */
197 AudioUnit *audio_unit = &(o->audio_unit);
199 err = AudioComponentInstanceNew(comp, &(o->audio_unit));
200 if (err) { AUBIO_ERR("audio_unit: failed creating the audio unit\n"); goto fail; }
204 err = AudioUnitSetProperty (*audio_unit, kAudioOutputUnitProperty_EnableIO,
205 kAudioUnitScope_Input, 1, &enabled, sizeof(enabled));
207 AUBIO_ERR("audio_unit: failed enabling input of audio unit\n");
212 UInt32 max_fps = MIN(o->blocksize, MAX_FPS);
213 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_MaximumFramesPerSlice,
214 kAudioUnitScope_Global, 0, &max_fps, sizeof(max_fps));
216 AUBIO_ERR("audio_unit: could not set maximum frames per slice property (%d)\n", (int)err);
220 AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_SetRenderCallback,
221 kAudioUnitScope_Input, 0, &(o->au_ios_cb_struct), sizeof(o->au_ios_cb_struct));
222 if (err) { AUBIO_ERR("audio_unit: failed setting audio unit render callback\n"); goto fail; }
225 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_SampleRate,
226 kAudioUnitScope_Input, 0, &samplerate, sizeof(Float64));
227 if (err) { AUBIO_ERR("audio_unit: could not set audio input sample rate\n"); goto fail; }
228 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_SampleRate,
229 kAudioUnitScope_Output, 1, &samplerate, sizeof(Float64));
230 if (err) { AUBIO_ERR("audio_unit: could not set audio input sample rate\n"); goto fail; }
233 audioFormat.mSampleRate = (Float64)samplerate;
234 audioFormat.mChannelsPerFrame = 2;
235 audioFormat.mFormatID = kAudioFormatLinearPCM;
236 audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
237 audioFormat.mFramesPerPacket = 1;
238 audioFormat.mBitsPerChannel = 8 * sizeof(SInt16);
239 #if 1 // interleaving
240 audioFormat.mBytesPerFrame = 2 * sizeof(SInt16);
241 audioFormat.mBytesPerPacket = 2 * sizeof(SInt16);
243 audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame = sizeof(SInt32);
244 audioFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
247 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_StreamFormat,
248 kAudioUnitScope_Input, 0, &audioFormat, sizeof(audioFormat));
249 if (err) { AUBIO_ERR("audio_unit: could not set audio output format\n"); goto fail; }
250 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_StreamFormat,
251 kAudioUnitScope_Output, 1, &audioFormat, sizeof(audioFormat));
252 if (err) { AUBIO_ERR("audio_unit: could not set audio input format\n"); goto fail; }
255 AudioStreamBasicDescription thruFormat;
256 thissize = sizeof(thruFormat);
257 err = AudioUnitGetProperty (*audio_unit, kAudioUnitProperty_StreamFormat,
258 kAudioUnitScope_Input, 0, &thruFormat, &thissize);
259 if (err) { AUBIO_ERR("audio_unit: could not get speaker output format, err: %d\n", (int)err); goto fail; }
260 err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_StreamFormat,
261 kAudioUnitScope_Output, 1, &thruFormat, sizeof(thruFormat));
262 if (err) { AUBIO_ERR("audio_unit: could not set input audio format, err: %d\n", (int)err); goto fail; }
265 /* time to initialize the unit */
266 err = AudioUnitInitialize(*audio_unit);
267 if (err) { AUBIO_ERR("audio_unit: failed initializing audio, err: %d\n", (int)err); goto fail; }
275 /* perform function */
277 aubio_audio_unit_process(void *closure, AudioUnitRenderActionFlags * action_flags,
278 const AudioTimeStamp * time_stamp, UNUSED UInt32 bus_number, UInt32 inNumber_frames,
279 AudioBufferList * input_output)
281 UInt32 b; int err = 0;
282 aubio_audio_unit_t *o = (aubio_audio_unit_t *)closure;
283 AudioUnit thisUnit = o->audio_unit;
285 if (o->input_enabled) {
286 err = AudioUnitRender(thisUnit, action_flags, time_stamp, 1,
287 inNumber_frames, input_output);
289 AUBIO_ERR("audio_unit: error performing AudioUnitRender (%d)\n", err);
294 // get the number of frames from the audio buffer list, NOT inNumber_frames
295 UInt32 number_frames = input_output->mBuffers[0].mDataByteSize/ sizeof(SInt16) / 2;
297 // FIXME find out why this happens
298 if (number_frames < 10) {
299 AUBIO_ERR("audio_unit: got number_frames %d\n", (int)number_frames);
303 if (o->total_frames >= (signed)number_frames) {
306 if (o->au_ios_start + number_frames > AU_IOS_MAX_FRAMES) {
307 // start reminder samples writing at reminder
308 int reminder = AU_IOS_MAX_FRAMES - o->au_ios_start;
309 int starter = (o->au_ios_start + number_frames) - AU_IOS_MAX_FRAMES;
310 for (b = 0; b < input_output->mNumberBuffers; b++) {
311 data = (SInt16 *)(input_output->mBuffers[b].mData);
312 /* copy microphone output to input buffer */
313 memcpy (o->au_ios_inbuf + o->au_ios_start * 2, data, reminder * 2 * sizeof(SInt16));
314 memcpy (o->au_ios_inbuf, data + reminder * 2, starter * 2 * sizeof(SInt16));
315 /* silence data before copying from output */
316 //memset (data, 0, input_output->mBuffers[b].mDataByteSize);
317 /* copy output buffer to speakers */
318 memcpy (data, o->au_ios_outbuf + o->au_ios_start * 2, reminder * 2 * sizeof(SInt16));
319 memcpy (data + reminder * 2, o->au_ios_outbuf, starter * 2 * sizeof(SInt16));
322 for (b = 0; b < input_output->mNumberBuffers; b++) {
323 data = (SInt16 *)(input_output->mBuffers[b].mData);
324 /* copy microphone samples to au_ios_inbuf */
325 memcpy(o->au_ios_inbuf + o->au_ios_start * 2, data, number_frames * 2 * sizeof(SInt16));
326 /* silence data before copying from output */
327 //memset (data, 0, input_output->mBuffers[b].mDataByteSize);
328 /* copy output buffer to speakers */
329 memcpy(data, o->au_ios_outbuf + o->au_ios_start * 2, number_frames * 2 * sizeof(SInt16));
332 o->au_ios_start += number_frames;
333 o->au_ios_start %= AU_IOS_MAX_FRAMES;
334 o->total_frames -= number_frames;
338 if (o->total_frames > 0) o->dio_error = 1;
339 for (b = 0; b < input_output->mNumberBuffers; b++) {
340 memset (input_output->mBuffers[b].mData, 0,
341 input_output->mBuffers[b].mDataByteSize);
348 while ( o->total_frames < (signed)number_frames ) {
349 //AUBIO_DBG ("audio_unit: total_frames = %d, number_frames = %d, o->au_ios_start = %d, o->au_ios_end = %d",
350 // o->total_frames, number_frames, o->au_ios_start, o->au_ios_end);
351 aubio_audio_unit_blocking(o);
357 int aubio_audio_unit_blocking(aubio_audio_unit_t *o)
359 uint_t sw_output_channels, sw_input_channels,
360 hw_output_channels, hw_input_channels,
362 if (! o->callback) return -1;
366 sw_output_channels = o->sw_output_channels;
367 sw_input_channels = o->sw_input_channels;
368 hw_output_channels = o->hw_output_channels;
369 hw_input_channels = o->hw_input_channels;
370 blocksize = o->blocksize;
372 if (!sw_input_channels && !sw_output_channels) goto fail;
375 AUBIO_WRN("audio_unit: dio error %d\n", o->total_frames);
379 if (o->au_ios_inbuf) {
380 /* copy samples from input buffer */
381 tbuf = o->input_frames->data;
382 if (o->input_enabled) {
383 for (j = 0; j < blocksize;j++) {
384 for (i = 0; i < sw_input_channels && i < hw_input_channels; i++) {
386 // (smpl_t)(o->au_ios_inbuf[i + (j + o->au_ios_end) * sw_input_channels] / 32768.);
387 // on iphone, input is mono, copy right to left channel
389 (smpl_t) o->au_ios_inbuf[0 + (j + o->au_ios_end) * hw_input_channels]
394 // input is disabled, fill with zeroes
395 for (j = 0; j < blocksize; j++) {
396 for (i = 0; i < sw_input_channels && i < hw_input_channels; i++) {
403 o->callback(o->callback_closure, o->input_frames, o->output_frames);
405 /* copy samples to output buffer */
406 tbuf = o->output_frames->data;
407 for (i = 0; i < o->output_frames->height; i++) {
408 for (j = 0; j < o->output_frames->length; j++) {
409 smpl_t val = tbuf[i][j];
410 if (val < -1.0) val = -1.0;
411 if (val > 1.0) val = 1.0;
412 o->au_ios_outbuf[i + (j + o->au_ios_end) * hw_output_channels ] = (SInt16)(val * 32767);
416 o->au_ios_end += blocksize;
417 o->au_ios_end %= AU_IOS_MAX_FRAMES;
418 o->total_frames += blocksize;
423 AUBIO_ERR("audio_unit: callback() failed\n");
424 o->total_frames += AU_IOS_MAX_FRAMES;
428 sint_t aubio_audio_unit_get_info (aubio_audio_unit_t *o)
430 UInt32 thissize, input_hw_channels, output_hw_channels, max_fps;
431 Float32 latency, input_latency, output_latency, input_hw_volume, output_hw_volume;
435 // Show some info about the opened unit
437 /* get sampling rate */
438 thissize = sizeof(samplerate);
439 err = AudioUnitGetProperty (o->audio_unit, kAudioUnitProperty_SampleRate,
440 kAudioUnitScope_Output, 1, &samplerate, &thissize);
441 if (err) { AUBIO_ERR("audio_unit: could not get audio unit sample rate (%d)\n",
442 (int)err); goto fail; }
444 /* get hardware input channels */
445 thissize = sizeof(input_hw_channels);
446 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels,
447 &thissize, &input_hw_channels);
448 if (err) { AUBIO_ERR("audio_unit: could not get hardware input channels (%d)\n",
449 (int)err); goto fail; }
451 /* get hardware output channels */
452 thissize = sizeof(output_hw_channels);
453 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputNumberChannels,
454 &thissize, &output_hw_channels);
455 if (err) { AUBIO_ERR("audio_unit: could not get hardware output channels (%d)\n",
456 (int)err); goto fail; }
458 /* get hardware input volume */
459 thissize = sizeof(input_hw_volume);
460 err = AudioSessionGetProperty(kAudioSessionProperty_InputGainScalar,
461 &thissize, &input_hw_volume);
462 if (err) { AUBIO_ERR("audio_unit: could not get hardware input volume (%d)\n",
463 (int)err); goto fail; }
465 /* get hardware output volume */
466 thissize = sizeof(output_hw_volume);
467 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputVolume,
468 &thissize, &output_hw_volume);
469 if (err) { AUBIO_ERR("audio_unit: could not get hardware output volume (%d)\n",
470 (int)err); goto fail; }
472 AUBIO_MSG("audio_unit: opened at %.0fHz, sw channels %din/%dout, hw channels %din/%dout, hw vol %.2fin/%.2fout\n",
474 o->sw_input_channels, o->sw_output_channels,
475 (unsigned int)input_hw_channels, (unsigned int)output_hw_channels,
476 input_hw_volume, output_hw_volume);
478 /* get max frames per slice */
479 thissize = sizeof(max_fps);
480 err = AudioUnitGetProperty (o->audio_unit, kAudioUnitProperty_MaximumFramesPerSlice,
481 kAudioUnitScope_Global, 0, &max_fps, &thissize);
482 if (err) { AUBIO_ERR("audio_unit: could not get maximum frames per slice property %d\n",
483 (int)err); goto fail; }
485 /* get hardware latency */
486 thissize = sizeof(latency);
487 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
488 &thissize, &latency);
489 if (err) { AUBIO_ERR("audio_unit: could not get hardware latency %d\n",
490 (int)err); goto fail; }
492 /* get input latency */
493 thissize = sizeof(input_latency);
494 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputLatency,
495 &thissize, &input_latency);
496 if (err) { AUBIO_ERR("audio_unit: could not get input latency %d\n",
497 (int)err); goto fail; }
499 /* get output harlatency */
500 thissize = sizeof(output_latency);
501 err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,
502 &thissize, &output_latency);
503 if (err) { AUBIO_ERR("audio_unit: could not get output latency %d\n",
504 (int)err); goto fail; }
506 AUBIO_MSG("audio_unit: I/O latency: %.2fms, %d frames, (%.2fms, %d frames in, %.2fms %d frames out)\n",
507 latency*1000., (sint_t)round(latency*samplerate),
508 input_latency*1000., (sint_t)ROUND(input_latency*samplerate),
509 output_latency*1000., (sint_t)ROUND(output_latency*samplerate));
515 sint_t aubio_audio_unit_start(aubio_audio_unit_t *o) {
519 // print some info about the current settings
520 aubio_audio_unit_get_info (o);
523 /* time to start the unit */
524 err = AudioOutputUnitStart (o->audio_unit);
525 if (err) { AUBIO_ERR("audio_unit: could not start unit (%d)\n", (int)err); }
529 sint_t aubio_audio_unit_stop(aubio_audio_unit_t *o)
531 if (o->audio_unit == NULL) return -1;
532 OSStatus err = AudioOutputUnitStop (o->audio_unit);
533 if (err) { AUBIO_WRN("audio_unit: failed stopping audio unit (%d)\n", (int)err); }
534 err = AudioUnitUninitialize (o->audio_unit);
535 if (err) { AUBIO_WRN("audio_unit: failed unitializing audio unit (%d)\n", (int)err); }
536 err = AudioSessionSetActive(false);
537 if (err) { AUBIO_WRN("audio_unit: failed stopping audio session (%d)\n", (int)err); }
541 uint_t aubio_audio_unit_set_callback(aubio_audio_unit_t *o,
542 aubio_device_callback_t callback, void *closure) {
543 o->callback = callback;
544 o->callback_closure = closure;
548 /* interruption listeners */
549 void audio_unit_interruption_listener(void *closure, UInt32 inInterruptionState)
552 aubio_audio_unit_t *o = (aubio_audio_unit_t *) closure;
553 AudioUnit this_unit = o->audio_unit;
555 if (inInterruptionState == kAudioSessionEndInterruption) {
556 AUBIO_WRN("audio_unit: session interruption ended\n");
557 err = AudioSessionSetActive(true);
559 AUBIO_ERR("audio_unit: could not make session active after interruption (%d)\n", (int)err);
562 err = AudioOutputUnitStart(this_unit);
564 AUBIO_ERR("audio_unit: failed starting unit (%d)\n", (int)err);
568 if (inInterruptionState == kAudioSessionBeginInterruption) {
569 AUBIO_WRN("audio_unit: session interruption started\n");
570 err = AudioOutputUnitStop(this_unit);
572 AUBIO_ERR("audio_unit: could not stop unit at interruption (%d)\n", (int)err);
575 err = AudioSessionSetActive(false);
577 AUBIO_ERR("audio_unit: could not make session inactive after interruption (%d)\n", (int)err);
585 UInt32 audio_unit_get_audio_session_category () {
586 UInt32 category, thissize;
587 thissize = sizeof(category);
588 OSStatus err = AudioSessionGetProperty(kAudioSessionProperty_AudioCategory,
589 &thissize, &category);
591 AUBIO_ERR("audio_unit: could not get audio category (%d)\n", (int)err);
594 if (category == kAudioSessionCategory_AmbientSound) {
595 AUBIO_MSG("audio_unit: session category is AmbiantSound\n");
596 } else if (category == kAudioSessionCategory_SoloAmbientSound) {
597 AUBIO_MSG("audio_unit: session category is SoloAmbiantSound\n");
598 } else if (category == kAudioSessionCategory_MediaPlayback) {
599 AUBIO_MSG("audio_unit: session category is MediaPlayback\n");
600 } else if (category == kAudioSessionCategory_RecordAudio) {
601 AUBIO_MSG("audio_unit: session category is RecordAudio\n");
602 } else if (category == kAudioSessionCategory_PlayAndRecord) {
603 AUBIO_MSG("audio_unit: session category is PlayAndRecord\n");
604 } else if (category == kAudioSessionCategory_AudioProcessing) {
605 AUBIO_MSG("audio_unit: session category is AudioProcessing\n");
610 OSStatus audio_unit_set_audio_session_category(bool has_input, bool verbose)
612 //if we have input, set the session category accordingly
616 category = kAudioSessionCategory_PlayAndRecord;
617 if (verbose) AUBIO_MSG("audio_unit: setting category to PlayAndRecord\n");
619 category = kAudioSessionCategory_MediaPlayback;
620 if (verbose) AUBIO_MSG("audio_unit: setting category to MediaPlayback\n");
622 err = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
623 sizeof(category), &category);
625 AUBIO_ERR("audio_unit: could not set audio category\n");
629 UInt32 allowMixing = 1;
630 AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,
631 sizeof (allowMixing), &allowMixing);
633 AUBIO_ERR("audio_unit: could not set audio session to mix with others\n");
639 void audio_unit_check_audio_route(aubio_audio_unit_t *o) {
640 CFStringRef currentRoute;
641 UInt32 val, thissize = sizeof(currentRoute);
642 OSStatus err = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &thissize, ¤tRoute);
643 if (err) { AUBIO_ERR("audio_unit: could not get current route\n"); goto fail; }
645 char *route = (char *)CFStringGetCStringPtr ( currentRoute, kCFStringEncodingUTF8);
648 route = calloc(bufferSize, sizeof(char));
649 CFStringGetCString ( currentRoute, route, bufferSize,
650 kCFStringEncodingUTF8);
653 AUBIO_MSG ("audio_unit: current route is %s\n", route);
658 if( CFStringCompare( currentRoute, CFSTR("Headset"), 0 ) == kCFCompareEqualTo ) {
659 val = kAudioSessionOverrideAudioRoute_None;
660 } else if( CFStringCompare( currentRoute, CFSTR("Receiver" ), 0 ) == kCFCompareEqualTo ) {
661 val = kAudioSessionOverrideAudioRoute_Speaker;
662 } else if( CFStringCompare( currentRoute, CFSTR("ReceiverAndMicrophone" ), 0 ) == kCFCompareEqualTo ) {
663 val = kAudioSessionOverrideAudioRoute_Speaker;
664 } else if( CFStringCompare( currentRoute, CFSTR("SpeakerAndMicrophone" ), 0 ) == kCFCompareEqualTo ) {
665 val = kAudioSessionOverrideAudioRoute_Speaker;
666 } else if( CFStringCompare( currentRoute, CFSTR("HeadphonesAndMicrophone" ), 0 ) == kCFCompareEqualTo ) {
667 val = kAudioSessionOverrideAudioRoute_None;
668 } else if( CFStringCompare( currentRoute, CFSTR("HeadsetInOut" ), 0 ) == kCFCompareEqualTo ) {
669 val = kAudioSessionOverrideAudioRoute_None;
671 val = kAudioSessionOverrideAudioRoute_None;
674 o->input_enabled = true;
675 if (val == kAudioSessionOverrideAudioRoute_Speaker) {
676 if (o->prevent_feedback) {
677 o->input_enabled = false;
679 AUBIO_MSG ("audio_unit: disabling input to avoid feedback\n");
682 AUBIO_WRN ("audio_unit: input not disabled as prevent_feedback set to 0, risking feedback\n");
686 err = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute,
687 sizeof(UInt32), &val);
688 if (err) { AUBIO_ERR("audio_unit: could not set session OverrideAudioRoute to Speaker\n"); }
693 if ( currentRoute ) free((void*)currentRoute);
699 audio_unit_get_route_change_reason(CFDictionaryRef routeChangeDic) {
700 CFNumberRef routeChangeReasonRef = (CFNumberRef)CFDictionaryGetValue(routeChangeDic,
701 CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
702 SInt32 change_reason_number;
703 CFNumberGetValue ( routeChangeReasonRef, kCFNumberSInt32Type, &change_reason_number);
704 switch (change_reason_number) {
705 case kAudioSessionRouteChangeReason_NewDeviceAvailable:
706 AUBIO_MSG("audio_unit: route changed to NewDeviceAvailable\n");
708 case kAudioSessionRouteChangeReason_OldDeviceUnavailable:
709 AUBIO_MSG("audio_unit: route changed to OldDeviceUnavailable\n");
711 case kAudioSessionRouteChangeReason_CategoryChange:
712 AUBIO_MSG("audio_unit: route changed to CategoryChange\n");
713 audio_unit_get_audio_session_category();
715 case kAudioSessionRouteChangeReason_Override:
716 AUBIO_MSG("audio_unit: route changed to Override\n");
718 case kAudioSessionRouteChangeReason_WakeFromSleep:
719 AUBIO_MSG("audio_unit: route changed to WakeFromSleep\n");
721 case kAudioSessionRouteChangeReason_NoSuitableRouteForCategory:
722 AUBIO_MSG("audio_unit: route changed to NoSuitableRouteForCategory\n");
724 case kAudioSessionRouteChangeReason_Unknown:
726 AUBIO_ERR("audio_unit: route changed for an unknown reason!?\n");
729 return change_reason_number;
732 /* route change listeners */
734 audio_unit_route_change_listener(void *closure, AudioSessionPropertyID inID,
735 UInt32 dataSize, const void *inData)
738 UNUSED aubio_audio_unit_t *o = (aubio_audio_unit_t *)closure;
739 UNUSED UInt32 size = dataSize;
740 if (inID == kAudioSessionProperty_AudioRouteChange) {
743 //AudioUnit audio_unit = o->audio_unit;
746 // show route change reason
747 audio_unit_get_route_change_reason((CFDictionaryRef)inData);
750 // check current audio route, changing it to prevent feedback as needed
751 audio_unit_check_audio_route(o);
754 // print some info about the current settings
755 aubio_audio_unit_get_info(o);
763 uint_t del_aubio_audio_unit(aubio_audio_unit_t *o)
766 err = aubio_audio_unit_stop(o);
767 if (o->au_ios_inbuf) free(o->au_ios_inbuf);
768 o->au_ios_inbuf = NULL;
769 if (o->au_ios_outbuf) free(o->au_ios_outbuf);
770 o->au_ios_outbuf = NULL;
771 del_fmat (o->input_frames);
772 del_fmat (o->output_frames);
773 o->audio_unit = NULL;
777 #endif /* TARGET_OS_IPHONE */