Concatenating Audio Buffers in ObjectiveC - ios

First of all I am new bee on c and objective c
I try to fft a buffer of audio and plot the graph of it.
I use audio unit callback to get audio buffer. the callback brings 512 frames but after 471 frames it brings 0. (I dont know this is normal or not. It used to bring 471 frames with full of numbers. but now somehow 512 frames with 0 after 471. Please let me know if this is normal)
Anyway. I can get the buffer from the callback, apply fft and draw it . this works perfect. and here is the outcome below. the graph very smooth as long as I get buffer in each callback
but in my case I need 3 second of buffer in order to apply fft and draw. so I try to concatenate the buffers from two callback and then apply fft and draw it. but the result is not like what I expect . while the above one is very smooth and precise during record( only the magnitude change on the 18 and 19 khz), when I concatenate the two buffers, the simualator display mainly two different views that swapping between them very fast. they are displayed below. Of course they basically display 18 and 19 khz. but I need precise khz so I can apply more algorithms for the app I work on.
and here is my code in callback
//FFTInputBufferLen, FFTInputBufferFrameIndex is gloabal
//also tempFilteredBuffer is allocated in global
//by the way FFTInputBufferLen = 1024;
static OSStatus performRender (void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
UInt32 bus1 = 1;
CheckError(AudioUnitRender(effectState.rioUnit,
ioActionFlags,
inTimeStamp,
bus1,
inNumberFrames,
ioData), "Couldn't render from RemoteIO unit");
Float32 * renderBuff = ioData->mBuffers[0].mData;
ViewController *vc = (__bridge ViewController *) inRefCon;
// inNumberFrames comes 512 as I described above
for (int i = 0; i < inNumberFrames ; i++)
{
//I defined InputBuffers[5] in global.
//then added 5 Float32 InputBuffers and allocated in global
InputBuffers[bufferCount][FFTInputBufferFrameIndex] = renderBuff[i];
FFTInputBufferFrameIndex ++;
if(FFTInputBufferFrameIndex == FFTInputBufferLen)
{
int bufCount = bufferCount;
dispatch_async( dispatch_get_main_queue(), ^{
tempFilteredBuffer = [vc FilterData_rawSamples:InputBuffers[bufCount] numSamples:FFTInputBufferLen];
[vc CalculateFFTwithPlotting_Data:tempFilteredBuffer NumberofSamples:FFTInputBufferLen ];
free(InputBuffers[bufCount]);
InputBuffers[bufCount] = (Float32*)malloc(sizeof(Float32) * FFTInputBufferLen);
});
FFTInputBufferFrameIndex = 0;
bufferCount ++;
if (bufferCount == 5)
{
bufferCount = 0;
}
}
}
return noErr;
}
here is my AudioUnit setup
- (void)setupIOUnit
{
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
AudioComponent comp = AudioComponentFindNext(NULL, &desc);
CheckError(AudioComponentInstanceNew(comp, &_rioUnit), "couldn't create a new instance of AURemoteIO");
UInt32 one = 1;
CheckError(AudioUnitSetProperty(_rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof(one)), "could not enable input on AURemoteIO");
// I removed this in order to not getting recorded audio back on speakers! Am I right?
//CheckError(AudioUnitSetProperty(_rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &one, sizeof(one)), "could not enable output on AURemoteIO");
UInt32 maxFramesPerSlice = 4096;
CheckError(AudioUnitSetProperty(_rioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(UInt32)), "couldn't set max frames per slice on AURemoteIO");
UInt32 propSize = sizeof(UInt32);
CheckError(AudioUnitGetProperty(_rioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, &propSize), "couldn't get max frames per slice on AURemoteIO");
AudioUnitElement bus1 = 1;
AudioStreamBasicDescription myASBD;
myASBD.mSampleRate = 44100;
myASBD.mChannelsPerFrame = 1;
myASBD.mFormatID = kAudioFormatLinearPCM;
myASBD.mBytesPerFrame = sizeof(Float32) * myASBD.mChannelsPerFrame ;
myASBD.mFramesPerPacket = 1;
myASBD.mBytesPerPacket = myASBD.mFramesPerPacket * myASBD.mBytesPerFrame;
myASBD.mBitsPerChannel = sizeof(Float32) * 8 ;
myASBD.mFormatFlags = 9 | 12 ;
// I also remove this for not getting audio back!!
// CheckError(AudioUnitSetProperty (_rioUnit,
// kAudioUnitProperty_StreamFormat,
// kAudioUnitScope_Input,
// bus0,
// &myASBD,
// sizeof (myASBD)), "Couldn't set ASBD for RIO on input scope / bus 0");
CheckError(AudioUnitSetProperty (_rioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
bus1,
&myASBD,
sizeof (myASBD)), "Couldn't set ASBD for RIO on output scope / bus 1");
effectState.rioUnit = _rioUnit;
AURenderCallbackStruct renderCallback;
renderCallback.inputProc = performRender;
renderCallback.inputProcRefCon = (__bridge void *)(self);
CheckError(AudioUnitSetProperty(_rioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&renderCallback,
sizeof(renderCallback)), "couldn't set render callback on AURemoteIO");
CheckError(AudioUnitInitialize(_rioUnit), "couldn't initialize AURemoteIO instance");
}
My questions are : why this happens, why there are two main different views on output when I concatenate the two buffers. is there another way to collect buffers and apply DSP? what do I do wrong! if the way I concatenate is correct, is my logic incorrect? (though I checked it many times)
Here I try to say : how can I get 3 sn of buffer in perfect condition
I really need help , best Regards

