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/>.
21 #include "aubio_priv.h"
22 #ifdef HAVE_AUDIO_UNIT
26 #include "io/audio_unit.h"
28 #include <AudioToolbox/AudioToolbox.h>
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
35 #define INT_TO_FLOAT 3.0517578125e-05 // 1. / 32768.
37 struct _aubio_audio_unit_t {
41 uint_t sw_input_channels;
42 uint_t sw_output_channels;
43 uint_t hw_output_channels;
44 uint_t hw_input_channels;
48 fmat_t *output_frames;
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
55 bool prevent_feedback;
59 AURenderCallbackStruct au_ios_cb_struct;
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);
68 static int aubio_audio_unit_blocking(aubio_audio_unit_t *o);
70 static void audio_unit_check_audio_route(aubio_audio_unit_t *o);
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 ();
78 aubio_audio_unit_t * new_aubio_audio_unit(uint_t samplerate,
79 uint_t sw_input_channels, uint_t sw_output_channels,
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;
95 o->input_enabled = true;
96 o->prevent_feedback = 1;
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);
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);
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);
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);
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);
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);
130 sint_t aubio_audio_unit_set_preferred_latency (aubio_audio_unit_t *o, smpl_t latency)
132 o->latency = latency;
136 sint_t aubio_audio_unit_set_prevent_feedback (aubio_audio_unit_t *o, uint_t prevent_feedback)
138 o->prevent_feedback = prevent_feedback;
142 sint_t aubio_audio_unit_set_verbose (aubio_audio_unit_t *o, uint_t verbose)
144 o->verbose = verbose;
149 sint_t aubio_audio_unit_init (aubio_audio_unit_t *o)
151 OSStatus err = noErr;
152 Float32 latency = o->latency;
153 Float64 samplerate = (Float64)o->samplerate;
155 o->au_ios_cb_struct.inputProc = aubio_audio_unit_process;
156 o->au_ios_cb_struct.inputProcRefCon = o;
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; }
162 audio_unit_set_audio_session_category(o->input_enabled, o->verbose);
163 audio_unit_check_audio_route(o);
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; }
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; }
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"); }
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;
190 AudioStreamBasicDescription audioFormat;
192 /* look for a component that match the description */
193 AudioComponent comp = AudioComponentFindNext(NULL, &desc);
195 /* create the audio component */
196 AudioUnit *audio_unit = &(o->audio_unit);
198 err = AudioComponentInstanceNew(comp, &(o->audio_unit));
199 if (err) { AUBIO_ERR("audio_unit: failed creating the audio unit\n"); goto fail; }
203 err = AudioUnitSetProperty (*audio_unit, kAudioOutputUnitProperty_EnableIO,
204 kAudioUnitScope_Input, 1, &enabled, sizeof(enabled));
206 AUBIO_ERR("audio_unit: failed enabling input of audio unit\n");
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));
215 AUBIO_ERR("audio_unit: could not set maximum frames per slice property (%d)\n", (int)err);
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; }
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; }
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);
242 audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame = sizeof(SInt32);
243 audioFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
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; }
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; }
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; }
274 /* perform function */
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)
280 UInt32 b; int err = 0;
281 aubio_audio_unit_t *o = (aubio_audio_unit_t *)closure;
282 AudioUnit thisUnit = o->audio_unit;
284 if (o->input_enabled) {
285 err = AudioUnitRender(thisUnit, action_flags, time_stamp, 1,
286 inNumber_frames, input_output);
288 AUBIO_ERR("audio_unit: error performing AudioUnitRender (%d)\n", err);
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;
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);
302 if (o->total_frames >= (signed)number_frames) {
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));
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));
331 o->au_ios_start += number_frames;
332 o->au_ios_start %= AU_IOS_MAX_FRAMES;
333 o->total_frames -= number_frames;
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);
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);
356 int aubio_audio_unit_blocking(aubio_audio_unit_t *o)
358 uint_t sw_output_channels, sw_input_channels,
359 hw_output_channels, hw_input_channels,
361 if (! o->callback) return -1;
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;
371 if (!sw_input_channels && !sw_output_channels) goto fail;
374 AUBIO_WRN("audio_unit: dio error %d\n", o->total_frames);
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++) {
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
388 (smpl_t) o->au_ios_inbuf[0 + (j + o->au_ios_end) * hw_input_channels]
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++) {
402 o->callback(o->callback_closure, o->input_frames, o->output_frames);
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);
415 o->au_ios_end += blocksize;
416 o->au_ios_end %= AU_IOS_MAX_FRAMES;
417 o->total_frames += blocksize;
422 AUBIO_ERR("audio_unit: callback() failed\n");
423 o->total_frames += AU_IOS_MAX_FRAMES;
427 sint_t aubio_audio_unit_get_info (aubio_audio_unit_t *o)
429 UInt32 thissize, input_hw_channels, output_hw_channels, max_fps;
430 Float32 latency, input_latency, output_latency, input_hw_volume, output_hw_volume;
434 // Show some info about the opened unit
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; }
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; }
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; }
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; }
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; }
471 AUBIO_MSG("audio_unit: opened at %.0fHz, sw channels %din/%dout, hw channels %din/%dout, hw vol %.2fin/%.2fout\n",
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);
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; }
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; }
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; }
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; }
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));
514 sint_t aubio_audio_unit_start(aubio_audio_unit_t *o) {
518 // print some info about the current settings
519 aubio_audio_unit_get_info (o);
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); }
528 sint_t aubio_audio_unit_stop(aubio_audio_unit_t *o)
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); }
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;
547 /* interruption listeners */
548 void audio_unit_interruption_listener(void *closure, UInt32 inInterruptionState)
551 aubio_audio_unit_t *o = (aubio_audio_unit_t *) closure;
552 AudioUnit this_unit = o->audio_unit;
554 if (inInterruptionState == kAudioSessionEndInterruption) {
555 AUBIO_WRN("audio_unit: session interruption ended\n");
556 err = AudioSessionSetActive(true);
558 AUBIO_ERR("audio_unit: could not make session active after interruption (%d)\n", (int)err);
561 err = AudioOutputUnitStart(this_unit);
563 AUBIO_ERR("audio_unit: failed starting unit (%d)\n", (int)err);
567 if (inInterruptionState == kAudioSessionBeginInterruption) {
568 AUBIO_WRN("audio_unit: session interruption started\n");
569 err = AudioOutputUnitStop(this_unit);
571 AUBIO_ERR("audio_unit: could not stop unit at interruption (%d)\n", (int)err);
574 err = AudioSessionSetActive(false);
576 AUBIO_ERR("audio_unit: could not make session inactive after interruption (%d)\n", (int)err);
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);
590 AUBIO_ERR("audio_unit: could not get audio category (%d)\n", (int)err);
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");
609 OSStatus audio_unit_set_audio_session_category(bool has_input, bool verbose)
611 //if we have input, set the session category accordingly
615 category = kAudioSessionCategory_PlayAndRecord;
616 if (verbose) AUBIO_MSG("audio_unit: setting category to PlayAndRecord\n");
618 category = kAudioSessionCategory_MediaPlayback;
619 if (verbose) AUBIO_MSG("audio_unit: setting category to MediaPlayback\n");
621 err = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
622 sizeof(category), &category);
624 AUBIO_ERR("audio_unit: could not set audio category\n");
628 UInt32 allowMixing = 1;
629 AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,
630 sizeof (allowMixing), &allowMixing);
632 AUBIO_ERR("audio_unit: could not set audio session to mix with others\n");
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; }
644 char *route = (char *)CFStringGetCStringPtr ( currentRoute, kCFStringEncodingUTF8);
647 route = calloc(bufferSize, sizeof(char));
648 CFStringGetCString ( currentRoute, route, bufferSize,
649 kCFStringEncodingUTF8);
652 AUBIO_MSG ("audio_unit: current route is %s\n", route);
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;
670 val = kAudioSessionOverrideAudioRoute_None;
673 o->input_enabled = true;
674 if (val == kAudioSessionOverrideAudioRoute_Speaker) {
675 if (o->prevent_feedback) {
676 o->input_enabled = false;
678 AUBIO_MSG ("audio_unit: disabling input to avoid feedback\n");
681 AUBIO_WRN ("audio_unit: input not disabled as prevent_feedback set to 0, risking feedback\n");
685 err = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute,
686 sizeof(UInt32), &val);
687 if (err) { AUBIO_ERR("audio_unit: could not set session OverrideAudioRoute to Speaker\n"); }
692 if ( currentRoute ) free((void*)currentRoute);
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");
707 case kAudioSessionRouteChangeReason_OldDeviceUnavailable:
708 AUBIO_MSG("audio_unit: route changed to OldDeviceUnavailable\n");
710 case kAudioSessionRouteChangeReason_CategoryChange:
711 AUBIO_MSG("audio_unit: route changed to CategoryChange\n");
712 audio_unit_get_audio_session_category();
714 case kAudioSessionRouteChangeReason_Override:
715 AUBIO_MSG("audio_unit: route changed to Override\n");
717 case kAudioSessionRouteChangeReason_WakeFromSleep:
718 AUBIO_MSG("audio_unit: route changed to WakeFromSleep\n");
720 case kAudioSessionRouteChangeReason_NoSuitableRouteForCategory:
721 AUBIO_MSG("audio_unit: route changed to NoSuitableRouteForCategory\n");
723 case kAudioSessionRouteChangeReason_Unknown:
725 AUBIO_ERR("audio_unit: route changed for an unknown reason!?\n");
728 return change_reason_number;
731 /* route change listeners */
733 audio_unit_route_change_listener(void *closure, AudioSessionPropertyID inID,
734 UInt32 dataSize, const void *inData)
737 UNUSED aubio_audio_unit_t *o = (aubio_audio_unit_t *)closure;
738 UNUSED UInt32 size = dataSize;
739 if (inID == kAudioSessionProperty_AudioRouteChange) {
742 //AudioUnit audio_unit = o->audio_unit;
745 // show route change reason
746 audio_unit_get_route_change_reason((CFDictionaryRef)inData);
749 // check current audio route, changing it to prevent feedback as needed
750 audio_unit_check_audio_route(o);
753 // print some info about the current settings
754 aubio_audio_unit_get_info(o);
762 uint_t del_aubio_audio_unit(aubio_audio_unit_t *o)
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;
776 #endif /* HAVE_AUDIO_UNIT */