AudioUnit框架详细解析(二十二) —— 构建您的应用程序(一)

版本记录

版本号 时间
V1.0 2018.07.05

前言

AudioUnit框架作为您的应用程序添加复杂的音频操作和处理功能。 创建在主机应用程序中生成或修改音频的音频单元扩展。接下来几篇我们就一起看一下这个框架,感兴趣的看上面几篇文章。
1. AudioUnit框架详细解析(一) —— 基本概览
2. AudioUnit框架详细解析(二) —— 关于Audio Unit Hosting之概览(一)
3. AudioUnit框架详细解析(三) —— 关于Audio Unit Hosting之如何使用本文档和参考资料(二)
4. AudioUnit框架详细解析(四) —— 音频单元提供快速的模块化音频处理之iOS中的Audio Units(一)
5. AudioUnit框架详细解析(五) —— 音频单元提供快速的模块化音频处理之在Concert中使用两个音频单元API(二)
6. AudioUnit框架详细解析(六) —— 音频单元提供快速的模块化音频处理之使用标识符来指定和获取音频单元(三)
7. AudioUnit框架详细解析(七) —— 音频单元提供快速的模块化音频处理之使用范围和元素来指定音频单元的部分(四)
8. AudioUnit框架详细解析(八) —— 音频单元提供快速的模块化音频处理之使用属性来配置音频单元(五)
9. AudioUnit框架详细解析(九) —— 音频单元提供快速的模块化音频处理之使用参数和UIKit为用户提供控制(六)
10. AudioUnit框架详细解析(十) —— 音频单元提供快速的模块化音频处理之I / O单元的基本特性(七)
11. AudioUnit框架详细解析(十一) —— 音频处理图管理音频单元之音频处理图具有一个I / O单元(一)
12. AudioUnit框架详细解析(十二) —— 音频处理图管理音频单元之音频处理图提供线程安全性(二)
13. AudioUnit框架详细解析(十三) —— 音频处理图管理音频单元之音频使用Pull通过图表(三)
14. AudioUnit框架详细解析(十四) —— 呈现回调函数将音频馈送到音频单元之了解音频单元呈现回调函数(一)
15. AudioUnit框架详细解析(十五) —— 音频流格式启用数据流之使用AudioStreamBasicDescription结构(一)
16. AudioUnit框架详细解析(十六) —— 音频流格式启用数据流之了解何处以及如何设置流格式(二)
17. AudioUnit框架详细解析(十七) —— 从选择设计模式开始之I / O传递(一)
18. AudioUnit框架详细解析(十八) —— 从选择设计模式开始之没有渲染回调函数的I / O(二)
19. AudioUnit框架详细解析(十九) —— 从选择设计模式开始之具有渲染回调函数的I / O(三)
20. AudioUnit框架详细解析(二十) —— 从选择设计模式开始之仅具有渲染回调函数的输出(四)
21. AudioUnit框架详细解析(二十一) —— 从选择设计模式开始之其他音频单元Hosting设计模式(五)

Constructing Your App - 构建您的应用程序

无论您选择哪种设计模式,构建音频单元hosting应用程序的步骤基本相同:

    1. 配置音频会话。
    1. 指定音频单元。
    1. 创建音频处理图,然后获取音频单元。
    1. 配置音频单元。
    1. 连接音频单元节点。
    1. 提供用户界面。
    1. 初始化然后启动音频处理图。

Configure Your Audio Session - 配置音频会话

接下来,使用音频会话对象请求系统使用您的首选采样率作为设备硬件采样率,如Listing 2-1所示。 这里的目的是避免硬件和您的应用程序之间的采样率转换。 这可以最大限度地提高CPU性能和音质,并最大限度地减少电池消耗。

Listing 2-1 Configuring an audio session
    self.graphSampleRate = 44100.0; // Hertz
 
NSError *audioSessionError = nil;
AVAudioSession *mySession = [AVAudioSession sharedInstance]; //1 
[mySession setPreferredHardwareSampleRate: graphSampleRate //2
error: &audioSessionError]; 
[mySession setCategory: AVAudioSessionCategoryPlayAndRecord //3
error: &audioSessionError]; 
[mySession setActive: YES //4
error: &audioSessionError];
self.graphSampleRate = [mySession currentHardwareSampleRate]; //5

