如何在 AUGraph 中添加 kAudioUnitSubType_LowPassFilter?

Posted

技术标签:

【中文标题】如何在 AUGraph 中添加 kAudioUnitSubType_LowPassFilter?【英文标题】:How to add kAudioUnitSubType_LowPassFilter in a AUGraph? 【发布时间】:2016-06-16 12:16:26 【问题描述】:

我正在尝试将kAudioUnitSubType_LowPassFilter 添加到AUGraph,但没有成功。

我实现了以下代码:

// create a new AUGraph
[Utilities checkError:NewAUGraph(&self.info->mGraph)
            operation: "Couldn't create a new AUGraph"];

AUNode rioNode;
AUNode lpfNode;

// Create filter audio unit
AudioComponentDescription lpfAU_description;
lpfAU_description.componentType = kAudioUnitType_Effect;
lpfAU_description.componentSubType = kAudioUnitSubType_LowPassFilter;
lpfAU_description.componentFlags = 0;
lpfAU_description.componentFlagsMask = 0;
lpfAU_description.componentManufacturer = kAudioUnitManufacturer_Apple;


// Create RemoteIO audio unit
AudioComponentDescription rioAU_description;
rioAU_description.componentType = kAudioUnitType_Output;
rioAU_description.componentSubType = kAudioUnitSubType_RemoteIO;
rioAU_description.componentManufacturer = kAudioUnitManufacturer_Apple;
rioAU_description.componentFlags = 0;
rioAU_description.componentFlagsMask = 0;

[Utilities checkError:AUGraphAddNode(self.info->mGraph, &rioAU_description, &rioNode)
            operation: "Couldn't add Graph Node"];
[Utilities checkError:AUGraphAddNode(self.info->mGraph, &lpfAU_description, &lpfNode )
            operation: "Couldn't add Graph Node"];

// Open graph
[Utilities checkError:AUGraphOpen(self.info->mGraph)
            operation: "Couldn't open graph"];

// Get audio units
[Utilities checkError:AUGraphNodeInfo(self.info->mGraph, lpfNode, NULL, &self.info->lpfUnit)
            operation: "Couldn't link node to audio unit"];

[Utilities checkError:AUGraphNodeInfo(self.info->mGraph, rioNode, NULL, &self.info->rioUnit)
            operation: "Couldn't link node to audio unit"];

// Make connections
[Utilities checkError:AUGraphConnectNodeInput(self.info->mGraph, rioNode, 1, lpfNode, 1)
            operation: "Couldn't connect remoteIO output scope bus 1 to filter input scope bus 1"];// input -> filter

[Utilities checkError: AUGraphConnectNodeInput(self.info->mGraph, lpfNode, 0, rioNode, 0)
            operation: "Couldn't connect filter output scope bus 0 to remoteIO input scope bus 0"]; // filter -> output

// Enable IO for input and output (recording and playing)
AudioUnitElement kRemoteIOInputScopeMic = 1;
AudioUnitElement kRemoteIOOutputScopeSpeaker = 0;

UInt32 enableInput = 1;
[Utilities checkError:AudioUnitSetProperty (self.info->rioUnit,
                                            kAudioOutputUnitProperty_EnableIO,
                                            kAudioUnitScope_Input,
                                            kRemoteIOInputScopeMic,
                                            &enableInput,
                                            sizeof(enableInput))
            operation: "Couldn't enable RIO input"];

// set up the rio unit for playback
UInt32 enableOutput = 1;
[Utilities checkError:AudioUnitSetProperty (self.info->rioUnit,
                                            kAudioOutputUnitProperty_EnableIO,
                                            kAudioUnitScope_Output,
                                            kRemoteIOOutputScopeSpeaker,
                                            &enableOutput,
                                            sizeof(enableOutput))
            operation: "Couldn't enable RIO output"];

// Set property to low-pass filter
AudioUnitSetParameter(self.info->lpfUnit,
                      kLowPassParam_CutoffFrequency,
                      kAudioUnitScope_Global,
                      0,
                      1000,
                      0);



// Set callbacks
// Callback for input
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = recordingCallback;
callbackStruct.inputProcRefCon = (__bridge void*) self;


