Search code examples
iosmacoscore-audio

iOS Core Audio : Converting between kAudioFormatFlagsCanonical and kAudioFormatFlagsAudioUnitCanonical


I need to convert between this format :

        format.mSampleRate  = 44100.0; 
        format.mFormatID = kAudioFormatLinearPCM;
        format.mFormatFlags = kAudioFormatFlagsCanonical | kLinearPCMFormatFlagIsNonInterleaved;
        format.mBytesPerPacket = sizeof(AudioUnitSampleType);
        format.mFramesPerPacket = 1;
        format.mBytesPerFrame = sizeof(AudioUnitSampleType);
        format.mChannelsPerFrame = 2 ;
        format.mBitsPerChannel = sizeof(AudioUnitSampleType)*8;

and this format

format.mSampleRate  = 44100.0; 
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
format.mBytesPerPacket = sizeof(AudioUnitSampleType);
format.mFramesPerPacket = 1;
format.mBytesPerFrame = sizeof(AudioUnitSampleType);
format.mChannelsPerFrame = 2; 
format.mBitsPerChannel = sizeof(AudioUnitSampleType)*8;

within the confines of a audio render callback where there is the following code and buffer[] is in the 2nd format and array[] requires the 1st format.

for (k = 0; k < channels; k++){
    buffer = (AudioUnitSampleType *) ioData->mBuffers[k].mData;
    for(j=0; j < samples; j++){
        array[j] = buffer[j];
    }
}

I know you can use the Apple converter unit, but I cannot use the Apple Converter audio unit in my situation (there's a reason).

Basically the only difference between the 2 formats the following flag for format.mFormatFlags (kAudioUnitSampleFractionBits << kLinearPCMFormatFlagsSampleFractionShift).

How can I convert buffer[] (containing data in the 2nd format) to array[] (containing data in the 1st format) and vice-versa?

Thank you.


Solution

  • Well, if you refer to the docs on kAudioFormatFlagsAudioUnitCanonical, you see:

    kAudioFormatFlagsAudioUnitCanonical The flags for the canonical audio unit sample 
    type. This matches AudioUnitSampleType.
    

    and

    The canonical audio sample type for audio units and other audio processing in 
    iPhone OS is noninterleaved linear PCM with 8.24-bit fixed-point samples.
    

    So, the samples in buffer[] array are in 8.24-bit fixed-point format. What does it mean?

    8.24-bit fixed-point format is used to represent float numbers with fixed precision - a 32-bit integer where the first 8 bits represent the whole part, and the last 24 bits represent the fractional part (the numbers after the decimal). (Further reading)

    In iOS Audio Units, there's a minor difference - this float number (usually) ranges in [-1, 1) ([-1.000000000000, +0.999969482421875] to be exact). Values outside of this range are simply clipped when converting to 16-bit PCM. You can validate that the first 8 bits would be 0x00 or 0xff (-1 in two's compliment) for most part.

    To convert this representation to a 16-bit number, use this:

    SIGN((SInt8)(val >> 24)) * 32768 * (val & 0xFFFFFF)/(float)(1<<24)
    

    That is: extract the sign from the 8 MSB, extract fractional value from 24 LSB and divide by range of 24-bit integer (2^24) resulting in a float between 0 and 1, and finally multiply this by 32768 to get a value in desired range.

    I haven't tried this myself though - you might have to adjust a few things here and there.