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, ¤tRoute);
+ 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>'