Your render callback may be writing data into the same buffer that is being processed in another thread (the main queue), thus overwriting and altering part of the data being processed.
Try using more than one buffer. Don't write into a buffer that is still being processed (by your filter & fft methods). Perhaps recycle the buffers for reuse after the FFT calculation method is finished.

I have successfully cancatenated the buffers without any unstable graphics. how did I do is to convert AVAudioSession category to PlayAndRecord from Record. then I have commented out the two AudioUnitSetProperty lines . then I started to get 470~471 frames per render. then I cancenated them like I did on the code I have posted. I have also used buffers in the code too. Now it works. But now it plays through the sound . In order to close it I applied the code below
for (UInt32 i=0; i<ioData->mNumberBuffers; ++i)
{
memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize);
}
then I started to get 3sec of buffers. when I plot it on the screen I got a similar view of the first graph

Related

AudioConverterRef sample rate conversion (iOS)

I write a voip app that uses "novocaine" library for recording and playback of sound. I set sample rate as 8kHz. This sample rate is set in novocaine in AudioStreamBasicDescription of audio unit and as audio session property kAudioSessionProperty_PreferredHardwareSampleRate. I understand that setting preferred hardware sample rate has no guarantee that actual hardware sample rate will be changed but it worked for all devices except iPhone6s and iPhone6s+ (when route is changed to speaker). With iPhone6s(+) and speaker route I receive 48kHz sound from microphone. So I need to somehow convert this 48 kHz sound to 8kHz. In documentation I found that AudioConverterRef can be used in this case but I have troubles with using it.
I use AudioConverterFillComplexBuffer for sample rate conversion but it always returns -50 OSStatus (one or more parameters passed to the function were not valid). This is how I use audio converter:
// Setup AudioStreamBasicDescription for input
inputFormat.mSampleRate = 48000.0;
inputFormat.mFormatID = kAudioFormatLinearPCM;
inputFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
inputFormat.mChannelsPerFrame = 1;
inputFormat.mBitsPerChannel = 8 * sizeof(float);
inputFormat.mFramesPerPacket = 1;
inputFormat.mBytesPerFrame = sizeof(float) * inputFormat.mChannelsPerFrame;
inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;
// Setup AudioStreamBasicDescription for output
outputFormat.mSampleRate = 8000.0;
outputFormat.mFormatID = kAudioFormatLinearPCM;
outputFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
outputFormat.mChannelsPerFrame = 1;
outputFormat.mBitsPerChannel = 8 * sizeof(float);
outputFormat.mFramesPerPacket = 1;
outputFormat.mBytesPerFrame = sizeof(float) * outputFormat.mChannelsPerFrame;
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;
// Create new instance of audio converter
AudioConverterNew(&inputFormat, &outputFormat, &converter);
// Set conversion quality
UInt32 tmp = kAudioConverterQuality_Medium;
AudioConverterSetProperty( converter, kAudioConverterCodecQuality,
sizeof( tmp ), &tmp );
AudioConverterSetProperty( converter, kAudioConverterSampleRateConverterQuality, sizeof( tmp ), &tmp );
// Get the size of the IO buffer(s)
UInt32 bufferSizeFrames = 0;
size = sizeof(UInt32);
AudioUnitGetProperty(self.inputUnit,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global,
0,
&bufferSizeFrames,
&size);
UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);
// Allocate an AudioBufferList plus enough space for array of AudioBuffers
UInt32 propsize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * outputFormat.mChannelsPerFrame);
// Malloc buffer lists
convertedInputBuffer = (AudioBufferList *)malloc(propsize);
convertedInputBuffer->mNumberBuffers = 1;
// Pre-malloc buffers for AudioBufferLists
convertedInputBuffer->mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame;
convertedInputBuffer->mBuffers[0].mDataByteSize = bufferSizeBytes;
convertedInputBuffer->mBuffers[0].mData = malloc(bufferSizeBytes);
memset(convertedInputBuffer->mBuffers[0].mData, 0, bufferSizeBytes);
// Setup callback for converter
static OSStatus inputProcPtr(AudioConverterRef inAudioConverter,
UInt32* ioNumberDataPackets,
AudioBufferList* ioData,
AudioStreamPacketDescription* __nullable* __nullable outDataPacketDescription,
void* __nullable inUserData)
{
// Read data from buffer
}
// Perform actual sample rate conversion
AudioConverterFillComplexBuffer(converter, inputProcPtr, NULL, &numberOfFrames, convertedInputBuffer, NULL)
inputProcPtr callback is never called. I tried to set different number of frames but still receive OSStatus -50.
1) Is using AudioConverterRef is correct way to make sample rate conversion or it could be done in different way?
2) What is wrong with my conversion implementation?
Thank you all in advance
One problem is this:
AudioUnitGetProperty(self.inputUnit,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global,
0,
&bufferSizeFrames,
&size);
kAudioDevicePropertyBufferFrameSize is an OSX property, and doesn't exist on iOS. How is this code even compiling?
If you've somehow made it compile, check the return code from this function! I've got a feeling that it's failing, and bufferSizeFrames is zero. That would make AudioConverterFillComplexBuffer return -50 (kAudio_ParamError).
So on iOS, either pick a bufferSizeFrames yourself or base it on AVAudioSession's IOBufferDuration if you must.
Another problem: check your return codes. All of them!
e.g.
UInt32 tmp = kAudioConverterQuality_Medium;
AudioConverterSetProperty( converter, kAudioConverterCodecQuality,
sizeof( tmp ), &tmp );
I'm pretty sure there's no codec to speak of in LPCM->LPCM conversions, and that kAudioConverterQuality_Medium is not the right value to use with kAudioConverterCodecQuality in any case. I don't see how this call can succeed.