前面的行执行以下操作:

    1. 获取应用程序的单例音频会话对象的引用。
    1. 请求硬件采样率。 系统可能会或可能不会授予请求,具体取决于设备上的其他音频活动。
    1. 请求所需的音频会话类别。 此处指定的play and record类别支持音频输入和输出。
    1. 请求激活您的音频会话。
    1. 激活音频会话后,根据系统提供的实际采样率更新您自己的采样率变量。

您可能需要配置另一个硬件特性:音频硬件I / O缓冲区持续时间。 在44.1 kHz采样率下,默认持续时间约为23 ms,相当于1,024个样本的切片大小。 如果您的应用中的I / O延迟至关重要,您可以请求较小的持续时间,最低约0.005毫秒(相当于256个样本),如下所示:

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

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


Specify the Audio Units You Want - 指定您想要的音频单元

在运行时,运行音频会话配置代码后,您的应用尚未获取音频单元。 您可以使用AudioComponentDescription结构指定所需的每个。 有关如何执行此操作,请参阅Use Identifiers to Specify and Obtain Audio Units。 每个iOS音频单元的标识符键列在Identifier Keys for Audio Units中。

获取指定的音频单元说明符,然后根据您选择的模式构建音频处理图。


Build an Audio Processing Graph - 构建音频处理图

在此步骤中,您将创建本章第一部分中介绍的其中一种设计模式的框架。 具体来说,你:

    1. 实例化AUGraph opaque类型。 该实例表示音频处理图。
    1. 实例化一个或多个AUNode不透明类型,每个类型代表图中的一个音频单元。
    1. 将节点添加到图表中。
    1. 打开图形并实例化音频单元。
    1. 获取音频单元的引用。

Listing 2-2显示了如何为包含远程I / O单元和多通道混合器单元的图形执行这些步骤。 它假设您已经为每个音频单元定义了AudioComponentDescription结构。

Listing 2-2 Building an audio processing graph

AUGraph processingGraph;
NewAUGraph (&processingGraph);

AUNode ioNode;
AUNode mixerNode;

AUGraphAddNode (processingGraph, &ioUnitDesc, &ioNode);
AUGraphAddNode (processingGraph, &mixerDesc, &mixerNode);

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

AUGraphOpen (processingGraph);

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

AudioUnit ioUnit;
AudioUnit mixerUnit;

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

ioUnitmixerUnit变量现在包含对图形中音频单元实例的引用,允许您配置并互连音频单元。


Configure the Audio Units - 配置音频单元

每个iOS音频设备都需要自己的配置,如Using Specific Audio Units中所述。 但是,有些配置很常见,所有iOS音频开发人员都应该熟悉它们。

默认情况下,远程I / O单元已启用输出并禁用输入。 如果您的应用程序同时执行I / O或仅使用输入,则必须相应地重新配置I / O单元。 有关详细信息,请参阅Audio Unit Properties Reference中的kAudioOutputUnitProperty_EnableIO属性。

除了远程I/O和语音处理I/O单元之外,所有iOS音频单元都需要配置它们的kAudioUnitProperty_MaximumFramesPerSlice属性。此属性确保音频单元准备生成足够数量的音频数据帧以响应呈现调用。有关详细信息,请参见Audio Unit Properties Reference中的kAudioUnitProperty_MaximumFramesPerSlice

所有音频单元都需要在输入、输出或两者上定义它们的音频流格式。有关音频流格式的解释,请参阅Audio Stream Formats Enable Data Flow。对于各种iOS音频单元的特定流格式要求,请参见Using Specific Audio Units


Write and Attach Render Callback Functions - 编写并附加呈现回调函数

对于采用渲染回调函数的设计模式,您必须编写这些函数,然后将它们附加到正确的点。Render Callback Functions Feed Audio to Audio Units介绍了这些回调的作用并说明了它们的工作原理。 有关工作回调的示例,请查看iOS参考库中的各种音频单元示例代码项目,包括音频混合器(MixerHost)aurioTouch以及SynthHost

当音频不流动时,您可以使用音频单元API立即附加渲染回调,如Listing 2-3所示。

Listing 2-3 Attaching a render callback immediately

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

