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, &currentRoute);
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