-- 2016/07/10追記 --
私が下の方で書いたコードだと、ファイルによってはいつまで経っても
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);
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;
}
AudioConverterが不要なのは良いですね。(実際はExtAudioFileが内包しているようです)
kExtAudioFileProperty_FileLengthFramesで総フレーム数が取得できるのもありがたい…。
まだVBRなファイルは試してませんが、問題なく行けそうな気がします。