携手创作,共同成长!这是我参与「日新方案 8 月更文挑战」的第32天,点击查看活动概况

在上文现已详细讲解了Audio Unit框架的原理和规划形式,本文将开端分析如何构建一个APP

1. 构建过程知道

构建过程:

  1. 装备音频会话
  2. 指定音频单元
  3. 创立音频处理graph,然后获取音频单元
  4. 装备音频单元
  5. 衔接音频单元节点
  6. 供给用户界面
  7. 初始化然后启动音频处理graph

2. 装备音频会话

音频会话的特性在很大程度上决定了app的音频功能以及与体系其它部分的交互性。

1、指定要在app中运用的采样率:

self.graphSampleRate = 44100.0; // Hertz

2、设置首选采样率为硬件采样率:

音频会话目标恳求体系运用您的首选采样率作为设备硬件的采样率。这儿的意图是防止硬件和app之间的采样率转换。这能够最大程度的提高cpu的性能和音质,并最大极限的减少电池耗费。

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. 获取app 的单例音频会话目标的引证
  2. 恳求硬件的采样率
  3. 恳求咱们想要的音频会话category。这儿是录制和播映
  4. 激活音频会话
  5. 音频会话激活后,依据体系供给的实际采样率更新自己的采样率变量

3、音频硬件I/O 缓冲区持续时间

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

阐明:

  • 在44.1KHZ采样率下,形式持续时间约为23ms。相当于1024个样本的slice巨细
  • 假如app的I/O 延迟至关重要,那么咱们需求恳求较小的持续时间,最低月为0.005毫秒

3. 指定音频单元

在运行时,音频会话装备代码后,其实app还是没有获取音频单元。

咱们能够运用AudioComponentDescription指定音频单元。

获取到音频单元阐明符,然后依据咱们选择的形式构建音频处理graph。

AudioComponentDescription ioUnitDescription;
ioUnitDescription.componentType          = kAudioUnitType_Output;
ioUnitDescription.componentSubType       = kAudioUnitSubType_RemoteIO;
ioUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
ioUnitDescription.componentFlags         = 0;
ioUnitDescription.componentFlagsMask     = 0;

4. 创立音频处理graph,然后获取音频单元

AUGraph processingGraph;
NewAUGraph (&processingGraph);
AUNode ioNode;
AUNode mixerNode;
AUGraphAddNode (processingGraph, &ioUnitDesc, &ioNode);
AUGraphAddNode (processingGraph, &mixerDesc, &mixerNode)

过程:

  1. 实例化AUGraph目标。该目标代表音频处理grahp
  2. 实例化一个或许多个AUNode 目标。每个类型代表grahp中的音频单元
  3. 将 AUNode 加入到 AUGraph中
  4. 翻开图形并实例化音频单元
  5. 获取对音频单元的引证

AUGraphAddNode 函数调用运用音频单元阐明符ioUnitDesc和mixerDesc。此刻,graph 将被实例化,并具有app中运用的结点。

翻开graph 并实例化音频单元运用AUGraphOpen。

AUGraphOpen (processingGraph);

经过AUGraphNodeInfo函数获取对音频单元实例的引证

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

5. 装备音频单元

每个ios 音频单元都需求自己的装备。

  • 默许情况下,长途I/O 单元已启用输出但是禁止输入。假如想app一起履行I/O 或许只是运用输入,那么有必要要重新装备I/O 单元。
  • 除了长途I/O 和语音处理I/O 单元外,一切的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)
);

经过运用音频处理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);

6. 衔接音频单元节点

在大多数情况下,运用音频处理graph API中的AUGraphConnectNodeInput和AUGraphDisconnectNodeInput函数树立或断开音频之间的衔接是最好和最容易的。
这些函数是线程安全的,能够防止显示定义衔接的编码开支,由于在不运用graph的时分有必要这么做。

下列代码显示如何处理运用音频处理grahp API 混合器结点的输出衔接到I/O 单元输出元件的输入

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特点,

如下,该办法要求咱们为每个衔接定义AudioUnitConnection结构以用做其特点值。。

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,              // destination scope
    ioUnitOutputElement,                // destination element
    &mixerOutToIoUnitIn,                // connection definition
    sizeof (mixerOutToIoUnitIn)
);

7. 供给用户界面

到这儿,咱们构建的app现已完成构建和装备。

在许多情况下,咱们需求供给一个用户界面,让用户微调音频行为。

咱们能够定制用户界面以允许用户调整特定的音频单元参数,并在某些特使情况下调整音频单元特点。

8. 初始化然后启动音频处理graph

在开端音频流之前,咱们有必要经过调用AUGraphInitialize函数来初始化音频graph

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

阐明:

  1. 经过为每个音频单独的调用AudioUnitInitialize函数来初始化graph所具有的音频单元
  2. 验证graph的衔接和音频流数据格局
  3. 在音频单元衔接上传播流格局