Writing and reading an audio file with Extended Audio File

I m using ExtAudioFileWriteAsync to write an audio file while using device recording, but once I get recording finished I try to read it with ExtAudioFileRead function and samples I get are not same samples I m writing... Anyone know why this could happen?
For writing:
self.audioManager.inputBlock = ^(float *data, UInt32 numFrames, UInt32 numChannels) {
for (int i = 0; i < numFrames*numChannels; i++) {
printf("write*%f\n", data[i]);
}
UInt32 numIncomingBytes = numFrames*numChannels*sizeof(float);
UInt32 *outputBuffer =(UInt32*)malloc(numIncomingBytes);
memcpy(outputBuffer, recordedData, numIncomingBytes);
AudioBufferList outgoingAudio;
outgoingAudio.mNumberBuffers = 1;
outgoingAudio.mBuffers[0].mNumberChannels = numChannels;
outgoingAudio.mBuffers[0].mDataByteSize = numIncomingBytes;
outgoingAudio.mBuffers[0].mData = self.outputBuffer;
if( 0 == pthread_mutex_trylock( &outputAudioFileLock ) )
{
ExtAudioFileWriteAsync(outputFile, numFrames, &outgoingAudio);
}
pthread_mutex_unlock( &outputAudioFileLock );
};
[self.audioManager play];
For reading:
UInt32 *outputBuffer = (UInt32 *)malloc(numFrames*numChannels*sizeof(float));
AudioBufferList convertedData;
convertedData.mNumberBuffers = 1;
convertedData.mBuffers[0].mNumberChannels = numChannels;
convertedData.mBuffers[0].mDataByteSize = numFrames*numChannels*sizeof(float);
convertedData.mBuffers[0].mData = outputBuffer;
NSMutableArray *samplesArray = [[NSMutableArray alloc]init];
while (numFrames > 0) {
ExtAudioFileRead(inputFile, &numFrames, &convertedData);
if (numFrames > 0) {
AudioBuffer audioBuffer = convertedData.mBuffers[0];
float *samples = (float *)audioBuffer.mData;
for (int i = 0; i < frameCount*numChannels; i++) {
printf("read*%f\n", samples[i]);
}
}
}
By the way I'm using Novocaine project in order to get device audio. I can reproduce saved audio with Novocaine code or with any other player.
When writing ExtAudioFileRef output :
ExtAudioFileCreateWithURL(audioFileRef, kAudioFileM4AType, &outputFileDesc, NULL, kAudioFileFlags_EraseFile, &outputFile);
Where outputFileDesc is
AudioStreamBasicDescription outputFileDesc = {44100.0, kAudioFormatMPEG4AAC, 0, 0, 1024, 0, thisNumChannels, 0, 0};
outputFileDesc.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
When reading ExtAudioFileRef inputFile:
ExtAudioFileOpenURL(audioFileRef, &inputFile):
And in both cases (writing and reading) it is applied same format:
AudioStreamBasicDescription outputFormat;
_outputFormat.mSampleRate = self.samplingRate;
_outputFormat.mFormatID = kAudioFormatLinearPCM;
_outputFormat.mFormatFlags = kAudioFormatFlagIsFloat;
_outputFormat.mBytesPerPacket = 4*self.numChannels;
_outputFormat.mFramesPerPacket = 1;
_outputFormat.mBytesPerFrame = 4*self.numChannels;
_outputFormat.mChannelsPerFrame = self.numChannels;
_outputFormat.mBitsPerChannel = 32;
ExtAudioFileSetProperty(outputFile, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &_outputFormat);
ExtAudioFileSetProperty(inputFile, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &_outputFormat);
And by the way, even if read samples are not equal to written samples, mean value of both signals are quite similar. But I do not fully understand why are not totally equal!
Any idea what I'm doing wrong?
It sounds like there is an implicit format conversion from one or both of the ExtaudioFileRefs and you are seeing different samples as a result of the conversion. You have three formats: audio_in_format, file_format, and audio_out_format. If audio_in_format is different than file_format, The writing ExtAudioFileRef will create an audio converter for you to convert the input audio to file_format before writing to disk. And the reading ExtAudioFileRef will also create a converter if file_format is different than audio_out_format.
Opinion:
It's confusing that you named your writing ExtAudioFileRef "outputFile", and your reading ExtAudioFileRef "inputFile". I would use something like audioWriter and audioReader.

