Search code examples
objective-ccore-audiocircular-bufferextaudiofileread

Glitches occur when data is read using ExtAudioFile in combination with a circular buffer


I am trying to process audio data that is read using ExtAudioFile. However when I try to put this data in a circular buffer I hear several glitches.

In the example code below I pass data from "inBuffer" directly to "outBuffer" (both of type TPCircularBuffer by Michael Tyson). Both buffers are of size 10 x numberOfFrames * sizeof(float). The client stream format is mono with float samples.

Why do I hear these glitches without even processing any data? Without a circular buffer the sound streams well. What do I wrong or would another approach better?

static OSStatus recordingCallback(
                                  void*                       inRefCon,
                                  AudioUnitRenderActionFlags* ioActionFlags,
                                  const AudioTimeStamp*       inTimeStamp,
                                  UInt32                      inBusNumber,
                                  UInt32                      inNumberFrames,
                                  AudioBufferList*            ioData){
    PlayAudioFile* player = (__bridge PlayAudioFile*)inRefCon;
    Info *info = (Info *)player.info;


    ExtAudioFileRead(info->extAudioFile, &inNumberFrames, info->inputBuffer);

    if (inNumberFrames == 0) {
        ExtAudioFileSeek(info->extAudioFile, 0);
    }

    // Put data in circular buffer
     TPCircularBufferProduceBytes(info->inBuffer, info->inputBuffer->mBuffers[0].mData, info->inputBuffer->mBuffers[0].mDataByteSize);


    UInt32 frameSize = inNumberFrames*sizeof(float);
    if (info->inBuffer->fillCount >= frameSize) {
        int32_t availableBytes;

        float *tail = TPCircularBufferTail(info->inBuffer, &availableBytes);
        if (availableBytes > frameSize){
            memcpy(info->tempBuffer, tail, frameSize);
            TPCircularBufferConsume(info->inBuffer, frameSize);

            // Currently, data is not processed but just put to the output buffer

            TPCircularBufferProduceBytes(info->outBuffer, info->tempBuffer, frameSize);
        }
    }

    return noErr;
}



static OSStatus renderCallback(
                               void*                       inRefCon,
                               AudioUnitRenderActionFlags* ioActionFlags,
                               const AudioTimeStamp*       inTimeStamp,
                               UInt32                      inBusNumber,
                               UInt32                      inNumberFrames,
                               AudioBufferList*            ioData){
    PlayAudioFile* player = (__bridge PlayAudioFile*)inRefCon;
    Info *info = (Info *)play.info;

    for (int i=0; i < ioData->mNumberBuffers; i++) {
        AudioBuffer buffer = ioData->mBuffers[i];

        if (info->outBuffer->fillCount > ioData->mBuffers[0].mDataByteSize) {

            int32_t availableBytes;
            float *tail = TPCircularBufferTail(info->outBuffer, &availableBytes);

            memcpy(buffer.mData, tail, buffer.mDataByteSize);
            TPCircularBufferConsume(info->outBuffer, buffer.mDataByteSize);

   /* Messaging is not used for this example
            // Notify delegate
            if ([player.delegate respondsToSelector:@selector(player:audioBuffer:bufferSize:)])
            {
                [player.delegate player:player
                            audioBuffer:buffer.mData
                             bufferSize:inNumberFrames];
            }
    */
        }
        else {
            memset((char*)ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize  * info->clientFormat.mBytesPerFrame);

        }
    }
    return noErr;
}

Edit Based on the answer below I modified the code by allocating and freeing memory outside the callback functions, and by increasing the size of the circular buffers. Unfortunately, the sound is still worse than without circular buffers. Could there be another reason for these glitches?


Solution

  • Allocating or freeing memory should not be done inside audio callbacks. Neither should sending Objective C messages.

    A render callback should always put data into the buffer list buffers. But your code is missing an else statement to do so. Try a bigger circular buffer and make sure it is sufficiently pre-filled before starting render callbacks.