Core Audio框架详细解析(二) —— 基于CoreAudio的ios音频服务总结分析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Core Audio框架详细解析(二) —— 基于CoreAudio的ios音频服务总结分析相关的知识,希望对你有一定的参考价值。

参考技术A

Core Audio 是ios和MAC系统中的关于数字音频处理的基础,它是应用程序用来处理音频的一组软件框架,所有关于iOS音频开发的接口都是由Core Audio来提供或者经过它提供的接口来进行封装的。

其实一句话,它是任何iOS或者MAC系统音频处理框架的基础。

具体可以用官方文档的一张图表示。

接下来我们就一起分析一下。

这里的高级别服务,更加接近于顶层,基本上我们很多关于音频开发的工作在这一层就可以完成。

它位于框架 AudioToolbox 中。

提供录制、播放、暂停、循环、和同步音频它自动采用必要的编解码器处理压缩的音频格式。

要在iOS设备上播放和录制音频,苹果推荐我们使用 AVFoundation 框架中的 AVAudioPlayer 和 AVAudioRecorder 类。虽然用法比较简单,但是不支持流式;这就意味着:在播放音频前,必须等到整个音频加载完成后,才能开始播放音频;录音时,也必须等到录音结束后,才能获取到录音数据。这给应用造成了很大的局限性。为了解决这个问题,我们就需要使用 Audio Queue Services 来播放和录制音频。感兴趣的可以看我前面写的几篇关于 Audio Queue Services 的文章。这里只是简单的给出录音和播放的原理图,具体原理和流程,看我前面写的那几篇,都有详细的介绍。

它位于框架 AVFoundation 中。

是专为IOS平台提供的基于Objective-C接口的音频播放类,可以支持iOS所支持的所有音频的播放,它主要支持以下音频格式。

这个是纯OC的实现,特点就是调用简单,下面简单的看一下他的API。

由 Audio File 与 Audio Converter 组合而成,提供压缩及无压缩音频文件的读写能力。

它与 Audio File Services 、 Audio File Stream Services 和 Audio Queue Services 等同时存在 AudioToolbox 框架中。 ExtendedAudioFile 相对 Audio File Services 和 Audio Converter Services ,API调用非常简单和明确,并且不需要去处理 AudioStreamPacketDescription ,在实际开发中逻辑更为清晰。

它就是存在框架 OpenAL 中。

是CoreAudio对OpenAL标准的实现,可以播放3D混音效果。

OpenAL 主要的功能是在来源物体、音效缓冲和收听者中编码。来源物体包含一个指向缓冲区的指标、声音的速度、位置和方向,以及声音强度。收听者物体包含收听者的速度、位置和方向,以及全部声音的整体增益。缓冲里包含 8 或 16 位元、单声道或立体声 PCM 格式的音效资料,表现引擎进行所有必要的计算,如距离衰减、多普勒效应等。

不同于 OpenGL 规格,OpenAL 规格包含两个API分支;以实际 OpenAL 函式组成的核心,和 ALC API , ALC 用于管理表现内容、资源使用情况,并将跨平台风格封在其中。还有 “ALUT ”程式库,提供高阶“易用”的函式,其定位相当于 OpenGL 的 GLUT 。

该层功能比较齐全,包括音频数据格式转换,音频文件读写,音频流解析,插件工作支持等。

它位于框架 AudioToolbox 中。

负责音频数据格式的转换

它位于框架 AudioToolbox 中。

负责音频数据的读写。

它位于框架 AudioToolbox 中。

支持均衡器和混音器等数字信号处理的插件。

它位于框架 AudioToolbox 中。

负责流解析。

它位于框架 Core Audio 中。

负责音频音频时钟同步。

该主要在MAC上的音频APP实现中并且需要最大限度的实时性能的情况下使用,大部分音频APP不需要使用该层的服务。而且,在iOS上也提供了具备较高实时性能的高层API达到你的需求。例如 OpenAL ,在游戏中具备与I/O直接调用的实时音频处理能力。

它在 IOKit 框架中,与硬件驱动交互。

获得用户空间访问硬件设备和驱动程序。 I / O Kit 框架通过设备接口机制实现对I / O Kit对象(驱动程序和结点)的非内核访问。

音频硬件抽象层,使API调用与实际硬件相分离,保持独立。

它位于 Core MIDI 框架中,与MIDI设备(如硬件键盘和合成器)进行通信。

