对于我的 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;
}