iOS - How to solve echo problems with speex? - ios

My App call to the intercom has an echo, I have used the native method, but it is useless, I want to use the echo cancellation of speex, but I have encountered some problems, in the echo cancellation of speex, need three parameters, ref, echo and End, I only have the bufferList.mBuffers[0].mData variable, I don't know what the other two parameters are to be substituted for?
OSStatus recordingCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimestamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
AudioProcessor *audioProcessor = (__bridge AudioProcessor *)inRefCon;
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mData = NULL;
OSStatus status = AudioUnitRender(audioProcessor.audioUnit, ioActionFlags, inTimestamp, inBusNumber, inNumberFrames, &bufferList);
if (status != noErr) {
return status;
}
SpeexEchoState *echo_state = speex_echo_state_init(bufferList.mBuffers[0].mDataByteSize, 882);
SpeexPreprocessState *den = speex_preprocess_state_init(bufferList.mBuffers[0].mDataByteSize, 44100);
short *ref_buf = (short*)bufferList.mBuffers[0].mData;
short *echo_buf = (short*)malloc(bufferList.mBuffers[0].mDataByteSize);
short *endbuf = (short*)malloc(bufferList.mBuffers[0].mDataByteSize);
speex_echo_cancellation(echo_state,
(spx_int16_t*)ref_buf,
(spx_int16_t*)echo_buf,
(spx_int16_t*)endbuf);
speex_preprocess_run(den, (spx_int16_t*)endbuf);
[audioProcessor.delegate processAudioData:endbuf dataSize:bufferList.mBuffers[0].mDataByteSize];
free(ref_buf);
free(echo_buf);
free(endbuf);
return noErr;
}

If you build-in AEC is not working, it means that there is a good chance that your echo behavior is not trivial and you should look for more robust solutions than Speex. Google for echo cancellation software to find few alternatives.

Related

How to merge two Audio Units into AudioBufferList for AURenderCallback

Please see code below. All I want to, is to merge bufflist1 and bufflist2, then inset to ioData. But I don't know how.
OSStatus PlayCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
ALNPlayer *player = (__bridge ALNPlayer *)inRefCon;
OSStatus status;
player->buffList->mBuffers[0].mDataByteSize = CONST_BUFFER_SIZEV3;
//read audio1 data to bufflist1
status = AudioConverterFillComplexBuffer(player->audioConverter1, lyInInputDataProcV1, inRefCon, &inNumberFrames, player->buffList1, NULL);
//read audio2 data to bufflist2
status = AudioConverterFillComplexBuffer(player->audioConverter2, lyInInputDataProcV2, inRefCon, &inNumberFrames, player->buffList2, NULL);
//below is copy bufferlist1 to ioData
//and now i want to merge bufflist1 and buflist2,then inset to iodata. But I don't know how.
memcpy(ioData->mBuffers[0].mData, player->buffList->mBuffers[0].mData, player->buffList->mBuffers[0].mDataByteSize);
}

AURemoteIO::IOThread(18):EXE_BAD_ACCESS (Code1, address 0X20)

