构建 Audio Unit 应用程序

构建 Audio Unit 应用程序

  • 构建 Audio Unit 应用程序
    • 从选择设计模式开始
      • I/O Pass Through
      • I/O Without a Render Callback Function
      • I/O with a Render Callback Function
      • Output-Only with a Render Callback Function
      • 其他设计模式
    • 构建应用程序
      • 配置 audio session
      • 指定 audio unit
      • 创建 audio processing graph
      • 配置 audio unit
      • 编写并绑定渲染回调函数
      • 连接 audio unit nodes
      • 提供用户界面
      • 初始化然后开启 audio processing graph
    • Debug 小技巧

构建 Audio Unit 应用程序

使用 Audio Unit 构建应用程序主要步骤是选择一个设计模式,然后编写代码来实现该模式。

从选择设计模式开始

在 iOS 应用程序中,Audio Unit 有很多基本设计模式。每种模式都有的共同特征:

  • 仅仅只有一个 I/O 单元。

  • 在整个 audio processing graph 中使用单一的音频流格式,尽管该格式可能存在变化。

  • 要求在特定位置设置流格式或部分流格式。

正确设置流格式对于建立音频数据流至关重要。这些模式大多依赖于音频单元连接提供的音频流格式从源到目的地的自动传播。合理利用这种传播的特性可以减少了编写和维护的代码量。同时,必须保证清楚了解每种模式需要如何进行设置。例如,必须在 iPod EQ 单元的输入和输出上设置完整的流格式。

在大多数情况下,设计模式都会使用 AUGraph。虽然可以在不使用 graph 的情况下实现这些模式中的任何一种,但使用 graph 可以简化代码并支持动态重新配置。

I/O Pass Through

I/O Pass Through 模式将传入的音频直接发送到输出硬件,没有处理音频数据的选项。虽然这没有什么实际价值,但基于这种模式构建 Audio Unit 应用程序是验证和巩固对 Audio Unit 概念的理解的好方法。图 2-1 说明了这种模式。

截屏2024-06-21 10.31.25

如图所示,音频输入硬件将其流格式强加在 Remote I/O unit 的 input element 的外向一侧。开发者需要指定要在此元素的内侧使用的格式, 音频单元内部将根据需要执行格式转换。为了避免不必要的采样率转换,在定义流格式时最好使用音频硬件的采样率。我们也不必指定 Remote I/O unit 的 output element 的流格式,因为流格式会通过连接从 input element 传给 output element。同理,传给硬件的流将会根据硬件需要完成一次自动转换。

I/O Without a Render Callback Function

可以在 Remote I/O unit 的元素之间添加一个或多个其他音频单元,例如,使用多通道混音器单元将传入的麦克风音频定位在立体声域中,或提供输出音量控制。在这个设计模式中,仍然没有渲染回调函数,如图 2-2 所示。这简化了模式,但限制了其效用。如果没有渲染回调函数,就无法直接操作音频。

截屏2024-06-21 11.09.29

在此模式中,可以像在 I/O Pass Through 模式中一样配置 Remote I/O unit 的两个元素。要设置多通道混音器单元,必须在混音器输出上设置流格式的采样率。混音器的输入流格式通过音频单元连接从 Remote I/O unit 的 input element 的输出中传播,自动建立。同样,Remote I/O unit 的 output element 输入范围的流格式由音频单元连接建立,这要归功于混音器单元输出的传播。

在这种模式的任何情况下,每当使用 I/O unit 以外的其他音频单元时,必须设置 kAudioUnitProperty_MaximumFramesPerSlice 属性。与 I/O Without a Render Callback Function 模式一样,无需配置任何音频数据缓冲区。

I/O with a Render Callback Function

通过在 Remote I/O unit 的输入和输出元素之间放置渲染回调函数,可以在传入音频到达输出硬件之前进行操作,例如:使用渲染回调函数来调整输出音量,还可以添加颤音、环调制、回声或其他效果。这种模式如图 2-3 所示。

