shithub: aubio

Download patch

ref: 4084cd8c23011c3141539fc166d35323c87e0c1a
parent: 2c2fe442a06fd116a52c2bbdc111505da72ba7ad
parent: ef47246321466880c8313fbbe06a4d375b02ea9f
author: Paul Brossier <piem@piem.org>
date: Tue Oct 15 15:06:15 EDT 2013

merge with develop

--- a/src/aubio.h
+++ b/src/aubio.h
@@ -184,6 +184,7 @@
 #include "tempo/tempo.h"
 #include "io/source.h"
 #include "io/sink.h"
+#include "io/audio_unit.h"
 #include "synth/sampler.h"
 
 #if AUBIO_UNSTABLE
--- /dev/null
+++ b/src/io/audio_unit.c
@@ -1,0 +1,777 @@
+/*
+  Copyright (C) 2013 Paul Brossier <piem@aubio.org>
+
+  This file is part of aubio.
+
+  aubio is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  aubio is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "config.h"
+#ifdef TARGET_OS_IPHONE
+#include "aubio_priv.h"
+
+#include "fvec.h"
+#include "fmat.h"
+#include "io/audio_unit.h"
+
+#include <AudioToolbox/AudioToolbox.h>
+
+#define AU_IOS_MAX_OUT 64
+#define AU_IOS_MAX_FRAMES AU_IOS_MAX_OUT * 16 * 2
+#define PREFERRED_LATENCY 0.010
+#define MAX_FPS 4096
+
+#define INT_TO_FLOAT 3.0517578125e-05 // 1. / 32768.
+
+struct _aubio_audio_unit_t {
+  AudioUnit audio_unit;
+  uint_t samplerate;
+  uint_t blocksize;
+  uint_t sw_input_channels;
+  uint_t sw_output_channels;
+  uint_t hw_output_channels;
+  uint_t hw_input_channels;
+  Float32 latency;
+  sint_t total_frames;
+  fmat_t *input_frames;
+  fmat_t *output_frames;
+  SInt16 *au_ios_inbuf;
+  SInt16 *au_ios_outbuf;
+  aubio_device_callback_t callback;
+  void *callback_closure;
+  int dio_error; // flag to check if we had a read error
+  bool input_enabled;
+  bool prevent_feedback;
+  bool verbose;
+  int au_ios_start;
+  int au_ios_end;
+  AURenderCallbackStruct au_ios_cb_struct;
+};
+
+
+static OSStatus
+aubio_audio_unit_process(void *closure, AudioUnitRenderActionFlags * action_flags,
+    const AudioTimeStamp * time_stamp, UInt32 bus_number, UInt32 inNumber_frames,
+    AudioBufferList * input_output);
+
+static int aubio_audio_unit_blocking(aubio_audio_unit_t *o);
+
+static void audio_unit_check_audio_route(aubio_audio_unit_t *o);
+
+static void audio_unit_interruption_listener(void *closure, UInt32 inInterruptionState);
+static void audio_unit_route_change_listener(void *closure, AudioSessionPropertyID
+    inID, UInt32 dataSize, const void *inData);
+static OSStatus audio_unit_set_audio_session_category(bool has_input, bool verbose);
+static UInt32 audio_unit_get_audio_session_category ();
+
+aubio_audio_unit_t * new_aubio_audio_unit(uint_t samplerate,
+    uint_t sw_input_channels, uint_t sw_output_channels,
+    uint_t blocksize)
+{
+  aubio_audio_unit_t * o = AUBIO_NEW(aubio_audio_unit_t);
+  o->hw_output_channels = 2;
+  o->hw_input_channels = 2;
+  o->sw_output_channels = sw_output_channels;
+  o->sw_input_channels = sw_input_channels;
+  o->samplerate = samplerate;
+  o->latency = PREFERRED_LATENCY;
+  o->blocksize = blocksize;
+
+  o->au_ios_start = 0;
+  o->au_ios_end = 0;
+
+  o->verbose = 0;
+  o->input_enabled = true;
+  o->prevent_feedback = 1;
+  o->dio_error = 0;
+
+  o->total_frames = 0;
+
+  /* the integers coming from and to the audio unit */
+  o->au_ios_outbuf = AUBIO_ARRAY(SInt16, AU_IOS_MAX_FRAMES * o->hw_output_channels);
+  o->au_ios_inbuf = AUBIO_ARRAY(SInt16, AU_IOS_MAX_FRAMES * o->hw_input_channels);
+
+  /* the floats coming from and to the device callback */
+  o->output_frames = new_fmat(blocksize, sw_output_channels);
+  o->input_frames = new_fmat(blocksize, sw_input_channels);
+
+  /* check for some sizes */
+  if ( o->hw_output_channels != o->output_frames->height ) {
+    AUBIO_ERR ("got hw_output_channels = %d, but output_frames has %d rows",
+        o->hw_output_channels, o->output_frames->height);
+  }
+  if ( o->blocksize != o->output_frames->length ) {
+    AUBIO_ERR ("got blocksize = %d, but output_frames has length %d",
+        o->blocksize, o->output_frames->length);
+  }
+  if ( o->hw_input_channels != o->input_frames->height ) {
+    AUBIO_ERR ("got hw_input_channels = %d, but input_frames has %d rows",
+        o->hw_input_channels, o->input_frames->height);
+  }
+  if ( o->blocksize != o->input_frames->length ) {
+    AUBIO_ERR ("got blocksize = %d, but input_frames has length %d",
+        o->blocksize, o->input_frames->length);
+  }
+
+  return o;
+}
+
+sint_t aubio_audio_unit_set_preferred_latency (aubio_audio_unit_t *o, smpl_t latency)
+{
+  o->latency = latency;
+  return 0;
+}
+
+sint_t aubio_audio_unit_set_prevent_feedback (aubio_audio_unit_t *o, uint_t prevent_feedback)
+{
+  o->prevent_feedback = prevent_feedback;
+  return 0;
+}
+
+sint_t aubio_audio_unit_set_verbose (aubio_audio_unit_t *o, uint_t verbose)
+{
+  o->verbose = verbose;
+  return 0;
+}
+
+
+sint_t aubio_audio_unit_init (aubio_audio_unit_t *o)
+{
+  OSStatus err = noErr;
+  Float32 latency = o->latency;
+  Float64 samplerate = (Float64)o->samplerate;
+
+  o->au_ios_cb_struct.inputProc = aubio_audio_unit_process;
+  o->au_ios_cb_struct.inputProcRefCon = o;
+
+  /* setting up audio session with interruption listener */
+  err = AudioSessionInitialize(NULL, NULL, audio_unit_interruption_listener, o);
+  if (err) { AUBIO_ERR("audio_unit: could not initialize audio session (%ld)", err); goto fail; }
+
+  audio_unit_set_audio_session_category(o->input_enabled, o->verbose);
+  audio_unit_check_audio_route(o);
+
+  /* add route change listener */
+  err = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
+      audio_unit_route_change_listener, o);
+  if (err) { AUBIO_ERR("audio_unit: could not set route change listener (%ld)", err); goto fail; }
+
+  /* set latency */
+  err = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
+      sizeof(latency), &latency);
+  if (err) { AUBIO_ERR("audio_unit: could not set preferred latency (%ld)", err); goto fail; }
+
+#if 0 // only for iphone OS >= 3.1
+  UInt32 val = 1; // set to 0 (default) to use ear speaker in voice application
+  err = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,
+      sizeof(UInt32), &val);
+  if (err) { AUBIO_ERR("audio_unit: could not set session property to default to speaker"); }
+#endif
+
+  /* setting up audio unit */
+  AudioComponentDescription desc;
+  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+  desc.componentSubType = kAudioUnitSubType_RemoteIO;
+  desc.componentType = kAudioUnitType_Output;
+  desc.componentFlags = 0;
+  desc.componentFlagsMask = 0;
+
+  AudioStreamBasicDescription audioFormat;
+
+  /* look for a component that match the description */
+  AudioComponent comp = AudioComponentFindNext(NULL, &desc);
+
+  /* create the audio component */
+  AudioUnit *audio_unit = &(o->audio_unit);
+
+  err = AudioComponentInstanceNew(comp, &(o->audio_unit));
+  if (err) { AUBIO_ERR("audio_unit: failed creating the audio unit"); goto fail; }
+
+  /* enable IO */
+  UInt32 enabled = 1;
+  err = AudioUnitSetProperty (*audio_unit, kAudioOutputUnitProperty_EnableIO,
+      kAudioUnitScope_Input, 1, &enabled, sizeof(enabled));
+  if (err) {
+    AUBIO_ERR("audio_unit: failed enabling input of audio unit");
+    goto fail;
+  }
+
+  /* set max fps */
+  UInt32 max_fps = MIN(o->blocksize, MAX_FPS);
+  err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_MaximumFramesPerSlice,
+      kAudioUnitScope_Global, 0, &max_fps, sizeof(max_fps));
+  if (err) {
+    AUBIO_ERR("audio_unit: could not set maximum frames per slice property (%ld)", err);
+    goto fail;
+  }
+
+  AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_SetRenderCallback,
+      kAudioUnitScope_Input, 0, &(o->au_ios_cb_struct), sizeof(o->au_ios_cb_struct));
+  if (err) { AUBIO_ERR("audio_unit: failed setting audio unit render callback"); goto fail; }
+
+#if 0
+  err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_SampleRate,
+      kAudioUnitScope_Input, 0, &samplerate, sizeof(Float64));
+  if (err) { AUBIO_ERR("audio_unit: could not set audio input sample rate"); goto fail; }
+  err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_SampleRate,
+      kAudioUnitScope_Output, 1, &samplerate, sizeof(Float64));
+  if (err) { AUBIO_ERR("audio_unit: could not set audio input sample rate"); goto fail; }
+#endif
+
+  audioFormat.mSampleRate = (Float64)samplerate;
+  audioFormat.mChannelsPerFrame = 2;
+  audioFormat.mFormatID = kAudioFormatLinearPCM;
+  audioFormat.mFormatFlags = kAudioFormatFlagsCanonical;
+  audioFormat.mFramesPerPacket = 1;
+  audioFormat.mBitsPerChannel = 8 * sizeof(AudioSampleType);
+#if 1  // interleaving
+  audioFormat.mBytesPerFrame = 2 * sizeof(AudioSampleType);
+  audioFormat.mBytesPerPacket = 2 * sizeof(AudioSampleType);
+#else
+  audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame = sizeof(AudioUnitSampleType);
+  audioFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
+#endif
+
+  err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_StreamFormat,
+      kAudioUnitScope_Input, 0, &audioFormat, sizeof(audioFormat));
+  if (err) { AUBIO_ERR("audio_unit: could not set audio output format"); goto fail; }
+  err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_StreamFormat,
+      kAudioUnitScope_Output, 1, &audioFormat, sizeof(audioFormat));
+  if (err) { AUBIO_ERR("audio_unit: could not set audio input format"); goto fail; }
+
+#if 0
+  AudioStreamBasicDescription thruFormat;
+  thissize = sizeof(thruFormat);
+  err = AudioUnitGetProperty (*audio_unit, kAudioUnitProperty_StreamFormat,
+      kAudioUnitScope_Input, 0, &thruFormat, &thissize);
+  if (err) { AUBIO_ERR("audio_unit: could not get speaker output format, err: %d", (int)err); goto fail; }
+  err = AudioUnitSetProperty (*audio_unit, kAudioUnitProperty_StreamFormat,
+      kAudioUnitScope_Output, 1, &thruFormat, sizeof(thruFormat));
+  if (err) { AUBIO_ERR("audio_unit: could not set input audio format, err: %d", (int)err); goto fail; }
+#endif
+
+  /* time to initialize the unit */
+  err = AudioUnitInitialize(*audio_unit);
+  if (err) { AUBIO_ERR("audio_unit: failed initializing audio, err: %d", (int)err); goto fail; }
+
+  return 0;
+
+fail:
+  return err;
+}
+
+/* perform function */
+OSStatus
+aubio_audio_unit_process(void *closure, AudioUnitRenderActionFlags * action_flags,
+    const AudioTimeStamp * time_stamp, UNUSED UInt32 bus_number, UInt32 inNumber_frames,
+    AudioBufferList * input_output)
+{
+  UInt32 b; int err = 0;
+  aubio_audio_unit_t *o = (aubio_audio_unit_t *)closure;
+  AudioUnit thisUnit = o->audio_unit;
+
+  if (o->input_enabled) {
+    err = AudioUnitRender(thisUnit, action_flags, time_stamp, 1,
+        inNumber_frames, input_output);
+    if (err) {
+      AUBIO_ERR("audio_unit: error performing AudioUnitRender (%d)", err);
+      return err;
+    }
+  }
+
+  // get the number of frames from the audio buffer list, NOT inNumber_frames
+  UInt32 number_frames = input_output->mBuffers[0].mDataByteSize/ sizeof(SInt16) / 2;
+
+  // FIXME find out why this happens
+  if (number_frames < 10) {
+    AUBIO_ERR("audio_unit: got number_frames %d", (int)number_frames);
+    return -1;
+  }
+
+  if (o->total_frames >= (signed)number_frames) {
+
+    SInt16 *data;
+    if (o->au_ios_start + number_frames > AU_IOS_MAX_FRAMES) {
+      // start reminder samples writing at reminder
+      int reminder = AU_IOS_MAX_FRAMES - o->au_ios_start;
+      int starter = (o->au_ios_start + number_frames) - AU_IOS_MAX_FRAMES;
+      for (b = 0; b < input_output->mNumberBuffers; b++) {
+        data = (SInt16 *)(input_output->mBuffers[b].mData);
+        /* copy microphone output to input buffer */
+        memcpy (o->au_ios_inbuf + o->au_ios_start * 2, data, reminder * 2 * sizeof(SInt16));
+        memcpy (o->au_ios_inbuf, data + reminder * 2, starter * 2 * sizeof(SInt16));
+        /* silence data before copying from output */
+        //memset (data, 0, input_output->mBuffers[b].mDataByteSize);
+        /* copy output buffer to speakers */
+        memcpy (data, o->au_ios_outbuf + o->au_ios_start * 2, reminder * 2 * sizeof(SInt16));
+        memcpy (data + reminder * 2, o->au_ios_outbuf, starter * 2 * sizeof(SInt16));
+      }
+    } else {
+      for (b = 0; b < input_output->mNumberBuffers; b++) {
+        data = (SInt16 *)(input_output->mBuffers[b].mData);
+        /* copy microphone samples to au_ios_inbuf */
+        memcpy(o->au_ios_inbuf + o->au_ios_start * 2, data, number_frames * 2 * sizeof(SInt16));
+        /* silence data before copying from output */
+        //memset (data, 0, input_output->mBuffers[b].mDataByteSize);
+        /* copy output buffer to speakers */
+        memcpy(data, o->au_ios_outbuf + o->au_ios_start * 2, number_frames * 2 * sizeof(SInt16));
+      }
+    }
+    o->au_ios_start += number_frames;
+    o->au_ios_start %= AU_IOS_MAX_FRAMES;
+    o->total_frames -= number_frames;
+
+#if 1
+  } else {
+    if (o->total_frames > 0) o->dio_error = 1;
+    for (b = 0; b < input_output->mNumberBuffers; b++) {
+      memset (input_output->mBuffers[b].mData, 0,
+          input_output->mBuffers[b].mDataByteSize);
+    }
+    //total_frames = 0;
+#endif
+  }
+
+  // now call callback
+  while ( o->total_frames < (signed)number_frames ) {
+    //AUBIO_DBG ("audio_unit: total_frames = %d, number_frames = %d, o->au_ios_start = %d, o->au_ios_end = %d",
+    //    o->total_frames, number_frames, o->au_ios_start, o->au_ios_end);
+    aubio_audio_unit_blocking(o);
+  }
+
+  return err;
+}
+
+int aubio_audio_unit_blocking(aubio_audio_unit_t *o)
+{
+  uint_t sw_output_channels, sw_input_channels,
+         hw_output_channels, hw_input_channels,
+         i, j, blocksize;
+  if (! o->callback) return -1;
+
+  smpl_t ** tbuf;
+
+  sw_output_channels = o->sw_output_channels;
+  sw_input_channels = o->sw_input_channels;
+  hw_output_channels = o->hw_output_channels;
+  hw_input_channels = o->hw_input_channels;
+  blocksize = o->blocksize;
+
+  if (!sw_input_channels && !sw_output_channels) goto fail;
+
+  if (o->dio_error) {
+    AUBIO_WRN("audio_unit: dio error %d", o->total_frames);
+    o->dio_error = 0;
+  }
+
+  if (o->au_ios_inbuf) {
+    /* copy samples from input buffer */
+    tbuf = o->input_frames->data;
+    if (o->input_enabled) {
+      for (j = 0; j < blocksize;j++) {
+        for (i = 0; i < sw_input_channels && i < hw_input_channels; i++) {
+          //tbuf[i][j] =
+          //   (smpl_t)(o->au_ios_inbuf[i + (j + o->au_ios_end) * sw_input_channels] / 32768.);
+          // on iphone, input is mono, copy right to left channel
+          tbuf[i][j] =
+            (smpl_t) o->au_ios_inbuf[0 + (j + o->au_ios_end) * hw_input_channels]
+            * INT_TO_FLOAT;
+        }
+      }
+    } else {
+      // input is disabled, fill with zeroes
+      for (j = 0; j < blocksize; j++) {
+        for (i = 0; i < sw_input_channels && i < hw_input_channels; i++) {
+          tbuf[i][j] = 0;
+        }
+      }
+    }
+  }
+
+  o->callback(o->callback_closure, o->input_frames, o->output_frames);
+
+  /* copy samples to output buffer */
+  tbuf = o->output_frames->data;
+  for (i = 0; i < o->output_frames->height; i++)	{
+    for (j = 0; j < o->output_frames->length; j++) {
+      smpl_t val = tbuf[i][j];
+      if (val < -1.0) val = -1.0;
+      if (val > 1.0) val = 1.0;
+      o->au_ios_outbuf[i + (j + o->au_ios_end) * hw_output_channels ] = (SInt16)(val * 32767);
+    }
+  }
+
+  o->au_ios_end += blocksize;
+  o->au_ios_end %= AU_IOS_MAX_FRAMES;
+  o->total_frames += blocksize;
+
+  return 0;
+
+fail:
+  AUBIO_ERR("audio_unit: callback() failed");
+  o->total_frames += AU_IOS_MAX_FRAMES;
+  return 1;
+}
+
+sint_t aubio_audio_unit_get_info (aubio_audio_unit_t *o)
+{
+  UInt32 thissize, input_hw_channels, output_hw_channels, max_fps;
+  Float32 latency, input_latency, output_latency, input_hw_volume, output_hw_volume;
+  Float64 samplerate;
+  OSStatus err = 0;
+
+  // Show some info about the opened unit
+
+  /* get sampling rate */
+  thissize = sizeof(samplerate);
+  err = AudioUnitGetProperty (o->audio_unit, kAudioUnitProperty_SampleRate,
+      kAudioUnitScope_Output, 1, &samplerate, &thissize);
+  if (err) { AUBIO_ERR("audio_unit: could not get audio unit sample rate (%ld)",
+      err); goto fail; }
+
+  /* get hardware input channels */
+  thissize = sizeof(input_hw_channels);
+  err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels,
+      &thissize, &input_hw_channels);
+  if (err) { AUBIO_ERR("audio_unit: could not get hardware input channels (%ld)",
+      err); goto fail; }
+
+  /* get hardware output channels */
+  thissize = sizeof(output_hw_channels);
+  err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputNumberChannels,
+      &thissize, &output_hw_channels);
+  if (err) { AUBIO_ERR("audio_unit: could not get hardware output channels (%ld)",
+      err); goto fail; }
+
+  /* get hardware input volume */
+  thissize = sizeof(input_hw_volume);
+  err = AudioSessionGetProperty(kAudioSessionProperty_InputGainScalar,
+      &thissize, &input_hw_volume);
+  if (err) { AUBIO_ERR("audio_unit: could not get hardware input volume (%ld)",
+      err); goto fail; }
+
+  /* get hardware output volume */
+  thissize = sizeof(output_hw_volume);
+  err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputVolume,
+      &thissize, &output_hw_volume);
+  if (err) { AUBIO_ERR("audio_unit: could not get hardware output volume (%ld)",
+      err); goto fail; }
+
+  AUBIO_MSG("audio_unit: opened at %.0fHz, sw channels %din/%dout, hw channels %ldin/%ldout, hw vol %.2fin/%.2fout",
+      samplerate,
+      o->sw_input_channels, o->sw_output_channels,
+      input_hw_channels, output_hw_channels,
+      input_hw_volume, output_hw_volume);
+
+  /* get max frames per slice */
+  thissize = sizeof(max_fps);
+  err = AudioUnitGetProperty (o->audio_unit, kAudioUnitProperty_MaximumFramesPerSlice,
+      kAudioUnitScope_Global, 0, &max_fps, &thissize);
+  if (err) { AUBIO_ERR("audio_unit: could not get maximum frames per slice property %ld",
+      err); goto fail; }
+
+  /* get hardware latency */
+  thissize = sizeof(latency);
+  err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
+      &thissize, &latency);
+  if (err) { AUBIO_ERR("audio_unit: could not get hardware latency %ld",
+      err); goto fail; }
+
+  /* get input latency */
+  thissize = sizeof(input_latency);
+  err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputLatency,
+      &thissize, &input_latency);
+  if (err) { AUBIO_ERR("audio_unit: could not get input latency %ld",
+      err); goto fail; }
+
+  /* get output harlatency */
+  thissize = sizeof(output_latency);
+  err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,
+      &thissize, &output_latency);
+  if (err) { AUBIO_ERR("audio_unit: could not get output latency %ld",
+      err); goto fail; }
+
+  AUBIO_MSG("audio_unit: I/O latency: %.2fms, %d frames, (%.2fms, %d frames in, %.2fms %d frames out)",
+      latency*1000., (sint_t)round(latency*samplerate),
+      input_latency*1000., (sint_t)ROUND(input_latency*samplerate),
+      output_latency*1000., (sint_t)ROUND(output_latency*samplerate));
+
+fail:
+  return err;
+}
+
+sint_t aubio_audio_unit_start(aubio_audio_unit_t *o) {
+  OSStatus err = 0;
+
+  if (o->verbose) {
+    // print some info about the current settings
+    aubio_audio_unit_get_info (o);
+  }
+
+  /* time to start the unit */
+  err = AudioOutputUnitStart (o->audio_unit);
+  if (err) { AUBIO_ERR("audio_unit: could not start unit (%ld)", err); }
+  return err;
+}
+
+sint_t aubio_audio_unit_stop(aubio_audio_unit_t *o)
+{
+  if (o->audio_unit == NULL) return -1;
+  OSStatus err = AudioOutputUnitStop (o->audio_unit);
+  if (err) { AUBIO_WRN("audio_unit: failed stopping audio unit (%ld)", err); }
+  err = AudioUnitUninitialize (o->audio_unit);
+  if (err) { AUBIO_WRN("audio_unit: failed unitializing audio unit (%ld)", err); }
+  err = AudioSessionSetActive(false);
+  if (err) { AUBIO_WRN("audio_unit: failed stopping audio session (%ld)", err); }
+  return err;
+}
+
+uint_t aubio_audio_unit_set_callback(aubio_audio_unit_t *o,
+    aubio_device_callback_t callback, void *closure) {
+  o->callback = callback;
+  o->callback_closure = closure;
+  return 0;
+}
+
+/* interruption listeners */
+void audio_unit_interruption_listener(void *closure, UInt32 inInterruptionState)
+{
+  OSStatus err = 0;
+  aubio_audio_unit_t *o = (aubio_audio_unit_t *) closure;
+  AudioUnit this_unit = o->audio_unit;
+
+  if (inInterruptionState == kAudioSessionEndInterruption) {
+    AUBIO_WRN("audio_unit: session interruption ended");
+    err = AudioSessionSetActive(true);
+    if (err) {
+      AUBIO_ERR("audio_unit: could not make session active after interruption (%ld)", err);
+      goto fail;
+    }
+    err = AudioOutputUnitStart(this_unit);
+    if (err) {
+      AUBIO_ERR("audio_unit: failed starting unit (%ld)", err);
+      goto fail;
+    }
+  }
+  if (inInterruptionState == kAudioSessionBeginInterruption) {
+    AUBIO_WRN("audio_unit: session interruption started");
+    err = AudioOutputUnitStop(this_unit);
+    if (err) {
+      AUBIO_ERR("audio_unit: could not stop unit at interruption (%ld)", err);
+      goto fail;
+    }
+    err = AudioSessionSetActive(false);
+    if (err) {
+      AUBIO_ERR("audio_unit: could not make session inactive after interruption (%ld)", err);
+      goto fail;
+    }
+  }
+fail:
+  return;
+}
+
+UInt32 audio_unit_get_audio_session_category () {
+  UInt32 category, thissize;
+  thissize = sizeof(category);
+  OSStatus err = AudioSessionGetProperty(kAudioSessionProperty_AudioCategory,
+      &thissize, &category);
+  if (err) {
+    AUBIO_ERR("audio_unit: could not get audio category (%ld)", err);
+    return err;
+  }
+  if (category == kAudioSessionCategory_AmbientSound) {
+    AUBIO_MSG("audio_unit: session category is AmbiantSound");
+  } else if (category == kAudioSessionCategory_SoloAmbientSound) {
+    AUBIO_MSG("audio_unit: session category is SoloAmbiantSound");
+  } else if (category == kAudioSessionCategory_MediaPlayback) {
+    AUBIO_MSG("audio_unit: session category is MediaPlayback");
+  } else if (category == kAudioSessionCategory_RecordAudio) {
+    AUBIO_MSG("audio_unit: session category is RecordAudio");
+  } else if (category == kAudioSessionCategory_PlayAndRecord) {
+    AUBIO_MSG("audio_unit: session category is PlayAndRecord");
+  } else if (category == kAudioSessionCategory_AudioProcessing) {
+    AUBIO_MSG("audio_unit: session category is AudioProcessing");
+  }
+  return category;
+}
+
+OSStatus audio_unit_set_audio_session_category(bool has_input, bool verbose)
+{
+  //if we have input, set the session category accordingly
+  OSStatus err = 0;
+  UInt32 category;
+  if (has_input) {
+    category = kAudioSessionCategory_PlayAndRecord;
+    if (verbose) AUBIO_MSG("audio_unit: setting category to PlayAndRecord");
+  } else {
+    category = kAudioSessionCategory_MediaPlayback;
+    if (verbose) AUBIO_MSG("audio_unit: setting category to MediaPlayback");
+  }
+  err = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
+      sizeof(category), &category);
+  if (err) {
+    AUBIO_ERR("audio_unit: could not set audio category");
+  }
+
+  // Audiob.us style
+  UInt32 allowMixing = 1;
+  AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,
+      sizeof (allowMixing), &allowMixing);
+  if (err) {
+    AUBIO_ERR("audio_unit: could not set audio session to mix with others");
+  }
+
+  return err;
+}
+
+void audio_unit_check_audio_route(aubio_audio_unit_t *o) {
+  CFStringRef currentRoute;
+  UInt32 val, thissize = sizeof(currentRoute);
+  OSStatus err = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &thissize, &currentRoute);
+  if (err) { AUBIO_ERR("audio_unit: could not get current route"); goto fail; }
+  else {
+    char *route = (char *)CFStringGetCStringPtr ( currentRoute, kCFStringEncodingUTF8);
+    if (route == NULL) {
+      int bufferSize = 25;
+      route = calloc(bufferSize, sizeof(char));
+      CFStringGetCString ( currentRoute, route, bufferSize,
+          kCFStringEncodingUTF8);
+    }
+    if (o->verbose) {
+      AUBIO_MSG ("audio_unit: current route is %s", route);
+    }
+    free(route);
+  }
+  if( currentRoute ) {
+    if( CFStringCompare( currentRoute, CFSTR("Headset"), 0 ) == kCFCompareEqualTo ) {
+      val = kAudioSessionOverrideAudioRoute_None;
+    } else if( CFStringCompare( currentRoute, CFSTR("Receiver" ), 0 ) == kCFCompareEqualTo ) {
+      val = kAudioSessionOverrideAudioRoute_Speaker;
+    } else if( CFStringCompare( currentRoute, CFSTR("ReceiverAndMicrophone" ), 0 ) == kCFCompareEqualTo ) {
+      val = kAudioSessionOverrideAudioRoute_Speaker;
+    } else if( CFStringCompare( currentRoute, CFSTR("SpeakerAndMicrophone" ), 0 ) == kCFCompareEqualTo ) {
+      val = kAudioSessionOverrideAudioRoute_Speaker;
+    } else if( CFStringCompare( currentRoute, CFSTR("HeadphonesAndMicrophone" ), 0 ) == kCFCompareEqualTo ) {
+      val = kAudioSessionOverrideAudioRoute_None;
+    } else if( CFStringCompare( currentRoute, CFSTR("HeadsetInOut" ), 0 ) == kCFCompareEqualTo ) {
+      val = kAudioSessionOverrideAudioRoute_None;
+    } else {
+      val = kAudioSessionOverrideAudioRoute_None;
+    }
+
+    o->input_enabled = true;
+    if (val == kAudioSessionOverrideAudioRoute_Speaker) {
+      if (o->prevent_feedback) {
+        o->input_enabled = false;
+        if (o->verbose) {
+          AUBIO_MSG ("audio_unit: disabling input to avoid feedback");
+        }
+      } else {
+        AUBIO_WRN ("audio_unit: input not disabled as prevent_feedback set to 0, risking feedback");
+      }
+    }
+
+    err = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute,
+        sizeof(UInt32), &val);
+    if (err) { AUBIO_ERR("audio_unit: could not set session OverrideAudioRoute to Speaker"); }
+
+  }
+
+fail:
+  if ( currentRoute ) free((void*)currentRoute);
+  return;
+
+}
+
+SInt32
+audio_unit_get_route_change_reason(CFDictionaryRef routeChangeDic) {
+  CFNumberRef routeChangeReasonRef = (CFNumberRef)CFDictionaryGetValue(routeChangeDic,
+      CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
+  SInt32 change_reason_number;
+  CFNumberGetValue ( routeChangeReasonRef, kCFNumberSInt32Type, &change_reason_number);
+  switch (change_reason_number) {
+    case kAudioSessionRouteChangeReason_NewDeviceAvailable:
+      AUBIO_MSG("audio_unit: route changed to NewDeviceAvailable");
+      break;
+    case kAudioSessionRouteChangeReason_OldDeviceUnavailable:
+      AUBIO_MSG("audio_unit: route changed to OldDeviceUnavailable");
+      break;
+    case kAudioSessionRouteChangeReason_CategoryChange:
+      AUBIO_MSG("audio_unit: route changed to CategoryChange");
+      audio_unit_get_audio_session_category();
+      break;
+    case kAudioSessionRouteChangeReason_Override:
+      AUBIO_MSG("audio_unit: route changed to Override");
+      break;
+    case kAudioSessionRouteChangeReason_WakeFromSleep:
+      AUBIO_MSG("audio_unit: route changed to WakeFromSleep");
+      break;
+    case kAudioSessionRouteChangeReason_NoSuitableRouteForCategory:
+      AUBIO_MSG("audio_unit: route changed to NoSuitableRouteForCategory");
+      break;
+    case kAudioSessionRouteChangeReason_Unknown:
+    default:
+      AUBIO_ERR("audio_unit: route changed for an unknown reason!?");
+      break;
+  }
+  return change_reason_number;
+}
+
+/* route change listeners */
+void
+audio_unit_route_change_listener(void *closure, AudioSessionPropertyID inID,
+    UInt32 dataSize, const void *inData)
+{
+
+  UNUSED aubio_audio_unit_t *o = (aubio_audio_unit_t *)closure;
+  UNUSED UInt32 size = dataSize;
+  if (inID == kAudioSessionProperty_AudioRouteChange) {
+
+    // OSStatus err = 0;
+    //AudioUnit audio_unit = o->audio_unit;
+
+    if (o->verbose) {
+      // show route change reason
+      audio_unit_get_route_change_reason((CFDictionaryRef)inData);
+    }
+
+    // check current audio route, changing it to prevent feedback as needed
+    audio_unit_check_audio_route(o);
+
+    if (o->verbose) {
+      // print some info about the current settings
+      aubio_audio_unit_get_info(o);
+    }
+
+  }
+
+}
+
+/* delete object */
+uint_t del_aubio_audio_unit(aubio_audio_unit_t *o)
+{
+  int err = 0;
+  err = aubio_audio_unit_stop(o);
+  if (o->au_ios_inbuf) free(o->au_ios_inbuf);
+  o->au_ios_inbuf = NULL;
+  if (o->au_ios_outbuf) free(o->au_ios_outbuf);
+  o->au_ios_outbuf = NULL;
+  del_fmat (o->input_frames);
+  del_fmat (o->output_frames);
+  o->audio_unit = NULL;
+  return (int)err;
+}
+
+#endif /* TARGET_OS_IPHONE */
--- /dev/null
+++ b/src/io/audio_unit.h
@@ -1,0 +1,61 @@
+/*
+  Copyright (C) 2013 Paul Brossier <piem@aubio.org>
+
+  This file is part of aubio.
+
+  aubio is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  aubio is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef _AUBIO_AUDIO_UNIT_H
+#define _AUBIO_AUDIO_UNIT_H
+
+/** \file
+
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _aubio_audio_unit_t aubio_audio_unit_t;
+
+aubio_audio_unit_t * new_aubio_audio_unit(uint_t samplerate, uint_t inchannels,
+    uint_t outchannels, uint_t blocksize);
+
+typedef uint_t (*aubio_device_callback_t) (void * closure, fmat_t *ibuf, fmat_t *obuf);
+
+uint_t aubio_audio_unit_set_callback(aubio_audio_unit_t *o,
+    aubio_device_callback_t callback, void *closure);
+
+sint_t aubio_audio_unit_set_verbose (aubio_audio_unit_t *o, uint_t verbose);
+sint_t aubio_audio_unit_set_preferred_latency (aubio_audio_unit_t *o, smpl_t
+    latency);
+sint_t aubio_audio_unit_set_prevent_feedback (aubio_audio_unit_t *o, uint_t
+    prevent_feedback);
+
+sint_t aubio_audio_unit_get_info (aubio_audio_unit_t *o);
+
+sint_t aubio_audio_unit_init (aubio_audio_unit_t *o);
+
+sint_t aubio_audio_unit_start (aubio_audio_unit_t *o);
+sint_t aubio_audio_unit_stop (aubio_audio_unit_t *o);
+
+uint_t del_aubio_audio_unit(aubio_audio_unit_t *o);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AUBIO_AUDIO_UNIT_H */
--- a/src/wscript_build
+++ b/src/wscript_build
@@ -11,8 +11,11 @@
 
 # build libaubio
 from waflib import Options
-if Options.platform == 'ios': build_lib_func = ctx.stlib
-else: build_lib_func = ctx.shlib
+if Options.platform == 'ios':
+    build_lib_func = ctx.stlib
+else:
+    build_lib_func = ctx.shlib
+
 build_lib_func(
     includes = ['.'],
     source = source,
@@ -24,6 +27,5 @@
 
 # install headers, except _priv.h ones
 ctx.install_files('${PREFIX}/include/aubio/',
-
     ctx.path.ant_glob('**/*.h', excl = ['**_priv.h', 'config.h']),
     relative_trick=True)
--- a/wscript
+++ b/wscript
@@ -72,19 +72,34 @@
     ctx.env.FRAMEWORK = ['CoreFoundation', 'AudioToolbox', 'Accelerate']
     ctx.define('HAVE_ACCELERATE', 1)
 
-  if Options.platform == 'ios':
+  if Options.platform in [ 'ios', 'iosimulator' ]:
     ctx.env.CC = 'clang'
     ctx.env.LD = 'clang'
     ctx.env.LINK_CC = 'clang'
-    SDKVER="7.0"
-    DEVROOT="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer"
-    SDKROOT="%(DEVROOT)s/SDKs/iPhoneOS%(SDKVER)s.sdk" % locals()
-    ctx.env.FRAMEWORK = ['CoreFoundation', 'AudioToolbox', 'Accelerate']
     ctx.define('HAVE_ACCELERATE', 1)
-    ctx.env.CFLAGS += [ '-miphoneos-version-min=6.1', '-arch', 'armv7',
-            '--sysroot=%s' % SDKROOT]
-    ctx.env.LINKFLAGS += ['-std=c99', '-arch', 'armv7', '--sysroot=%s' %
-            SDKROOT]
+    ctx.define('TARGET_OS_IPHONE', 1)
+    ctx.env.FRAMEWORK = ['CoreFoundation', 'AudioToolbox', 'Accelerate']
+    SDKVER="7.0"
+    MINSDKVER="6.1"
+    if Options.platform == 'ios':
+        DEVROOT="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer"
+        SDKROOT="%(DEVROOT)s/SDKs/iPhoneOS%(SDKVER)s.sdk" % locals()
+        ctx.env.CFLAGS += [ '-arch', 'armv7' ]
+        ctx.env.CFLAGS += [ '-arch', 'armv7s' ]
+        ctx.env.LINKFLAGS += ['-arch', 'armv7']
+        ctx.env.LINKFLAGS += ['-arch', 'armv7s']
+    else:
+        DEVROOT="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer"
+        SDKROOT="%(DEVROOT)s/SDKs/iPhoneSimulator%(SDKVER)s.sdk" % locals()
+        ctx.env.CFLAGS += [ '-arch', 'i386' ]
+        ctx.env.LINKFLAGS += ['-arch', 'i386']
+        ctx.env.CFLAGS += [ '-arch', 'x86_64' ]
+        ctx.env.LINKFLAGS += ['-arch', 'x86_64']
+    ctx.env.CFLAGS += [ '-miphoneos-version-min=' + MINSDKVER ]
+    ctx.env.CFLAGS += [ '--sysroot=%s' % SDKROOT]
+    ctx.env.CFLAGS += ['-std=c99']
+    ctx.env.LINKFLAGS += ['-std=c99']
+    ctx.env.LINKFLAGS += ['--sysroot=%s' % SDKROOT]
 
   # check for required headers
   ctx.check(header_name='stdlib.h')
@@ -177,7 +192,7 @@
   # add sub directories
   bld.recurse('src')
   from waflib import Options
-  if Options.platform != 'ios':
+  if Options.platform not in ['ios', 'iosimulator']:
       bld.recurse('examples')
       bld.recurse('tests')
 
@@ -211,7 +226,7 @@
 
 def shutdown(bld):
     from waflib import Options, Logs
-    if Options.platform == 'ios':
+    if Options.platform in ['ios', 'iosimulator']:
           msg ='aubio built for ios, contact the author for a commercial license'
           Logs.pprint('RED', msg)
           msg ='   Paul Brossier <piem@aubio.org>'