Core Audio: Float32 to SInt16 conversion artefacts

I am converting from the following format:
const int four_bytes_per_float = 4;
const int eight_bits_per_byte = 8;
_stereoGraphStreamFormat.mFormatID = kAudioFormatLinearPCM;
_stereoGraphStreamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
_stereoGraphStreamFormat.mBytesPerPacket = four_bytes_per_float;
_stereoGraphStreamFormat.mFramesPerPacket = 1;
_stereoGraphStreamFormat.mBytesPerFrame = four_bytes_per_float;
_stereoGraphStreamFormat.mChannelsPerFrame = 2;
_stereoGraphStreamFormat.mBitsPerChannel = eight_bits_per_byte * four_bytes_per_float;
_stereoGraphStreamFormat.mSampleRate = 44100;
to the following format:
interleavedAudioDescription.mFormatID = kAudioFormatLinearPCM;
interleavedAudioDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger;
interleavedAudioDescription.mChannelsPerFrame = 2;
interleavedAudioDescription.mBytesPerPacket = sizeof(SInt16)*interleavedAudioDescription.mChannelsPerFrame;
interleavedAudioDescription.mFramesPerPacket = 1;
interleavedAudioDescription.mBytesPerFrame = sizeof(SInt16)*interleavedAudioDescription.mChannelsPerFrame;
interleavedAudioDescription.mBitsPerChannel = 8 * sizeof(SInt16);
interleavedAudioDescription.mSampleRate = 44100;
Using the following code:
int32_t availableBytes = 0;
void* tailL = TPCircularBufferTail(inputBufferL(), &availableBytes);
void* tailR = TPCircularBufferTail(inputBufferR(), &availableBytes);
// If we have no data in the buffer, we simply return
if (availableBytes <= 0)
{
return;
}
// ========== Non-Interleaved to Interleaved (Plus Samplerate Conversion) =========
// Get the number of frames available
UInt32 frames = availableBytes / this->mInputFormat.mBytesPerFrame;
pcmOutputBuffer->mBuffers[0].mDataByteSize = frames * interleavedAudioDescription.mBytesPerFrame;
struct complexInputDataProc_t data = (struct complexInputDataProc_t) { .self = this, .sourceL = tailL, .sourceR = tailR, .byteLength = availableBytes };
// Do the conversion
OSStatus result = AudioConverterFillComplexBuffer(interleavedAudioConverter,
complexInputDataProc,
&data,
&frames,
pcmOutputBuffer,
NULL);
// Tell the buffers how much data we consumed during the conversion so that it can be removed
TPCircularBufferConsume(inputBufferL(), availableBytes);
TPCircularBufferConsume(inputBufferR(), availableBytes);
// ========== Buffering Of Interleaved Samples =========
// If we got converted frames back from the converter, we want to add it to a separate buffer
if (frames > 0)
{
// Make sure we have enough space in the buffer to store the new data
TPCircularBufferHead(&pcmCircularBuffer, &availableBytes);
if (availableBytes > pcmOutputBuffer->mBuffers[0].mDataByteSize)
{
// Add the newly converted data to the buffer
TPCircularBufferProduceBytes(&pcmCircularBuffer, pcmOutputBuffer->mBuffers[0].mData, frames * interleavedAudioDescription.mBytesPerFrame);
}
else
{
printf("No Space in Buffer\n");
}
}
However I am getting the following output:
It should be a perfect sine wave, however as you can see it is not.
I have been working on this for days now and just can’t seem to find where it is going wrong.
Can anyone see something that I might be missing?
Edit:
Buffer initialisation:
TPCircularBuffer pcmCircularBuffer;
static SInt16 pcmOutputBuf[BUFFER_SIZE];
pcmOutputBuffer = (AudioBufferList*)malloc(sizeof(AudioBufferList));
pcmOutputBuffer->mNumberBuffers = 1;
pcmOutputBuffer->mBuffers[0].mNumberChannels = 2;
pcmOutputBuffer->mBuffers[0].mData = pcmOutputBuf;
TPCircularBufferInit(&pcmCircularBuffer, BUFFER_SIZE);
Complex input data proc:
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData) {
struct complexInputDataProc_t *arg = (struct complexInputDataProc_t*)inUserData;
BroadcastingServices::MP3Encoder *self = (BroadcastingServices::MP3Encoder*)arg->self;
if ( arg->byteLength <= 0 )
{
*ioNumberDataPackets = 0;
return 100; //kNoMoreDataErr;
}
UInt32 framesAvailable = arg->byteLength / self->interleavedAudioDescription.mBytesPerFrame;
if (*ioNumberDataPackets > framesAvailable)
{
*ioNumberDataPackets = framesAvailable;
}
ioData->mBuffers[0].mData = arg->sourceL;
ioData->mBuffers[0].mDataByteSize = arg->byteLength;
ioData->mBuffers[1].mData = arg->sourceR;
ioData->mBuffers[1].mDataByteSize = arg->byteLength;
arg->byteLength = 0;
return noErr;
}
I see a few things that raise a red flag.
1) as mentioned in a comment above, the fact that you are overwriting availableBytes for the left input with that from the right:
void* tailL = TPCircularBufferTail(inputBufferL(), &availableBytes);
void* tailR = TPCircularBufferTail(inputBufferR(), &availableBytes);
If the two input streams are changing asynchronously to this code then most certainly you have a race condition.
2) Truncation errors: availableBytes is not necessarily a multiple of bytes per frame. If not then the following bit of code could cause you to consume more bytes than you actually converted.
void* tailL = TPCircularBufferTail(inputBufferL(), &availableBytes);
void* tailR = TPCircularBufferTail(inputBufferR(), &availableBytes);
...
UInt32 frames = availableBytes / this->mInputFormat.mBytesPerFrame;
...
TPCircularBufferConsume(inputBufferL(), availableBytes);
TPCircularBufferConsume(inputBufferR(), availableBytes);
3) If the output buffer is not ready to consume all of the input you just throw the input buffer away. That happens in this code.
if (availableBytes > pcmOutputBuffer->mBuffers[0].mDataByteSize)
{
...
}
else
{
printf("No Space in Buffer\n");
}
I'd be really curious if your seeing the print output.
Here's is how I would suggest doing it. It's going to be pseudo-codeish since I don't have anything necessary to compile and test it.
int32_t availableBytesInL = 0;
int32_t availableBytesInR = 0;
int32_t availableBytesOut = 0;
// figure out how many bytes are available in each buffer.
void* tailL = TPCircularBufferTail(inputBufferL(), &availableBytesInL);
void* tailR = TPCircularBufferTail(inputBufferR(), &availableBytesInR);
TPCircularBufferHead(&pcmCircularBuffer, &availableBytesOut);
// figure out how many full frames are available
UInt32 framesInL = availableBytesInL / mInputFormat.mBytesPerFrame;
UInt32 framesInR = availableBytesInR / mInputFormat.mBytesPerFrame;
UInt32 framesOut = availableBytesOut / interleavedAudioDescription.mBytesPerFrame;
// figure out how many frames to process this time.
UInt32 frames = min(min(framesInL, framesInL), framesOut);
if (frames == 0)
return;
int32_t bytesConsumed = frames * mInputFormat.mBytesPerFrame;
struct complexInputDataProc_t data = (struct complexInputDataProc_t) {
.self = this, .sourceL = tailL, .sourceR = tailR, .byteLength = bytesConsumed };
// Do the conversion
OSStatus result = AudioConverterFillComplexBuffer(interleavedAudioConverter,
complexInputDataProc,
&data,
&frames,
pcmOutputBuffer,
NULL);
int32_t bytesProduced = frames * interleavedAudioDescription.mBytesPerFrame;
// Tell the buffers how much data we consumed during the conversion so that it can be removed
TPCircularBufferConsume(inputBufferL(), bytesConsumed);
TPCircularBufferConsume(inputBufferR(), bytesConsumed);
TPCircularBufferProduceBytes(&pcmCircularBuffer, pcmOutputBuffer->mBuffers[0].mData, bytesProduced);
Basically what I've done here is to figure out up front how many frames should be processed making sure I'm only processing as many frames as the output buffer can handle. If it were me I'd also add some checking for buffer underruns on the output and buffer overruns on the input. Finally, I'm not exactly sure of the semantics of AudioConverterFillComplexBuffer wrt the frame parameter that is passing in and out. It's conceivable that the # frames out would be less or more than the number of frames in. Although, since your not doing sample rate conversion that's probably not going to happen. I've attempted to account for that condition by assigning bytesProduced after the conversion.
Hope this helps. If not you have 2 other clues. One is that the drop outs are periodic and two is that the size of the drop outs looks to be about the same. If you can figure out how many samples each are then you can look for those numbers in your code.
I don't think your output buffer, pcmCircularBuffer, is big enough.
Try replacing
TPCircularBufferInit(&pcmCircularBuffer, BUFFER_SIZE);
with
TPCircularBufferInit(&pcmCircularBuffer, sizeof(pcmOutputBuf));
Even if that is the solution, I think there are some problems with your code. I don't know exactly what you're doing, I guess encoding mp3 (which by itself is an uphill battle on iOS, why not use hardware AAC?), but unless you have realtime demands on both input and output, why use ring buffers at all? Also, I recommend using units to visually catch type frame/byte size mismatches: e.g. BUFFER_SIZE_IN_FRAMES
If it's not the solution, then I want to see the sine generator.

