Linux 音訊ALSA技術說明--part2[轉&學習]

飛翔的魚tsj發表於2014-10-27

轉載:http://ivqinwei.blog.163.com/blog/static/110931542011722111128343/


2       音訊基礎

2.1   數字音訊基礎

      音訊訊號是一種連續變化的模擬訊號,但計算機只能處理和記錄二進位制的數字訊號,由自然音源得到的音訊訊號必須經過一定的變換,成為數字音訊訊號之後,才能送到計算機中作進一步的處理。[QW--模數轉換

      數字音訊系統通過將聲波的波型轉換成一系列二進位制資料,來實現對原始聲音的重現,實現這一步驟的裝置常被稱為模/數轉換器(A/D)。A/D轉換器以每秒鐘上萬次的速率對聲波進行取樣,每個取樣點都記錄下了原始模擬聲波在某一時刻的狀態,通常稱之為樣本(sample),而每一秒鐘所取樣的數目則稱為取樣頻率,通過將一串連續的樣本連線起來,就可以在計算機中描述一段聲音了。對於取樣過程中的每一個樣本來說,數字音訊系統會分配一定儲存位來記錄聲波的振幅,一般稱之為取樣分辯率或者取樣精度,取樣精度越高,聲音還原時就會越細膩。

      數字音訊涉及到的概念非常多,對於在Linux下 進行音訊程式設計的程式設計師來說,最重要的是理解聲音數字化的兩個關鍵步驟:取樣和量化。取樣就是每隔一定時間就讀一次聲音訊號的幅度,而量化則是將取樣得到的 聲音訊號幅度轉換為數字值,從本質上講,取樣是時間上的數字化,而量化則是幅度上的數字化。下面介紹幾個在進行音訊程式設計時經常需要用到的技術指標:

1.取樣頻率 

      取樣頻率是指將模擬聲音波形進行數字化時,每秒鐘抽取聲波幅度樣本的次數。取樣頻率的選擇應該遵循奈奎斯特(Harry Nyquist)取樣理論:如果對某一模擬訊號進行取樣,則取樣後可還原的最高訊號頻率只有取樣頻率的一半,或者說只要取樣頻率高於輸入訊號最高頻率的兩倍,就能從取樣訊號系列重構原始訊號。正常人聽覺的頻率範圍大約在20Hz~20kHz之間,根據奈奎斯特取樣理論,為了保證聲音不失真,取樣頻率應該在40kHz左右。常用的音訊取樣頻率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果採用更高的取樣頻率,還可以達到DVD的音質。 

2.量化位數 

      量化位數是對模擬音訊訊號的幅度進行數字化,它決定了模擬訊號數字化以後的動態範圍,常用的有8、12位和16。量化位越高,訊號的動態範圍越大,數字化後的音訊訊號就越可能接近原始訊號,但所需要的存貯空間也越大。 

3.聲道數 

      聲道數是反映音訊數字化質量的另一個重要因素,它有單聲道和雙聲道之分。雙聲道又稱為立體聲,在硬體中有兩條線路,音質和音色都要優於單聲道,但數字化後佔據的儲存空間的大小要比單聲道多一倍。

2.2   ALSA基礎

      ALSA由許多音效卡的音效卡驅動程式組成,同時它也提供一個稱為libasound的API庫。應用程式開發者應該使用libasound而不是核心中的ALSA介面。因為libasound提供最高階並且程式設計方便的程式設計介面。並且提供一個裝置邏輯命名功能,這樣開發者甚至不需要知道類似裝置檔案這樣的低層介面。相反,OSS/Free驅動是在核心系統呼叫級上程式設計,它要求開發者提供裝置檔名並且利用ioctrl來實現相應的功能。為了向後相容,ALSA提供核心模組來模擬OSS,這樣之前的許多在OSS基礎上開發的應用程式不需要任何改動就可以在ALSA上執行。另外,libaoss庫也可以模擬OSS,而它不需要核心模組。

      ALSA包含外掛功能,使用外掛可以擴充套件新的音效卡驅動,包括完全用軟體實現的虛擬音效卡。ALSA提供一系列基於命令列的工具集,比如混音器(mixer),音訊檔案播放器(aplay),以及控制特定音效卡特定屬性的工具。

2.3   裝置命名

      API庫使用邏輯裝置名而不是裝置檔案。裝置名字可以是真實的硬體名字也可以是外掛名字。硬體名字使用hw:i,j這樣的格式。其中i是卡號,j是這塊音效卡上的裝置號。第一個聲音裝置是hw:0,0.這個別名預設引用第一塊聲音裝置並且在本文示例中一直會被用到。外掛使用另外的唯一名字。比如plughw:,表示一個外掛,這個外掛不提供對硬體裝置的訪問,而是提供像取樣率轉換這樣的軟體特性,硬體本身並不支援這樣的特性。

2.4   聲音快取和資料傳輸

      每個音效卡都有一個硬體快取區來儲存記錄下來的樣本。當快取區足夠滿時,音效卡將產生一箇中斷。核心音效卡驅動然後使用直接記憶體(DMA)訪問通道將樣本傳送到記憶體中的應用程式快取區。類似地,對於回放,任何應用程式使用DMA將自己的快取區資料傳送到音效卡的硬體快取區中。

      這樣硬體快取區是環快取。也就是說當資料到達快取區末尾時將重新回到快取區的起始位置。ALSA維護一個指標來指向硬體快取以及應用程式快取區中資料操作的當前位置。從核心外部看,我們只對應用程式的快取區感興趣,所以本文只討論應用程式快取區。


QW--本文只討論應用程式快取區

      應用程式快取區的大小可以通過ALSA庫函式呼叫來控制。快取區可以很大,一次傳輸操作可能會導致不可接受的延遲,我們把它稱為延時(latency)。為了解決這個問題,ALSA將快取區拆分成一系列週期(period)(OSS/Free中叫片斷fragments).ALSA以period為單元來傳送資料。

      一個週期(period)儲存一些幀(frames)。每一幀包含時間上一個點所抓取的樣本。對於立體聲裝置,一個幀會包含兩個通道上的樣本。1QW--此處應該有個說明圖,算了算了,看文字說明把]展示了分解過程:一個快取區分解成周期,然後是幀,然後是樣本。圖中包含一些假定的數值。圖中左右通道資訊被交替地儲存在一個幀內。這稱為交錯(interleaved)模式。在非交錯模式中,一個通道的所有樣本資料儲存在另外一個通道的資料之後。

 

 