截屏2024-06-21 11.23.14

如图所示,此模式使用 Remote I/O unit 的两个元素。将渲染回调函数附加到 output element 的input scope。当该元素需要另一组音频数据时,系统会触发回调。反过来,回调通过调用 Remote I/O unit 的 input element 的渲染回调函数来获得新的音频数据。

与其他 I/O 模式一样,您必须在 Remote I/O unit 上明确启用输入,因为默认情况下,输入是禁用的。而且无需配置任何音频数据缓冲区。

请注意,当使用渲染回调函数建立从一个音频单元到另一个音频单元的音频路径时,回调取代了音频单元连接。

Output-Only with a Render Callback Function

在最简单的情况下,这种模式涉及一个直接连接到 Remote I/O unit 的 output element 的 input scope 的渲染回调函数,如图 2-4 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以利用此模式完成复杂的音频结构。例如,将几个声音混合在一起,然后通过设备的输出硬件播放它们。图 2-5 显示了这种情况。

截屏2024-06-21 11.36.30

在图中,需要在 iPod EQ 的输入和输出上设置完整的流格式,多通道混音器只需要在其输出上设置正确的采样率。正如前面说到的,完整的音频流格式信息会在传递的过程中自动赋值。

对于每个多通道混合器单元输入,要设置完整的流格式。对于 input 0,需要显式设置它的流格式。对于 input 1,流格式由音频单元连接从 iPod EQ 单元的输出传播。一般来说,必须单独考虑每个音频单元的流格式需求。

其他设计模式

Audio Unit 还有另外两种主要设计模式:

  1. Input-only with a Render Callback Function:回调函数由应用程序调用,将音频数据传给 Remote I/O unit 的 input element。然而,在大多数情况下,对于这样的应用程序来说,更好的选择是使用输入音频队列对象(使用 AudioQueueNewInput 函数实例化的 AudioQueueRef 类型),使用音频队列对象提供了更大的灵活性,因为它的渲染回调功能不在实时线程上。

  2. Generic Output unit:离线音频处理。与 Remote I/O unit 不同,该音频单元无法连接到设备的音频硬件。当使用它向应用程序发送音频时,它仅仅取决于应用程序调用其渲染方法。

构建应用程序

无论选择哪种设计模式,构建 Audio Unit 应用程序的步骤基本相同:

  1. 配置 audio session。
  2. 指定 audio unit。
  3. 创建 audio processing graph,然后获取 audio unit。
  4. 配置 audio unit。
  5. 连接 audio unit nodes。
  6. 提供用户界面。
  7. 初始化然后开启 audio processing graph。

配置 audio session

构建 Audio Unit 应用程序的第一步与任何 iOS 音频应用程序的步骤相同:配置音频会话。音频会话是应用程序和硬件交互的中介,它的特征在很大程度上决定了应用程序的音频功能及其与系统其他部分的交互性。首先指定要在应用程序中使用的采样率,如下所示:

self.graphSampleRate = 44100.0; // 单位:赫兹

接下来,使用音频会话对象请求系统使用指定采样率作为设备硬件采样率。这里的目的是避免硬件和应用程序之间的采样率转换。这可以最大限度地提高 CPU 性能和音质,并最大限度地减少功耗。

NSError *audioSessionError = nil;
// 获取 audio session 单例对象
AVAudioSession *mySession = [AVAudioSession sharedInstance];
// 请求当前设备硬件使用的采样率
[mySession setPreferredHardwareSampleRate: graphSampleRateerror: &audioSessionError];
// 设置音频分类,AVAudioSessionCategoryPlayAndRecord 指的是支持音频输入与输出
[mySession setCategory: AVAudioSessionCategoryPlayAndRecorderror: &audioSessionError];
// 激活 audio session
[mySession setActive: YES error: &audioSessionError];
// 激活会话后更新采样率
self.graphSampleRate = [mySession currentHardwareSampleRate];