[Utilities checkError: AudioUnitSetProperty(self.info->rioUnit,
                                            kAudioOutputUnitProperty_SetInputCallback,
                                            kAudioUnitScope_Global,
                                            1, // Input bus
                                            &callbackStruct,
                                            sizeof(AURenderCallbackStruct))
            operation: "AudioUnitSetProperty set RenderCalback"];

// Callback to render data
callbackStruct.inputProc = renderCallback;
callbackStruct.inputProcRefCon = (__bridge void*) self;


[Utilities checkError: AudioUnitSetProperty(self.info->rioUnit,
                                            kAudioUnitProperty_SetRenderCallback,
                                            kAudioUnitScope_Input, //kAudioUnitScope_Global, ????
                                            0, // Output bus
                                            &callbackStruct,
                                            sizeof(AURenderCallbackStruct))
            operation: "AudioUnitSetProperty set RenderCalback"];

[Utilities checkError: AUGraphInitialize(self.info->mGraph)
            operation: "Couldn't initialize graph"];


// Input ASBD
AudiostreamBasicDescription inputasbd;
UInt32 propSize = sizeof(inputasbd);
[Utilities checkError:AudioUnitGetProperty(self.info->rioUnit,
                                           kAudioUnitProperty_StreamFormat,
                                           kAudioUnitScope_Input,
                                           1,
                                           &inputasbd,
                                           &propSize)
            operation:"Failed to get stream format of microphone input scope"];

// Output ASBD
AudioStreamBasicDescription outputasbd;
UInt32 typeByteSize   = sizeof(float);
outputasbd.mBitsPerChannel   = 8 * typeByteSize;
outputasbd.mChannelsPerFrame = 1;
outputasbd.mBytesPerFrame    = typeByteSize * outputasbd.mChannelsPerFrame;
outputasbd.mFormatFlags      = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved;
outputasbd.mFormatID         = kAudioFormatLinearPCM;
outputasbd.mFramesPerPacket  = 1;
outputasbd.mBytesPerPacket   = outputasbd.mFramesPerPacket * outputasbd.mBytesPerFrame;
outputasbd.mSampleRate       = inputasbd.mSampleRate;

// Set stream format to output scope of input bus
[Utilities checkError:AudioUnitSetProperty (self.info->rioUnit,
                                            kAudioUnitProperty_StreamFormat,
                                            kAudioUnitScope_Output,
                                            1,
                                            &inputasbd,
                                            sizeof (AudioStreamBasicDescription))
            operation: "Couldn't set ASBD for RIO on output scope / bus 1"];

// Set format on inputscope of output bus
[Utilities checkError:AudioUnitSetProperty(self.info->rioUnit,
                                           kAudioUnitProperty_StreamFormat,
                                           kAudioUnitScope_Input,
                                           0,
                                           &outputasbd,
                                           sizeof(AudioStreamBasicDescription))
            operation: "Couldn't set ASBD for RIO on input scope / bus 0"];


// Start AUGraph
[Utilities checkError:AUGraphStart(self.info->mGraph)
        operation: "Couldn't start AUGraph"];

但不幸的是,AU Graph 无法以此代码 (OSStatus -10863) 开始。 当我通过注释掉AUGraphConnectNodeInput 方法删除过滤器或使用remoteIO AU 直接从麦克风播放音频到扬声器时没有问题。

谁能给我一个提示我在配置kAudioUnitSubType_LowPassFilter时做错了什么?

【问题讨论】:

