我在前面的文章(Android智能手機上的音頻淺析)中說過Android手機上有一塊專門用於音頻處理的DSP,它的特色是頻率低(通常幾百MHZ)、內部memory小(一般不超過100k word)。要想讓Audio DSP上放下更多的內容以及能流暢的運行,要有一些應對措施。今天就聊聊這些措施。html
1,頻率低的應對措施算法
因爲DSP的頻率低,要想軟件能流暢的運行,就得把運行時的load降下來。主要的措施有兩種,定點化和load優化。先看定點化。編程
DSP有定點DSP和浮點DSP之分。通常來講,定點DSP具備速度快,功耗低,價格便宜的特色;而浮點DSP則計算精確,動態範圍大,速度快,易於編程,功耗大,價格高。音頻處理用的DSP一般都是定點DSP。定點DSP,處理定點數據會至關快,可是處理浮點數據就會很是慢。那在定點DSP上涉及到浮點運算怎麼辦呢?解決方法是浮點運算定點化來節約處理時間,即用定點數表示浮點數。定點數中最高位表示符號位,符號位右邊n位表示整數,剩下的表示小數。這種表示方法叫Q格式。數組
Q格式表示爲:Qm.n,表示數據用m比特表示整數部分,n比特表示小數部分,共須要m+n+1位來表示這個數據,多餘的一位用做符合位(不表示出來)。例如Q15表示小數部分有15位,一個short型數據,佔2個字節,最高位是符號位,後面15位是小數位,表示的範圍是:-1<X<0.9999695 。浮點數據轉化爲Q15,將數據乘以2^15;Q15數據轉化爲浮點數據,將數據除以2^15。例如假設數據存儲空間爲2個字節,0.333×2^15=10911=0x2A9F,0.333的全部運算就能夠用0x2A9F表示,同理10911×2^(-15)=0.332977294921875,能夠看出浮點數據經過Q格式轉化後是有偏差的。函數
再看看Q格式的加減乘除基本運算。加減法時須轉換成相同的Q格式才能加減。不一樣Q格式的數據相乘,至關於Q值相加,要右移n位獲得正確的值。不一樣Q格式的數據相除,至關於Q值相減,要左移n位獲得正確的值。這裏舉一個乘的例子:兩個小數相乘,0.333*0.414=0.137862優化
0.333*2^15=10911=0x2A9F,0.414*2^15=13565=0x34FDspa
short a = 0x2A9F;code
short b = 0x34FD;htm
short c = a * b >> 15; // 兩個Q15格式的數據相乘後爲Q30格式數據,所以爲了獲得Q15的數據結果須要右移15位blog
這樣c的結果是0x11A4=0001000110100100,這個數據一樣是Q15格式的,它的小數點假設在第15位左邊,即爲0.001000110100100=0.1378173828125...和實際結果0.137862差距不大。或者0x11A4 / 2^15 = 0.1378173828125
其餘的基本運算就不舉例了,網上講Q格式的有一些文章,感興趣本身去看。
實際應用中,浮點運算大都時候都是既有整數部分,也有小數部分的。因此要選擇一個適當的定標格式才能更好的處理運算。通常用以下兩種方法:一是使用適中的定標,既能夠表示必定的整數復位也能夠表示小數復位,如對於2812的32位系統,使用Q15格式,可表示-65536.0~65535.999969482區間內的數據。二是所有采用小數,這樣由於小數之間相乘永遠是小數,永遠不會溢出。取一個極限最大值(最好使用2的n次冪),轉換成x/Max的小數(若是Max是取的2的 n次冪,就可使用移位代替除法)。剛開始用Q格式時會很彆扭,不習慣,用多了就慢慢習慣了。
爲了降load,咱們在軟件開發過程當中要作到如下兩點:一是選擇算法實現時必定要用定點實現。Audio DSP上會運行好多音頻處理算法,好比各類codec,這些codec的制定者通常會提供兩套reference code,一套定點實現的,一套浮點實現的,咱們在選擇時必定要選用定點實現的。對於應用在ARM上的音頻處理算法,一樣是推薦用定點實現的算法,雖然ARM頻率高。在ARM上仍是要優化算法把load降到儘可能低。二是在咱們本身的代碼中如遇到要作浮點運算(好比算百分比),要用Q格式定點化去算,而不是直接用浮點數去運算。
再來看load優化。我在前面的文章(音頻的編解碼及其優化方法和經驗)中load優化的一些通用方法,在DSP上也適用。不過因爲DSP頻率低,且DSP上運行的音頻處理算法多,有些算法又比較耗load(好比AEC),要想讓音頻軟件流暢的運行,複雜的算法都是要作彙編優化的。作這些不只專業並且耗時,load優化沒有最低只有更低。
2,memory低的應對措施
DSP的內部memory分兩種,DTCM(Data Tightly Coupled Memory, 數據緊密耦合存儲器)和PTCM(Program Tightly Coupled Memory, 程序緊密耦合存儲器)。DTCM用於存data,PTCM用於存code。同時還有外部memory(DDR),它也可存data和code。data和code儘可能放內部,由於這樣速度快效率高,不到萬不得已纔將它們放在外部memory上。即便放在外部的也是一些低頻訪問的data和code,如初始化函數等。今天咱們主要講的是應對DTCM(data memory)小的措施。DTCM主要分如下幾個區域:const區、data區、bss區、overlay區。const區顧名思義就是放一些常量的,data區是放已初始化的全局變量和靜態變量,bss區是放未初始化的全局變量和靜態變量,overlay區主要是根據場景的互斥作一些memory的複用,這是應對memory小的主要措施。咱們先看overlay機制。
Overlay機制是指不會同時發生的場景下使用的memory能夠複用,在音頻上主要有兩種狀況。一是music使用的memory能夠和voice使用的memory複用,由於音樂播放和打電話不可能同時發生。一般是音樂播放時來電話,音樂播放就暫停,把音樂播放的context以及還在buffer中未播放的數據拷貝到外部memory上,把music使用的memory讓給voice用。打電話結束後再把放在外部memory上的音樂相關的context以及未播放的數據拷進內部memory原先的位置上,從而繼續音樂的播放。二是各類codec使用的memory的複用,由於同時只有一種codec在使用。音樂的decoder有MP三、AAC,播放音樂時只可能有一種decoder在用。Voice的codec有AMR-NB、AMR-WB、EVS,打電話時只可能有一種codec在用。它與第一種的區別是不須要保存上下文。這樣能夠畫出DTCM的分佈圖,以下圖:
上圖中memory地址是由低向高增加,分別是const區、data區、bss區、overlay區。在overlay區music和voice有相同的起始地址,在music中decoder MP3和AAC又有相同的起始地址,在voice中codec AMR-NB、AMR-WB和EVS又有相同的起始地址。
對於PTCM,一樣能夠用overlay機制。不過除非一些代碼重寫,通常很難省code size了。
除了overlay機制,其餘的就要一點一點的摳來省memory了,主要有如下幾點:
1) 定義數據類型時能用short的就不要用int
2)在overlay區域,buffer的大小都是指定的。指定時要正確算出大小值,不要指定大了,指定大了就浪費了。
3)在data區或者bss區的buffer要看是否是分大了,好比有的buffer分三塊就夠了,也就不必分四塊了,分四塊一是浪費了buffer,二是有些場景下增長了時延。
4) DSP上每一個thread/task的棧的大小都是指定的。爲了省memory,棧的大小不可能很大,通常不超過1k word。這就要求寫代碼時不能有大的局部變量數組等,遇到時就要經過一些技巧解決。如一個要把雙聲道的數據從interleave變成non-interleave的函數,寫成了以下實現,避免了大的局部變量數組。一般的作法是用一個大的局部變量數組先存右聲道數據,最後再一塊兒拷到指定位置上。
void interleave_to_noninterleave(int16_t *buf, int32_t frame_cnt)
{
int i,j;
short temp;
for(i = 1; i < frame_cnt; i++) {
temp = buf[2*i];
for(j = 0; j < i; j++)
buf[2*i - j] = buf[2*i - j -1];
buf[i] = temp;
}
}
5)代碼編好後會生成一個各buffer起始地址和大小的文件。關注那些size較大的buffer,分析有沒有減少的可能。
整體而言,DSP因爲頻率低和memory小的限制,在上面寫代碼比在ARM上要求高些,花的時間長些。長時間在ARM上寫代碼,轉到DSP上會有些不習慣,有個適應過程。