还可以配置其他硬件特征:音频硬件 I/O 缓冲区持续时间。采样率在 44.1kHz 的延时约为 23ms,相当于每次采集 1024 个采样点。如果 I/O 延迟在应用程序中至关重要,可以设置更短的 duration,低至约 0.005ms(相当于 256 个采样点),如下所示:

self.ioBufferDuration = 0.005;
[mySession setPreferredIOBufferDuration: ioBufferDurationerror: &audioSessionError];

有关如何配置和使用音频会话对象的完整说明,请参阅:Audio Session Programming Guide。

指定 audio unit

在运行时,配置音频会话后,应用程序尚未获取音频单元。可以使用 AudioComponentDescription 结构来得到一个指定的音频单元,然后根据音频单元说明符和选择的设计模式构建一个 audio processing graph。

创建 audio processing graph

在此步骤中,将创建设计模式的骨架。具体来说,有以下几步:

  1. 实例化 AUGraph 对象,该实例代表 audio processing graph。
  2. 实例化一个或多个 AUNode 对象,每个对象代表 graph 中的一个音频单元。
  3. 添加 nodes 到 graph。
  4. 打开 graph 并且实例化 audio units。
  5. 获得 audio units 引用。

下面代码显示了如何对包含 Remote I/O unit 和多通道混合器单元的 graph 执行这些步骤。假设已经为每个音频单元定义了 AudioComponentDescription 结构。

AUGraph processingGraph;
NewAUGraph (&processingGraph);AUNode ioNode;
AUNode mixerNode;// 已经为每个音频单元定义了 AudioComponentDescription 结构
AUGraphAddNode(processingGraph, &ioUnitDesc, &ioNode);
AUGraphAddNode(processingGraph, &mixerDesc, &mixerNode);

AUGraphAddNode 函数调用使用音频单元说明符 ioUnitDesc 和 mixerDesc。此时,图形被实例化,并拥有您将在应用程序中使用的节点。要打开 graph 并实例化音频单元,请调用 AUGraphOpen:

AUGraphOpen (processingGraph);

然后,通过 AUGraphNodeInfo 函数获取对音频单元实例的引用,如下所示:

AudioUnit ioUnit;
AudioUnit mixerUnit;AUGraphNodeInfo(processingGraph, ioNode, NULL, &ioUnit);
AUGraphNodeInfo(processingGraph, mixerNode, NULL, &mixerUnit);

ioUnit 和 mixerUnit 变量现在保存对图形中音频单元实例的引用,允许对它们进行配置和互连音频单元。

配置 audio unit

每个 iOS 音频单元都需要自己的配置,这里介绍一些常见的配置:

  • 默认情况下,Remote I/O unit 启用输出并禁用输入。如果同时执行 I/O,或仅使用输入,必须相应地重新配置 I/O unit。

  • 除 Remote I/O unit 和 Voice-Processing I/O unit 外,所有 iOS 音频单元都需要配置其 kAudioUnitProperty_MaximumFramesPerSlice 属性。此属性确保音频单元准备好生成足够数量的音频数据帧,以响应渲染调用。

  • 所有音频单元都需要在输入、输出或两者上定义其音频流格式。

编写并绑定渲染回调函数

对于使用渲染回调函数的设计模式,必须编写这些函数,然后在正确的点添加它们。

可以在音频数据不流动时,使用 audio unit API 立即添加渲染回调:

AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc        = &renderCallback;
callbackStruct.inputProcRefCon  = soundStructArray;AudioUnitSetProperty (myIOUnit,kAudioUnitProperty_SetRenderCallback,kAudioUnitScope_Input,0,                 // output element&callbackStruct,sizeof (callbackStruct)
);

也可以使用 audio processing graph API 以线程安全的方式附加渲染回调,即使在音频数据流动时:

AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc        = &renderCallback;
callbackStruct.inputProcRefCon  = soundStructArray;AUGraphSetNodeInputCallback (processingGraph,myIONode,0,                 // output element&callbackStruct
);
// ... some time later
Boolean graphUpdated;
AUGraphUpdate (processingGraph, &graphUpdated);

连接 audio unit nodes

在大多数情况下,最好使用 audio processing graph API 中的 AUGraphConnectNodeInput 和 AUGraphDisconnectNodeInput 函数建立或断开音频单元之间的连接。这些函数是线程安全的,避免了显式定义连接的编码开销。

AudioUnitElement mixerUnitOutputBus  = 0;
AudioUnitElement ioUnitOutputElement = 0;AUGraphConnectNodeInput (processingGraph,mixerNode,           // source nodemixerUnitOutputBus,  // source node busiONode,              // destination nodeioUnitOutputElement  // desinatation node element
);

或者,可以使用音频单元属性机制直接建立和断开音频单元之间的连接。要做到这一点,请使用 AudioUnitSetProperty 函数以及 kAudioUnitProperty_MakeConnection 属性。这种方法要求为每个连接定义一个 AudioUnitConnection 结构,作为其属性值。

AudioUnitElement mixerUnitOutputBus  = 0;
AudioUnitElement ioUnitOutputElement = 0;AudioUnitConnection mixerOutToIoUnitIn;
mixerOutToIoUnitIn.sourceAudioUnit    = mixerUnitInstance;
mixerOutToIoUnitIn.sourceOutputNumber = mixerUnitOutputBus;
mixerOutToIoUnitIn.destInputNumber    = ioUnitOutputElement;AudioUnitSetProperty (ioUnitInstance,                     // connection destinationkAudioUnitProperty_MakeConnection,  // property keykAudioUnitScope_Input,              // destination scopeioUnitOutputElement,                // destination element&mixerOutToIoUnitIn,                // connection definitionsizeof (mixerOutToIoUnitIn)
);

提供用户界面

在许多情况下,需要提供一个用户界面,允许用户调整特定的音频单元参数,并在某些特殊情况下调整音频单元属性,比如:要更改 iPod EQ单元的活动均衡曲线,需要更改 kAudioUnitProperty_PresentPreset 属性的值。无论哪种情况,用户界面还应提供有关当前设置的视觉反馈。

初始化然后开启 audio processing graph

在开始音频流之前,必须通过调用 AUGraphInitialize 函数来初始化 audio processing graph。这个关键步骤:

  1. 通过为每个单元单独自动调用 AudioUnitInitialize 函数来初始化 graph 拥有的音频单元(如果要在不使用 graph 的情况下构建处理链,则必须依次显式初始化每个音频单元)。
  2. 验证 graph 的连接和音频数据流格式。
  3. 跨音频单元连接,传播流格式。
OSStatus result = AUGraphInitialize(processingGraph);
// Check for error. On successful initialization, start the graph...
AUGraphStart(processingGraph);
// Some time later
AUGraphStop(processingGraph);

Debug 小技巧

  • 通过函数返回值可以检查调用是否成功。

  • 注意函数调用之间的依赖性。例如,只有在成功初始化 audio processing graph 后,才能启动它。检查 AUGraphInitialize 的返回值。如果函数成功返回,就可以启动图表。如果失败了,利用 CAShow 函数将 graph 的状态打印到控制台。

  • 确保您将每个 AudioStreamBasicDescription 结构初始化为 0,如下所示:AudioStreamBasicDescription stereoStreamFormat = {0};。将 ASBD 的字段初始化为 0 可确保没有字段包含垃圾数据(注意:作为类声明中的实例变量,其字段会自动初始化为 0,无需自己初始化它们)。

  • 可以将 AudioStreamBasicDescription 结构的字段值打印到 Xcode 控制台,这在开发过程中非常有用。

    - (void) printASBD: (AudioStreamBasicDescription) asbd {char formatIDString[5];UInt32 formatID = CFSwapInt32HostToBig(asbd.mFormatID);bcopy(&formatID, formatIDString, 4);formatIDString[4] = '\0';NSLog (@"  Sample Rate:         %10.0f",  asbd.mSampleRate);NSLog (@"  Format ID:           %10s",    formatIDString);NSLog (@"  Format Flags:        %10X",    asbd.mFormatFlags);NSLog (@"  Bytes per Packet:    %10d",    asbd.mBytesPerPacket);NSLog (@"  Frames per Packet:   %10d",    asbd.mFramesPerPacket);NSLog (@"  Bytes per Frame:     %10d",    asbd.mBytesPerFrame);NSLog (@"  Channels per Frame:  %10d",    asbd.mChannelsPerFrame);NSLog (@"  Bits per Channel:    %10d",    asbd.mBitsPerChannel);
    }
    

    这种方法可以快速揭示 ASBD 中的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/36213.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

