天天看點

ALSA程式設計細節分析一. 程式設計細節

[Loong]:之前寫過基于ALSA的WAV播放錄音程式,見http://blog.csdn.net/sepnic/archive/2011/01/14/6140824.aspx。現在本想好好整理一下ALSA的程式設計思想,但Google了一下,發現已經有同道做了類似的工作,故将其轉載過來,并添加一些本人的疑問以及補充(将會繼續補充,原文很多重要的ALSA參數沒有提到)。

原文:http://blogold.chinaunix.net/u3/112227/showart_2251390.html

一. 程式設計細節

按照上面的流程,其中有許多細節我們可以加以控制,這裡僅僅指出應用程式需要關心的:

1.1 裝置層次

在alsa驅動這一層,目前為止,抽象出了4層裝置:

一是hw:0,0;

二是plughw:0,0;

三是default:0;

四是default。

至于一是清楚了,二和二以上可以做資料轉換,以支援一個動态的範圍,比如你要播放7000hz的東西,那麼就可以用二和二以上的。而你用7000hz作為參數,去設定一,就會報錯。三和四,支援軟體混音。我覺得default:0表示對第一個聲霸卡軟體混音,default表示對整個系統軟體混音。

這裡提出兩點:

1.1.1 一般為了讓所有的程式都可以發音,為使用更多的預設政策,我們選用三和四,這樣少一些控制權,多一些友善。

1.1.2 對不同的層次的裝置,相同的函數,結果可能是不一樣的。比如,設定Hardware Parameters裡的period和buffer size,這個是對硬體的設定,是以,default和default:0這兩種裝置是不能設定的。

如果直接操作hw:0,0,那麼snd_pcm_writei隻能寫如8的倍數的frame,比如16、24等,否則就會剩下一點不寫入而退回,而 default,就可以想寫多少就寫多少,我們也不必要關心裡面具體的政策。

[Loong]:之前都是使用了default,還真沒留意過這些裝置有何差別。

1.2 Hardware Parameters

說明:之是以叫做Hardware Parameters,是因為alsa這一層api是較為底層的,它允許使用者對audio interface和alsa-core兩層都做設定。其中對alsa-core設定,叫做Software Parameters,而對audio interface的設定叫做Hardware Parameters。(當然要設定hardware parameters,也肯定是通過alsa驅動來完成,隻不過哪些參數是指導硬體的,哪些是指導alsa-core的,分開設定了)

1.2.1 Sample rate: 采樣率

1.2.2 Sample format: 采用格式

1.2.3 Number of channels: 聲道數

1.2.4 Data access and layout:

簡單點說,在一個period以内,資料是按照channel1排完了再排channel2呢,還是一個frame一個frame的來排(frame在alsa裡指的是一次采樣時間内,兩個channel的資料放一塊兒就是一個frame)。預設是第二種。

1.2.5 Interrupt interval:

中斷間隔,就是靠periods決定的,有函數來設定periods,也就是說這個hardware buffer在一次周遊之内,要中斷多少次,來通知alsa-driver來寫入或讀走資料。比如buffer是8192個frame大,而 period設為4個frame大,那麼比如playback,則每當有4個frame大的hardware buffer空間空出,就會中斷,通知核心(alsa驅動)來寫如資料。這個是影響實時效果的關鍵。一般不用調整。

1.2.6 Buffer size:

hardware buffer的大小,如果alsa整套體系主要靠這個來做緩沖,那麼這個的大小,将影響緩沖效果,但是一般也不調整。

[Loong]:缺少buffer time、peroid time、peroid size等參數說明,這些參數一般情況下都要設定的。

1.3 Software Parameters

1.3.1 snd_pcm_sw_params_set_avail_min (playback_handle, sw_params, 4096)

這個僅用在interrupt-driven模式。這個模式是alsa驅動層的,不是硬體interrupt。它的意思是,使用者使用 snd_pcm_wait()時,這個實際封裝的是系統的poll調用,表示使用者在等待,那麼在等待什麼呢?對于playback來講,就是等待下面的聲霸卡的hardware buffer裡有一定數量的空間,可以放入新的資料了,對于record來講,就是等待下面聲霸卡新采集的資料達到了一定數量了。這個一定數量,就是用 snd_pcm_sw_params_set_avail_min來設定,機關是frame。實際運作,沒讀驅動代碼,不是很清楚,可能是alsa驅動根據使用者設的這個參數,來設定Hardware Parameters裡面的period,也可能是不改變硬體的period,每次硬體中斷還是copy到自己的空間,然後資料積累到一定數量再 interrupt應用程式,使之從wait()出來。我不知道,也不必深究。

這種模式的使用,需要使用者在snd_pcm_wait()出來以後,調用一個平常的wirtei或readi函數,來寫入或讀取一定數量的資料。如果使用者不用interrupt-driven模式,那麼這個函數不必使用。

[Loong]:什麼是interrupt-driven模式?

1.3.2 snd_pcm_sw_params_set_start_threshold (playback_handle, sw_params, 0U)

這個函數指導什麼時候開啟audio interface的AD/DA,就是什麼時候啟動聲霸卡。

對于playback,假設第三個參數設為320,那麼就是說,當使用者調用writei,寫入的資料,将暫時存在alsa驅動空間裡,當這個資料量達到 320幀時,alsa驅動才開始将資料寫入hardware buffer,并啟動DA轉換。對于record,當使用者調用readi,這個資料量達到320幀時,alsa驅動才開始啟動AD轉換,捕捉資料。我一般把它設為0,我沒試過非0,如果是非0, 我想第一次的writei和readi一定得夠數量才行,否則裝置不啟動。

這個對實時效果是需要的,将第三個參數設定為0,保證聲霸卡的立即啟動。

1.4 what to do about xruns

xrun指的是,聲霸卡period一到,引發一個中斷,告訴alsa驅動,要填入資料,或讀走資料,但是,問題在于alsa的讀取和寫入操作必須使用者調用writei和readi才會發生的,它不會去緩存資料。如果上層沒有使用者調用writei和readi,那麼就會産生 overrun(錄制時,資料都滿了,還沒被alsa驅動讀走)和underrun(需要資料來播放,alsa驅動卻不寫入資料),統稱為xrun。

這個東西,需要用一些函數來設定,比如snd_pcm_sw_params_set_silence_threshold(),是針對playback 的,就是設定當xxx的情況下,就用silence來寫入hardware buffer。至于xxx情況,以及寫入多少silence,我都不是很清楚,還有,比如xrun到什麼情況下,可以停止這個裝置等等函數。一般情況下用alsa驅動的預設的xrun處理政策。

但是關于xrun,最好這樣寫:

while ((pcmreturn = snd_pcm_writei(pcm_handle, data, frames)) < 0) {

        snd_pcm_prepare(pcm_handle);

        fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>/n");

}

就是說,如果這次讀/寫距離上次讀/寫,時間可能過長,那麼這次去讀/寫的時候,device已經xrun了,在不知道alsa驅動對xrun的預設政策的情況下,最好調用snd_pcm_prepare()來重新準備好裝置,然後再開始下一次讀寫。

1.5 transfer chunk size

這個應該是用不上的,我沒找到文檔裡有用這個的。

[Loong]:這個其實是非常重要的,如果snd_pcm_writei/ snd_pcm_readi不是每次寫入chunk size資料的話,那麼放音/錄音不是你所期望的聲音。詳細見:http://alsa-project.org/main/index.php/FramesPeriods

繼續閱讀