通过使用音频处理图API,您可以以线程安全的方式附加渲染回调,即使在音频流动时也是如此。 Listing 2-4显示了如何做。

Listing 2-4 Attaching a render callback in a thread-safe manner
 
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc        = &renderCallback;
callbackStruct.inputProcRefCon  = soundStructArray;
AUGraphSetNodeInputCallback (
    processingGraph,
    myIONode,
    0,                 // output element
    &callbackStruct
);
// ... some time later
Boolean graphUpdated;
AUGraphUpdate (processingGraph, &graphUpdated);

Connect the Audio Unit Nodes - 连接音频单元节点

在大多数情况下,使用音频处理图API中的AUGraphConnectNodeInputAUGraphDisconnectNodeInput函数建立或断开音频单元之间的连接是最好和最容易的。 这些函数是线程安全的,可以避免显式定义连接的编码开销,因为在不使用图形时必须这样做。

Listing 2-5显示了如何使用音频处理图API将混合器节点的输出连接到I / O单元输出element的输入。

Listing 2-5 Connecting two audio unit nodes using the audio processing graph API

AudioUnitElement mixerUnitOutputBus  = 0;
AudioUnitElement ioUnitOutputElement = 0;
AUGraphConnectNodeInput (
    processingGraph,
    mixerNode,           // source node
    mixerUnitOutputBus,  // source node bus
    iONode,              // destination node
    ioUnitOutputElement  // desinatation node element
);

或者,您可以使用音频单元属性机制直接建立和断开音频单元之间的连接。 为此,请使用AudioUnitSetProperty函数以及kAudioUnitProperty_MakeConnection属性,如Listing 2-6所示。 此方法要求您为每个连接定义AudioUnitConnection结构以用作其属性值。

Listing 2-6 Connecting two audio units directly
 
AudioUnitElement mixerUnitOutputBus  = 0;
AudioUnitElement ioUnitOutputElement = 0;
AudioUnitConnection mixerOutToIoUnitIn;
mixerOutToIoUnitIn.sourceAudioUnit    = mixerUnitInstance;
mixerOutToIoUnitIn.sourceOutputNumber = mixerUnitOutputBus;
mixerOutToIoUnitIn.destInputNumber    = ioUnitOutputElement;
AudioUnitSetProperty (
    ioUnitInstance,                     // connection destination
    kAudioUnitProperty_MakeConnection,  // property key
    kAudioUnitScope_Input,
    ioUnitOutputElement,
    &mixerOutToIoUnitIn,
    sizeof (mixerOutToIoUnitIn)
);
// destination scope
// destination element
// connection definition

Provide a User Interface - 提供一个用户界面

此时,在构建应用程序时,音频单元(通常是音频处理图形)已完全构建和配置。在许多情况下,您需要提供一个用户界面,让您的用户微调音频行为。您可以定制用户界面以允许用户调整特定的音频单元参数,并在某些特殊情况下调整音频单元属性。在任何一种情况下,用户界面还应提供有关当前设置的视觉反馈。

Use Parameters and UIKit to Give Users Control介绍了构建用户界面以让用户控制参数值的基础知识。有关工作示例,请查看示例代码项目Audio Mixer(MixerHost)

iPod EQ单元是一种不寻常的情况,为了更改其活动均衡曲线,您可以更改kAudioUnitProperty_PresentPreset属性的值。无论音频是否正在运行,您都可以执行此操作。有关工作示例,请查看示例代码项目iPhoneMixerEQGraphTest


Initialize and Start the Audio Processing Graph - 初始化并启动音频处理图

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

  • 通过为每个音频单独自动调用AudioUnitInitialize函数来初始化图形所拥有的音频单元。 (如果要在不使用图形的情况下构建处理链,则必须依次显式初始化每个音频单元。)
  • 验证图形的连接和音频数据流格式。
  • 在音频单元连接上传播流格式。

Listing 2-7显示了如何使用AUGraphInitialize。

Listing 2-7 Initializing and starting an audio processing graph

OSStatus result = AUGraphInitialize (processingGraph);
// Check for error. On successful initialization, start the graph...
AUGraphStart (processingGraph);
// Some time later
AUGraphStop (processingGraph);

后记

本篇主要讲述了构建您的应用程序,感兴趣的给个赞或者关注~~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容