Audio Unit: iOS中最底層最強大音頻控制API

閱讀前提:

  • Audio Session基礎(Audio Session)
  • Core Audio基本數據結構(Core Audio)
  • 音視頻基礎知識
  • C/C++ 簡單數據結構,函數使用

如下概念是文中經常使用的詞語,由於其含義通常直接用英文表達, 通常不需中文翻譯,可將其理解爲固定名詞詞組.html

  • audio unit: 主要介紹的技術名稱
  • audio processing graph: 另外一種處理audio unit的技術
  • node: 承載audio unit的容器
  • input scope /output scope : 可理解爲音頻流動的位置(好比從input scope流向output scope)
  • input element : 鏈接輸入端硬件(如麥克風)的一個組件.
  • output element : 鏈接輸出端硬件(如揚聲器)的一個組件.
  • bus: 與element概念相同,在文中強調信號流時使用「bus」,在強調音頻單元的特定功能方面時使用「element」,\
  • I/O Units: 輸入輸出經常使用的audio unit類型,其中包括Remote I/O unit, Voice-Processing I/O, Generic Output unit三種類型.

注意每一個element還可能具備input scope與output scope.node

Overview

Audio Unit : iOS提供音頻處理插件,支持混合,均衡,格式轉換和實時輸入/輸出,用於錄製,播放,離線渲染和實時對話,例如VoIP(互聯網協議語音).能夠從iOS應用程序動態加載和使用它.編程

Audio Unit一般在稱爲audio processing graph的封閉對象的上下文中工做,如圖所示。在此示例中,您的應用程序經過一個或多個回調函數將音頻發送到graph中的第一個audio unit,並對每一個audio unit進行單獨控制。 最終生成的I / O unit直接輸出給鏈接的硬件.設計模式

1.ex

audio unit是iOS音頻層面中最底層的編碼層,若是要充分利用它須要對audio unit有更深刻的瞭解.除非你須要實時播放同步的聲音,低延遲的輸入輸出或是一些音頻優化的其餘特性,不然請使用Media Player, AV Foundation, OpenAL, or Audio Toolbox等上層框架.數組

優勢

Audio Unit提供了更快,模塊化的音頻處理,同時提供了強大的個性化功能,如立體聲聲像,混音,音量控制和音頻電平測量。若是想充分使用它的功能,必須深刻了解包括音頻數據流格式,回調函數和音頻單元架構等基礎知識。安全

  • 出色的響應能力: 能夠經過回調函數訪問實時的音頻數據.能夠直接使用audio unit合成樂器音,實時同步語音.
  • 動態的從新配置: 圍繞AUGraph opaque類型構建的 audio processing graph API容許以線程安全的方式動態組裝,從新配置和從新排列複雜的音頻處理鏈,同時處理音頻。這是iOS中惟一提供此功能的音頻API。

生命週期

  • 運行時,獲取對動態可連接庫的引用,該庫定義您要使用的audio unit
  • 新建一個audio unit實例
  • 根據需求配置audio unit
  • 初始化audio unit以準備處理音頻
  • 開啓audio unit
  • 控制audio unit
  • 用完後釋放audio unit

選擇設計模式

  • 配置audio session負責app與硬件間的交互,配置I/O unit: I/O units有兩個獨立元素(elements)組成,一個從輸入端硬件接收音頻數據,一個將音頻數據送給輸出端硬件.能夠根據需求選擇咱們要開啓的元素.
  • 構造audio processing graph: 在audio processing graph中,必須指定音頻數據流格式.
  • 控制audio units的生命週期:創建audio unit的鏈接並註冊回調函數.

一.工做原理

2

如圖所示,audio unit是iOS中音頻最底層的API,audio unit僅在高性能,專業處理聲音的需求下使用纔有意義.bash

1. audio unit提供了快速的,模塊化的音頻處理

使用場景網絡

  • 以最低延遲的方式同步音頻的輸入輸出,如VoIP.
  • 手動同步音視頻,如遊戲,直播類軟件
  • 使用特定的audio unit:如回聲消除,混音,音調均衡
  • 一種處理鏈架構:將音頻處理模塊組裝成靈活的網絡。這是iOS中惟一提供此功能的音頻API。

1.1. iOS中的audio unit

功能 Audio units
Effect iPod Equalizer
Mixing 3D Mixer,Multichannel Mixer
I/O Remote I/O, Voice-Processing I/O, Generic Output
Format conversion Format Converter
  • Effect Unit

提供一組預設均衡曲線,如重低音,流行音等等。session

  • Mixer Units數據結構

    • 3D Mixer unit: OpenAL構建的基礎,若是須要3D Mixer unit特性,建議直接使用OpenAL,由於它提供了不少封裝好的功能強大的API.
    • Multichannel Mixer unit: 爲一個或多個聲道的聲音提供混音功能,以立體聲輸出.你能夠單獨打開或關閉其中一個聲道的聲音,調節音量,快進快退等.
  • I/O Units

    • Remote I/O unit: 直接鏈接輸入,輸出的音頻硬件,以低延遲的方式訪問單個接收或發出的音頻採樣點.提供了格式轉換功能,
    • Voice-Processing I/O: 經過聲學的回聲消除拓展了Remote I/O unit,經常使用於VoIP或語音通訊的應用.它還提供了自動增益校訂,語音處理質量調整和靜音等功能.
    • Generic Output unit: 不鏈接音頻硬件而是提供了一種機制:將處理鏈的輸出傳遞給應用程序.一般用來作離線音頻處理.
  • Format Converter Unit

    一般經過I/O unit間接使用.

