src/io/audio_unit.c: added ios driver
[aubio.git] / src / io / audio_unit.c
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 "config.h"
22 #ifdef TARGET_OS_IPHONE
23 #include "aubio_priv.h"
24
25 #include "fvec.h"
26 #include "fmat.h"
27 #include "io/audio_unit.h"
28
29 #include <AudioToolbox/AudioToolbox.h>
30
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
34 #define MAX_FPS 4096
35
36 #define INT_TO_FLOAT 3.0517578125e-05 // 1. / 32768.
37
38 struct _aubio_audio_unit_t {
39   AudioUnit audio_unit;
40   uint_t samplerate;
41   uint_t blocksize;
42   uint_t sw_input_channels;
43   uint_t sw_output_channels;
44   uint_t hw_output_channels;
45   uint_t hw_input_channels;
46   Float32 latency;
47   sint_t total_frames;
48   fmat_t *input_frames;
49   fmat_t *output_frames;
50   SInt16 *au_ios_inbuf;
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
55   bool input_enabled;
56   bool prevent_feedback;
57   bool verbose;
58   int au_ios_start;
59   int au_ios_end;
60   AURenderCallbackStruct au_ios_cb_struct;
61 };
62
63
64 static OSStatus
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);
68
69 static int aubio_audio_unit_blocking(aubio_audio_unit_t *o);
70
71 static void audio_unit_check_audio_route(aubio_audio_unit_t *o);
72
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 ();
78
79 aubio_audio_unit_t * new_aubio_audio_unit(uint_t samplerate,
80     uint_t sw_input_channels, uint_t sw_output_channels,
81     uint_t blocksize)
82 {
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;
91
92   o->au_ios_start = 0;
93   o->au_ios_end = 0;
94
95   o->verbose = 0;
96   o->input_enabled = true;
97   o->prevent_feedback = 1;
98   o->dio_error = 0;
99
100   o->total_frames = 0;
101
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);
105
106   /* the floats coming from and to the device callback */
107   o->output_frames = new_fmat(blocksize, sw_output_channels);
108   o->input_frames = new_fmat(blocksize, sw_input_channels);
109
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",
113         o->hw_output_channels, o->output_frames->height);
114   }
115   if ( o->blocksize != o->output_frames->length ) {
116     AUBIO_ERR ("got blocksize = %d, but output_frames has length %d",
117         o->blocksize, o->output_frames->length);
118   }
119   if ( o->hw_input_channels != o->input_frames->height ) {
120     AUBIO_ERR ("got hw_input_channels = %d, but input_frames has %d rows",
121         o->hw_input_channels, o->input_frames->height);
122   }
123   if ( o->blocksize != o->input_frames->length ) {
124     AUBIO_ERR ("got blocksize = %d, but input_frames has length %d",
125         o->blocksize, o->input_frames->length);
126   }
127
128   return o;
129 }
130
131 sint_t aubio_audio_unit_set_preferred_latency (aubio_audio_unit_t *o, smpl_t latency)
132 {
133   o->latency = latency;
134   return 0;
135 }
136
137 sint_t aubio_audio_unit_set_prevent_feedback (aubio_audio_unit_t *o, uint_t prevent_feedback)
138 {
139   o->prevent_feedback = prevent_feedback;
140   return 0;
141 }
142
143 sint_t aubio_audio_unit_set_verbose (aubio_audio_unit_t *o, uint_t verbose)
144 {
145   o->verbose = verbose;
146   return 0;
147 }
148
149
150 sint_t aubio_audio_unit_init (aubio_audio_unit_t *o)
151 {
152   OSStatus err = noErr;
153   Float32 latency = o->latency;
154   Float64 samplerate = (Float64)o->samplerate;
155
156   o->au_ios_cb_struct.inputProc = aubio_audio_unit_process;
157   o->au_ios_cb_struct.inputProcRefCon = o;
158
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 (%ld)", err); goto fail; }
162
163   audio_unit_set_audio_session_category(o->input_enabled, o->verbose);
164   audio_unit_check_audio_route(o);
165
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 (%ld)", err); goto fail; }
170
171   /* set latency */
172   err = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
173       sizeof(latency), &latency);
174   if (err) { AUBIO_ERR("audio_unit: could not set preferred latency (%ld)", err); goto fail; }
175
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"); }
181 #endif
182
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;
190
191   AudioStreamBasicDescription audioFormat;
192
193   /* look for a component that match the description */
194   AudioComponent comp = AudioComponentFindNext(NULL, &desc);
195
196   /* create the audio component */
197   AudioUnit *audio_unit = &(o->audio_unit);
198
199   err = AudioComponentInstanceNew(comp, &(o->audio_unit));
200   if (err) { AUBIO_ERR("audio_unit: failed creating the audio unit"); goto fail; }
201
202   /* enable IO */
203   UInt32 enabled = 1;
204   err = AudioUnitSetProperty (*audio_unit, kAudioOutputUnitProperty_EnableIO,
205       kAudioUnitScope_Input, 1, &enabled, sizeof(enabled));
206   if (err) {
207     AUBIO_ERR("audio_unit: failed enabling input of audio unit");
208     goto fail;
209   }
210
211   /* set max fps */
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));
215   if (err) {
216     AUBIO_ERR("audio_unit: could not set maximum frames per slice property (%ld)", err);
217     goto fail;
218   }
219
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"); goto fail; }
223
224 #if 0
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"); 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"); goto fail; }
231 #endif
232
233   audioFormat.mSampleRate = (Float64)samplerate;
234   audioFormat.mChannelsPerFrame = 2;
235   audioFormat.mFormatID = kAudioFormatLinearPCM;
236   audioFormat.mFormatFlags = kAudioFormatFlagsCanonical;
237   audioFormat.mFramesPerPacket = 1;
238   audioFormat.mBitsPerChannel = 8 * sizeof(AudioSampleType);
239 #if 1  // interleaving
240   audioFormat.mBytesPerFrame = 2 * sizeof(AudioSampleType);
241   audioFormat.mBytesPerPacket = 2 * sizeof(AudioSampleType);
242 #else
243   audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame = sizeof(AudioUnitSampleType);
244   audioFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
245 #endif
246
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"); 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"); goto fail; }
253
254 #if 0
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", (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", (int)err); goto fail; }
263 #endif
264
265   /* time to initialize the unit */
266   err = AudioUnitInitialize(*audio_unit);
267   if (err) { AUBIO_ERR("audio_unit: failed initializing audio, err: %d", (int)err); goto fail; }
268
269   return 0;
270
271 fail:
272   return err;
273 }
274
275 /* perform function */
276 OSStatus
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)
280 {
281   UInt32 b; int err = 0;
282   aubio_audio_unit_t *o = (aubio_audio_unit_t *)closure;
283   AudioUnit thisUnit = o->audio_unit;
284
285   if (o->input_enabled) {
286     err = AudioUnitRender(thisUnit, action_flags, time_stamp, 1,
287         inNumber_frames, input_output);
288     if (err) {
289       AUBIO_ERR("audio_unit: error performing AudioUnitRender (%d)", err);
290       return err;
291     }
292   }
293
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;
296
297   // FIXME find out why this happens
298   if (number_frames < 10) {
299     AUBIO_ERR("audio_unit: got number_frames %d", (int)number_frames);
300     return -1;
301   }
302
303   if (o->total_frames >= (signed)number_frames) {
304
305     SInt16 *data;
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));
320       }
321     } else {
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));
330       }
331     }
332     o->au_ios_start += number_frames;
333     o->au_ios_start %= AU_IOS_MAX_FRAMES;
334     o->total_frames -= number_frames;
335
336 #if 1
337   } else {
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);
342     }
343     //total_frames = 0;
344 #endif
345   }
346
347   // now call callback
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);
352   }
353
354   return err;
355 }
356
357 int aubio_audio_unit_blocking(aubio_audio_unit_t *o)
358 {
359   uint_t sw_output_channels, sw_input_channels,
360          hw_output_channels, hw_input_channels,
361          i, j, blocksize;
362   if (! o->callback) return -1;
363
364   smpl_t ** tbuf;
365
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;
371
372   if (!sw_input_channels && !sw_output_channels) goto fail;
373
374   if (o->dio_error) {
375     AUBIO_WRN("audio_unit: dio error %d", o->total_frames);
376     o->dio_error = 0;
377   }
378
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++) {
385           //tbuf[i][j] =
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
388           tbuf[i][j] =
389             (smpl_t) o->au_ios_inbuf[0 + (j + o->au_ios_end) * hw_input_channels]
390             * INT_TO_FLOAT;
391         }
392       }
393     } else {
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++) {
397           tbuf[i][j] = 0;
398         }
399       }
400     }
401   }
402
403   o->callback(o->callback_closure, o->input_frames, o->output_frames);
404
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);
413     }
414   }
415
416   o->au_ios_end += blocksize;
417   o->au_ios_end %= AU_IOS_MAX_FRAMES;
418   o->total_frames += blocksize;
419
420   return 0;
421
422 fail:
423   AUBIO_ERR("audio_unit: callback() failed");
424   o->total_frames += AU_IOS_MAX_FRAMES;
425   return 1;
426 }
427
428 sint_t aubio_audio_unit_get_info (aubio_audio_unit_t *o)
429 {
430   UInt32 thissize, input_hw_channels, output_hw_channels, max_fps;
431   Float32 latency, input_latency, output_latency, input_hw_volume, output_hw_volume;
432   Float64 samplerate;
433   OSStatus err = 0;
434
435   // Show some info about the opened unit
436
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 (%ld)",
442       err); goto fail; }
443
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 (%ld)",
449       err); goto fail; }
450
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 (%ld)",
456       err); goto fail; }
457
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 (%ld)",
463       err); goto fail; }
464
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 (%ld)",
470       err); goto fail; }
471
472   AUBIO_MSG("audio_unit: opened at %.0fHz, sw channels %din/%dout, hw channels %ldin/%ldout, hw vol %.2fin/%.2fout",
473       samplerate,
474       o->sw_input_channels, o->sw_output_channels,
475       input_hw_channels, output_hw_channels,
476       input_hw_volume, output_hw_volume);
477
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 %ld",
483       err); goto fail; }
484
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 %ld",
490       err); goto fail; }
491
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 %ld",
497       err); goto fail; }
498
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 %ld",
504       err); goto fail; }
505
506   AUBIO_MSG("audio_unit: I/O latency: %.2fms, %d frames, (%.2fms, %d frames in, %.2fms %d frames out)",
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));
510
511 fail:
512   return err;
513 }
514
515 sint_t aubio_audio_unit_start(aubio_audio_unit_t *o) {
516   OSStatus err = 0;
517
518   if (o->verbose) {
519     // print some info about the current settings
520     aubio_audio_unit_get_info (o);
521   }
522
523   /* time to start the unit */
524   err = AudioOutputUnitStart (o->audio_unit);
525   if (err) { AUBIO_ERR("audio_unit: could not start unit (%ld)", err); }
526   return err;
527 }
528
529 sint_t aubio_audio_unit_stop(aubio_audio_unit_t *o)
530 {
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 (%ld)", err); }
534   err = AudioUnitUninitialize (o->audio_unit);
535   if (err) { AUBIO_WRN("audio_unit: failed unitializing audio unit (%ld)", err); }
536   err = AudioSessionSetActive(false);
537   if (err) { AUBIO_WRN("audio_unit: failed stopping audio session (%ld)", err); }
538   return err;
539 }
540
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;
545   return 0;
546 }
547
548 /* interruption listeners */
549 void audio_unit_interruption_listener(void *closure, UInt32 inInterruptionState)
550 {
551   OSStatus err = 0;
552   aubio_audio_unit_t *o = (aubio_audio_unit_t *) closure;
553   AudioUnit this_unit = o->audio_unit;
554
555   if (inInterruptionState == kAudioSessionEndInterruption) {
556     AUBIO_WRN("audio_unit: session interruption ended");
557     err = AudioSessionSetActive(true);
558     if (err) {
559       AUBIO_ERR("audio_unit: could not make session active after interruption (%ld)", err);
560       goto fail;
561     }
562     err = AudioOutputUnitStart(this_unit);
563     if (err) {
564       AUBIO_ERR("audio_unit: failed starting unit (%ld)", err);
565       goto fail;
566     }
567   }
568   if (inInterruptionState == kAudioSessionBeginInterruption) {
569     AUBIO_WRN("audio_unit: session interruption started");
570     err = AudioOutputUnitStop(this_unit);
571     if (err) {
572       AUBIO_ERR("audio_unit: could not stop unit at interruption (%ld)", err);
573       goto fail;
574     }
575     err = AudioSessionSetActive(false);
576     if (err) {
577       AUBIO_ERR("audio_unit: could not make session inactive after interruption (%ld)", err);
578       goto fail;
579     }
580   }
581 fail:
582   return;
583 }
584
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);
590   if (err) {
591     AUBIO_ERR("audio_unit: could not get audio category (%ld)", err);
592     return err;
593   }
594   if (category == kAudioSessionCategory_AmbientSound) {
595     AUBIO_MSG("audio_unit: session category is AmbiantSound");
596   } else if (category == kAudioSessionCategory_SoloAmbientSound) {
597     AUBIO_MSG("audio_unit: session category is SoloAmbiantSound");
598   } else if (category == kAudioSessionCategory_MediaPlayback) {
599     AUBIO_MSG("audio_unit: session category is MediaPlayback");
600   } else if (category == kAudioSessionCategory_RecordAudio) {
601     AUBIO_MSG("audio_unit: session category is RecordAudio");
602   } else if (category == kAudioSessionCategory_PlayAndRecord) {
603     AUBIO_MSG("audio_unit: session category is PlayAndRecord");
604   } else if (category == kAudioSessionCategory_AudioProcessing) {
605     AUBIO_MSG("audio_unit: session category is AudioProcessing");
606   }
607   return category;
608 }
609
610 OSStatus audio_unit_set_audio_session_category(bool has_input, bool verbose)
611 {
612   //if we have input, set the session category accordingly
613   OSStatus err = 0;
614   UInt32 category;
615   if (has_input) {
616     category = kAudioSessionCategory_PlayAndRecord;
617     if (verbose) AUBIO_MSG("audio_unit: setting category to PlayAndRecord");
618   } else {
619     category = kAudioSessionCategory_MediaPlayback;
620     if (verbose) AUBIO_MSG("audio_unit: setting category to MediaPlayback");
621   }
622   err = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
623       sizeof(category), &category);
624   if (err) {
625     AUBIO_ERR("audio_unit: could not set audio category");
626   }
627
628   // Audiob.us style
629   UInt32 allowMixing = 1;
630   AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,
631       sizeof (allowMixing), &allowMixing);
632   if (err) {
633     AUBIO_ERR("audio_unit: could not set audio session to mix with others");
634   }
635
636   return err;
637 }
638
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, &currentRoute);
643   if (err) { AUBIO_ERR("audio_unit: could not get current route"); goto fail; }
644   else {
645     char *route = (char *)CFStringGetCStringPtr ( currentRoute, kCFStringEncodingUTF8);
646     if (route == NULL) {
647       int bufferSize = 25;
648       route = calloc(bufferSize, sizeof(char));
649       CFStringGetCString ( currentRoute, route, bufferSize,
650           kCFStringEncodingUTF8);
651     }
652     if (o->verbose) {
653       AUBIO_MSG ("audio_unit: current route is %s", route);
654     }
655     free(route);
656   }
657   if( currentRoute ) {
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;
670     } else {
671       val = kAudioSessionOverrideAudioRoute_None;
672     }
673
674     o->input_enabled = true;
675     if (val == kAudioSessionOverrideAudioRoute_Speaker) {
676       if (o->prevent_feedback) {
677         o->input_enabled = false;
678         if (o->verbose) {
679           AUBIO_MSG ("audio_unit: disabling input to avoid feedback");
680         }
681       } else {
682         AUBIO_WRN ("audio_unit: input not disabled as prevent_feedback set to 0, risking feedback");
683       }
684     }
685
686     err = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute,
687         sizeof(UInt32), &val);
688     if (err) { AUBIO_ERR("audio_unit: could not set session OverrideAudioRoute to Speaker"); }
689
690   }
691
692 fail:
693   if ( currentRoute ) free((void*)currentRoute);
694   return;
695
696 }
697
698 SInt32
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");
707       break;
708     case kAudioSessionRouteChangeReason_OldDeviceUnavailable:
709       AUBIO_MSG("audio_unit: route changed to OldDeviceUnavailable");
710       break;
711     case kAudioSessionRouteChangeReason_CategoryChange:
712       AUBIO_MSG("audio_unit: route changed to CategoryChange");
713       audio_unit_get_audio_session_category();
714       break;
715     case kAudioSessionRouteChangeReason_Override:
716       AUBIO_MSG("audio_unit: route changed to Override");
717       break;
718     case kAudioSessionRouteChangeReason_WakeFromSleep:
719       AUBIO_MSG("audio_unit: route changed to WakeFromSleep");
720       break;
721     case kAudioSessionRouteChangeReason_NoSuitableRouteForCategory:
722       AUBIO_MSG("audio_unit: route changed to NoSuitableRouteForCategory");
723       break;
724     case kAudioSessionRouteChangeReason_Unknown:
725     default:
726       AUBIO_ERR("audio_unit: route changed for an unknown reason!?");
727       break;
728   }
729   return change_reason_number;
730 }
731
732 /* route change listeners */
733 void
734 audio_unit_route_change_listener(void *closure, AudioSessionPropertyID inID,
735     UInt32 dataSize, const void *inData)
736 {
737
738   UNUSED aubio_audio_unit_t *o = (aubio_audio_unit_t *)closure;
739   UNUSED UInt32 size = dataSize;
740   if (inID == kAudioSessionProperty_AudioRouteChange) {
741
742     // OSStatus err = 0;
743     //AudioUnit audio_unit = o->audio_unit;
744
745     if (o->verbose) {
746       // show route change reason
747       audio_unit_get_route_change_reason((CFDictionaryRef)inData);
748     }
749
750     // check current audio route, changing it to prevent feedback as needed
751     audio_unit_check_audio_route(o);
752
753     if (o->verbose) {
754       // print some info about the current settings
755       aubio_audio_unit_get_info(o);
756     }
757
758   }
759
760 }
761
762 /* delete object */
763 uint_t del_aubio_audio_unit(aubio_audio_unit_t *o)
764 {
765   int err = 0;
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;
774   return (int)err;
775 }
776
777 #endif /* TARGET_OS_IPHONE */