前面学习了AudioUnit相关理论姿势,现在结合实际demo继续深入学习一下。。。
AudioUnit–播放本地音频
因为AudioUnit只支持PCM文件,所以这里我们先使用一个本地的PCM文件进行播放,后续会有播放其他格式文件的demo,需要用到转码的相关API。
这里我们使用NSInputStream直接读取音频文件,因为不需要转码,所以读取到的数据就是可以直接拿来播放的数据。
|
|
初始化AudioUnit相关内容前,我们需要全局设置一下AVAudioSession
|
|
然后可以开始初始化AudioUnit,这里我们需要的是Output type
|
|
接下来有一个kAudioOutputUnitProperty_EnableIO属性的设置,这里如果只是播放功能的话,实际上是不需要设置的,因为AudioUnit默认就是支持播放的,如果需要输入功能(比如录音)时,就必须设置这个属性,之后的demo会有使用,这里我们先不设置这个属性,设置的相关格式如下:
|
|
然后我们需要设置AudioUnit的播放音频的格式,这里一般都是PCM格式
|
|
最后设置一下输出的回调,这个回调是每次扬声器需要播放数据时都会调一次这个回调,然后我们在这个回调里面把要播放的数据塞给扬声器。
|
|
然后就可以开始播放音频了
|
|
AudioUnit–录音+播放+保存本地
这个demo做的事情是把本地播放的音频和录制的音频一起输出并写入文件。播放音频部分和上面的流程一样,这里主要说一下录制音频部分的流程。
AVAudioSession的设置,这里我们需要用到录音功能,同时我们还需要设置一下采样频率。
|
|
我们使用AudioBufferList 这个数据结构来保存获取到的录音音频数据,先对该数据结构做一个初始化
|
|
接着上个demo的代码,这里我们再设置一下输入的数据结构
|
|
然后我们需要设置AudioUnit的输入能力
|
|
之后设置一下录音的回调,在回调中获取录音的音频数据
|
|
这里我们把录音的音频数据保存在了bufferList中。这个bufferList中的数据将被手动塞给播放的回调进行播放,播放(输出)的回调代码如下:
|
|
这里我们给ioData->mBuffers塞了2组数据,一组是从bufferList中获取的录音音频数据,另外一组是从inputStream中读取的本地音频数据,所以播放时既能听到本地音频也能听到我们录音的音频。
将音频写入文件的代码如下:
|
|
AudioUnit–播放MP3
上面说过,AudioUnit是不支持直接播放MP3文件格式的,所以这里我们先把MP3转码为pcm格式然后再塞给播放回调去播放。
播放的逻辑我们已经很清楚了,这里我主要说一下怎么转码。这里我们用到了AudioFile和AudioConverter两个类,AudioFile主要做音频相关信息的读取,AudioConverter主要做转码的工作。
先来看一下AudioFile的相关代码:
|
|
这里说一下packetnum这个属性,因为音频数据流都是以一个个packet的格式封装起来的,所以这个获取的packetnum实际上就是音频的总长度,而每个packet里面又以帧(frame)为单位做了一层封装,每个packet里面的帧数不固定。每一帧就是实际的音频数据了。
因为数据的处理都在播放回调中,我们来看看AudioConverter都做了哪些工作:
|
|
可以看到,是调用了AudioConverterFillComplexBuffer这个方法做的转码,其中第二个参数又是一个回调函数,转码后的数据同样是保存在converter->bufferList这个结构里面,然后在后面塞给ioData->mBuffers去播放。
|
|
这个回调函数主要负责读取mp3文件给上面的AudioConverterFillComplexBuffer函数用来做转码处理。
AudioUnit–更简单的播放MP3
上一个demo中我们使用AudioUnit播放了一个MP3文件,用到了AudioFile和AudioConverter类的相关方法,可以看到,使用这种方式播放MP3文件是比较繁琐的,我们要获取音频的很多信息,要设置回调,要使用一系列的相关api。。。下面介绍一种更简单的播放MP3的方式,使用ExtAudioFile,看名字就知道,它是上面介绍的AudioFile的扩展。
|
|
AudioUnit相关的代码参考上面的几个demo. 这个必需的操作就是open file,获取到extAudioFile。然后我们看看回调里面做了什么
|
|
这里我们只调用了一个ExtAudioFileRead方法,就把MP3文件转成了pcm文件存到了player->bufferList中,最终塞给ioData->mBuffers去播放即可。是不是比上面demo要简单很多呢。
AudioUnit–使用AUGraph播放伴奏+录音
前面都是直接使用AudioUnit实现播放,录音相关工作,这一小节我们来学习一下使用AUGraph管理AudioUnit,当你需要多个AudioUnit来实现不同的模块,且各模块的输入输出直接又有相关联时,使用AUGraph是比较好的选择,因为它可以为AudioUnit之间建立连接,直接进行数据传递,省去了我们手动传递数据的麻烦。
这里我是分了三步来实现整个功能的:
- 1.先播放本地音频
- 2.加入录音功能
- 3.将录音音频和本地音频送给mix,mix的输出绑定到io的输出
首先来看第一步,使用AUGraph播放本地音频
|
|
io的回调函数
|
|
第二步:加入录音功能,这里其实只需要对第一步做一下修改,将io回调数据处理的bus改为输入,设置输入相关属性即可
|
|
io的回调处理
|
|
第三步:获取2路音频并同时播放
|
|
mix的2路数据的回调
|
|
在mix的2路回调中,我们分别获取到了数据并塞给mix的输入,然后mix直接输出到io的输出bus进行播放,中间不在需要我们做额外的工作。