在回调函数中使用ExtAudioFileWriteAsync()。无法运行

时间:2016-06-07 03:38:43

标签: objective-c macos core-audio

似乎无法在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音频数据的立体声通道。

我在这里缺少什么?

2 个答案:

答案 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;
}