我的 iOS 音频处理总结

前言

前段时间在阅读苹果音频文档(均列在参考资料一节里面了),并做了一些音频相关的开发(主要是带回音消除的录音)。这里做一个总结。

关于 Audio Session

每一个 app 带有一个 AVAudioSession 的单例(也就是说正常情况下你无法获得第二个 AVAudioSession 实例)。iOS 系统上每个 app 有各自不同的 AVAudioSession 实例。通过使用这个实例的方法可以告诉系统当前的 app 是怎样使用手机的音频服务的,然后系统会根据每个 app 的配置进行相应的协调,尽量满足所有 app 的请求,当无法满足的时候,系统尽量满足前台 app 的要求或者系统电话服务等。比如说,如果当前另外一个 app 正在播放的话,当前 app 可能希望能将其播放的音频和其他 app 的音频一起播放,而不是暂停其他 app 的音频服务;又比如说当前 app 需要播放音频或者进行录音;比如当前 app 只是播放音频;比如当前 app 只是录音;比如当前 app 播放音频时候屏蔽所有其他 app 的音频等等,总之就是告诉系统当前 app 是如何使用它的音频服务的。

Audio Session 有三个比较重要的概念:

  1. category
  2. mode
  3. option

通过配置这三个内容,表达了当前 app 的使用音频服务的具体意图。

在某些情况下,我们不需要配置 Audio Session 的 category,比如如果使用 AVAudioRecorder 来录音,并不需要配置 category 为 AVAudioSessionCategoryRecord,因为系统在我们使用 AVAudioRecorder 的录音服务的时候已经为我们配置了。同时 Audio Session 有默认配置(如果当前 app 不进行配置的话)。当默认配置无法满足需求的时候,就可以手动配置 Audio Session

Audio Session 另一个重要功能是配置系统音频服务硬件参数,比如配置输入的声道数,采样率,IO 缓存时间等等。其 API 中 setPreferred__ 开头的方法作用就是这些。

配置完 Audio Session 以后,当我们要求的音频服务受到打断(比如,电话来了,则系统要停止录音和播放;比如,app 退到后台运行了,如果没有配置后台运行的话,系统也会停止当前 app 的音频服务;),我们可以使用通知中心的方式来监听,并做一下相应的处理。音频服务中断有两个概念比较重要,就是中断开始以及中断结束,我们可以在中断开始的时候记录当前播放时间点,中断结束的时候重新开始播放(当然系统默认行为是会在中断结束时重新开始播放音频,但是如果默认行为无法满足需求时候,就需要自行处理了)。

Audio Session 另一个重要我们需要监听的变化是路由变化 (Route Change)。比如有新的输出源来了(比如用户把耳机插进去或者是用户开始使用蓝牙耳机),或者原来的输出源不可用了(用户拔掉耳机等)。

还有一些其他的功能,比如当前其他 app 是否在播放音频,请求麦克风权限等,可以查看具体的 API 文档 AVAudioSession。

Audio Queue Service

使用 Audio Queue Service 我们可以做到录音或者播放音频。当然我们使用 AVAudioPlayer 也能很简单的做播放音频功能,那为什么要用到 Audio Queue Service 呢?它有几个优点

  1. 设想你的要严格同步不同音频的播放。 Audio Queue Service 的回调函数包含相应的时间,来满足你的需求。
  2. 如果是播放音频用 Audio Queue Service,在将音频数据给它的回调函数之前做一些处理,比如变声等。
  3. 如果是录音,可以对回调函数传回来的音频数据做处理,比如写到文件或者对这些音频数据进行其他任何处理

