Attention! For Japanese customers, my Japanese blog might help.

Jul 30, 2016

How to handle non-interleaved audio data

I think it is too difficult to handle non-interleaved audio data in a app.
After a workround, I compiled points seemed to be important.
  1. How to set up AudioBufferList
  2. How to set up AudioStreamBasicDescription(ASBD)
  3. How to convert frame <-> bytes

1. How to set up AudioBufferList

In the case of non-interleaved data, you need to allocate additional space for AudioBuffers.
For example, Following cod may work.
    AudioBufferList *list = calloc(sizeof(AudioBufferList) + sizeof(AudioBuffer) * (numCh - 1), 1);

Reference: Core Audio その1 AudioBufferとAudioBufferList | Objective-Audio

2. How to set up AudioStreamBasicDescription(ASBD)

In the case of non-interleaved data, you should set ASBD.mBytesPerFrame for the size of ONE channel.
This is described in CoreAudioTypes.h as follows.

However, when an ASBD has the kAudioFormatFlagIsNonInterleaved flag, the
                    AudioBufferList has a different structure and semantic. In this case, the ASBD
                    fields will describe the format of ONE of the AudioBuffers that are contained in
                    the list, AND each AudioBuffer in the list is determined to have a single (mono)
                    channel of audio data. Then, the ASBD's mChannelsPerFrame will indicate the
                    total number of AudioBuffers that are contained within the AudioBufferList -
                    where each buffer contains one channel. This is used primarily with the
                    AudioUnit (and AudioConverter) representation of this list - and won't be found
                    in the AudioHardware usage of this structure.
3. How to convert  <-> bytes

As a result, in the case of non-ineterleaved data, the formula

mBytesPerFrame == (mChannelsPerFrame * mBitsPerChannel / 8)

is NOT satisfied.

So, for the cose the uses mBytesPerFrame, you need convert a manucal conversion such as
bytesPerFrame = (mChannelsPerFrame * mBitsPerChannel / 8)

I would used to use the code as follows:

+ (UInt32)BPFofASBD:(ASBD)desc {
    if ((desc.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == kAudioFormatFlagIsNonInterleaved) {
        return desc.mChannelsPerFrame * desc.mBitsPerChannel / 8;
    }
    
    return desc.mBytesPerFrame;
}

Try it!