ExtAudioFile functions provide the easer way to convert non-LPCM into LPCM than AudioFile functions do.
The following code converts MP3 or M4A files into LPCM data (big-endian).
#include <CoreServices/CoreServices.h>#include <AudioToolbox/AudioToolbox.h>#define _ERR_RETURN(err) {if(noErr != err){printf("%d - err:%d\n", __LINE__, err); return err;}}typedef AudioStreamBasicDescription ASBD;typedef AudioBufferList ABL;OSStatus DecodeExtFileAtPath(const char *inputFilePath, const char *outputFilePath);void SetStandardDescription(AudioStreamBasicDescription *descPtr);static inline ABL MakeABL(UInt32 ch, UInt32 bytes, void *buf);int main(int argc, char* argv[]) {if (argc != 3) {printf("usage: ./Mp3Decoder inFile outFile\n");return 0;}dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{OSStatus err = DecodeExtFileAtPath(argv[1], argv[2]);printf("done. err:%d\n", err);});dispatch_group_wait(group, DISPATCH_TIME_FOREVER);return 0;}/*Reads an audio file specified by inputFilePath, and write to outputFilePath as CDDA quality binary data (big-endian).
Although this function is designed for non-LPCM files such as MP3 and AAC, .wav and .aiff files are also acceptable.
When failed, an OSStatus at failed function will be returned.*/OSStatus DecodeExtFileAtPath(const char *inputFilePath, const char *outputFilePath) {CFURLRef url = CFURLCreateWithBytes(NULL,(const UInt8 *)inputFilePath,strlen(inputFilePath),kCFStringEncodingUTF8,NULL);ExtAudioFileRef file;OSStatus err = ExtAudioFileOpenURL(url,&file);_ERR_RETURN(err);CFRelease(url);ASBD clientDesc;SetStandardDescription(&clientDesc);UInt32 size = sizeof(clientDesc);err = ExtAudioFileSetProperty(file,kExtAudioFileProperty_ClientDataFormat,size,&clientDesc);_ERR_RETURN(err);SInt64 fileFrameLength;size = sizeof(fileFrameLength);err = ExtAudioFileGetProperty(file,kExtAudioFileProperty_FileLengthFrames,&size,&fileFrameLength);_ERR_RETURN(err);const UInt32 numFramesToReadInACycle = 1024*1024;const UInt32 bufferSize = clientDesc.mBytesPerFrame * numFramesToReadInACycle;void *buffer = malloc(bufferSize);FILE *fp = fopen(outputFilePath, "w");SInt64 frameOffset = 0;while (frameOffset != fileFrameLength) {UInt32 numFramesToRead = numFramesToReadInACycle;AudioBufferList list = MakeABL(clientDesc.mChannelsPerFrame,bufferSize,buffer);err = ExtAudioFileRead(file,&numFramesToRead,&list);_ERR_RETURN(err);
Compared to AudioFile functions, ExtAudioFile funcs are more comfortable because AudioConverter is unnecessary.// 0 if end-of-file
if (numFramesToRead == 0) {break;}fwrite(list.mBuffers[0].mData,list.mBuffers[0].mDataByteSize,1,fp);frameOffset += numFramesToRead;}fclose(fp);free(buffer);err = ExtAudioFileDispose(file);return err;}/*Sets CDDA-quality ASBD to descPtr.
For little-endian processor, it should be noted this function returns big-endian format.*/void SetStandardDescription(AudioStreamBasicDescription *descPtr) {descPtr->mSampleRate = 44100.0;descPtr->mFormatID = kAudioFormatLinearPCM;descPtr->mFormatFlags = kAudioFormatFlagIsBigEndian |kAudioFormatFlagIsSignedInteger |kAudioFormatFlagIsPacked;descPtr->mBytesPerPacket = 4;descPtr->mBytesPerFrame = 4;descPtr->mFramesPerPacket = 1;descPtr->mChannelsPerFrame = 2;descPtr->mBitsPerChannel = 16;}static inline ABL MakeABL(UInt32 ch, UInt32 bytes, void *buf) {ABL list;list.mNumberBuffers = 1;list.mBuffers[0].mNumberChannels = ch;list.mBuffers[0].mDataByteSize = bytes;list.mBuffers[0].mData = buf;return list;}
(annoying frame conversion calculations or data input callback are NOT needed!)
Andmore, total frames are available by using kExtAudioFileProperty_FileLengthFrames property.
The property helps us to estimate total byte size of LPCM data roughly before conversion.
However, note that there are cases that kExtAudioFileProperty_FileLengthFrames property does not return correct values.
So, you should check the number of read frames returned by ExtAudioFileRead() in a loop because the function returns 0 when reaches EOF.
Oh, by the way, I have not tried VBR files yet. But these will also be OK.