似乎无法在Core Audio中走得太远。我的目标是将捕获的音频数据从仪器单元写入文件。我已经在仪器单元上调用了一个回调函数:
CheckError(AudioUnitAddRenderNotify(player->instrumentUnit,
MyRenderProc,
&player),
"AudioUnitAddRenderNotify Failed");
我用这个设置文件和AudioStreamBasicDescription:
#define FILENAME @"output_IV.aif"
NSString *fileName = FILENAME; // [NSString stringWithFormat:FILENAME_FORMAT, hz];
NSString *filePath = [[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent: fileName];
NSURL *fileURL = [NSURL fileURLWithPath: filePath];
NSLog (@"path: %@", fileURL);
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mSampleRate = 44100.0;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
asbd.mChannelsPerFrame = 2; // CHANGED FROM 1 (STEREO)
asbd.mFramesPerPacket = 1;
asbd.mBitsPerChannel = 16;
asbd.mBytesPerFrame = 4;
asbd.mBytesPerPacket = 4;
CheckError(ExtAudioFileCreateWithURL((__bridge CFURLRef)fileURL, kAudioFileAIFFType, &asbd, NULL, kAudioFileFlags_EraseFile, &testRecordFile), "ExtAudioFileCreateWithURL failed");
CheckError(ExtAudioFileSetProperty(testRecordFile, kExtAudioFileProperty_ClientDataFormat, (UInt32)sizeof(asbd), &asbd), "ExtAudioFileSetProperty failed");
CheckError(ExtAudioFileWriteAsync(testRecordFile, 0, NULL), "ExtAudioFileWriteAsync 1st time failed");
我确认文件确实已创建。 testRecordFile是全局定义的(这是我现在可以运行的唯一方法):
ExtAudioFileRef testRecordFile;
我的回调功能是:
OSStatus MyRenderProc(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
if (*ioActionFlags & kAudioUnitRenderAction_PostRender){
static int TEMP_kAudioUnitRenderAction_PostRenderError = (1 << 8);
if (!(*ioActionFlags & TEMP_kAudioUnitRenderAction_PostRenderError)){
CheckError(ExtAudioFileWriteAsync(testRecordFile, inNumberFrames, ioData), "ExtAudioFileWriteAsync failed");
}
}
return noErr;
}
当我运行此程序时,程序会在ExtAudioFileWriteAsync调用上进入并进入调试器模式(lldb)。 inNumberFrames = 512,我已经验证我在ioData中获得了Float32音频数据的立体声通道。
我在这里缺少什么?
答案 0 :(得分:2)
首先,你的代码仍然有点复杂,包括一些&#34;暗角&#34; CoreAudio和Obj-C。这是一个更安全的选择,首先要确保在实时线程中,一切都按照预期的方式工作。一旦调试了这部分代码,就可以根据需要轻松添加Obj-C优雅。
如果为了简单起见忽略可能的字节顺序和文件格式转换问题,仍然有一个问题您必须使用API实用程序自动解决,或者&#34;手动&#34;:
AFAIK,System.Windows.Forms.OpenFileDialog
的数据格式必须是交错的,而AUGraph的流格式不是。假设我们不在这里处理endiannes和格式转换,这就是你可以手动修复它的方法(单通道示例)。如果您的ExtAudioFileWriteAsync()
流格式是非交错立体声,您可以在缓冲区中交错数据,如下所示:LRLRLRLRLR ...
asbd
这只是展示其运作方式的一个例子。通过研究OSStatus MyRenderProc(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
AudioBufferList bufferList;
Float32 samples[inNumberFrames+inNumberFrames];
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mData = samples;
bufferList.mBuffers[0].mNumberChannels = 1;
bufferList.mBuffers[0].mDataByteSize = (inNumberFrames+inNumberFrames)*sizeof(Float32);
Float32 *data = (Float32 *)ioData->mBuffers[0].mData;
if (*ioActionFlags & kAudioUnitRenderAction_PostRender){
static int TEMP_kAudioUnitRenderAction_PostRenderError = (1 << 8);
if (!(*ioActionFlags & TEMP_kAudioUnitRenderAction_PostRenderError) {
for(UInt32 i = 0; i < inNumberFrames; i++)
samples[i+i] = samples [i+i+1] = data[i];//copy buffer[0] to L & R
ExtAudioFileWriteAsync(testRecordFile, inNumberFrames, &bufferList);
}
}
return noErr;
}
并在以下位置设置正确的格式:
asbd.mFormatFlags
你可以更优雅地实现它,但这超出了你这篇文章的范围。
答案 1 :(得分:1)
这是16位线性大端立体声AIF文件的工作回调:
OSStatus MyRenderProc(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
SInt16 samples[inNumberFrames + inNumberFrames];
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mData = samples;
bufferList.mBuffers[0].mNumberChannels = 1;
bufferList.mBuffers[0].mDataByteSize = (inNumberFrames + inNumberFrames) * sizeof(SInt16);
Float32 *leftData = (Float32 *)ioData->mBuffers[0].mData;
Float32 *rightData = (Float32 *)ioData->mBuffers[1].mData;
if (*ioActionFlags & kAudioUnitRenderAction_PostRender){
static int TEMP_kAudioUnitRenderAction_PostRenderError = (1 << 8);
if (!(*ioActionFlags & TEMP_kAudioUnitRenderAction_PostRenderError)){
for (UInt32 i = 0; i < inNumberFrames; i++) {
samples[i + i] = CFSwapInt16HostToBig((SInt16) SHRT_MAX * (leftData)[i]);
samples[i + i + 1] = CFSwapInt16HostToBig((SInt16) SHRT_MAX * (rightData)[i]);
}
CheckError(ExtAudioFileWriteAsync(testRecordFile, inNumberFrames, &bufferList), "ExtAudioFileWriteAsync failed");
}
}
return noErr;
}