Over and Under Run

over  run----錄音時候的丟資料

Under run ---播放聲音時資料不夠

      當一個音效卡活動時,資料總是連續地在硬體快取區和應用程式快取區間傳輸。但是也有例外。在錄音例子中,如果應用程式讀取資料不夠快,迴圈快取區將會被新的資料覆蓋。這種資料的丟失被稱為over  run.在回放例子中,如果應用程式寫入資料到快取區中的速度不夠快,快取區將會"餓死"。這樣的錯誤被稱為"underrun"。在ALSA文件中,有時將這兩種情形統稱為"XRUN"。適當地設計應用程式可以最小化XRUN並且可以從中恢復過來。

2.5   訪問音訊裝置

[QW總結------------具體使用參考後邊相關介紹

          訪問音訊裝置的步驟:

                               open--建立與硬體之間的聯絡。

                               read--從裝置接受資料。

                               write--向裝置寫入資料。

                               ioctl---所有不符合讀/寫這一基本模式的操作

                              close---關閉


      無論是OSS還是ALSA,都是以核心驅動程式的形式執行在Linux核心空間中的,應用程式要想訪問音效卡這一硬體裝置,必須藉助於Linux核心所提供的系統呼叫(system call。從程式設計師的角度來說,對音效卡的操作在很大程度上等同於對磁碟檔案的操作:首先使用open系統呼叫建立起與硬體間的聯絡,此時返回的檔案描述符將作為隨後操作的標識;接著使用read系統呼叫從裝置接收資料,或者使用write系統呼叫向裝置寫入資料,而其它所有不符合讀/寫這一基本模式的操作都可以由ioctl系統呼叫來完成;最後,使用close系統呼叫告訴Linux核心不會再對該裝置做進一步的處理。

open系統呼叫 

      系統呼叫open可以獲得對音效卡的訪問權,同時還能為隨後的系統呼叫做好準備,其函式原型如下所示: 

      int open(const char *pathname, //QW--是要開啟的裝置檔案的名稱,一般是/dev/dsp

                                            int flags, //QW-開啟裝置檔案的方式,只讀,只寫,讀寫

                                            int mode  //QW-可選,裝置檔案不存在時用。

                          );

      引數pathname是將要被開啟的裝置檔案的名稱,對於音效卡來講一般是/dev/dsp。引數flags用來指明應該以什麼方式開啟裝置檔案,它可以是O_RDONLY、O_WRONLY或者O_RDWR,分別表示以只讀、只寫或者讀寫的方式開啟裝置檔案;引數mode通常是可選的,它只有在指定的裝置檔案不存在時才會用到,指明新建立的檔案應該具有怎樣的許可權。 

      如果open系統呼叫能夠成功完成,它將返回一個正整數作為檔案識別符號,在隨後的系統呼叫中需要用到該識別符號。如果open系統呼叫失敗,它將返回-1,同時還會設定全域性變數errno,指明是什麼原因導致了錯誤的發生。 

read系統呼叫 

      系統呼叫read用來從音效卡讀取資料,其函式原型如下所示: 

      int read(int fd            //QW--裝置檔案描述符

                 , char *buf     //QW--儲存音訊的緩衝區

                , size_t count //QW--限定最大讀取位元組數

                     );

      引數fd是裝置檔案的識別符號,它是通過之前的open系統呼叫獲得的;引數buf是指向緩衝區的字元指標,它用來儲存從音效卡獲得的資料;引數count則用來限定從音效卡獲得的最大位元組數。如果read系統呼叫成功完成,它將返回從音效卡實際讀取的位元組數,通常情況會比count的值要小一些;如果read系統呼叫失敗,它將返回-1,同時還會設定全域性變數errno,來指明是什麼原因導致了錯誤的發生。 

write系統呼叫 

      系統呼叫write用來向音效卡寫入資料,其函式原型如下所示: 

      size_t write(int fd,   

                           const char *buf, 

                             size_t count

                              );

      系統呼叫write和系統呼叫read在很大程度是類似的,差別只在於write是向音效卡寫入資料,而read則是從音效卡讀入資料。引數fd同樣是裝置檔案的識別符號,它也是通過之前的open系統呼叫獲得的;引數buf是指向緩衝區的字元指標,它儲存著即將向音效卡寫入的資料;引數count則用來限定向音效卡寫入的最大位元組數。 

      如果write系統呼叫成功完成,它將返回向音效卡實際寫入的位元組數;如果write系統呼叫失敗,它將返回-1,同時還會設定全域性變數errno,來指明是什麼原因導致了錯誤的發生。無論是read還是write,一旦呼叫之後Linux核心就會阻塞當前應用程式,直到資料成功地從音效卡讀出或者寫入為止。 

ioctl系統呼叫 -----QW--最最想弄明白的東東

      系統呼叫ioctl可以對音效卡進行控制,凡是對裝置檔案的操作不符合讀/寫基本模式的,都是通過ioctl來完成的,它可以影響裝置的行為,或者返回裝置的狀態,其函式原型如下所示: 

      int ioctl(int fd, //QW--裝置檔案描述符

              int request, //我想知道這個函式的引數都是有哪些能夠從哪裡找的到阿,

                    ...

            );

      引數fd是裝置檔案的識別符號,它是在裝置開啟時獲得的;如果裝置比較複雜,那麼對它的控制請求相應地也會有很多種,引數request的目的就是用來區分不同的控制請求;通常說來,在對裝置進行控制時還需要有其它引數,這要根據不同的控制請求才能確定,並且可能是與硬體裝置直接相關的。 

close系統呼叫 

      當應用程式使用完音效卡之後,需要用close系統呼叫將其關閉,以便及時釋放佔用的硬體資源,其函式原型如下所示: 

      int close(int fd);

      引數fd是裝置檔案的識別符號,它是在裝置開啟時獲得的。一旦應用程式呼叫了close系統呼叫,Linux核心就會釋放與之相關的各種資源,因此建議在不需要的時候儘量及時關閉已經開啟的裝置。

2.6   音訊裝置檔案

      對於Linux應用程式設計師來講,音訊程式設計介面實際上就是一組音訊裝置檔案,通過它們可以從音效卡讀取資料,或者向音效卡寫入資料,並且能夠對音效卡進行控制,設定取樣頻率和聲道數目等等。

/dev/sndstat [不宜用在應用程式開發中]

      裝置檔案/dev/sndstat是音效卡驅動程式提供的最簡單的介面,通常它是一個只讀檔案,作用也僅僅只限於彙報音效卡的當前狀態。一般說來,/dev/sndstat是提供給終端使用者來檢測音效卡的,不宜用於程式當中,因為所有的資訊都可以通過ioctl系統呼叫來獲得。 Linux提供的cat命令可以很方便地從/dev/sndstat獲得音效卡的當前狀態: 

safrans@safrans-desktop:~$ cat /dev/sndstat 

/dev/dsp   [非常非常非常重要

           [QW--只讀方式開啟---------聲音的輸入----錄音]

         [QW--只寫方式開啟---------聲音的輸出-----放音]

       音效卡驅動程式提供的/dev/dsp是用於數字取樣(sampling)和數字錄音(recording)的裝置檔案,它對於Linux下的音訊程式設計來講非常重要:向該裝置寫資料即意味著啟用音效卡上的D/A轉換器進行放音,而向該裝置讀資料則意味著啟用音效卡上的A/D轉換器進行錄音。目前許多音效卡都提供有多個數字取樣裝置,它們在Linux下可以通過/dev/dsp1等裝置檔案進行訪問。

      DSP是數字訊號處理器(Digital Signal Processor.)的簡稱,它是用來進行數字訊號處理的特殊晶片,音效卡使用它來實現模擬訊號和數字訊號的轉換。音效卡中的DSP裝置實際上包含兩個組成部分:在以只讀方式開啟時,能夠使用A/D轉換器進行聲音的輸入;而在以只寫方式開啟時,則能夠使用D/A轉換器進行聲音的輸出。嚴格說來,Linux下的應用程式要麼以只讀方式開啟/dev/dsp輸入聲音,要麼以只寫方式開啟/dev/dsp輸出聲音,但事實上某些音效卡驅動程式仍允許以讀寫的方式開啟/dev/dsp,以便同時進行聲音的輸入和輸出,這對於某些應用場合(如IP電話)來講是非常關鍵的。

     [QW-錄音] 在從DSP裝置讀取資料時,從音效卡輸入的模擬訊號經過A/D轉換器變成數字取樣後的樣本(sample),儲存在音效卡驅動程式的核心緩衝區中,當應用程式通過read系 統呼叫從音效卡讀取資料時,儲存在核心緩衝區中的數字取樣結果將被複制到應用程式所指定的使用者緩衝區中。需要指出的是,音效卡取樣頻率是由核心中的驅動程式所 決定的,而不取決於應用程式從音效卡讀取資料的速度。如果應用程式讀取資料的速度過慢,以致低於音效卡的取樣頻率,那麼多餘的資料將會被丟棄;如果讀取資料的 速度過快,以致高於音效卡的取樣頻率,那麼音效卡驅動程式將會阻塞那些請求資料的應用程式,直到新的資料到來為止。[QW--那麼我們能不能在應用程式中對取樣頻率進行設定呢?]

      [Qw--放音]在向DSP裝置寫入資料時,數字訊號會經過D/A轉換器變成模擬訊號,然後產生出聲音。應用程式寫入資料的速度同樣應該與音效卡的取樣頻率相匹配,否則過慢的話會產生聲音暫停或者停頓的現象,過快的話又會被核心中的音效卡驅動程式阻塞,直到硬體有能力處理新的資料為止。與其它裝置有所不同,音效卡通常不會支援非阻塞(non-blocking)的I/O操作

      無論是從音效卡讀取資料,或是向音效卡寫入資料,事實上都具有特定的格式(format),預設為8位無符號資料、單聲道、8KHz取樣率,如果預設值無法達到要求,可以通過ioctl系統呼叫來改變它們。通常說來,在應用程式中開啟裝置檔案/dev/dsp之後,接下去就應該為其設定恰當的格式,然後才能從音效卡讀取或者寫入資料。

/dev/audio [這貨也可忽略

     /dev/audio類似於/dev/dsp,它相容於Sun工作站上的音訊裝置,使用的是mu-law編碼方式。如果音效卡驅動程式提供了對/dev/audio的支援,那麼在Linux上就可以通過cat命令,來播放在Sun工作站上用mu-law進行編碼的音訊檔案: 

safrans@safrans-desktop:~$ cat audio.au > /dev/audio

      由於裝置檔案/dev/audio主要出於對相容性的考慮,所以在新開發的應用程式中最好不要嘗試用它,而應該以/dev/dsp進行替代。對於應用程式來說,同一時刻只能使用/dev/audio或者/dev/dsp其中之一,因為它們是相同硬體的不同軟體介面。 

/dev/mixer 

     在音效卡的硬體電路中,混音器(mixer)是一個很重要的組成部分,它的作用是將多個訊號組合或者疊加在一起,對於不同的音效卡來說,其混音器的作用可能各不相同。執行在Linux核心中的音效卡驅動程式一般都會提供/dev/mixer這一裝置檔案,它是應用程式對混音器進行操作的軟體介面。混音器電路通常由兩個部分組成:輸入混音器(input mixer)和輸出混音器(output mixer)。 

    [QW----

                                           [   輸入混音器

                          混音器電路

                                           [  輸出混音器

          -------QW

      輸入混音器負責從多個不同的訊號源接收模擬訊號,這些訊號源有時也被稱為混音通道或者混音裝置。模擬訊號通過增益控制器和由軟體控制的音量調節器後,在不同的混音通道中進行級別(level)調製,然後被送到輸入混音器中進行聲音的合成。混音器上的電子開關可以控制哪些通道中有訊號與混音器相連,有些音效卡只允許連線一個混音通道作為錄音的音源,而有些音效卡則允許對混音通道做任意的連線。經過輸入混音器處理後的訊號仍然為模擬訊號,它們將被送到A/D轉換器進行數字化處理。 

     輸出混音器的工作原理與輸入混音器類似,同樣也有多個訊號源與混音器相連,並且事先都經過了增益調節。當輸出混音器對所有的模擬訊號進行了混合之後, 通常還會有一個總控增益調節器來控制輸出聲音的大小,此外還有一些音調控制器來調節輸出聲音的音調。經過輸出混音器處理後的訊號也是模擬訊號,它們最終會 被送給喇叭或者其它的模擬輸出裝置。對混音器的程式設計包括如何設定增益控制器的級別以及怎樣在不同的音源間進行切換,這些操作通常來講是不連續的,而且不 會像錄音或者放音那樣需要佔用大量的計算機資源。由於混音器的操作不符合典型的讀/寫操作模式,因此除了open和close兩個系統呼叫之外,大部分的操作都是通過ioctl系統呼叫來完成的。與/dev/dsp不同,/dev/mixer允許多個應用程式同時訪問,並且混音器的設定值會一直保持到對應的裝置檔案被關閉為止。 

      為了簡化應用程式的設計,Linux上的音效卡驅動程式大多都支援將混音器的ioctl操作直接應用到聲音裝置上,也就是說如果已經開啟了/dev/dsp,那麼就不用再開啟/dev/mixer來對混音器進行操作,而是可以直接用開啟/dev/dsp時得到的檔案識別符號來設定混音器。 [QW--只開啟DSP檔案就夠了,直接用DSP檔案描述符就可以]


/dev/sequencer [QW----這貨也是不用看滴]

      目前大多數音效卡驅動程式還會提供/dev/sequencer這一裝置檔案,用來對音效卡內建的波表合成器進行操作,或者對MIDI匯流排上的樂器進行控制,一般只用於計算機音樂軟體中。 

相關文章