I am using following function to render the incoming audio.
static OSStatus audioUnitRenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberOfFrames,
AudioBufferList *ioData{
// App is crashing at this :
AudioUnitIO *SELF = (__bridge AudioUnitIO *)inRefCon;
// MICROPHONE: take the sound received, render it into bytes and hand them to the delegate
AudioUnitRender(SELF->g_audioUnit,
ioActionFlags,
inTimeStamp,
g_inputBus,
inNumberOfFrames,
ioData);
if (!SELF->recordingAudio) {
memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
// Some other code goes here
}
I am getting the crash as mentioned in question. Am I missing something?

Using multiple AudioUnits with MTAudioProcessingTap on iOS

I'm attempting to play audio files from the users' iPod library on an iOS device, while using AudioUnit to apply a parametric EQ effect. I have been using this sample as a guide: https://developer.apple.com/library/ios/samplecode/AudioTapProcessor/Introduction/Intro.html
I have the EQ effect working, but I need to add multiple EQ effects.
In my 'process' callback, I tried running AudioUnitRender multiple times, on multiple AudioUnit effects (all of type Parametric EQ).
status = AudioUnitRender(audioUnit, 0, &audioTimeStamp, 0, (UInt32)numberFrames, bufferListInOut);
With any more than 1 AudioUnitRender call, the audio skips and cuts out.
How can I use multiple Parametric EQ effects at once?
Thanks
I'm still not sure what the reason of the glitch is, but I've found the solution and it's probably what AUGraph does internally.
The trick is to call AudioUnitRender() of the next AU from within the render callback function of the previous AU. The last render callback calls MTAudioProcessingTapGetSourceAudio(). Assuming you have a single render callback for all your AU's and assuming you have an array with all the AU's you created:
UInt64 processedFrames;
UInt32 curAudioUnit;
UInt32 audioUnitCount;
AudioUnit audioUnits[MAX_AUDIO_UNITS];
OSStatus AU_RenderCallback(void *tap, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
curAudioUnit++;
if (curAudioUnit == audioUnitCount)
return MTAudioProcessingTapGetSourceAudio(tap, inNumberFrames, ioData, NULL, NULL, NULL);
AudioTimeStamp audioTimeStamp;
audioTimeStamp.mSampleTime = processedFrames;
audioTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
return AudioUnitRender(audioUnits[curAudioUnit], 0, &audioTimeStamp, 0, inNumberFrames, ioData);
}
void tap_ProcessCallback(MTAudioProcessingTapRef tap, CMItemCount inNumberFrames, MTAudioProcessingTapFlags flags, AudioBufferList *bufferListInOut, CMItemCount *numberFramesOut, MTAudioProcessingTapFlags *flagsOut)
{
if (audioUnitCount)
{
curAudioUnit = 0;
AudioTimeStamp audioTimeStamp;
audioTimeStamp.mSampleTime = processedFrames;
audioTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
AudioUnitRender(audioUnits[curAudioUnit], 0, &audioTimeStamp, 0, inNumberFrames, bufferListInOut);
}
else
MTAudioProcessingTapGetSourceAudio(tap, inNumberFrames, bufferListInOut, flagsOut, NULL, numberFramesOut);
processedFrames += inNumberFrames;
}

How to write output of AUGraph to a file?

I am trying to write (what should be) a simple app that has a bunch of audio units in sequence in an AUGraph and then writes the output to a file. I added a callback using AUGraphAddRenderNotify. Here is my callback function:
OSStatus MyAURenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *actionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
if (*actionFlags & kAudioUnitRenderAction_PostRender) {
ExtAudioFileRef outputFile = (ExtAudioFileRef)inRefCon;
ExtAudioFileWriteAsync(outputFile, inNumberFrames, ioData);
}
}
This sort of works. The file is playable and I can hear what I recorded but there is horrible amounts of static that makes it barely audible.
Does anybody know what is wrong with this? Or does anyone know of a better way to record the AUGraph output to a file?
Thanks for the help.
I had a epiphany with regards to Audio Units just now which helped me solve my own problem. I had a misconception about how audio unit connections and render callbacks work. I thought they were completely separate things but it turns out that a connection is just short hand for a render callback.
Doing an kAudioUnitProperty_MakeConnection from the output of audio unit A to the input of audio unit B is the same as doing kAudioUnitProperty_SetRenderCallback on the input of unit B and having the callback function call AudioUnitRender on the output of audio unit A.
I tested this by doing a make connection after setting my render callback and the render callback was no longer invoked.
Therefore, I was able to solve my problem by doing the following:
AURenderCallbackStruct callbackStruct = {0};
callbackStruct.inputProc = MyAURenderCallback;
callbackStruct.inputProcRefCon = mixerUnit;
AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&callbackStruct,
sizeof(callbackStruct));
And them my callback function did something like this:
OSStatus MyAURenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *actionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
AudioUnit mixerUnit = (AudioUnit)inRefCon;
AudioUnitRender(mixerUnit,
actionFlags,
inTimeStamp,
0,
inNumberFrames,
ioData);
ExtAudioFileWriteAsync(outputFile,
inNumberFrames,
ioData);
return noErr;
}
This probably should have been obvious to me but since it wasn't I'll bet there are others that were confused in the same way so hopefully this is helpful to them too.
I'm still not sure why I had trouble with the AUGraphAddRenderNotify callback. I will dig deeper into this later but for now I found a solution that seems to work.
Here is some sample code from Apple (the project is PlaySequence, but it isn't MIDI specific) that might help:
{
CAStreamBasicDescription clientFormat = CAStreamBasicDescription();
ca_require_noerr (result = AudioUnitGetProperty(outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, 0,
&clientFormat, &size), fail);
size = sizeof(clientFormat);
ca_require_noerr (result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat), fail);
{
MusicTimeStamp currentTime;
AUOutputBL outputBuffer (clientFormat, numFrames);
AudioTimeStamp tStamp;
memset (&tStamp, 0, sizeof(AudioTimeStamp));
tStamp.mFlags = kAudioTimeStampSampleTimeValid;
int i = 0;
int numTimesFor10Secs = (int)(10. / (numFrames / srate));
do {
outputBuffer.Prepare();
AudioUnitRenderActionFlags actionFlags = 0;
ca_require_noerr (result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL()), fail);
tStamp.mSampleTime += numFrames;
ca_require_noerr (result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL()), fail);
ca_require_noerr (result = MusicPlayerGetTime (player, &currentTime), fail);
if (shouldPrint && (++i % numTimesFor10Secs == 0))
printf ("current time: %6.2f beats\n", currentTime);
} while (currentTime < sequenceLength);
}
}
Maybe try this. Copy the data from the audio unit callback to a long buffer. Play the buffer to test it, then write the entire buffer to a file after you have verified that the whole buffer is OK.

How do I stop RemoteIO when file is finished?

I'm using the following to play audio:
OSStatus PlayRenderCallback (
void * inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
EffectState *effectState = (EffectState*) inRefCon;
if(effectState->play)
{
XThrowIfError(ExtAudioFileRead(effectState->sourceFile, &inNumberFrames, ioData), "Read failed!");
if(inNumberFrames == 0)
{
return noErr;
}
Then I process the audio and send it to the buffer.
What I want to know is what do I put in the return to stop playback? I tried:
AudioOutputUnitStop(remoteIOUnit);
And that works, once. I can't start the unit again.
Thanks all,
Jim
It seems you cannot stop a copy of a remote io. Rather than copying I used only the rio in my struct and now it works.

Resources