AUGraph结合RemoteI/O Unit与Mixer Unit

前言

相关文章:
使用VideoToolbox硬编码H.264
使用VideoToolbox硬解码H.264
使用AudioToolbox编码AAC
使用AudioToolbox播放AAC
HLS点播实现(H.264和AAC码流)
HLS推流的实现(iOS和OS X系统)
iOS在线音频流播放
Audio Unit播放PCM文件
Audio Unit录音(播放伴奏+耳返)
Audio Unit播放aac/m4a/mp3等文件
Audio Unit和ExtendedAudioFile播放音频
前文介绍了AudioUnit的录音/播放、AudioConvert进行音频转换、ExtendedAudioFile进行音频文件的读/写,其中AudioUnit的初始化都是通过AudioComponentInstanceNew实现,实际工程中更多使用的是AUGraph的方式进行AudioUnit的初始化。
本文尝试用AUGraph来管理RemoteI/O Unit和Mixer Unit,实现录音、伴奏播放、人声和伴奏混合的功能。

基础结构图

正文

1、概念介绍

AUGraph连接一组 audio unit 之间的输入和输出,构成一张图,同时也为audio unit 的输入提供了回调。AUGraph抽象了音频流的处理过程,子结构可以作为一个AUNode嵌入到更大的结构里面进行处理。AUGraph可以遍历整个图的信息,每个节点都是一个或者多个AUNode,音频数据在点与点之间流通,并且每个图都有一个输出节点。输出节点可以用来启动、停止整个处理过程。

每个AudioUnit都有Input, Output 和 Global 三个域。
input输入域是音频流进入unit的入口,output输出域是音频流离开unit的出口,global全局域则代表整个unit。
输入域和输出域都有若干个bus/element,比如说mixer unit有多个输入bus,只有一个输出bus;而splitter unit则有一个输入bus,有多个输出的bus。

注意的是,bus和channel不是一个东西,一个是音频流,一个是音频流的格式。
比如说Remote I/O Unit的输入域的inputBus是来自麦克风的音频流,其音频格式是双声道。

2、具体流程

  • 1、初始化文件流和AVAudioSession,分配buffer;
  • 2、新建AUGraph,并添加两个AUNode,一个是RemoteI/O Unit的节点,一个是Mixer Unit的节点。
    添加AUNode的节点有两个步骤,先通过AUGraphAddNode添加节点,再通过AUGraphNodeInfo获取节点对应的AudioUnit。
  • 3、建立两个AUNode的联系,AUGraphConnectNodeInput通过把Mixer Unit的outputBus的输出作为RemoteI/O Unit的outputBus的输入;
    (这里需要注意,不是RemoteI/O的inputBus 的输入,因为RemoteI/O Unit的inputBus的输入是麦克风)
    同时设置好RemoteI/O Unit的输入和输出格式、Record的回调函数;
  • 4、调用AUGraphInitialize初始化AUGraph,然后通过AUGraphStart开始整个AUGraph;
    在AUGraph开启后,麦克风收到录制数据后调用kAudioOutputUnitProperty_SetInputCallback的回调,把麦克风的数据回调给APP;
    Mixer Unit还会通过之前kAudioUnitProperty_SetRenderCallback设置好的回调,要求APP填充两个inputBus的输入;
    在Mixer Unit处理好数据之后,会按照之前AUGraphConnectNodeInput设置的,把数据发送给Remote I/O Unit;
    Remote I/O Unit再把数据发送给扬声器。

3、音频流解析

如下,是整个demo的音频流向:



伴奏文件被读取到内存,再被送到MixUnit的inputBus0;
麦克风录取到音频数据,送到Remote I/O Unit的inputBus,存到内存中,再被送到MixUnit的inputBus1;
MixUnit混合两个inputBus的数据,通过outputBus输出到Remote I/O Unit的outputBus中;
Remote I/O Unit再把outputBus的数据发送个扬声器。

遇到的问题

1、AUGraphNodeInfo无法初始化AudioUnit

实际运行时,报错是AudioUnitSetProperty方法,返回了-50的错误码。
检查错误码,是AudioUnitSetProperty的audio unit参数为空。
往上回溯,定位到AUGraphNodeInfo没正确初始化传入的audio unit参数,导致audio unit为空,并且当时没有报错,直到AudioUnitSetProperty时才报错。

经过仔细检查,发现是AUGraphOpen方法被遗漏。
必须先打开AUGraph,才进行获取AudioUnit的操作。