1.2. 同時使用兩個Audio Unit APIs

iOS有一個用於直接處理audio units的API,另外一個用於處理audio processing graphs,能夠同時使用這兩種API. 然而這兩種API中有一部分功能是相同的,以下:

  • 獲取audio units的動態可連接庫的引用
  • 實例化audio units
  • 鏈接audio units並註冊回調函數
  • 啓動和中止音頻流

1.3. 指定Audio Unit屬性以獲取其引用對象

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


AudioComponent foundIoUnitReference = AudioComponentFindNext (
                                          NULL,
                                          &ioUnitDescription
                                      );
AudioUnit ioUnitInstance;
AudioComponentInstanceNew (
    foundIoUnitReference,
    &ioUnitInstance
);
複製代碼

AudioComponentFindNext:第一個參數設置爲NULL表示使用系統定義的順序查找第一個匹配的audio unit.若是你將上一個使用的audio unit引用傳給該參數,則該函數將繼續尋找下一個與之描述匹配的audio unit.

還可使用audio processing graph API初始化audio unit

// Declare and instantiate an audio processing graph
AUGraph processingGraph;
NewAUGraph (&processingGraph);
 
// Add an audio unit node to the graph, then instantiate the audio unit
AUNode ioNode;
AUGraphAddNode (
    processingGraph,
    &ioUnitDescription,
    &ioNode
);
AUGraphOpen (processingGraph); // indirectly performs audio unit instantiation
 
// Obtain a reference to the newly-instantiated I/O unit
AudioUnit ioUnit;
AUGraphNodeInfo (
    processingGraph,
    ioNode,
    NULL,
    &ioUnit
);
複製代碼

1.4. Audio Units的Scopes,Elements.

以下圖,Audio Units由Scopes,Elements組成.

3

  • scope: audio unit內部的編程上下文,scope概念有一點抽象,能夠這樣理解,好比input scope表示裏面全部的element都須要一個輸入。output scope 表示裏面全部的element都會輸出到某個地方。至於global scope應該是用來配置一些和輸入輸出概念無關的屬性。

  • element: 當element是input/output scope的一部分時,它相似於物理音頻設備中的信號總線.所以這兩個術語"element, bus"在audio unit中是一個含義.本文檔在強調信號流時使用「bus」,在強調音頻單元的特定功能方面時使用「element」,例如I / O unit的輸入和輸出element.

上圖展現了audio unit的一種常見架構,其中輸入和輸出上的element數量相同。然而,不一樣的audio unit使用不一樣架構。例如,mixer unit可能具備多個輸入element可是具備單個輸出element。

  • global scope:做爲總體應用於audio unit而且不與任何特定音頻流相關聯.它只有一個element。該範圍僅適用於個別屬性,好比每一個片的最大幀數(kAudioUnitProperty_MaximumFramesPerSlice)

input , output scopes直接參與經過audio unit移動一個或多個音頻流.audio進入input scope並從output scope離開 。屬性或參數能夠做爲總體應用於input或output scope,例如kAudioUnitProperty_ElementCount.其餘屬性和參數(如enable I / O屬性(kAudioOutputUnitProperty_EnableIO)或volume參數(kMultiChannelMixerParam_Volume))適用於特定scope的element.

注意: 能夠這樣理解scope,scope就是音頻流動的方位,好比從input scope流動到 output scope, 而element是與硬件掛鉤的,好比input element是跟麥克風鏈接的,音頻從input element的input scope流入,從它的output scope流出.

1.5. 使用屬性配置Audio Units

UInt32 busCount = 2;
 
OSStatus result = AudioUnitSetProperty (
    mixerUnit,
    kAudioUnitProperty_ElementCount,   // the property key
    kAudioUnitScope_Input,             // the scope to set the property on
    0,                                 // the element to set the property on
    &busCount,                         // the property value
    sizeof (busCount)
);
複製代碼
  • kAudioOutputUnitProperty_EnableIO

用於在I / O unit上啓用或禁用輸入或輸出。默認狀況下,輸出已啓用但輸入已禁用。

  • kAudioUnitProperty_ElementCount

配置mixer unit上的輸入elements的數量

  • kAudioUnitProperty_MaximumFramesPerSlice

爲了指定音頻數據的最大幀數,audio unit應該準備好響應於回調函數調用而產生。對於大多數音頻設備,在大多數狀況下,您必須按照參考文檔中的說明設置此屬性。若是不這樣作,屏幕鎖定時您的音頻將中止。

  • kAudioUnitProperty_StreamFormat

指定特定audio unit輸入或輸出總線的音頻流數據格式。

大多數屬性只能在audio unit沒有初始化時指定,可是某些特定屬性能夠在audio unit運行時設置,如kAUVoiceIOProperty_MuteOutput靜音功能.

要測試屬性的可用性,訪問其值以及監視其值的更改,請使用如下函數:

  • AudioUnitGetPropertyInfo: 測試屬性是否可用;若是是,則爲其值提供數據大小.
  • AudioUnitGetProperty,AudioUnitSetProperty: 獲取,設置一個屬性值
  • AudioUnitAddPropertyListener,AudioUnitRemovePropertyListenerWithUserData: 監聽,移除監聽對於特定屬性.

