iOS在渲染回调处理之前添加音频效果?

Posted

技术标签:

【中文标题】iOS在渲染回调处理之前添加音频效果?【英文标题】:iOS add audio effect before render callback processing? 【发布时间】:2015-11-24 18:52:45 【问题描述】:

在我的应用程序中,我正在渲染回调中进行音频处理(仅输入,无输出)。 这是我初始化音频的方式:

-(void) initAudio 

OSStatus status;

NewAUGraph(&graph);

AudioComponentDescription desc;
desc.componentType          = kAudioUnitType_Output;
desc.componentSubType       = kAudioUnitSubType_RemoteIO;
desc.componentFlags         = 0;
desc.componentFlagsMask     = 0;
desc.componentManufacturer  = kAudioUnitManufacturer_Apple;

AUNode ioNode;

status = AUGraphAddNode(graph, &desc, &ioNode);
checkStatus(status, "At adding node");

AUGraphOpen(graph);

AUGraphNodeInfo(graph, ioNode, NULL, &audioUnit);

//Enable IO for recording
UInt32 enableInput = 1;
status = AudioUnitSetProperty(audioUnit,
                              kAudioOutputUnitProperty_EnableIO,
                              kAudioUnitScope_Input,
                              kInputBus,
                              &enableInput,
                              sizeof(enableInput));
checkStatus(status, "At setting property for input");

//Disable playback
UInt32 enableOutput = 0;
status = AudioUnitSetProperty(audioUnit,
                              kAudioOutputUnitProperty_EnableIO,
                              kAudioUnitScope_Output,
                              kOutputBus,
                              &enableOutput,
                              sizeof(enableOutput));
checkStatus(status, "At setting property for input");

// ASBD
AudiostreamBasicDescription audioFormatIn;
audioFormatIn.mSampleRate         = SampleRate;
audioFormatIn.mFormatID           = kAudioFormatLinearPCM;
audioFormatIn.mFormatFlags        = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormatIn.mFramesPerPacket    = 1;
audioFormatIn.mChannelsPerFrame   = 1;
audioFormatIn.mBitsPerChannel     = 16;//sizeof(AudioSampleType) * 8;
audioFormatIn.mBytesPerPacket     = 2 * audioFormatIn.mChannelsPerFrame;
audioFormatIn.mBytesPerFrame      = 2 * audioFormatIn.mChannelsPerFrame;

//Apply format
status = AudioUnitSetProperty(audioUnit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Output,
                              kInputBus,
                              &audioFormatIn,
                              sizeof(audioFormatIn));
checkStatus(status,"At setting property for AudioStreamBasicDescription for input");


//Set up input callback
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = recordingCallback;
callbackStruct.inputProcRefCon = (__bridge void *)self;

status = AudioUnitSetProperty(audioUnit,
                              kAudioOutputUnitProperty_SetInputCallback,
                              kAudioUnitScope_Global,
                              kInputBus,
                              &callbackStruct,
                              sizeof(callbackStruct));
checkStatus(status,"At setting property for recording callback");

// Disable buffer allocation for the recorder
UInt32  flag = 0;
status = AudioUnitSetProperty(audioUnit,
                              kAudioUnitProperty_ShouldAllocateBuffer,
                              kAudioUnitScope_Output,
                              kInputBus,
                              &flag,
                              sizeof(flag));
checkStatus(status, "At set property should allocate buffer");

// Allocate own buffers
tempBuffer.mNumberChannels  = 1;
tempBuffer.mDataByteSize    = 1024 * 2;
tempBuffer.mData            = malloc( 1024 * 2 );

status = AUGraphInitialize(graph);
checkStatus(status,"At AUGraph Initalize");

现在,我想在渲染回调中处理输入音频之前添加一个高通滤波器或带通滤波器。所以我想我应该添加这样的东西:

desc.componentType          = kAudioUnitType_Effect;
desc.componentSubType       = kAudioUnitSubType_BandPassFilter;
desc.componentFlags         = 0;
desc.componentFlagsMask     = 0;
desc.componentManufacturer  = kAudioUnitManufacturer_Apple;

但是我没有设法正确地创建/连接节点来完成这项工作...... 感谢您的帮助!

【问题讨论】:

您的渲染链是否在没有带通的情况下工作? @Dave 我曾经使用经典的音频单元初始化,但我开始切换到 AUGraph。即使我不确定我做得是否正确,我上面的代码似乎也能正常工作。 【参考方案1】:

当我想做类似的事情时,我发现了你的问题 - 不幸的是没有任何解决方案 :-( 现在几天后设法做到了。所以这是我的工作解决方案,适用于遇到同样问题的每个人:

    准备 Remote_IO 的输入作为 STANDALONE 音频单元用于录制音频。

    向输入添加回调。我将其称为“麦克风回调”。

    (注意:Apple 将其称为输入回调,而实际上,这只是一个 NOTIFICATION 回调,用于告诉您的应用程序样本可用,并且您必须明确地“询问”输入单元以呈现它们。 .)

    建立一个AU Graph:转换器单元->过滤器->通用输出

    (注意:通用输出可以转换,但过滤器不能。因此,如果您想在链中输入 8.24 格式以外的任何内容,那么您需要转换器。)

    向转换器单元的输入添加回调。我将其称为“进程回调”。

现在关键是操作系统对 Mic Callback 的常规调用将驱动整个处理链:

    在麦克风回调中,准备一个缓冲区,该缓冲区与麦克风可渲染的输入样本数量一样大,并“要求”通用输出(!)渲染 - 相同数量的 - 样本(通过调用 AudioUnitRender) 进入它。请注意,在图表的末尾,您不是要求输入单元进行渲染,而是要求输出单元进行渲染!

    Generic Output 将转发渲染请求,直到它到达转换器单元的输入回调,即 Process Callback。在这个中,您在输入参数中获得一个指向缓冲区的指针,您必须在其中填写请求的样本数。此时,要求 INPUT UNIT 将样本准确地渲染到此缓冲区。

还有中提琴,你完成了。您只需启动/停止麦克风!

整个过程的关键是始终有一个输出单元必须驱动整个渲染过程,并且由于通用输出没有任何常规“需要”来获取样本,因此您必须手动要求它渲染样本。并且这必须与麦克风的 A/D 转换器同步,它希望以固定的时间间隔输出样本。

理论上你可以链接输入单元和你的图表,但是有两个问题:

    麦克风输入单元算作一个输出单元,一个图中不能有两个输出单元...

    当 to 单元连接在一起时,回调之间不能存在。如果你把一个放在那里,它就永远不会被调用......所以你会把回调放在链的末尾,并期望“样本可用”通知将从麦克风单元的输出传播到通用输出。然而它不会。因此,您必须将处理分为麦克风输入单元和处理链。

最后说明:如果您想直接将麦克风的样本渲染到转换器单元,那么您必须将相同的流格式(最好是远程 io 的规范输出格式(即 16 位,整数)设置为转换器的输入单元和麦克风单元的输出(即远程io的输入范围,总线1)。

【讨论】:

以上是关于iOS在渲染回调处理之前添加音频效果?的主要内容,如果未能解决你的问题,请参考以下文章

如何正确使用 iOS AudioUnit 渲染回调

直接调用音频单元而不是回调 iOS

编写远程 I/O 渲染回调函数时遇到问题

添加效果后导出音频文件

核心音频:文件播放渲染回调函数

什么会影响音频单元渲染回调周期(周期)?