Core MIDI 框架提供了用于与MIDI(乐器数字接口)设备(包括硬件键盘和合成器)进行通信的API。 使用基座连接器或网络从iOS设备进行连接。 有关使用基座连接器的更多信息,请参阅Apple的 MFi program 。

访问电脑硬件时钟。

只实现音频的播放,没有其他需求, AVAudioPlayer 就可以满足需求。它的接口使用简单,不用关心其中的细节,通常只提供给它一个播放源的URL地址,并且调用其play、pause、stop等方法进行控制,observer其播放状态更新UI即可。

APP需要对音频进行流播放,就需要 AudioFileStreamer 加 Audio Queue ,将网络或者本地的流读取到内存,提交给 AudioFileStreamer 解析分离音频帧,分离出来的音频帧可以送给 AudioQueue 进行解码和播放,可参考下面。

AudioStreamer

FreeStreamer

AFSoundManager

APP需要需要对音频施加音效(均衡器、混响器),就是除了数据的读取和解析以外还需要用到AudioConverter或者Codec来把音频数据转换成PCM数据,再由AudioUnit+AUGraph来进行音效处理和播放,可参考下面。

DouAudioStreamer

TheAmazingAudioEngine

AudioKit

1. iOS Audio Unit(一)

OSS.Core基于Dapper封装(表达式解析+Emit)仓储层的构思及实现

    最近趁着不忙,在构思一个搭建一个开源的完整项目,至于原因以及整个项目框架后边文章我再说明。既然要起一个完整的项目,那么数据仓储访问就必不可少,这篇文章我主要介绍这个新项目(OSS.Core)中我对仓储层的简单思考和实现过程(当前项目还处在搭建阶段),主要集中在以下几个方面:

1. 数据仓储层的需求

2. ORM框架选择

3. OSS.Core仓储层设计实现

4. 调用示例

   下边的实现部分中可能需要你对.NET的 泛型,委托,扩展,表达式等有一个基础了解。正是因为这些语言特性,方便我们对操作共性的抽取统一。

一. 数据仓储层需求

  既然是一个完整的项目,数据访问是其最基本的部分,同时,数据访问也是整个项目最容易出现瓶颈的地方。在我的划分中,其承担的角色是负责整个数据的输入输出,不仅仅是针对单数据库(有时甚至多库),有时还需要完成一级缓存的实现,给逻辑层提供最基础的数据支撑。

   业务永远是在变化的,那么项目也要具备快速演进的能力,所以我希望数据层能够保持相对的简单,在结构上尽量减少复杂的耦合查询,在性能上尽量减少不必要的消耗,例如反射的大量使用。同时针对每个业务对象完成数据库层面基本的CRUD统一封装实现。如果有需要的时候还能在最少的改动下加入缓存的更新。(对于如何实现不同模块不同缓存存储策略,像Redis,Memcached会在后边文章介绍)

  同时,对于一个稍微有点规模的项目来说,解决数据库访问的最快速做法就是实现读写分离,所以,我希望这个框架能够在一开始在底层就实现了读写分离的支持,以避免后期再重头对业务代码的大量修改。  

二. ORM 框架选择

  当然,如果为了简单和性能,直接ADO.NET连接理论上来说是比较高效的做法,不过这样会造成大量的重复操作逻辑代码,同时也会造成代码的散乱,增加维护复杂度。作为技术人员,不仅需要解决业务问题提高效率,同时也要提高自己的效率,所以我会选择一个ORM框架来完成部分基础工作。

  当前在.NET体系下,开源的ORM框架很多,如:Entityframework,NHibernate,iBATIS.NET,Dapper等等,各有特色,基于前面我说的,保证效率的同时,兼顾简单还能最大程度减少性能的损耗,并且提供.net standard标准库下的支持。这里对比之后我选择Dapper这个半自动化的ORM作为仓储层的基础框架,选择原因如下:

  1. 其结构简单,整个封装主要集中Dapper.cs文件中,体积很小

      2. 封装功能简单强大,对原生SQL的支持上很灵活

    这点几乎完胜其他框架,无需任何多余的设置,同时基本上你可调用所有原生ADO.NET的功能,sql语句完全自己掌控,却又无需关心command的参数赋值,以及结果实体转换等。

  3. 性能上的高效

    很多ORM的实体映射通过反射来完成,这点上Dapper再次展现其魅力,在Commond参数赋值,以及实体转换等关键模块,使用了Reflection.Emit功能,间接实现了MSIL编译层面的赋值实现,之所以说间接,是因为其本身代码还需要编译器生成IL代码。在运行时根据类型属性动态创建赋值委托方法。

 