您对“配置有问题”kAudioUnitSubType_LowPassFilter 有何概念?是什么让你确定它?您是否尝试过连接另一个子类型的单元? 其实我的意思是我真的不知道问题出在哪里。我还尝试在图表中添加kAudioUnitSubType_MultiChannelMixer(只有一个来自麦克风的输入),但我收到了相同的错误代码。 OSStatus -10863 = kAudioToolboxErr_CannotDoInCurrentContext。问题可能出在您的渲染回调中,它似乎将remoteIO AU 置于播放配置中。尝试在建立AUGraphConnectNodeInput() 连接时将其注释掉,看看是否会发生任何事情? 我当前的应用程序直接使用远程 AU,但它在回调的实现上没有问题。由于我还在渲染回调中使用了 FIR 过滤器,我只是想为 AU 更改它以提高性能。因此,我不希望回调或流格式出现问题(注释掉并没有更改错误代码)。我很欣赏其他想法。 预计您的代码应该在单个 AU 配置中运行良好。当您将另一个 AU 添加到 DSP 链中并使其成为 graph 时,问题似乎发生了。在使用kAudioUnitProperty_SetRenderCallback 机制在 -graph_ 中注册回调时出现了一些关于 SO 的帖子。尝试通过以下方式注册您的回调:AUGraphSetNodeInputCallback (self.info->mGraph, lpfNode, busNumber, &callbackStruct) - 查看参考页面和代码示例。 【参考方案1】:

编辑 2:

将您的 ASBD 更改为立体声。过滤器需要它。

我认为AUGraphConnectNodeInput(self.info->mGraph, rioNode, 1, lpfNode, 1) 应该是AUGraphConnectNodeInput(self.info->mGraph, rioNode, 1, lpfNode, 0)。 inDestInputNumber 参数本质上是输入总线,过滤器只有一个总线,因此该参数应该是总线 0;如果这不能解决问题,请在您的 setStreamFormatWithInputASBD 方法中发布正在发生的事情(如果相关)。无论哪种方式,我通常都会明确设置我的所有输出 ASBD 以匹配它所连接的单元的输入。

给定这样的图 inUnit->unitA->unitB->outUnit 使用这种模式设置 ASBD:

(audioUnitGetProperty 和 ASBD 的 setproperty 的伪代码)

unitB.outputASBD = outUnit.inputASBD; 
unitA.outputASBD = unitB.inputASBD; 
inUnit.outputASBD = unitA.inputASBD; 

有时这不是必需的,但有时却是!大多数过滤器都将强制立体声浮动 ASBD 作为输入。

【讨论】:

不幸的是,我不在 iOS 中工作,但我熟悉 OSX CoreAudio API。 RemoteIO AU 是 iOS 特定的,它的 API 类似于 mac 上的 HAL AU,@Dave。渲染回调负责播放配置,定义输入和输出总线之间的连接,而输入回调可能是无关的。但是,如果您在 RemoteIO AU 的输入总线之间连接另一个 AU,则以这种方式注册的渲染回调很可能没有任何作用。在 AUGraph 中还有其他注册回调的机制不会破坏 API。 进一步说,@Dave,inSoruceInputNumber inDestInputNumber“本质上是输入通道,而过滤器只有一个通道”,您不准确。这些是节点的总线编号,不应与流格式中每帧的音频通道数混淆。 (-: 虽然我也尝试更改总线号,但它并没有更改错误代码。关于 user3078414 的评论,我认为这些是正确的。此外,为了清楚起见,我用我使用的 ASBD 编辑了我的问题。在我的输入回调中,我执行了几项分析(包括 FIR 过滤),将音频放在循环缓冲区中并可以执行记录。我使用渲染回调从缓冲区渲染音频并播放和绘制音频。 并使用解决方案编辑答案,您需要使用立体声 ASBD。 终于,我搞定了!问题确实与使用的 asbd 有关,尤其是过滤器对 32bit float 2 通道的奇怪要求。但也因为带有回调的单元不应该在图中连接!不幸的是,在 I/O 远程 AU 上配置我现有的输入和渲染回调仍然存在问题(对于这个问题,我将在 SO 上提出另一个问题)。

以上是关于如何在 AUGraph 中添加 kAudioUnitSubType_LowPassFilter?的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS5 中将 kAudioUnitSubType_Varispeed 添加到 AUGraph

ios 5.0 中的 ios AUGraph 音高偏移

如何编辑 AUGraph 的默认工具?

MacOS 音频:使用子图的 AUGraph API 的多个输出?

核心音频:不推荐使用 AUGraph 时如何通过 MusicPlayer 播放 MIDI MusicSequence?

AUGraph 回调延迟