編輯:關於Android編程
支持多媒體是Android設備的一個重要功能,可以想象一台不支持多媒體的設備是何等枯燥。通常意義上的多媒體(Multimedia)通常是指圖片、視頻、音頻、文本等等。這其中對於音頻的需求最高。可以通過種種手段來提升音頻的體驗,總體分為軟件和硬件兩大類。例如:HiFi耳機、smartPA、音源修復算法、音效算法等等。軟件層面,可以大致分為基於音頻數據流的處理方式以及基於Android的音效框架的處理方式。基於音頻數據流的處理方式比較容易理解,基本原理就是先把數據丟給3rd的庫進行處理,接著將處理完成的數據重新寫入原有數據流的節點即可。這裡主要探討下基於Android的音效框架的處理方式。
通過本文可以有如下的初步了解 可以指導修復基於Android原生audido effect架構的音效問題 可以知道集成類似audio effect的集成、開發等工作 可以了解Android 原生effect架構Framework java層的結構如下圖所示。向上層提供了一系列基礎的用於控制audio effect的類。需要注意的是,不建議直接使用AudioEffect這個父類;父類只是提供了統一的方法,並不是‘音效’的具體實現。
這部分是其主要實現,主要從如下方面入手進行介紹。
從初始化說起;所有的具體音效的java接口都繼承自AudioEffect.java,AudioEffect或調用native_setup 進行初始化。接下來的流程如下圖所示,一步步的跟就可以。其中Threads /AudioFilger/AudioSystem用的比較多了,不針對去進行說明了。這裡主要針對EffectModule /EffectChain/EffectHandle 這三個類進行一下介紹。EffectChain EffectModule EffectHandle
EffectModule
EffectModule是一個封裝類,封裝了3rd音效引擎的實現;可以控制類似
process()\command()通過不同進程的並發調用。保存著一個用於同所有客戶端進行同步的
EffectHandle用於處理音效狀態、參數更改;
EffectModule同時管控著音效引擎的狀態機:重置、使能、以及在狀態切換過程中的聲音淡入淡出效果。
EffectHandle
EffectHandle是
IEffect接口的具體實現,他提供了一系列資源來接受參數更新、跟蹤效果控制的所有權和狀態;並具有一個指向
EffectModule的指針控制著
EffectModule對象。每個應用只能使用一個
EffectHandle來控制。EffectHandle由
AudioFlinger::createEffect()創建。
EffectChain
EffectChain展示了一系列音效同audio session之間的關系;每一個output mixer thread (playbackthread)可以關聯任意多個
EffectChain對象。當
EffectChain同ID為0的session關聯時,
EffectChain作用於所有的output mix。
EffectChain EffectMode EffectHandle這三者的關系類似於MVC.當然這裡的view說起來可能有些牽強。但可以這麼來理解EffectMode就算這裡的Model,
EffectHandle就是這裡的control,EffectChain就是這裡的View。總的來說:
EffectChain代表了一系列音效之前的關系,
EffectMode是所有音效實現的抽象,
EffectHandle是所有音效的操作的抽象。
圖1-1 audio effect常見類時序圖
從上圖看到,我們不僅僅需要將上層的信息應用到音效庫中(setParamater),我們還需要感知下層的狀態;這裡選擇了一個統一的函數指針作為信息傳遞的中介。先來看看簽名:
typedef void (*effect_callback_t)(int32_t event,void *user,void *info);event 代表不同的消息類型,user代表傳遞給的不同客戶端,info代表所攜帶的信息
圖1-2 effectCallback 函數簽名
當下層的音效引擎的相關狀態發生改變時,由該機制通知客戶端。同一音效引擎可以被多個客戶端復用,但是在同一時刻只能有一個是處於活動狀態。不同的狀態取值如event_type所示:
圖1-3 傳遞event類型
EVENT_CONTROL_STATUS_CHANGE:<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjxiciAvPg0KtbHB7dK7v827p7bLs6LK1Mq508PP4M2stcR0eXBloaLTxc/IvLa4/LjftcSyzsr9tLS9qEF1ZGlvRWZmZWN0yrGjrLWxx7C/zbuntsu74bvxtcNlZmZlY3QgZW5naW5ltcS/2NbGyKiju7bU06a1xMHt0ru/zbuntsu+zbaqyqfBy7/Y1sbIqLKit6Kz9rjD17TMrDxiciAvPg0KPHN0cm9uZz5FVkVOVF9FTkFCTEVfU1RBVFVTX0NIQU5HRUSjujwvc3Ryb25nPjxiciAvPg0KtbFkaXNhYmxlL2VuYWJsZcqxo6zL+dPQw7vT0LvxtcNlZmZlY3S/2NbGtcSjrLvy1d/LtbK7ysfU2ryku+7XtMystcRlZmZlY3S2vLvhytW1vbjD17TMrDxiciAvPg0KPHN0cm9uZz5FVkVOVF9QQVJBTUVURVJfQ0hBTkdFo7o8L3N0cm9uZz48YnIgLz4NCrWxss7K/bj80MLKsaOsu+HK1bW9uMPXtMysPGJyIC8+DQo8c3Ryb25nPkVWRU5UX0VSUk9So7o8L3N0cm9uZz48YnIgLz4NCrWxbWVkaWEgc2VydmVyIHByb2Nlc3MgZGllc8qxu+HK3LW9uMPXtMysPGJyIC8+DQrNqLOjx+m/9s/Co6y++LTzsr+31kFQUFPKx9PJamF2YdPv0dSx4NC0o6y1q82ouf26r8r91rjV67XEt73Kvc7Sw8fX7rbgv8nS1L2r17TMrLTTtdey47XEc2+/4rSrtd21vUpOSbLjo7vI57rOvatzb7/i1tC1xNe0zKy0q7Xdtb3Jz7LjtcRBUFBT1tCjrMrHvdPPwsC00qrM1sLbtcSho9XiwO9BUFBTo6zO0sPHz8i88rWltcTA7b3is8lBdWRpb0VmZmVjdC5qYXZhtcTSu7j2yrXA/aOsYXVkaW9FZmZlY3RfY2xhc3PKx0F1ZGlvRWZmZWN0LmNsYXNztcTS/dPDo7thdWRpb0VmZmVjdF9yZWbU8srH1rhBdWRpb0VmZmVjdLXE0ru49sq1wP2ju8/gudjK/b7dveG5ucjnz8LL+cq+o7o8YnIgLz4NCjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160919/20160919093621267.png" title="\" />
圖1-4 JNI存儲java對象數據結構
接下來了解下初始化的過程,很簡單獲得AudioEffect.class的一個引用,保存AudioEffect的一個實例;然後將保存有AudioEffect.class引用和AudioEffect實例的結構體的地址給AudioEffect的構造函數。如下圖所示
圖1-5 JNI中保存java對象的初始化
AudioEffect構造函數如下所示:
圖1-6 AudioEffect構造函數
如何啟動
Audio effect的啟動,或者說所有audio effect so的加載是基於audiopolicy services來進行的。當AudioPolicyServices啟動之後,會創建一個AudioPolicyEffect對象;在AudioPolicyEffect構造函數中對所有的effect so進行加載。時序圖如下圖所示
圖1-7 audio effect so 加載過程
Audio Effect 如何實現
說到3rd audio effect的實現,必然會考慮到如何對其進行統一的管理,如何將其抽象出統一的接口。接下來參照如下數據結構audio_effect_library_t定義了所有effect的一個統一接口。
// 所有的音效庫必須實現一個名為AUDIO_EFFECT_LIBRARY_INFO_SYM的audio_effect_library_t的結構
typedef struct audio_effect_library_s {
// tag must be initialized to AUDIO_EFFECT_LIBRARY_TAG
uint32_t tag;
// Version of the effect library API
uint32_t version;
// Name of this library
const char *name;
// Author/owner/implementor of the library
const char *implementor;
int32_t (*create_effect)(const effect_uuid_t *uuid,
int32_t sessionId,
int32_t ioId,
effect_handle_t *pHandle);
int32_t (*release_effect)(effect_handle_t handle);
int32_t (*get_descriptor)(const effect_uuid_t *uuid,
effect_descriptor_t *pDescriptor);
} audio_effect_library_t;
看具體的實例,downmix(frameworks/av/media/libeffects/downmix/)。將
create_effect\release_effect\get_descriptor函數指針具體化。
// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
.tag = AUDIO_EFFECT_LIBRARY_TAG,
.version = EFFECT_LIBRARY_API_VERSION,
.name = "Downmix Library",
.implementor = "The Android Open Source Project",
.create_effect = DownmixLib_Create,
.release_effect = DownmixLib_Release,
.get_descriptor = DownmixLib_GetDescriptor,
};
audio_buffer_t定義了音效輸入輸出的數據格式
struct audio_buffer_s {
size_t frameCount;// number of frames in buffer
union {
void* raw;// raw pointer to start of buffer
int32_t* s32;// pointer to signed 32 bit data at start of buffer
int16_t* s16;// pointer to signed 16 bit data at start of buffer
uint8_t* u8; // pointer to unsigned 8 bit data at start of buffer
};
};
effect_param_t定義了音效之間、系統上下之間的通信協議(數據、格式等等)
// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM
// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command.
// psize and vsize represent the actual size of parameter and value.
typedef struct effect_param_s {
// Transaction status (unused for command, used for reply)
int32_t status;
uint32_t psize; // Parameter size
uint32_t vsize; // Value size
char data[]; // Start of Parameter + Value data
} effect_param_t;
1.這次的學習主要是,彌補上一篇文章:A07_TimePicker & DatePicker & AnalogClock & DigitalClock 的設置 2.jav
前言在我們的項目中,我們幾乎天天和一些固定的代碼打交道,比如在Activity中你要寫findViewById(int)方法來找到控件,然而這樣子的代碼對於一個稍微有點資
ool1手機的亮點在於雙攝2.0技術的1300萬雙後置鏡頭,系統也進化到樂視EUI。這樣的一款手機很多人很好奇它和年初發布的樂視2超級手機哪個好?那麼到底c
前言什麼是NDK?NDK全稱是Native Development Kit,NDK提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so和java應