1.6. 用戶交互

audio unit parameter是用戶能夠在audio unit運行時提交的參數,經過下面的函數實現.

  • AudioUnitGetParameter
  • AudioUnitSetParameter

1.7. I/O Units基本特徵

4.

儘管這兩個elements是audio unit的一部分,但你的app應該把它們當作兩個獨立的實體.例如,你能夠根據需求使用kAudioOutputUnitProperty_EnableIO屬性獨立啓用或禁用每一個element.如上圖,Element 1直接與音頻輸入硬件(麥克風)鏈接,在藍色區域輸入端的鏈接範圍對於開發者是不透明的,開發者能夠在黃色區域,即Element 1輸出端獲取麥克風採集的音頻數據.一樣地,Element 0的輸出端直接與音頻硬件(揚聲器)鏈接,開發者能夠將音頻數據交給Element 0的輸入端,輸出端是不透明的.

實際使用過程當中,咱們常常須要選擇使用哪一個element,使用時咱們常使用它們的編號而不是名稱,input element編號爲1,output elemnet編號爲0.(能夠將input首字母I當作1,ouput首字母O當作0來記憶)

如上圖所示,每一個element都有本身的輸入,輸入範圍.這是爲了更加清楚的描述.例如,在一個同時具有輸入輸出音頻的app中,你能夠收到音頻從input element的 output scope, 發送音頻給output element的input scope.

2. Audio Processing Graphs管理Audio Units

audio processing graph(AUGraph):基於Core Foundation風格的數據結構,經常使用來管理audio unit處理鏈. graph能夠利用多個audio unit與回調函數,以用來解決任意音頻處理方法。

  • AUGraph類型保證了線程安全.例如播放音頻時,容許你添加一個均衡器或者在mixer輸入端更換回調函數.AUGraph提供了音頻動態配置在iOS平臺.
  • AUNode: 表明graph上下文中單個的audio unit.使用graph時,咱們經常使用它做爲代理與audio unit交互,而不是直接使用audio unit.

當咱們將graph放在一塊兒時,必須使用audio unit的API配置每一個audio unit. 而nodes則不能直接配置audio unit.所以,使用graph必須同時使用這兩套API.

使用AUNode實例對象(使用node表明一個完整的audio processing subgraph)做爲一個複雜graph中的element.在這種狀況下, I/O unit結尾的subgraph必須是Generic Output unit(不能鏈接音頻硬件的I/O unit).

步驟

  • 向graph中添加nodes
  • 經過nodes直接配置audio units
  • 互相鏈接nodes
2.1. Audio Processing Graph擁有精確的I/O Unit.

不管你正在錄製,播放或是同步,每一個audio processing graph都有一個I/0 unit.經過AUGraphStartAUGraphStop能夠開啓或中止音頻流.經過AudioOutputUnitStartAudioOutputUnitStop能夠開啓或中止I/O unit.經過這種方式,graph的I / O單元負責graph中的音頻流。

2.2. 線程安全

audio processing graph API保證了線程安全.此API中的某些功能會將一個audio unit添加到稍後要執行的更改列表中.指定完整的更改集後,而後要求graph去實現它們。

如下是audio processing graph API支持的一些常見從新配置及其相關功能:

  • 添加,移除audio unit nodes (AUGraphAddNode, AUGraphRemoveNode)
  • 添加移除nodes間的鏈接(AUGraphConnectNodeInput, AUGraphDisconnectNodeInput)
  • 鏈接audio unit input bus的回調函數.(AUGraphSetNodeInputCallback)
    a1

如下是一個從新配置運行中的audio processing graph.例如,構建一個graph包含Multichannel Mixer unit與Remote I/O unit.用於播放合成兩種輸入源的混音效果.將兩個輸入源的數據送給Mix的input buses.mixer的輸出端鏈接I/OUnit的output element最終將聲音傳給硬件.

a2

在上面這種狀況下,用戶若是想在任一一路流前插入一個均衡器.能夠添加一個iPod EQ unit在從硬件輸入端到mixer輸入端以前.如圖,如下是從新配置的步驟

  • 1.經過調用AUGraphDisconnectNodeInput斷開mixer unit input 1 的「beats sound」回調。
  • 2.添加一個包含iPod EQ unit的audio unit node到graph中.能夠經過配置ASBD以生成iPod EQ unit,而後調用AUGraphAddNode將其加入到graph.此時,iPod EQ unit已具備實例化對象但未初始化,已經存在於graph中但未參與音頻流.
  • 3.配置,初始化iPod EQ unit.
    • 調用AudioUnitGetProperty從mixer的輸入端檢索kAudioUnitProperty_StreamFormat流格式.
    • 調用兩次AudioUnitSetProperty,一次設置iPod EQ unit’s input流格式,一次設置輸出流格式.
    • 調用AudioUnitInitialize以分配內存準備使用.這個函數是線程不安全的.可是,當iPod EQ unit還沒有主動參與audio processing graph時,必須在序列時執行它,由於此時沒有調用AUGraphUpdate函數。
  • 4.經過調用AUGraphSetNodeInputCallback將「beats sound」回調函數添加到iPod EQ input端。