Setting up audio unit iOS with mono input and stereo output

I'm trying to set up an audio unit capable of mono input and stereo output. Intend on playing a sine wave tone through the left channel output and a different sign wave periodically through the right channel out.
I am receiving the error,
'NSInternalInconsistencyException', reason: ' Error initialing unit: -50;
when I attempt to initialize my audio unit here,
// Initialize audio unit
OSErr err = AudioUnitInitialize(self.ioUnit);
NSAssert1(err == noErr, #"Error initializing unit: %hd", err);
I believe it has to do with how I am setting up the audio unit,
// Audio component description
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
// Get component
AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
// Get Audio units
AudioComponentInstanceNew(inputComponent, &ioUnit);
// Enable input, which disabled by default. Output enable by default
UInt32 enableInput = 1;
AudioUnitSetProperty(ioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
kInputBus,
&enableInput,
sizeof(enableInput));
AudioStreamBasicDescription monoStreamFormat;
monoStreamFormat.mSampleRate = 44100.00;
monoStreamFormat.mFormatID = kAudioFormatLinearPCM;
monoStreamFormat.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
monoStreamFormat.mBytesPerPacket = 2;
monoStreamFormat.mBytesPerFrame = 2;
monoStreamFormat.mFramesPerPacket = 1;
monoStreamFormat.mChannelsPerFrame = 1;
monoStreamFormat.mBitsPerChannel = 16;
// Apply format to input of ioUnit
AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kInputBus,
&monoStreamFormat,
sizeof(monoStreamFormat));
AudioStreamBasicDescription stereoStreamFormat;
stereoStreamFormat.mSampleRate = 44100.00;
stereoStreamFormat.mFormatID = kAudioFormatLinearPCM;
stereoStreamFormat.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
stereoStreamFormat.mBytesPerPacket = 4;
stereoStreamFormat.mBytesPerFrame = 4;
stereoStreamFormat.mFramesPerPacket = 1;
stereoStreamFormat.mChannelsPerFrame = 2;
stereoStreamFormat.mBitsPerChannel = 32;
// Apply format to output of ioUnit
AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kOutputBus,
&stereoStreamFormat,
sizeof(stereoStreamFormat));
// Set input callback
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = inputCallback;
callbackStruct.inputProcRefCon = (__bridge void *)(self);
AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
kInputBus,
&callbackStruct,
sizeof(callbackStruct));
// Set output callback
callbackStruct.inputProc = outputCallback;
callbackStruct.inputProcRefCon = (__bridge void *)(self);
AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
kOutputBus,
&callbackStruct,
sizeof(callbackStruct));
but I couldn't find anything on the error code -50 it's giving back so I'm not sure what it doesn't like. I'm on a deadline so any help it greatly appreciated.
kAudioFormatFlagsAudioUnitCanonical is 32-bit 8.24 fixed point format. That means mBytesPerFrame should be 4 for mono and 8 for stereo, and mBitsPerChannel should be 32 for both stereo and mono. Setting it to 16-bits for would probably produce this error.
That said, I doubt you want 8.24. You probably want kAudioFormatFlagsCanonical to get standard 16-bit audio. Or, if you want 32 bit, go with 32-bit float (kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved). Easier to deal with the math during your processing.
Also, you are setting the formats on the virtual inputs and outputs rather than the client inputs and outputs, which I don't think is what you want. The API here is rather confusing, but to set client input format, use kAudioUnitScope_Input, kOutputBus. The logic here is that you're setting the format of the output of the internal Core Audio input converter unit, which, from the perspective of your code, is the input audio. Confusing, I know. See Audio Unit docs for more detail. Likewise, to set client output format, use kAudioUnitScope_Output, kInputBus.
32 bits per channel is too many bits to fit 2 channels into 4 bytes. Try 16.