2、AUGraphSetNodeInputCallback给RemoteI/O Unit设置回调无效

如下,给RemoteI/O Unit设置回调可以用AudioUnitSetProperty方法修改kAudioOutputUnitProperty_SetInputCallback设置回调,但尝试用AUGraphSetNodeInputCallback对RemoteI/O Unit节点添加回调的时候,发现没法正常调用回调函数。

    AURenderCallbackStruct recordCallback;
    recordCallback.inputProc = RecordCallback;
    recordCallback.inputProcRefCon = (__bridge void *)self;
    
//    CheckError(AUGraphSetNodeInputCallback(auGraph, outputNode, INPUT_BUS, &recordCallback), "record callback set fail");  // 这个不行,因为scope不一致
    CheckError(AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Output, INPUT_BUS, &recordCallback, sizeof(recordCallback)), "set property fail");

AUGraphSetNodeInputCallback 默认是inputScope,如果在input bus的inputScope修改属性,会造成异常现象;

3、kAudioOutputUnitProperty_SetInputCallback 和 kAudioUnitProperty_SetRenderCallback 混淆

  • kAudioUnitProperty_SetRenderCallback 是audio unit需要数据,向Host请求数据;
  • kAudioOutputUnitProperty_SetInputCallback是audio unit通知Host数据已经就绪,可以通过AudioUnitRender拉取数据;

AudioUnitRender的解释是:Initiates a rendering cycle for an audio unit.
下图阐释了AudioUnit是如何通过AudioUnitRender去Pull音频流数据

4、AUGraphConnectNodeInput的BUS参数设置错误

AUGraphConnectNodeInput(auGraph, mixNode, OUTPUT_BUS, outputNode, OUTPUT_BUS),从字面看是把mixNode的输出作为outputNode的输入。
但是在bus的参数设置上,为什么Remote I/O Unit的bus不是inputBus?
因为Remote I/O Unit有输入域有两个Bus,inputBus对应的是麦克风的输入,outputBus对应的是app发送给Remote I/O Unit的数据。
这里Mixer Unit是把人声和伴奏混合后,输出给Remote I/O Unit,相当于app发送数据给Remote I/O Unit,所以这里应该填outputBus。

总结

demo中仍然存在问题,因为两个unit结构混乱:
麦克风=>I/O Unit=>APP=>MixUnit
文件=>APP=>MixUnit
然后再是MixUnit=>I/O Unit=>扬声器
其中,I/O Unit既指向MixUnit,同时MixUnit又指向I/O Unit。
更好的实现方案,用一个Unit来实现录音,再用另外一个Unit进行播放,形成 RecordUnit=>MixUnit=>PlayUnit这样的结构会更加漂亮。
这个设想就交由你去实现了!
demo的代码点击这里书写不易,不如来个喜欢支持下↓↓

附录

Core Audio Tips
Audio Unit Properties Reference PDF
Audio Unit Hosting Guide for iOS

推荐阅读更多精彩内容

  • 本例需求:将Mic采集的PCM转成AAC,可得到两种不同数据,本例采用AudioQueue/AudioUnit两种...
    小东邪啊阅读 15,903评论 67 50
  • 一、Audio Unit综述1.1、Audio Unit 概念点1.2、 AuidoUnit类型二、构建Audio...
    _羊羽_阅读 5,698评论 3 16
  • 前言 相关文章:使用VideoToolbox硬编码H.264使用VideoToolbox硬解码H.264使用Aud...
    落影loyinglin阅读 12,331评论 21 37
  • 国人一直都在改变世人看法,提高全民素质,但意识不该谈何容易。 环保在中国,刚刚起步。实行困难重重的,一个塑料袋引发...
    19号淡然阅读 72评论 0 0
  • 俺觉得有些同学,是青翠的故事 记忆泛黄了,不影响 他们是树,是草,是花,是鸟 是一片茂密森林 他们随风摇动百家姓中...
    乔桥阅读 238评论 3 4
  • 7、是走是留 关键是利益均衡 这次大会是正式改革的一个前奏,但是对于新人的工资待遇、保险合同问题,只字未提。 说到...
    fe480d2afa49阅读 100评论 0 0
  • 有一段时间,很长的一段时间里,都单曲循环田馥甄的这首《小幸运》。人生那么长,世界那么大,你最想留住的人是谁?...
    clairelou阅读 250评论 0 0
  • 笔友频频送鼓励, 桃心颗颗映眼里。 感恩之心送给你, 祝君奉献好文笔。
    旖旎i阅读 99评论 2 4