我的 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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容