如何将AUGraph的输出写入文件?

时间:2012-01-21 07:35:56

标签: ios core-audio

我正在尝试编写(应该是什么)一个简单的应用程序,它在AUGraph中按顺序包含一堆音频单元,然后将输出写入文件。我使用AUGraphAddRenderNotify添加了回调。这是我的回调函数:

OSStatus MyAURenderCallback(void *inRefCon,
                        AudioUnitRenderActionFlags *actionFlags,
                        const AudioTimeStamp *inTimeStamp,
                        UInt32 inBusNumber,
                        UInt32 inNumberFrames,
                        AudioBufferList *ioData) {
    if (*actionFlags & kAudioUnitRenderAction_PostRender) {
        ExtAudioFileRef outputFile = (ExtAudioFileRef)inRefCon;
        ExtAudioFileWriteAsync(outputFile, inNumberFrames, ioData);
    }
}

这种作品。该文件是可播放的,我可以听到我录制的内容,但是有大量静电使其几乎听不见。

有人知道这有什么问题吗?或者有没有人知道将AUGraph输出记录到文件的更好方法?

感谢您的帮助。

3 个答案:

答案 0 :(得分:6)

我刚才有关于Audio Units的顿悟,这帮助我解决了自己的问题。我对音频单元连接和渲染回调的工作方式存在误解。我认为它们是完全不同的东西,但事实证明连接只是渲染回调的简写。

从音频单元A的输出到音频单元B的输入执行kAudioUnitProperty_MakeConnection与在单元B的输入上执行kAudioUnitProperty_SetRenderCallback并使回调函数在音频单元A的输出上调用AudioUnitRender相同。

我在设置渲染回调后通过make连接测试了这个,并且不再调用渲染回调。

因此,我能够通过以下方式解决我的问题:

AURenderCallbackStruct callbackStruct = {0};
callbackStruct.inputProc = MyAURenderCallback;
callbackStruct.inputProcRefCon = mixerUnit;

AudioUnitSetProperty(ioUnit,
                     kAudioUnitProperty_SetRenderCallback,
                     kAudioUnitScope_Input,
                     0,
                     &callbackStruct,
                     sizeof(callbackStruct));

他们的回调函数就是这样的:

OSStatus MyAURenderCallback(void *inRefCon,
                        AudioUnitRenderActionFlags *actionFlags,
                        const AudioTimeStamp *inTimeStamp,
                        UInt32 inBusNumber,
                        UInt32 inNumberFrames,
                        AudioBufferList *ioData) {

    AudioUnit mixerUnit = (AudioUnit)inRefCon;

    AudioUnitRender(mixerUnit,
                    actionFlags,
                    inTimeStamp,
                    0,
                    inNumberFrames,
                    ioData);

    ExtAudioFileWriteAsync(outputFile, 
                           inNumberFrames, 
                           ioData);

    return noErr;
}

这对我来说应该是显而易见的,但是因为不是我敢打赌,其他人也会以同样的方式感到困惑,所以希望这对他们也有帮助。

我仍然不确定为什么我遇到AUGraphAddRenderNotify回调问题。我稍后会深入研究这个问题,但现在我找到了一个似乎有效的解决方案。

答案 1 :(得分:1)

以下是Apple的一些示例代码(该项目是PlaySequence,但它不是特定于MIDI)可能会有所帮助:

{
    CAStreamBasicDescription clientFormat = CAStreamBasicDescription();
    ca_require_noerr (result = AudioUnitGetProperty(outputUnit,
                                                    kAudioUnitProperty_StreamFormat,
                                                    kAudioUnitScope_Output, 0,
                                                    &clientFormat, &size), fail);
    size = sizeof(clientFormat);
    ca_require_noerr (result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat), fail);

    {
        MusicTimeStamp currentTime;
        AUOutputBL outputBuffer (clientFormat, numFrames);
        AudioTimeStamp tStamp;
        memset (&tStamp, 0, sizeof(AudioTimeStamp));
        tStamp.mFlags = kAudioTimeStampSampleTimeValid;
        int i = 0;
        int numTimesFor10Secs = (int)(10. / (numFrames / srate));
        do {
            outputBuffer.Prepare();
            AudioUnitRenderActionFlags actionFlags = 0;
            ca_require_noerr (result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL()), fail);

            tStamp.mSampleTime += numFrames;

            ca_require_noerr (result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL()), fail);    

            ca_require_noerr (result = MusicPlayerGetTime (player, &currentTime), fail);
            if (shouldPrint && (++i % numTimesFor10Secs == 0))
                printf ("current time: %6.2f beats\n", currentTime);
        } while (currentTime < sequenceLength);
    }
}

答案 2 :(得分:0)

也许试试这个。将数据从音频单元回调复制到长缓冲区。播放缓冲区进行测试,然后在验证整个缓冲区正常后将整个缓冲区写入文件。