音频图输出单元回调函数-无法读取数据

Callback function of output unit of audio graph - can't read data

我正在尝试将输入数据流捕获到音频图中的输出单元,以便将其写入文件。创建图形后,我已经为输出单元(默认输出)注册了一个输入回调函数:

    AudioComponent comp = AudioComponentFindNext (NULL, &cd);
if (comp == NULL) {
    printf ("can't get output unit");
    exit (-1);
}
CheckError (AudioComponentInstanceNew(comp, &player->outputUnit),
            "Couldn't open component for outputUnit"); // outputUnit is of type AudioUnit

// register render callback
AURenderCallbackStruct input;
input.inputProc = MyRenderProc;
input.inputProcRefCon = player;
CheckError(AudioUnitSetProperty(player->outputUnit,
                                kAudioUnitProperty_SetRenderCallback,
                                kAudioUnitScope_Input,
                                0,
                                &input,
                                sizeof(input)),
           "AudioUnitSetProperty failed");

// initialize unit
CheckError (AudioUnitInitialize(player->outputUnit),"Couldn't initialize output unit");

回调函数被调用,但是当我尝试从 ioData->mBuffers[0].mData 读取输入数据流时,我得到的只是零。这是回调函数:

OSStatus MyRenderProc(void *inRefCon, // PLAYER CODE
                        AudioUnitRenderActionFlags *ioActionFlags,
                        const AudioTimeStamp *inTimeStamp,
                        UInt32 inBusNumber,
                        UInt32 inNumberFrames,
                        AudioBufferList * ioData) {

int frame = 0;
Float32 leftFloat = 0;
Float32 rightFloat = 0;
NSNumber *leftNumber;
NSNumber *rightNumber;
for (frame = 0; frame < inNumberFrames; ++frame)
{
    Float32 *data = (Float32*)ioData->mBuffers[0].mData;
    leftFloat = (data)[frame];
    leftNumber = [NSNumber numberWithDouble:leftFloat];
    (data)[frame] = 0;

    // copy to right channel too
    data = (Float32*)ioData->mBuffers[1].mData;
    rightFloat = (data)[frame];
    rightNumber = [NSNumber numberWithDouble:(data)[frame]];
    (data)[frame] = 0;

    [leftData addObject:leftNumber];
    [rightData addObject:rightNumber];

}

return noErr;
}

此外,如果我不将数据清零,我会在播放过程中听到噪音,这表明我误解了 mBuffers 的功能。我在这里做错了什么?

如果AUGraph捕获输入数据是任务,则代码的关键部分(或多或少)归结为这个最简单的单通道演示示例:

OSStatus MyRenderProc(void *inRefCon, 
                      AudioUnitRenderActionFlags *ioActionFlags,
                      const AudioTimeStamp *inTimeStamp,
                      UInt32 inBusNumber,
                      UInt32 inNumberFrames,
                      AudioBufferList * ioData) 
{        
    Float32 buf [inNumberFrames]; //just for one channel!

    MyMIDIPlayer *player = (MyMIDIPlayer *)inRefCon;

    if (*ioActionFlags & kAudioUnitRenderAction_PostRender){
        static int TEMP_kAudioUnitRenderAction_PostRenderError = (1 << 8);
        if (!(*ioActionFlags & TEMP_kAudioUnitRenderAction_PostRenderError)){

            Float32* data = (Float32 *)ioData->mBuffers[0].mData; //just for one channel!

            memcpy(buf, data, inNumberFrames*sizeof(Float32));

            //do something with buff - there are nice examples of ExtAudioFileWriteAsync()
        }
    }
    return noErr;
}

setupAUGraph()中可以通过以下方式设置此回调:

void setupAUGraph(MyMIDIPlayer *player) 
{
    // the beginning follows the textbook example setup pattern
    {…  …  …}

    // this is the specific part
    AURenderCallbackStruct input = {0};
    input.inputProc = MyRenderProc;
    input.inputProcRefCon = player->instrumentUnit;

    CheckError(AudioUnitAddRenderNotify(player->instrumentUnit,
                                    MyRenderProc,
                                    &player),
           "AudioUnitAddRenderNotify Failed");

    // now initialize the graph (causes resources to be allocated)
    CheckError(AUGraphInitialize(player->graph),
           "AUGraphInitialize failed");
}

请注意渲染回调"taps" 仪器节点的输出和输出节点的输入之间的连接,捕获来自上游的内容。回调只是将 ioData 复制到其他可以保存的缓冲区中。据我所知,这是访问 ioData 的最简单方法,我知道它可以工作,而不会破坏 API.

另请注意,有非常有效的纯 C 方法可用于测试这是否适用于特定实现 - 回调中不需要 Objective-C 方法。在纯 C 实时回调中摆弄一些 NSArrays、添加对象等会引入优先级问题的风险,这可能会在以后变得难以调试。 CoreAudio API 是用 plain-C 编写的,这是有原因的。在 Obj-C 运行时的核心,有很多事情不能在不冒故障风险的情况下在实时线程上发生(锁、内存管理等)。因此,在 实时线程 上远离 Obj-C 会更安全。
希望这可以帮助。

输出音频单元上的渲染回调不用于捕获输出数据。就是提供输出数据(sample mBuffers)供输出单元播放。