/* 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 "aubio_priv.h"#ifdef HAVE_AUDIO_UNIT #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 OSStatusaubio_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(sw_output_channels, blocksize); o->input_frames = new_fmat(sw_input_channels, blocksize); /* 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\n", 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\n", 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\n", 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\n", 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 (%d)\n", (int)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 (%d)\n", (int)err); goto fail; } /* set latency */ err = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(latency), &latency); if (err) { AUBIO_ERR("audio_unit: could not set preferred latency (%d)\n", (int)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\n"); }#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\n"); 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\n"); 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 (%d)\n", (int)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\n"); 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\n"); 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\n"); goto fail; }#endif audioFormat.mSampleRate = (Float64)samplerate; audioFormat.mChannelsPerFrame = 2; audioFormat.mFormatID = kAudioFormatLinearPCM; audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; audioFormat.mFramesPerPacket = 1; audioFormat.mBitsPerChannel = 8 * sizeof(SInt16);#if 1 // interleaving audioFormat.mBytesPerFrame = 2 * sizeof(SInt16); audioFormat.mBytesPerPacket = 2 * sizeof(SInt16);#else audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame = sizeof(SInt32); 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\n"); 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\n"); 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\n", (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\n", (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\n", (int)err); goto fail; } return 0; fail: return err;} /* perform function */OSStatusaubio_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)\n", 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\n", (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\n", 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\n"); 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 (%d)\n", (int)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 (%d)\n", (int)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 (%d)\n", (int)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 (%d)\n", (int)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 (%d)\n", (int)err); goto fail; } AUBIO_MSG("audio_unit: opened at %.0fHz, sw channels %din/%dout, hw channels %din/%dout, hw vol %.2fin/%.2fout\n", samplerate, o->sw_input_channels, o->sw_output_channels, (unsigned int)input_hw_channels, (unsigned int)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 %d\n", (int)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 %d\n", (int)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 %d\n", (int)err); goto fail; } /* get output harlatency */ thissize = sizeof(output_latency); err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,Lines 1–500 of 776. Load more
/* 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 "aubio_priv.h" #ifdef HAVE_AUDIO_UNIT #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(sw_output_channels, blocksize); o->input_frames = new_fmat(sw_input_channels, blocksize); /* 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\n", 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\n", 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\n", 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\n", 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 (%d)\n", (int)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 (%d)\n", (int)err); goto fail; } /* set latency */ err = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(latency), &latency); if (err) { AUBIO_ERR("audio_unit: could not set preferred latency (%d)\n", (int)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\n"); } #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\n"); 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\n"); 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 (%d)\n", (int)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\n"); 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\n"); 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\n"); goto fail; } #endif audioFormat.mSampleRate = (Float64)samplerate; audioFormat.mChannelsPerFrame = 2; audioFormat.mFormatID = kAudioFormatLinearPCM; audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; audioFormat.mFramesPerPacket = 1; audioFormat.mBitsPerChannel = 8 * sizeof(SInt16); #if 1 // interleaving audioFormat.mBytesPerFrame = 2 * sizeof(SInt16); audioFormat.mBytesPerPacket = 2 * sizeof(SInt16); #else audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame = sizeof(SInt32); 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\n"); 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\n"); 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\n", (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\n", (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\n", (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)\n", 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\n", (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\n", 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\n"); 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 (%d)\n", (int)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 (%d)\n", (int)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 (%d)\n", (int)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 (%d)\n", (int)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 (%d)\n", (int)err); goto fail; } AUBIO_MSG("audio_unit: opened at %.0fHz, sw channels %din/%dout, hw channels %din/%dout, hw vol %.2fin/%.2fout\n", samplerate, o->sw_input_channels, o->sw_output_channels, (unsigned int)input_hw_channels, (unsigned int)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 %d\n", (int)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 %d\n", (int)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 %d\n", (int)err); goto fail; } /* get output harlatency */ thissize = sizeof(output_latency); err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,