source: src/io/audio_unit.c @ 8fa71eb

sampler
Last change on this file since 8fa71eb was cc81763, checked in by Paul Brossier <piem@piem.org>, 9 years ago

wscript: add option to not build with CoreAudio/AudioToolbox?

  • Property mode set to 100644
File size: 27.6 KB
Line 
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 HAVE_AUDIO_UNIT
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
38struct _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
64static OSStatus
65aubio_audio_unit_process(void *closure, AudioUnitRenderActionFlags * action_flags,
66    const AudioTimeStamp * time_stamp, UInt32 bus_number, UInt32 inNumber_frames,
67    AudioBufferList * input_output);
68
69static int aubio_audio_unit_blocking(aubio_audio_unit_t *o);
70
71static void audio_unit_check_audio_route(aubio_audio_unit_t *o);
72
73static void audio_unit_interruption_listener(void *closure, UInt32 inInterruptionState);
74static void audio_unit_route_change_listener(void *closure, AudioSessionPropertyID
75    inID, UInt32 dataSize, const void *inData);
76static OSStatus audio_unit_set_audio_session_category(bool has_input, bool verbose);
77static UInt32 audio_unit_get_audio_session_category ();
78
79aubio_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(sw_output_channels, blocksize);
108  o->input_frames = new_fmat(sw_input_channels, blocksize);
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\n",
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\n",
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\n",
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\n",
125        o->blocksize, o->input_frames->length);
126  }
127
128  return o;
129}
130
131sint_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
137sint_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
143sint_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
150sint_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 (%d)\n", (int)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 (%d)\n", (int)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 (%d)\n", (int)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\n"); }
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\n"); 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\n");
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 (%d)\n", (int)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\n"); 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\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; }
231#endif
232
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);
242#else
243  audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame = sizeof(SInt32);
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\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; }
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\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; }
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\n", (int)err); goto fail; }
268
269  return 0;
270
271fail:
272  return err;
273}
274
275/* perform function */
276OSStatus
277aubio_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)\n", 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\n", (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
357int 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\n", 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
422fail:
423  AUBIO_ERR("audio_unit: callback() failed\n");
424  o->total_frames += AU_IOS_MAX_FRAMES;
425  return 1;
426}
427
428sint_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 (%d)\n",
442      (int)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 (%d)\n",
449      (int)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 (%d)\n",
456      (int)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 (%d)\n",
463      (int)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 (%d)\n",
470      (int)err); goto fail; }
471
472  AUBIO_MSG("audio_unit: opened at %.0fHz, sw channels %din/%dout, hw channels %din/%dout, hw vol %.2fin/%.2fout\n",
473      samplerate,
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);
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 %d\n",
483      (int)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 %d\n",
490      (int)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 %d\n",
497      (int)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 %d\n",
504      (int)err); goto fail; }
505
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));
510
511fail:
512  return err;
513}
514
515sint_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 (%d)\n", (int)err); }
526  return err;
527}
528
529sint_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 (%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); }
538  return err;
539}
540
541uint_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 */
549void 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\n");
557    err = AudioSessionSetActive(true);
558    if (err) {
559      AUBIO_ERR("audio_unit: could not make session active after interruption (%d)\n", (int)err);
560      goto fail;
561    }
562    err = AudioOutputUnitStart(this_unit);
563    if (err) {
564      AUBIO_ERR("audio_unit: failed starting unit (%d)\n", (int)err);
565      goto fail;
566    }
567  }
568  if (inInterruptionState == kAudioSessionBeginInterruption) {
569    AUBIO_WRN("audio_unit: session interruption started\n");
570    err = AudioOutputUnitStop(this_unit);
571    if (err) {
572      AUBIO_ERR("audio_unit: could not stop unit at interruption (%d)\n", (int)err);
573      goto fail;
574    }
575    err = AudioSessionSetActive(false);
576    if (err) {
577      AUBIO_ERR("audio_unit: could not make session inactive after interruption (%d)\n", (int)err);
578      goto fail;
579    }
580  }
581fail:
582  return;
583}
584
585UInt32 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 (%d)\n", (int)err);
592    return err;
593  }
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");
606  }
607  return category;
608}
609
610OSStatus 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\n");
618  } else {
619    category = kAudioSessionCategory_MediaPlayback;
620    if (verbose) AUBIO_MSG("audio_unit: setting category to MediaPlayback\n");
621  }
622  err = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
623      sizeof(category), &category);
624  if (err) {
625    AUBIO_ERR("audio_unit: could not set audio category\n");
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\n");
634  }
635
636  return err;
637}
638
639void 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\n"); 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\n", 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\n");
680        }
681      } else {
682        AUBIO_WRN ("audio_unit: input not disabled as prevent_feedback set to 0, risking feedback\n");
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\n"); }
689
690  }
691
692fail:
693  if ( currentRoute ) free((void*)currentRoute);
694  return;
695
696}
697
698SInt32
699audio_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");
707      break;
708    case kAudioSessionRouteChangeReason_OldDeviceUnavailable:
709      AUBIO_MSG("audio_unit: route changed to OldDeviceUnavailable\n");
710      break;
711    case kAudioSessionRouteChangeReason_CategoryChange:
712      AUBIO_MSG("audio_unit: route changed to CategoryChange\n");
713      audio_unit_get_audio_session_category();
714      break;
715    case kAudioSessionRouteChangeReason_Override:
716      AUBIO_MSG("audio_unit: route changed to Override\n");
717      break;
718    case kAudioSessionRouteChangeReason_WakeFromSleep:
719      AUBIO_MSG("audio_unit: route changed to WakeFromSleep\n");
720      break;
721    case kAudioSessionRouteChangeReason_NoSuitableRouteForCategory:
722      AUBIO_MSG("audio_unit: route changed to NoSuitableRouteForCategory\n");
723      break;
724    case kAudioSessionRouteChangeReason_Unknown:
725    default:
726      AUBIO_ERR("audio_unit: route changed for an unknown reason!?\n");
727      break;
728  }
729  return change_reason_number;
730}
731
732/* route change listeners */
733void
734audio_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 */
763uint_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 /* HAVE_AUDIO_UNIT */
Note: See TracBrowser for help on using the repository browser.