私の iOS アプリケーションでは、NSData オブジェクトに格納されている MP3 ファイルを AAC 形式に変換する必要があります。これは後でストリーミングするために NSData オブジェクトにも格納されます。変換の合間に DSP も実行しているので、AudioFileOpenWithCallbacks を利用して既存のメモリ内の MP3 ファイルを開くと、これは正常に機能します。
ただし、AudioFileInitializeWithCallbacks (AFIWCB) を使用して読み取り可能な AAC ファイルを作成し、NSMutableData オブジェクトを埋めるのに問題があります。オーディオをテストするために、変換が完了したときに NSMutableData オブジェクトをディスクに書き込んでいますが、このファイルのメタデータを調べると、ビット レートやチャネル情報がなく、ファイルを再生できません。ただし、ファイルサイズはほぼ正しいです。
また、AFIWCB をスキップして、ExtAudioFileCreateWithURL を使用してディスクに直接書き込むと、完全に機能しますが、ディスクへの書き込みはアプリケーションにとって望ましくありません。
AFIWCB を使用してオーディオをメモリ内バッファーに書き込むことに慣れている人はいますか? ドキュメントはあまり明確ではなく、何かを省略しているか、コールバックを正しく使用していないと感じています。
ご協力いただきありがとうございます。
編集: 私の問題を理解しました。それは outputWriteProc コールバックにありました。以下に修正しました。
コード:
-(void) convertData: (NSData *) audioData {
AudioFileID refInputAudioFileID; //these will be wrapped in Ext Audio File
AudioFileID refOutputAudioFileID;
ExtAudioFileRef inputFileID; //these deal with the actual reading and writing
ExtAudioFileRef outputFileID;
// Client Audio Format Description
AudioStreamBasicDescription clientFormat;
memset(&clientFormat, 0, sizeof(clientFormat));
clientFormat.mFormatID = kAudioFormatLinearPCM;
clientFormat.mFramesPerPacket = 1;
clientFormat.mChannelsPerFrame = 2;
clientFormat.mBitsPerChannel = 32;
clientFormat.mBytesPerPacket = clientFormat.mBytesPerFrame = 4 * clientFormat.mChannelsPerFrame;
clientFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;// | kAudioFormatFlagIsNonInterleaved;
clientFormat.mSampleRate = 44100;
//Output Audio Format Description
AudioStreamBasicDescription outputFormat;
memset(&outputFormat, 0, sizeof(outputFormat));
outputFormat.mChannelsPerFrame = 2;
outputFormat.mSampleRate = 44100;
outputFormat.mFormatID = kAudioFormatMPEG4AAC;
outputFormat.mFormatFlags = kMPEG4Object_AAC_Main;
outputFormat.mBitsPerChannel = 0;
outputFormat.mBytesPerFrame = 0;
outputFormat.mBytesPerPacket = 0;
outputFormat.mFramesPerPacket = 1024;
//Open the Source Audio File (in Memory) and wrap it with an ExtAudioFile (this works fine)
OSStatus result = AudioFileOpenWithCallbacks(audioData, readProc, 0, getSizeProc, 0, kAudioFileMP3Type, &refInputAudioFileID);
if(result != noErr)
[self CheckResult:result withMessage:@"AudioFileOpenWithCallbacks failed "];
//2) wrap with ExtAudioFile (this works fine)
result = ExtAudioFileWrapAudioFileID(refInputAudioFileID, false, &inputFileID);
[self CheckResult:result withMessage:@"ExtAudioFileWrap failed for input audio "];
UInt64 fileSizeInFrames;
UInt32 sizeProp = sizeof(fileSizeInFrames);
result = 0;
result = ExtAudioFileGetProperty(inputFileID, kExtAudioFileProperty_FileLengthFrames, &sizeProp, &fileSizeInFrames);
if(result!=noErr)
[self CheckResult:result withMessage:@"ExtAudioFileGet Prop FileLengthFrames failed "];
else
sourceAudioFileSizeinFrames = fileSizeInFrames;
//Initialize the destination audio file using NSMutableData and wrap it with ExtAudioFile (this is where I'm having problems)
destAudioData = [[NSMutableData alloc] initWithCapacity:1000000];
result = 0;
result = AudioFileInitializeWithCallbacks(destAudioData, outputReadProc, outputWriteProc,
getOutputSizeProc, setOutputSizeProc, kAudioFileM4AType,
&outputFormat, 0, &refOutputAudioFileID);
[self CheckResult:result withMessage:@"AudioFIleIWithCallbacks failed "];
result = 0;
result = ExtAudioFileWrapAudioFileID(refOutputAudioFileID, true, &outputFileID);
[self CheckResult:result withMessage:@"ExtAudioFilWrap for dest audio failed "];
UInt32 outputFormatSize = sizeof(outputFormat);
result = 0;
result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &outputFormatSize, &outputFormat);
[self CheckResult:result withMessage:@"AudioFormatGetProp failed on output audio "];
// Set Up Client Formats for Input
int size = sizeof(clientFormat);
result = 0;
result = ExtAudioFileSetProperty(inputFileID, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
[self CheckResult:result withMessage:@"Error on ExtAudioFileSetProperty ClientFormat on Input "];
// Specify the software codec
UInt32 codec = kAppleSoftwareAudioCodecManufacturer;
result = 0;
result = ExtAudioFileSetProperty(outputFileID, kExtAudioFileProperty_CodecManufacturer, sizeof(UInt32), &codec);
[self CheckResult:result withMessage:@"Error Setting Audio Codec for Output File "];
//specify client format on output
size = sizeof(clientFormat);
result = 0;
result = ExtAudioFileSetProperty(outputFileID, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);
[self CheckResult:result withMessage:@"Error on ExtAudioFileSetProperty ClientDataFormat for Output File "];
UInt64 totalFrames = 0;
int ioBufferSizeSamples = 1024;
while (1) {
UInt32 bufferByteSize = ioBufferSizeSamples * 4 * 2;
char srcBuffer[bufferByteSize];
UInt32 numFrames = (bufferByteSize/clientFormat.mBytesPerFrame);
AudioBufferList fillBufList;
fillBufList.mNumberBuffers = 1;
fillBufList.mBuffers[0].mNumberChannels = clientFormat.mChannelsPerFrame;
fillBufList.mBuffers[0].mDataByteSize = bufferByteSize;
fillBufList.mBuffers[0].mData = srcBuffer;
result = 0;
//read samples
result = ExtAudioFileRead(inputFileID, &numFrames, &fillBufList);
if (result != noErr) {
[self CheckResult:result withMessage:@"Error on ExtAudioFileRead for input "];
totalFrames = 0;
break;
}
if (!numFrames)
break;
/**********Do DSP HERE*****************/
totalFrames = totalFrames + numFrames;
//write output audio here
result = 0;
result = ExtAudioFileWrite(outputFileID,
numFrames,
&fillBufList);
[self CheckResult:result withMessage:@"Error on ExtAudioFileWrite for output "];
}
// clean up
result = 0;
result = ExtAudioFileDispose(inputFileID);
[self CheckResult:result withMessage:@"Error on ExtAudioFileDispose InputFileId "];
result = 0;
AudioFileClose(refInputAudioFileID);
[self CheckResult:result withMessage:@"Error on AudioFile Clsoe InputFileId "];
result = 0;
ExtAudioFileDispose(outputFileID);
[self CheckResult:result withMessage:@"Error on ExtAudioFileDispose OutputFileID "];
result = 0;
AudioFileClose(refOutputAudioFileID);
[self CheckResult:result withMessage:@"Error on AudioFileClose OutputFileID "];
//save the destination audio file here...
NSString *destAudioPath = [[Utils audioFilePathPrefix] stringByAppendingPathComponent:
[NSString stringWithFormat:@"tone.m4a"]];
NSURL *destURL = [NSURL fileURLWithPath:destAudioPath];
BOOL writeOK = [destAudioData writeToURL:destURL atomically:YES];
if(!writeOK)
NSLog(@"problem writing the destination audio to its path \n");
}
/* *********These are the callbacks required for AudioFileOpen With Callbacks and they work fine **********/
static OSStatus readProc(void *inClientData,
SInt64 position,
UInt32 requestCount,
void *buffer,
UInt32 *actualCount)
{
NSData *inAudioData = (NSData *) inClientData;
size_t dataSize = inAudioData.length;
size_t bytesToRead = 0;
if(position < dataSize) {
size_t bytesAvailable = dataSize - position;
bytesToRead = requestCount <= bytesAvailable ? requestCount : bytesAvailable;
[inAudioData getBytes: buffer range:NSMakeRange(position, bytesToRead)];
*actualCount = bytesToRead;
} else {
NSLog(@"data was not read \n");
bytesToRead = 0;
*actualCount = 0;
}
return noErr;
}
static SInt64 getSizeProc(void* inClientData) {
NSData *inAudioData = (NSData *) inClientData;
size_t dataSize = inAudioData.length;
return dataSize;
}
/**************These are the callbacks for AudioFileInitializeWithCallbacks ********/
static OSStatus outputReadProc (void *outClientData,
SInt64 outputReadPosition,
UInt32 outputReadRequestCount,
void *outputReadBuffer,
UInt32 *outputReadActualCount)
{
NSData *inAudioData = (NSData *) outClientData;
size_t dataSize = inAudioData.length;
size_t bytesToRead = 0;
if(outputReadPosition < dataSize) {
size_t bytesAvailable = dataSize - outputReadPosition;
bytesToRead = outputReadRequestCount <= bytesAvailable ? outputReadRequestCount : bytesAvailable;
[inAudioData getBytes: outputReadBuffer range:NSMakeRange(outputReadPosition, bytesToRead)];
*outputReadActualCount = bytesToRead;
} else {
bytesToRead = 0;
*outputReadActualCount = 0;
}
return noErr;
}
static OSStatus outputWriteProc(void *outClientData,
SInt64 writePosition,
UInt32 writeRequestCount,
const void *writeBuffer,
UInt32 *writeActualCount){
NSMutableData *outAudioData = (NSMutableData *) outClientData;
UInt32 dataLen = [outAudioData length];
if(writePosition + writeRequestCount - 1 > dataLen){
[outAudioData increaseLengthBy:(writePosition + writeRequestCount - dataLen)];
}
[outAudioData replaceBytesInRange: NSMakeRange(writePosition, writeRequestCount) withBytes: writeBuffer];
*writeActualCount = writeRequestCount;
return noErr;
}
static SInt64 getOutputSizeProc(void *outClientData) {
NSMutableData *inAudioData = (NSMutableData *) outClientData;
size_t dataSize = inAudioData.length;
return dataSize;
}
static OSStatus setOutputSizeProc(void *outClientData, SInt64 inSize){
NSMutableData *inAudioData = (NSMutableData *)outClientData;
[inAudioData setLength: inSize];
return noErr;
}