三. OSS.Core仓储层设计实现

   通过Dapper可以实现在数据库访问部分一层简单的封装,不过我依然需要手动编写不少的sql语句,同时还要进行参数化的处理,包括数据的读写分离等。那么这些功能的实现我将在OSS.Core.RepDapper中完成,为了方便理解,先贴出一个简单的封装后的方法调用传输流程:

技术分享

  在这个图里展示一个简单的方法调用流程,围绕这张图的几个核心部分,我分别介绍下:

  1. 接口设计

  因为我希望这个是完整的示例项目,所以后边希望能够兼容不同数据库,因此对外的仓储访问都基于接口调用。当然如果你的项目根本没有切换数据库的需求,我更建议去掉这一环节,直接在基类中实现单例模式,业务逻辑层直接调用。

  图中可以看到接口层独立于实现部分,我将具体业务实体模型和接口 单独放在了OSS.Core.DomainMos 类库中,一方面是为了实体模型在各模块中的共用,另一方面解耦业务逻辑层(Services)和仓储层(Reps)之间的依赖关系。

  同时一个项目中数据库访问代码多数都会以CRUD为主,所以这里我定义了一个基础接口(IBaseRep),其包含的方法主要有(表达式部分在后边介绍):

技术分享

  具体的业务数据接口继承至基础接口就好,其中表达式部分是我自己做了一个封装,后边会简单介绍。

 

  2. 仓储基类实现(BaseRep)

  首先,如图所示,我们实现了读写分离的两个扩展,其实最终都会经过Excute方法,那么这里展示下方法的具体实现:

技术分享

  可以看到在这个方法提供了一个针对IDbConnection的委托,提供调用层自由使用Dapper方法的同时,统一了数据访问方法入口,便于日志记录,和排查。

 

  其次,在很多项目中会出现用户和订单在不同库中的这类情况,因为涉及到分库的情况,所以需要子类中能有修改连接串能力,那么这里我通过构造函数的形式,提供了两个可空参数:

技术分享

  可以看到,如果子类中定义了自己的连接串,则以子类自定义为主,否则走默认的连接信息。

  

  最后,我们也实现了针对基础接口方法的具体实现,举一示例:

技术分享

  同时,为了保证子类中能够加入缓存处理,所以采用了虚方法(virtual)的形式,保证子类能够重写。

 

  3. 基于Connection的扩展

  这个地方主要分为两个部分,a. 表达式的解析,以及参数化的处理   b. 扩展Connection的Insert,Update...等Dapper没有扩展的方法:

  a. 熟悉Expression表达式的朋友应该比较了解,表达式本身是一个树形接口,根据不同的类型,可以不断的解析其子表达式,直到不具备继续解析的可能。所以这个就很简单就是递归的不断迭代,根据其不同的NodeType可以组装不同的sql元素,因为代码较长,可以参见github下的SqlExpressionVisitor.cs类,其中参数的赋值部分,没有采用反射,而是使用的反射发射,代码详见SqlParameterEmit.cs

  b. 有了表达式的扩展之后,就可以获取对应的sql和参数,通过this扩展Connection方法即可,代码见ConnoctionExtention.cs

  

四. 调用示例

  1. 我们定义一个简单UserInfoMo实体(包含mobile等属性)

  2. 定义接口  IUserInfoRep: IBaseRep

  3. 定义实现类  UserInfoRep : BaseRep, IUserInfoRep

  在不添加其他代码的基础上,我们就可以完成下面的调用:

技术分享

 



以上是关于Core Audio框架详细解析(二) —— 基于CoreAudio的ios音频服务总结分析的主要内容,如果未能解决你的问题,请参考以下文章

OSS.Core基于Dapper封装(表达式解析+Emit)仓储层的构思及实现

vue webpack 脚手架项目详细解析系列(二,项目依赖说明 package.json)

Spring源码深度解析

面试系列二:精选大数据面试真题JVM专项-附答案详细解析

无法替换 ASP.Core 3 中的默认 JSON 合同解析器

django.core.exceptions.ImproperlyConfigured:无法使用视图名称“用户详细信息”解析超链接关系的 URL