上面1,2,4步使用AUGraph*開頭的函數,都會被添加到graph的任務執行列表中.經過調用AUGraphUpdate執行這些未開始任務.若是成功返回,則graph已經被動態從新配置而且iPod EQ也已經就位正在處理音頻數據.

2.3. 經過graph "pull" 音頻流

在audio processing graph可使用相似生產者消費者模式,消費者在須要更多音頻數據時通知生產者。請求音頻數據流的方向與音頻流提供的方向正好相反.

2.

對一組音頻數據的每一個請求稱爲渲染調用(render call),也稱爲拉流(pull)。該圖表示拉流爲灰色「控制流」箭頭。拉流請求的數據更恰當地稱爲一組音頻樣本幀(audio sample frames)。反過來,響應拉流而提供的一組音頻樣本幀被稱爲slice.提供slice的代碼稱爲渲染回調函數( render callback function).

  • 調用AUGraphStart函數.虛擬輸出設備調用Remote I/O unit output element的回調函數.該調用請求一片處理過的音頻數據幀。
  • Remote I/O unit的回調函數在其輸入緩衝區中查找要處理的音頻數據。若是有數據等待處理,Remote I/O unit將使用它.不然,如圖所示,它將調用應用程序鏈接到其輸入的任何內容的回調函數。Remote I/O unit的輸入端鏈接一個effect unit的輸出端.I/O uni從effect unit中拉流,請求一組音頻數據幀.
  • effect unit的行爲與Remote I/O unit同樣.當它須要音頻數據時,它從輸入鏈接中獲取它.上例中,effect unit從回調函數中獲取音頻數據
  • effect unit處理回調函數中獲取的音頻數據. effect unit而後將先前請求的(在步驟2中)處理的數據提供給Remote / O unit
  • Remote I/O unit經過effect unit提供的音頻片。而後,Remote I/O unit 將最初請求的處理過的片(在步驟1中)提供給虛擬輸出設備。這完成了一個拉動週期。

3. 回調函數中將音頻傳給audio unit

爲了從本地文件或內存中將音頻傳給audio unit的input bus.使用符合AURenderCallback回調函數完成.audio unit input須要一些音頻數據幀將調用回調函數.

回調函數是惟一能夠對音頻幀作處理的地方,同時,回調函數必須遵照嚴格的性能要求.以錄製爲例,回調函數是按照固定時間間隔進行喚醒調用,若是咱們在間隔時間內尚未處理完上一幀數據,那麼下一幀數據到達時將產生一個間隙的效果.所以回調函數內應儘可能避免加鎖,分配內存,訪問文件系統或網絡鏈接,或以其餘方式在回調函數的主體中執行耗時的任務。

