source: src/io/audio_unit.c @ 4d6a228

feature/crepe_org
Last change on this file since 4d6a228 was 33d0242, checked in by Paul Brossier <piem@piem.org>, 8 years ago

src/aubio_priv.h: move include config.h here

  • Property mode set to 100644
File size: 27.5 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 "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
37struct _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
63static OSStatus
64aubio_audio_unit_process(void *closure, AudioUnitRenderActionFlags * action_flags,
65    const AudioTimeStamp * time_stamp, UInt32 bus_number, UInt32 inNumber_frames,
66    AudioBufferList * input_output);
67
68static int aubio_audio_unit_blocking(aubio_audio_unit_t *o);
69
70static void audio_unit_check_audio_route(aubio_audio_unit_t *o);
71
72static void audio_unit_interruption_listener(void *closure, UInt32 inInterruptionState);
73static void audio_unit_route_change_listener(void *closure, AudioSessionPropertyID
74    inID, UInt32 dataSize, const void *inData);
75static OSStatus audio_unit_set_audio_session_category(bool has_input, bool verbose);
76static UInt32 audio_unit_get_audio_session_category ();
77
78aubio_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
130sint_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
136sint_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
142sint_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
149sint_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
270fail:
271  return err;
272}
273
274/* perform function */
275OSStatus
276aubio_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
356int 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
421fail:
422  AUBIO_ERR("audio_unit: callback() failed\n");
423  o->total_frames += AU_IOS_MAX_FRAMES;
424  return 1;
425}
426
427sint_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
510fail:
511  return err;
512}
513
514sint_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
528sint_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
540uint_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 */
548void 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  }
580fail:
581  return;
582}
583
584UInt32 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
609OSStatus 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
638void 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
691fail:
692  if ( currentRoute ) free((void*)currentRoute);
693  return;
694
695}
696
697SInt32
698audio_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 */
732void
733audio_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 */
762uint_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 */
Note: See TracBrowser for help on using the repository browser.