info about ExtFileReader C API for iOS audio file manipulation

So ,I need to reverse some audio *.caf file,
I have seen that the way to do it should be:
You cannot just reverse the byte data. I have achieved the same
effect using CoreAudio and AudioUnits. Use ExtFileReader C API to read
the file into lPCM buffers and then you can reverse the buffers as
needed.
But I cannot find any documentation of the use of
ExtFileReader C API
So if I have a *.caf file, how can I read it in to a linear PCM, I have checked the Core Audio overview but cant find how to accomplish this?
How can i then, read my caf file to linear PCM?
thanks!
ExtendedAudioFile is in the AudioToolbox framework. It's pretty straightforward to read in a file to whatever format you'd like. Here's a quick (compiles, but not tested) example of reading in to 32-bit float non-interleaved Linear PCM:
#import <AudioToolbox/AudioToolbox.h>
...
ExtAudioFileRef audioFile = NULL;
CFURLRef url = NULL;
OSStatus err = ExtAudioFileOpenURL(url, &audioFile);
AudioStreamBasicDescription asbd;
UInt32 dataSize = sizeof(asbd);
// get the audio file's format
err = ExtAudioFileGetProperty(audioFile, kExtAudioFileProperty_FileDataFormat, &dataSize, &asbd);
// now set the client format to what we want on read (LPCM, 32-bit floating point)
AudioStreamBasicDescription clientFormat = asbd;
clientFormat.mFormatID = kAudioFormatLinearPCM;
clientFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagIsPacked;
clientFormat.mBitsPerChannel = 32;
clientFormat.mBytesPerPacket = 4;
clientFormat.mFramesPerPacket = 1;
clientFormat.mBytesPerFrame = 4;
err = ExtAudioFileSetProperty(audioFile, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);
// okay, now the ext audio file is set up to convert samples to LPCM on read
// get the total number of samples
SInt64 numFrames = 0;
dataSize = sizeof(numFrames);
err = ExtAudioFileGetProperty(audioFile, kExtAudioFileProperty_FileLengthFrames, &dataSize, &numFrames);
// prepare an audio buffer list to hold the data when we read it from the file
UInt32 maxReadFrames = 4096; // how many samples will we read in at a time?
AudioBufferList *bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer) * (asbd.mChannelsPerFrame - 1));
bufferList->mNumberBuffers = asbd.mChannelsPerFrame;
for (int ii = 0; ii < bufferList->mNumberBuffers; ++ii) {
bufferList->mBuffers[ii].mDataByteSize = maxReadFrames * sizeof(float);
bufferList->mBuffers[ii].mData = malloc(bufferList->mBuffers[ii].mDataByteSize);
bzero(bufferList->mBuffers[ii].mData, bufferList->mBuffers[ii].mDataByteSize);
bufferList->mBuffers[ii].mNumberChannels = 1;
}
while(numFrames > 0) {
UInt32 framesToRead = (maxReadFrames > numFrames) ? numFrames : maxReadFrames;
err = ExtAudioFileRead(audioFile, &framesToRead, bufferList);
// okay, your LPCM audio data is in `bufferList` -- do whatever processing you'd like!
}
// clean up
for (int ii = 0; ii < bufferList->mNumberBuffers; ++ii) {
free(bufferList->mBuffers[ii].mData);
}
free(bufferList);
ExtAudioFileDispose(audioFile);

Resources