Android音頻開發(7):使用 OpenSL ES API(下)

來源:互聯網
上載者:User

標籤:android   es   opensl   音頻   audio   

本文是我的《Android音頻開發》系列的第七篇文章,上一篇文章總整體上介紹了 Android OpenSL ES API 的基本概況,告訴了大家這個架構有什麼特性,可以做什麼,不能做什麼。本文則重點介紹 OpenSL ES 架構及其API介面的一些關鍵的設計和概念,只有理解了它們,你才能更好地讀懂 OpenSL ES 的相關代碼。範例程式碼則放到了文章的最後,相信大家理解了這些基本的概念後,就能很容易地讀懂這些代碼的細節了。


1. 物件導向的 C 語言介面


OpenSL ES 雖然是 C 語言編寫,但是它的介面採用的是物件導向的方式,並不是提供一系列的函數介面,而是以 Interface 的方式來提供 API,這是理解 OpenSL ES API 的一個比較重要的點。


可能這麼說比較抽象,舉例來說,一般的 C 語言庫,比如:math 庫,提供的介面可能是這樣的:


double cosh(double);double sinh(double);double tanh(double);


我們直接在代碼中調用這個函數即可,但是 OpenSL ES 卻不是這樣提供 API 的,它的大都數 API 需要這樣訪問:


// 下面代碼是對 Audio Engine 對象進行 “初始化”SLEngineItf engineObject;SLresult result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);


由此可見,OpenSL ES 的 API 大都數是通過 “對象” 來調用的,如果在 Android NDK 下開發過 C 代碼,就應該不會太陌生,因為我們調用 “JNI* env” 的函數也是這個樣子去調用的。


2. Objects 和 Interfaces


OpenSL ES 有兩個必須理解的概念,就是 Object 和 Interface,Object 可以想象成 Java 的 Object 類,Interface 可以想象成 Java 的 Interface,但它們並不完全相同,下面進一步解釋他們的關係:


(1) 每個 Object 可能會存在一個或者多個 Interface,官方為每一種 Object 都定義了一系列的 Interface

(2)每個 Object 對象都提供了一些最基礎的操作,比如:Realize,Resume,GetState,Destroy 等等,如果希望使用該對象支援的功能函數,則必須通過其 GetInterface 函數拿到 Interface 介面,然後通過 Interface 來訪問功能函數

(3)並不是每個系統上都實現了 OpenSL ES 為 Object 定義的所有 Interface,所以在擷取 Interface 的時候需要做一些選擇和判斷


通過查看 “OpenSLES.h” 檔案,我們可以看到 OpenSL ES 定義的所有 Object 對象的 ID,我們可以通過 Object ID 來建立對應的對象執行個體。


650) this.width=650;" src="http://s2.51cto.com/wyfs02/M01/7F/D6/wKiom1cvHVvg-z39AAIbcyKy2jA188.png" title="1.png" alt="wKiom1cvHVvg-z39AAIbcyKy2jA188.png" />


其中,我們比較常用的應該就是:ENGINE、AUDIOPLAYER 和 AUDIORECORDER 對象了。


同樣,“OpenSLES.h” 檔案中還定義了所有的 Interface ID,通過 Interface ID 我們可以從對象中擷取到對應的功能介面。


650) this.width=650;" src="http://s5.51cto.com/wyfs02/M01/7F/D3/wKioL1cvHlKT-AA-AAcnHCYfm7s495.png" title="2.png" alt="wKioL1cvHlKT-AA-AAcnHCYfm7s495.png" />


3. OpenSL ES 的狀態機器制


OpenSL ES 還有一個比較重要的概念,就是它的狀態機器制,:


650) this.width=650;" src="http://s4.51cto.com/wyfs02/M02/7F/D3/wKioL1cvHoOwUlRqAAGULV5vk6Y741.png" title="3.png" alt="wKioL1cvHoOwUlRqAAGULV5vk6Y741.png" />

任何一個 OpenSL ES 的對象,建立成功後,都進入 SL_OBJECT_STATE_UNREALIZED 狀態,這種狀態下,系統不會為它分配任何資源,直到調用 Realize 函數為止。


Realize 後的對象,就會進入 SL_OBJECT_STATE_REALIZED 狀態,這是一種“可用”的狀態,只有在這種狀態下,對象的各個功能和資源才能正常地訪問。


當一些系統事件發生後,比如出現錯誤或者 Audio 裝置被其他應用搶佔,OpenSL ES 對象會進入 SL_OBJECT_STATE_SUSPENDED 狀態,如果希望恢複正常使用,需要調用 Resume 函數。


當調用對象的 Destroy 函數後,則會釋放資源,並回到 SL_OBJECT_STATE_UNREALIZED 狀態。