金融行业自动化运维的研究与实践

金融行业自动化运维的研究与实践 在金融行业中,信息技术(IT)运维已经成为保障业务连续性和稳定性的重要环节。随着金融业务的复杂化和信息系统的多样化,传统的手工运维模式已无法满足高效、安全的需求。自动化运维技术的应用变得…

流计算状态算子灵活开发指南

随着实时数据流处理需求的不断增长,高效、可扩展的流计算框架变得愈发重要。DolphinDB 作为一款高性能分布式时间序列数据库,不仅在数据存储和查询上表现出色,还通过引入面向对象编程(OOP)编程范式,使得开发…

聚焦 HW 行动,构筑重保邮件安全防线

随着信息技术的飞速发展,网络安全已成为国家安全的重要组成部分。HW行动作为国家级网络安全演练,通过模拟实战攻防,检验和提升国家关键信息基础设施的防护能力。 CACTER凭借多年HW防护经验,提供全面的邮件安全防护体系&#xff0…

MySQL详细介绍:开源关系数据库管理系统的魅力

学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……) 2、学会Oracle数据库入门到入土用法(创作中……) 3、手把手教你开发炫酷的vbs脚本制作(完善中……) 4、牛逼哄哄的 IDEA编程利器技巧(编写中……) 5、面经吐血整理的 面试技…

Flutter 小技巧之为什么推荐 Widget 使用 const

今天收到这个问题,本来想着简单回复下,但是感觉这个话题又可以稍微展开讲讲,干脆就整理成一篇简单的科普,这样也能更方便清晰地回答这个问题。 聊这个问题之前,我们需要把一个“老生常谈”的概念拿出来说,那…

Open3d 点云投影到 xoy yoz 平面最简单的方式(附python 代码)

