私が下の方で書いたコードだと、ファイルによってはいつまで経っても
whileループから抜け出さないケースに遭遇しました。
どうやらkExtAudioFileProperty_FileLengthFramesが返す総フレーム数と
実際の総フレーム数が異なるためwhileループの条件がfalseにならず
延々と読み込み処理を続けてしまうようでした。
ExtAudioFileRead()はファイルがEOFに達するとフレーム数には0が入るようです。
参考: Extended Audio File Services Reference
この動作を利用してExtAudioFileRead()の後に
という処理を入れ、ループを抜ける仕組みを入れた方が良いでしょう。// 0ならend-of-fileらしいif (numFramesToRead == 0) {break;}
-- 2016/07/10追記終了 --
前回の続き、というかExtAudioFileを使えば簡単にnon-LPCMを変換できることに気づきました…。
というわけで変更後のソースは以下の通り。
#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;}/*inputFilePathで指定したオーディオファイルを読み込みCDフォーマット(ただしbig-endian)のバイナリデータとしてoutputFilePathにファイルとして書き出す。主にMP3, AACなどnon-LPCMデータのLPCMデータへの変換動作確認用。(一応.wav, .aiffも読める)処理に失敗した場合は失敗箇所でのOSStatusを返す。*/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);
AudioConverterが不要なのは良いですね。(実際はExtAudioFileが内包しているようです)fwrite(list.mBuffers[0].mData,list.mBuffers[0].mDataByteSize,1,fp);frameOffset += numFramesToRead;}fclose(fp);free(buffer);err = ExtAudioFileDispose(file);return err;}/*descPtrにCD音質のASBDをセットする。ただし、big-endianなのでlittle-endianなプロセッサで扱う場合は注意*/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;}
kExtAudioFileProperty_FileLengthFramesで総フレーム数が取得できるのもありがたい…。
まだVBRなファイルは試してませんが、問題なく行けそうな気がします。