簡言之,一個 OpenSL ES 對象的生命週期,就是從 create 到 destroy 的過程,生命週期的控制,都是通過開發人員顯示調用來完成的。


4. 常用的對象和結構體


心中保持一個概念,就是在 OpenSL ES 中,一切 API 的訪問和控制都是通過 Interface 來完成的,連 OpenSL ES 裡面的 Object 也是通過 SLObjectItf Interface 來訪問和使用的。


4.1 Engine Object 和 SLEngineItf Interface


OpenSL ES 裡面最核心的對象就是:Engine Object,音頻引擎對象,它主要提供如下兩個功能:


(1)管理 Audio Engine 的生命週期

(2)提供管理介面: SLEngineItf,該介面可以用來建立所有其他的 Object 對象

(3)提供裝置屬性查詢介面:SLEngineCapabilitiesItf 和 SLAudioIODeviceCapabilitiesItf,這些介面可以查詢裝置的一些屬性資訊


Engine Object 對象的建立方法如下:


SLObjectItf engineObject;slCreateEngine( &engineObject, 0, nullptr, 0, nullptr, nullptr );


初始化/銷毀:


(*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);(*engineObject)->Destroy(engineObject);


擷取管理介面:


SLEngineItf engineEngine;(*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &(engineEngine));


下面我們就可以愉快地使用 engineEngine 來建立所有 OpenSL ES 的其他對象了。


4.2 Media Object


OpenSL ES 裡面另一組比較重要的對象就是 Media Object ,代表著多媒體功能的抽象,比如:player、recorder 等等。


我們可以通過 SLEngineItf 提供的 CreateAudioPlayer 方法來建立一個 player 對象執行個體,可以通過 SLEngineItf 提供的 CreateAudioRecorder 方法來建立一個 recorder 執行個體。


4.3 Data Source 和 Data Sink


OpenSL ES 裡面,這兩個結構體均是作為建立 Media Object 對象時的參數而存在的,data source 代表著輸入源的資訊,即資料從哪兒來、輸入的資料參數是怎樣的;而 data sink 則代表著輸出的資訊,即資料輸出到哪兒、以什麼樣的參數來輸出。


4.3.1 基本定義


Data Source 的定義如下:


typedef struct SLDataSource_ {      void *pLocator;      void *pFormat;} SLDataSource;


Data Sink 的定義如下:


typedef struct SLDataSink_ {    void *pLocator;    void *pFormat;} SLDataSink;


其中,pLocator 主要有如下幾種:


SLDataLocator_AddressSLDataLocator_BufferQueueSLDataLocator_IODeviceSLDataLocator_MIDIBufferQueueSLDataLocator_URI


也就是說,Media Object 對象的輸入源/輸出源,既可以是 URL,也可以 Device,或者來自於緩衝區隊列等等,完全是由 Media Object 對象的具體類型和應用情境來配置。


4.3.2 樣本說明


不同的 Media Object 對象執行個體,data source 和 data sink 的具體內容是不一樣的。


例如,對於 player 而言:


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/7F/D6/wKiom1cvHuezbdo3AAIuZfv-pHc958.png" title="4.png" alt="wKiom1cvHuezbdo3AAIuZfv-pHc958.png" />


而對於 recorder 而言:


650) this.width=650;" src="http://s5.51cto.com/wyfs02/M01/7F/D3/wKioL1cvH9DhoyauAAHH2i9iQPU292.png" title="5.png" alt="wKioL1cvH9DhoyauAAHH2i9iQPU292.png" />

5. 樣本程式及參考資料


限於篇幅,關於 OpenSL ES 的 Player 和 Recorder 相關 API 詳細的用法,就不在本文展開了,其實理解了上面介紹了這些概念之後,結合官方的 《OpenSL_ES_Specification_1.0.1.pdf》,以及我給出的範例程式碼,很容易就能讀懂和掌握這些 API 的用法了,範例程式碼地址如下:


https://github.com/Jhuster/AudioDemo


6. 小結


關於如何 Android OpenSL ES API 就介紹到這兒了,文章中有不清楚的地方歡迎留言或者來信 [email protected] 交流,或者關注我的新浪微博 @盧_俊 或者 公眾號 @Jhuster 擷取最新的文章和資訊。


650) this.width=650;" src="http://s1.51cto.com/wyfs02/M02/7F/D6/wKiom1cvHxqDp9WlAACb8XAe6Uo227.jpg" title="weixin_jhuster.jpg" alt="wKiom1cvHxqDp9WlAACb8XAe6Uo227.jpg" />


本文出自 “Jhuster的專欄” 部落格,請務必保留此出處http://ticktick.blog.51cto.com/823160/1771239

Android音頻開發(7):使用 OpenSL ES API(下)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.