最简单的方式,就是直接把原有的点云的数据的 z or x 赋值为0, 然后生成一个新的点云。 filename_model1 r"1.pcd"down 10point_cloud o3d.io.read_point_cloud(filename_model1) point_cloud point_cloud.uniform_down_sample(int(down)) print(降采样…

metasfresh开源ERP系统Windows开发环境配置参考

目录 概述 开发环境 配置过程 后端启动 前端启动 登陆系统 其他 概述 Compiere闭源之后衍生出了Admpiere等若干开源的产品,metasfresh就是其中之一,metasfresh截至发稿时在GitHub上已有64000多次的修改提交,而且仍在维护中&#xff0…

GIS避坑指南!工作中ArcGIS常用的40个小技巧

01图斑的边界线太粗而且无法修改 之前有群友遇到这样一个问题,边界线粗到连图斑都看不见: 查看符号系统,很正常: 究其原因,是地图视图比例的问题,正常情况下,地图的视图比例会随着视图范围自动调…

未来20年人工智能将如何塑造社会

照片由Brian McGowan在Unsplash上拍摄 更多资讯,请访问 2img.ai “人工智能会成为我们的救星还是我们的末日?” 几十年来,这个问题一直困扰着哲学家、科学家和科幻爱好者。 当我们踏上技术革命的边缘时,是时候透过水晶球&#x…

我国氮化硼市场规模逐渐扩大 市场集中度有望不断提升

我国氮化硼市场规模逐渐扩大 市场集中度有望不断提升 氮化硼(BN)俗称为白石墨,是由硼原子和氮原子所构成的一种晶体材料,在常温条件下多表现为一种棕色或暗红色晶体。氮化硼具有导热性好、硬度大、熔点高、抗化学侵蚀性等优点&…

快来看,错过了今天就要设置为vip文章了----openEuler@2024全球发展展望与战略规划

会议主题:openEuler2024全球发展展望与战略规划 OpenEuler2024项目在2024年成功推出了多个长期支持(LTS)版本,标志着其在智能技术领域的全新篇章,并致力于构建全球性的开源新生态。以下是该项目的主要内容和成就概览&a…

【日记】软考居然一次过了(620 字)

正文 早上空闲的时候,上 QQ 看了一下,许久不见动静的系统架构设计师群有人说出分了。我想高级都出分了,中级应该也出来了,于是用手机查了一下。看到分数几乎快要泪从中来。为什么软考能一次过,银行从业资格证考了两三…

MST霍尔传感器IC-MH251,MH253,GT3144在卷发器方案中的应用

霍尔传感器驱动卷发器应用 卷发器在我们的日常生活中已经成为了不可或缺的一种生活工具,它时刻可以护理我们的头发,保养我们的发质。霍尔传感器驱动卷发器,那么霍尔传感器是如何运用在卷发器中的呢?霍尔传感器在卷发器中的工作原…

快速阅读参考文献:kimi请求出战!

学境思源,一键生成论文初稿: AcademicIdeas - 学境思源AI论文写作 上篇文章,我们为大家演示了“如何使用kimi创建论文中的流程图”。今天继续为大家介绍“使用kimi快速阅读学术参考文献”。 在学术研究的海洋中,文献阅读是一项基…

Windows环境下安装MySQL数据库的步骤

说明: 由于环境的不同,安装过程中可能会遇到各种各样的问题,不用慌,先根据错误提示搜索,多试一下。 安装前,请先认真看一下,有可能会遇到的几个问题: 1、证书链问题,一般…

【投稿优惠|稳定检索】2024年文化传播、交流与考古学国际会议 (CCEA 2024)

2024年文化传播、交流与考古学国际会议 (CCEA 2024) 2024 International Conference on Cultural Communication, Exchange, and Archaeology 【重要信息】 大会地点:西安 官网地址:http://www.icccea.com 投稿邮箱:iccceasub-conf.com 【注…

阿里1688商家数据采集软件

大镜山阿里1688商家数据采集一款采集阿里巴巴1688.com商家数据的软件,采集的数据包括店铺名称、联系人姓名、手机号码等。 一、大镜山阿里1688商家数据采集特色 — 大镜山阿里1688商家数据采集一款采集阿里巴巴1688.com商家数据的软件,采集的数据包括店…

一键系统重装教程:电脑重装系统,5个方法轻松恢复电脑

在日常使用电脑的过程中,难免会遇到系统故障、运行缓慢或者病毒感染等问题,重装系统成为解决这些问题的有效途径。然而,对于许多小伙伴来说,电脑重装系统似乎是一项复杂且耗时的任务。其实,只要掌握了正确的方法&#…

深圳比创达电子|EMC与EMI测试整改:从问题识别到效果验证

在现代电子设备的研发和生产过程中,电磁兼容性(EMC)和电磁干扰(EMI)的问题日益凸显。随着技术的不断进步,电子设备的集成度越来越高,工作频率也逐步提升,这使得电磁环境的复杂性不断…

自研一款共享集群数据库,有多难?

共享集群数据库管理系统是一种单库多实例的多活数据库管理系统,用户连接任意实例都可以访问同一个数据库,具备透明多写、高可用、高性能等特性。共享集群技术因其开发难度高,一直被国外垄断,也被称为数据库领域的“塔尖”技术。 2…