理解 Audio Queue Service 比较重要的是它的 buffer queue。拿录音来说,一般设置的缓存是3个。首先通过 AudioQueueEnqueueBuffer 将可用缓存提供给相应的 queue。然后系统开始将记录下的音频数据放到第一个缓存,当缓存满的时候,回调函数会将该 buffer 返回给你并将该缓存出列,在回调函数中我们可以对这些数据进行处理,与此同时系统开始将数据写到第二个缓存,当我们的回调函数处理完第一个返回的缓存时候,我们需要重新使用 AudioQueueEnqueueBuffer 将该缓存入列,以便系统再次使用。当第二个缓存返回的时候,系统开始往第三个缓存写数据,写完之后返回第三个缓存,并开始往之前返回的第一个缓存写数据。这就是一个典型的队列结构(先进先出,后进后出)。

Audio Unit

Audio Unit 是所有 iOS 以及 macOS 上音频框架的最底层,无论使用的是 AVAudioRecorder、AVAudioPlayer、或者 Audio Queue Service、OpenAL 等,最终底层实现都是通过 Audio Unit 来完成的。

在 iOS 上可用的 audio unit 是有限的,macOS 上面可以自定义一个 audio unit 但是 iOS 上不行,只能使用系统提供的 audio unit。

什么时候使用 Audio Unit ?官方的说法是,当你需要高度可控的、高性能、高灵活性或者需要某种特别的功能(比如回音消除,只在 audio unit 提供支持,所有高层 API 均不支持回音消除)的时候,才需要使用 audio unit。

有4类 audio unit(具体用途看名字就能理解):

  1. Effect
  2. Mixing
  3. I/O
  4. Format convert

使用 audio unit 有两种方式:

  1. 直接使用
  2. 混合构建使用,AUGraph

第一种方式是对于比较简单的结构。
第二种方式是用于构建复杂的音频处理流程。配置具体的 audio unit 的属性的时候还是会用到直接使用种的方法。

Audio Unit 重要概念

audio unit 重要的概念是 scope 和 element。scope 包含 element。

scope 分三种:

  1. Input scope
  2. Output scope
  3. global scope

scope 概念有一点抽象,可以这样理解 scope,比如 input scope 表示里面所有的 element 都需要一个输入。output scope 表示里面所有的 element 都会输出到某个地方。至于 global scope,应该是用来配置一些和输入输出概念无关的属性。

element 官方的解释是可以理解成 bus,就是将数据从 element 的一头传到另一头。

其他

  1. 音频格式转换
  2. 音频(流)读写

iOS 录音的几种方式

  1. 使用 AVAudioRecorder
  2. 使用 Audio Queue Service
  3. 使用 Audio Unit
  4. 使用 OpenAL

iOS 播放音频的方式

  1. 使用 AVAudioPlayer
  2. 使用 AVPlayer
  3. 使用 System Sound Services
  4. 使用 Audio Queue Service
  5. 使用 Audio Unit
  6. 使用 OpenAL

一些需要思考的问题

  1. 如何获取当前扬声器播放的音频数据?(包括其他 app)
  2. 如何实时录音,同时当前手机正在播放音频
  3. 如何做回音消除,或者不借助系统提供的回音消除功能来完成回音消除的需求?

这里有些问题我也不知道如何解答,若有了解的,请多多指教一下。

一些有用的开源代码

  1. aurioTouch 官方的关于 audio unit 使用 demo 代码。注意其中关于 AVAudioSession 配置的顺序是错的,你可以看它的代码和 AVAudioSession Api 的说明来知道错误的地方
  2. XBEchoCancellation 可以学习其中关于回音消除的使用。官方的 aurioTouch demo 简单的改 audio unit 类型为 voiceprocess 也可以做回音消除,但是当涉及到同时播放音频时,官方 demo 在某些 iPhone 上会失败,所以建议参考这个源码

参考资料

  1. Core Audio Overview
  2. Audio Session Programming Guide
  3. Audio Queue Services Programming Guide
  4. Audio Unit Programming Guide
  5. Audio Unit Hosting Guide for iOS
  6. Multimedia Programming Guide

你可能感兴趣的:(我的 iOS 音频处理总结)