static OSStatus MyAURenderCallback (
    void                        *inRefCon,
    AudioUnitRenderActionFlags  *ioActionFlags,
    const AudioTimeStamp        *inTimeStamp,
    UInt32                      inBusNumber,
    UInt32                      inNumberFrames,
    AudioBufferList             *ioData
) { /* callback body *
複製代碼
  • inRefCon: 註冊回調函數時傳遞的指針,通常可傳本類對象實例,由於回調函數是C語言形式,沒法直接訪問本類中屬性與方法,因此將本例實例化對象傳入能夠間接調用本類中屬性與方法.
  • ioActionFlags: 讓回調函數爲audio unit提供沒有處理音頻的提示.例如,若是應用程序是合成吉他而且用戶當前沒有播放音符,請執行此操做。在要爲其輸出靜默的回調調用期間,在回調體中使用以下語句:*ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;當您但願產生靜默時,還必須顯式地將ioData參數指向的緩衝區設置爲0。
  • inTimeStamp: 表示調用回調函數的時間,能夠用做音頻同步的時間戳.每次調用回調時, mSampleTime 字段的值都會由 inNumberFrames參數中的數字遞增。例如, 若是你的應用是音序器或鼓機, 則可使用 mSampleTime 值來調度聲音。
  • inBusNumber: 調用回調函數的audio unit bus.容許你經過該值在回調函數中進行分支.另外,當audio unit註冊回調函數時,能夠指定不一樣的inRefCon爲每一個bus.
  • inNumberFrames: 回調函數中提供的音頻幀數.這些幀的數據保存在ioData參數中.
  • ioData: 真正的音頻數據,若是設置靜音,須要將buffer中內容設置爲0.

4. 音頻流格式啓用數據流

  • AudioStreamBasicDescription結構 (簡稱ASBD)
struct AudioStreamBasicDescription {
    Float64 mSampleRate;
    UInt32  mFormatID;
    UInt32  mFormatFlags;
    UInt32  mBytesPerPacket;
    UInt32  mFramesPerPacket;
    UInt32  mBytesPerFrame;
    UInt32  mChannelsPerFrame;
    UInt32  mBitsPerChannel;
    UInt32  mReserved;
};
typedef struct AudioStreamBasicDescription  AudioStreamBasicDescription;


size_t bytesPerSample = sizeof (AudioUnitSampleType);
AudioStreamBasicDescription stereoStreamFormat = {0};
 
stereoStreamFormat.mFormatID          = kAudioFormatLinearPCM;
stereoStreamFormat.mFormatFlags       = kAudioFormatFlagsAudioUnitCanonical;
stereoStreamFormat.mBytesPerPacket    = bytesPerSample;
stereoStreamFormat.mBytesPerFrame     = bytesPerSample;
stereoStreamFormat.mFramesPerPacket   = 1;
stereoStreamFormat.mBitsPerChannel    = 8 * bytesPerSample;
stereoStreamFormat.mChannelsPerFrame  = 2;           // 2 indicates stereo
stereoStreamFormat.mSampleRate        = graphSampleRate;
複製代碼

首先,應該肯定採樣值的數據類型,上面使用的是AudioUnitSampleType,這是大部分audio units推薦的類型,在iOS平臺上,被定義爲8.24位固定的整型數據.接下來定義ASBD類型變量,初始化爲0表明不包含任何數據.(注意,不能跳過該步,不然可能產生一些想不到的問題.). 而後就是對ASBD賦值,設備PCM類型表示未壓縮的音頻數據,

  • mFormatFlags(metaflag): 某些audio unit使用不規則的音頻數據格式即不一樣的音頻數據類型,則mFormatFlags字段須要不一樣的標誌集。 例如:3D Mixer unit須要UInt16類型數據做爲採樣值且mFormatFlags須要設置爲kAudioFormatFlagsCanonical.

  • mBytesPerPacket: 每一個音頻包中有多少字節數

  • mBytesPerFrame: 每一幀中有多少字節

  • mFramesPerPacket: 每一個包中有多少幀

  • mBitsPerChannel: 每一個聲道有多少位

在audio processing graph中你必須在關鍵點設置音頻數據格式.其餘點,系統將會設置這個格式.IOS 設備上的音頻輸入和輸出硬件具備系統肯定的音頻流格式,而且格式始終是未壓縮的, 採用交錯的線性 PCM 格式.

5

如上圖, 麥克風表示輸入的音頻硬件。系統肯定輸入硬件的音頻流格式, 並將其施加到 Remote I/O unit’s 的input element的output scope內。一樣,揚聲器表示輸出音頻硬件。系統肯定輸出硬件的流格式,並將其施加到Remote I/O unit’s output element的output scope。

開發者則負責在兩個Remote I/O unit’s elements之間創建音頻流格式.I/O unit則自動在硬件與自身鏈接的一端作一個必要的格式轉換.

audio unit鏈接的一個關鍵功能 (如圖1-8 所示) 是, 該鏈接將音頻數據流格式從其源音頻單元的輸出傳播到目標音頻單元的輸入。這是一個關鍵點, 所以值得強調的是: 流格式傳播是經過音頻單元鏈接的方式進行的, 僅在一個方向上進行--從源音頻單元的輸出到目標音頻單元的輸入。

雖然咱們經過ASBD靈活的設置音頻數據流的屬性(如採樣率),可是建議仍是使用當前設備硬件默認使用值.由於若是保持一致,系統不須要作採樣率轉換,這能夠下降能耗同時提升音頻質量.

二.Audio Unit使用步驟

1.選擇設計模式

  • 僅僅只有一個 I/O unit.
  • audio processing graph中使用單一音頻流格式,
  • 要求你去設置流的格式,或則僅僅設置其中一部分.

正確的設置流格式在創建音頻流中顯得相當重要.這些模式大多依賴於音頻流格式從源到目的地的自動傳播,它們之間經過audio unit鏈接.合理利用傳播的特性能夠減小代碼書寫量,同時,你必須保證清楚瞭解每種模式須要如何進行設置.能夠在不使用audio processing graph的狀況下實現任一一種模式,可是使用它能夠減小代碼書寫量並支持動態從新配置.

1.1. I/O Pass Through

I/O Pass Through傳遞模式在不處理音頻的狀況下將傳入的音頻直接發送到輸出硬件.

6

如上圖所示,輸入端硬件將它的格式告訴Remote I/O unit的input element.開發者則在I/O unit的輸出端指定了流格式,audio unit內部將根據需求自動進行轉換,爲了不採樣率轉換,咱們在定義ASBD時最好使用硬件使用的採樣率.在上圖,咱們沒必要指定I/O unit的輸出element,由於數據格式會從輸入的element傳給輸出.同理,傳給硬件的流將會根據硬件須要完成一次自動轉換.

1.2. I/O不帶有回調函數

app能夠添加一個或多個audio unit在Remote I/O unit’s elements之間.例如使用多通道Mixer unit將傳入的麥克風音頻定位到立體聲域中,或提供輸出音量控制,在這中模式下,仍然沒有用到回調函數.它簡化了模式,但限制了其實用性。若是沒有呈現回調函數,就沒法直接操做音頻。

7

在這種模式下,和Pass Through模式相同,你能夠配置這兩個Remote I/O unit.爲了設置多通道Mixer unit,你必須把你的音頻流採樣率設置給mixer output.mixer的輸入端音頻流格式會自動設置經過接收從I/O unit輸出端傳遞來的音頻流.同理,Output element輸入端的格式也將自動設置經過流傳遞.

使用此模式必須設置kAudioUnitProperty_MaximumFramesPerSlice屬性.

1.3. I/O帶有回調函數

經過註冊回調函數在Remote I/O unit的input,output elements之間,開發者能夠在音頻數據送到輸出硬件以前操控它.好比,經過回調函數調節輸出音頻的音量,還能夠添加顫音、環調製、回波或其餘效果(Accelerate framework中有相關API).

8

如圖所示,這個模式使用兩個Remote I/O unit, 回調函數被附加在output element的input scope.當output element須要音頻數據時,系統會觸發回調,緊接着,回調完成後系統將數據傳給output element的input scope.

必須顯式啓動input在Remote I/O unit.由於它默認是禁止的.

1.4. 僅輸出的回調函數

該模式一般用於遊戲,專業音頻app使用.簡單的說,該模式在直接鏈接在Remote I/O unit的output element的input scope.能夠利用此模式完成複雜的音頻結構,如將幾種不一樣的聲音混合在一塊兒,而後經過輸出硬件播放他們,以下圖.

9

如上圖所示,注意iPod EQ須要開發者設置Input,output二者的流格式.而 Multichannel Mixer僅僅只須要設置它輸出的採樣率便可.正如前面說到過,完整的音頻流格式信息會在傳遞的過程當中自動賦值.

1.5. 其餘

audio unit還有兩種主要的設計模式.

  • 錄製與分析音頻: 建立一個帶有回調的僅輸入的app.回調函數會首先被喚醒,隨後將數據傳給Remote I/O unit’s input element.可是大多數狀況下直接使用audio queue更爲簡單方便. audio queue使用起來更加靈活,由於它的回調函數不在一個實時的線程上.
  • Generic Output unit: 離線音頻處理.不像Remote I/O unit,這個audio unit不鏈接設備的音頻硬件.當你使用它發送音頻到app時,它僅僅取決於你的應用程序調用它的渲染方法.

2.構建App

  • 配置audio session
  • 指定audio unit
  • 建立audio processing graph,而後獲取audio unit
  • 配置audio unit
  • 鏈接audio unit nodes
  • 提供用戶界面
  • 初始化而後開啓audio processing graph.

2.1. 配置Audio Session

audio session是app與硬件交互的中介。設置採樣率,

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.獲取audio session單例對象
  • 2.請求當前設備硬件使用的採樣率.
  • 3.設置音頻分類。AVAudioSessionCategoryPlayAndRecord指的是支持音頻輸入與輸出。
  • 4.激活audio session
  • 5.激活audio session後更新採樣率。

你還能夠配置一些其餘功能,如採樣率爲44.1 kHz默認的duration是大概23ms,至關於每次採集1024個採樣點。若是你的app要求延遲很低,你能夠最低設置0.005ms(至關於256個採樣點)

self.ioBufferDuration = 0.005;
[mySession setPreferredIOBufferDuration: ioBufferDuration
                                  error: &audioSessionError];
複製代碼

2.2. 指定audio unit

配置完audio session後還沒法直接得到audio unit,經過AudioComponentDescription賦值能夠獲得一個指定的audio unit. (1.3中講到如何賦值)

2.3. 構建Audio Processing Graph

  • 實例化AUGraph對象(表明 audio processing graph)。
  • 實例化一個或多個AUNode對象(每個表明一個 audio unit in the graph.)。
  • 添加nodes到graphgraph而且實例化
  • 打開graph而且實例化 audio units
  • 得到audio unit引用
AUGraph processingGraph;
NewAUGraph (&processingGraph);
 
AUNode ioNode;
AUNode mixerNode;
 
AUGraphAddNode (processingGraph, &ioUnitDesc, &ioNode);
AUGraphAddNode (processingGraph, &mixerDesc, &mixerNode);
複製代碼

調用AUGraphAddNode函數後,graph被實例化而且擁有nodes.爲了打開graph而且實例化audio unit須要調用AUGraphOpen AUGraphOpen (processingGraph);

經過AUGraphNodeInfo函數獲取對audio unit實例的引用

AudioUnit ioUnit;
AudioUnit mixerUnit;
 
AUGraphNodeInfo (processingGraph, ioNode, NULL, &ioUnit);
AUGraphNodeInfo (processingGraph, mixerNode, NULL, &mixerUnit);
複製代碼

2.4.配置audio unit.

iOS中每種audio unit都有其配置的方法,咱們應該熟悉一些經常使用的audio unit配置.

Remote I/O unit, 默認輸入禁用,輸出可用.若是僅僅使用輸入,你必須相應地從新配置 I/O unit.除了Remote I/O與Voice-Processing I/O unit以外全部的audio unit都必須配置kAudioUnitProperty_MaximumFramesPerSlice屬性.該屬性確保audio unit 準備好響應於回調函數產生足夠數量的音頻數據幀。

2.5. 註冊並實現回調函數

對於須要使用回調函數的設計模式,咱們必須註冊並實現相應的回調函數.此外,還能夠經過回調函數拉取音頻數據流.

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


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

複製代碼

2.6. 鏈接Audio Unit Nodes

使用AUGraphConnectNodeInputAUGraphDisconnectNodeInput函數能夠創建,斷開Node.它們是線程安全的而且下降代碼的開銷,由於若是不適用graph咱們將必須手動實現.

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

固然,咱們也能夠直接使用audio unit的屬性創建鏈接,以下.

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)
);
複製代碼

2.7. 提供用戶界面

通常而言,咱們須要提供一個用戶界面,讓用戶微調音頻行爲。能夠定製用戶界面以容許用戶調整特定的音頻單元參數,並在某些特殊狀況下調整音頻單元屬性。

2.8. 初始化並開啓Audio Processing Graph

調用AUGraphInitialize初始化audio processing graph.

  • 爲每一個audio units單獨自動調用AudioUnitInitialize函數來初始化graph所擁有的audio units。 (若是要在不使用graph的狀況下構建處理鏈,則必須依次顯式初始化每一個audio unit)
  • 驗證graph的鏈接與音頻數據流格式
  • 經過不一樣audio unit的鏈接傳播指定格式的音頻流數據。
OSStatus result = AUGraphInitialize (processingGraph);
// Check for error. On successful initialization, start the graph...
AUGraphStart (processingGraph);
 
// Some time later
AUGraphStop (processingGraph);
複製代碼

3.故障排除提示

經過函數返回值能夠檢查調用是否成功.

請留意函數調用之間的依賴,例如,僅僅只能在audio processing graph初始化成功後再開啓它.其次,利用CAShow函數能夠打印當前graph的狀態.

確保ASBD初始化賦值爲0. AudioStreamBasicDescription stereoStreamFormat = {0};.將ASBD的字段初始化爲0可確保沒有字段包含垃圾數據。(注意:做爲類聲明中的實例變量 - 其字段會自動初始化爲0,無需本身初始化它們)

- (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);
}
複製代碼

三.類型對比

1. 使用I/O Unit

iOS提供了三種I/O unit.大部分應用使用Remote I/O unit,它鏈接到輸入和輸出音頻硬件,並提供對各個傳入和傳出音頻樣本值的低延遲訪問.對於VoIP應用,Voice-Processing I/O unit經過添加聲學回聲消除和其餘功能來擴展遠程I / O單元。要將音頻發送迴應用程序而不是輸出音頻硬件,請使用通用輸出單元。

1.1. Remote I/O Unit

Remote I/O unit(子類型kAudioUnitSubType_RemoteIO)鏈接到設備硬件,用於輸入,輸出或同時輸入和輸出。用於播放,錄製或低延遲同時輸入和輸出,不須要回聲消除。

設備的音頻硬件將其音頻流格式強制放置在 Remote I/O unit的外側。audio unit提供硬件音頻格式和應用音頻格式之間的格式轉換,經過附帶的格式轉換器audio unit進行格式轉換.

input element input scope 從輸入硬件獲取音頻格式, output element output scope從輸出硬件獲取音頻格式,

在input元素的輸出範圍上設置應用程序格式。 input元素根據須要在其輸入和輸出範圍之間執行格式轉換。使用應用程序流格式的硬件採樣率。若是輸出元素的輸入範圍由音頻單元鏈接提供,則它從該鏈接獲取其流格式。可是,若是它由渲染回調函數提供,請在其上設置應用程序格式。

Audio unit feature Details
Elements 一個input element,一個output element
建議使用屬性 kAudioFormatLinearPCM,kAudioFormatFlags,AudioUnitSampleType,AudioUnitCanonical
注意點 Remote I/O unit朝外側從音頻硬件獲取其格式,input element從輸入端硬件獲取,output element 從輸出端硬件獲取.
屬性注意 不須要設置kAudioUnitProperty_MaximumFramesPerSlice

1.2.Voice-Processing I/O Unit

Voice-Processing I/O unit(子類型kAudioUnitSubType_VoiceProcessingIO)具備 Remote I/O unit 的特性,而且添加了回聲抑制。它還增長了自動增益校訂,語音處理質量調整和靜音功能。這是用於VoIP(互聯網協議語音)應用程序的正確I/O unit。

1.3. Generic Output Unit

在將audio processing graph的輸出發送到應用程序而不是輸出音頻硬件時,請使用此類型爲kAudioUnitSubType_GenericOutput的音頻單元。一般使用Generic Output Unit進行離線音頻處理。與其餘I / O units同樣,Generic Output unit包含格式轉換器。所以能夠在audio processing graph中使用的流格式與所需格式之間執行格式轉換。

2. Using Mixer Units

iOS提供兩種mixer units。在大多數狀況下,您應該使用Multichannel Mixer unit,它能夠爲任意數量的單聲道或立體聲流提供混音。若是您須要3D Mixer unit的功能,請使用OpenAL。 OpenAL創建在3D混音器單元之上,提供與簡單API相同的性能,很是適合遊戲應用程序開發。

默認狀況下,kAudioUnitProperty_MaximumFramesPerSlice屬性設置爲1024,當屏幕鎖定而且顯示器休眠時,這是不夠的。若是您的應用在屏幕鎖定時播放音頻,則必須增長此屬性的值,除非音頻輸入處於活動狀態。作以下:

  • 若是音頻輸入處於活動狀態,則無需爲kAudioUnitProperty_MaximumFramesPerSlice屬性設置值。
  • 若是音頻輸入未激活,請將此屬性設置爲值4096。

2.1.Multichannel Mixer Unit

Multichannel Mixer unit(子類型kAudioUnitSubType_MultiChannelMixer)可接收任意數量的單聲道或立體聲流,並將它們組合成單個立體聲輸出。它控制每一個輸入和輸出的音頻增益,並容許您分別打開或關閉每一個輸入。從iOS 4.0開始,多聲道混音器支持每一個輸入的立體聲聲像。

Audio unit feature Details
Elements 一個或多個(單聲道或多聲道)input element,一個多聲道output element
建議使用屬性 kAudioFormatLinearPCM,kAudioFormatFlags,AudioUnitSampleType,AudioUnitCanonical
注意點 若是輸入由audio unit鏈接則從該鏈接獲取其流格式。若是輸入由回調函數提供在bus上設置完整的流格式,使用與回調提供的數據相同的流格式。在output scope,僅須要設置採樣率。
屬性 kAudioUnitProperty_MeteringMode

注意點: iPod EQ單元提供一組預約義的色調均衡曲線做爲出廠預設。經過訪問音頻單元的kAudioUnitProperty_FactoryPresets屬性獲取可用EQ設置數組。使用它做爲kAudioUnitProperty_PresentPreset屬性的值來應用設置。

2.2. 3D Mixer Unit

3D Mixer unit: 控制每一個輸入的立體聲聲像,播放速度和增益,並控制其餘特徵,例如與收聽者的視距,輸出具備音頻增益控制。一般,若是須要3D Mixer unit的功能,最佳選擇是使用OpenAL.

Audio unit feature Details
Elements 一個或多個單聲道input element,一個多聲道output element
建議使用屬性 UInt16,kAudioFormatFlagsAudioUnitCanonical
注意點 若是輸入由audio unit鏈接則從該鏈接獲取其流格式。若是輸入由回調函數提供在bus上設置完整的流格式,使用與回調提供的數據相同的流格式。在output scope,僅須要設置採樣率。

注意點: iPod EQ單元提供一組預約義的色調均衡曲線做爲出廠預設。經過訪問音頻單元的kAudioUnitProperty_FactoryPresets屬性獲取可用EQ設置數組。使用它做爲kAudioUnitProperty_PresentPreset屬性的值來應用設置。

3.Using Effect Units

iPod EQ unit (子類型kAudioUnitSubType_AUiPodEQ)是iOS 4中提供的惟一效果單元。這與內置iPod應用程序使用的均衡器相同。要查看該音頻設備的iPod應用程序用戶界面,請轉至設置> iPod> EQ。該音頻單元提供一組預設均衡曲線,如低音加強器,流行音樂和口語。

Audio unit feature Details
Elements 一個input element,一個output element (該element能夠是單聲道或雙聲道)
建議使用屬性 kAudioFormatLinearPCM,AudioUnitSampleType,kAudioFormatFlagsAudioUnitCanonical
注意點 若是輸入由audio unit鏈接則從該鏈接獲取其流格式。若是輸入由回調函數提供在bus上設置完整的流格式,使用與回調提供的數據相同的流格式。在output scope,設置用於輸入的相同完整流格式。
Properties kAudioUnitProperty_FactoryPresets,kAudioUnitProperty_PresentPreset

注意點: iPod EQ單元提供一組預約義的色調均衡曲線做爲出廠預設。經過訪問音頻單元的kAudioUnitProperty_FactoryPresets屬性獲取可用EQ設置數組。使用它做爲kAudioUnitProperty_PresentPreset屬性的值來應用設置。

默認狀況下,kAudioUnitProperty_MaximumFramesPerSlice屬性設置爲1024,當屏幕鎖定而且顯示器休眠時,這是不夠的。若是您的應用在屏幕鎖定時播放音頻,則必須增長此屬性的值,除非音頻輸入處於活動狀態。作以下:

  • 若是音頻輸入處於活動狀態,則無需爲kAudioUnitProperty_MaximumFramesPerSlice屬性設置值。
  • 若是音頻輸入未激活,請將此屬性設置爲值4096。

4.Audio Units 標識符鍵

此表提供了訪問每一個iOS audio unit的動態連接庫所需的標識符鍵以及其簡要說明。

名稱與描述 鍵名稱 相應編解碼器
Converter unit (提供音頻格式轉換在線性PCM與其餘壓縮格式) kAudioUnitType_FormatConverter,kAudioUnitSubType_AUConverter,kAudioUnitManufacturer_Apple aufc,conv,appl
iPod Equalizer unit(提供iPod均衡器的功能) kAudioUnitType_Effect kAudioUnitSubType_AUiPodEQ
3D Mixer unit (支持混合多個音頻流,輸出平移,採樣率轉換等) kAudioUnitType_Mixer,kAudioUnitSubType_AU3DMixerEmbedded,kAudioUnitManufacturer_Apple aumx,3dem,appl
Multichannel Mixer unit (支持將多個音頻流混合到單個流中) kAudioUnitType_Mixer,kAudioUnitSubType_MultiChannelMixer,kAudioUnitManufacturer_Apple aumx,mcmx,appl
Generic Output unit (支持轉換爲線性PCM格式和從線性PCM格式轉換;可用於啓動和中止graph) kAudioUnitType_Output,kAudioUnitSubType_GenericOutput,kAudioUnitManufacturer_Apple auou,genr,appl
Remote I/O unit(鏈接到設備硬件以進行輸入,輸出或同時輸入和輸出) kAudioUnitType_Output,kAudioUnitSubType_RemoteIO,kAudioUnitManufacturer_Apple auou,rioc,appl
Voice Processing I/O unit(具備I / O單元的特性,併爲雙向通訊增長了回聲抑制功能) kAudioUnitType_Output,kAudioUnitSubType_VoiceProcessingIO,kAudioUnitManufacturer_Apple auou,vpio,appl

Apple官方文檔

相關文章
相關標籤/搜索