在看LFLiveKit代码的时候,看到音频部分使用的是audioUnit做的,所以把audioUnit学习了一下。总结起来包括几个部分:播放、录音、音频文件写入、音频文件读取.
demo放在VideoGather这个库,里面的audioUnitTest是各个功能的测试研究、singASong是集合各种音频处理组件来做的一个“播放伴奏+唱歌 ==> 混音合成歌曲”的功能。
基本认识
audioUnitScopes_2x.png
对于通用的audioUnit,可以有1-2条输入输出流,输入和输出不一定相等,比如mixer,可以两个音频输入,混音合成一个音频流输出。每个element表示一个音频处理上下文(context), 也称为bus。每个element有输出和输出部分,称为scope,分别是input scope和Output scope。Global scope确定只有一个element,就是element0,有些属性只能在Global scope上设置。
IO_unit_2x (1).png
对于remote_IO类型audioUnit,即从硬件采集和输出到硬件的audioUnit,它的逻辑是固定的:固定2个element,麦克风经过element1到APP,APP经element0到扬声器。
我们能把控的是中间的“APP内处理”部分,结合上图,淡黄色的部分就是APP可控的,Element1这个组件负责链接麦克风和APP,它的输入部分是系统控制,输出部分是APP控制;Element0负责连接APP和扬声器,输入部分APP控制,输出部分系统控制。
IOWithoutRenderCallback_2x (1).png
这个图展示了一个完整的录音+混音+播放的流程,在组件两边设置stream的格式,在代码里的概念是scope。
文件读取
demo在TFAudioUnitPlayer这个类,播放需要音频文件读取和输出的audioUnit。
文件读取使用ExtAudioFile,这个据我了解,有两点很重要:1.自带转码 2.只处理pcm。
不仅是ExtAudioFile,包括其他audioUnit,其实应该是流数据处理的性质,这些组件都是“输入+输出”的这种工作模式,这种模式决定了你要设置输出格式、输出格式等。
ExtAudioFileOpenURL使用文件地址构建一个ExtAudioFile
文件里的音频格式是保存在文件里的,不用设置,反而可以读取出来,比如得到采样率用作后续的处理。
设置输出格式
AudioStreamBasicDescription clientDesc;
clientDesc.mSampleRate = fileDesc.mSampleRate;
clientDesc.mFormatID = kAudioFormatLinearPCM;
clientDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
clientDesc.mReserved = 0;
clientDesc.mChannelsPerFrame = 1; //2
clientDesc.mBitsPerChannel = 16;
clientDesc.mFramesPerPacket = 1;
clientDesc.mBytesPerFrame = clientDesc.mChannelsPerFrame * clientDesc.mBitsPerChannel / 8;
clientDesc.mBytesPerPacket = clientDesc.mBytesPerFrame;
pcm是没有编码、没有压缩的格式,更方便处理,所以输出这种格式。首先格式用AudioStreamBasicDescription这个结构体描述,这里包含了音频相关的知识:
采样率SampleRate: 每秒钟采样的次数
帧frame:每一次采样的数据对应一帧
声道数mChannelsPerFrame:人的两个耳朵对统