(1)數字音頻
音頻信號是一種連續變化的模擬信號,但計算機只能處理和記錄二進制的數字信號,由自然音源得到的音頻信號必須經過一定的變換,成為數字音頻信號之後,才能送到計算機中作進一步的處理。
數字音頻系統通過將聲波的波型轉換成一系列二進制數據,來實現對原始聲音的重現,實現這一步驟的設備常被稱為模/數轉換器(A/D)。A/D轉換器以每秒鐘上萬次的速率對聲波進行采樣,每個采樣點都記錄下了原始模擬聲波在某一時刻的狀態,通常稱之為樣本(sample),而每一秒鐘所采樣的數目則稱為采樣頻率,通過將一串連續的樣本連接起來,就可以在計算機中描述一段聲音了。對於采樣過程中的每一個樣本來說,數字音頻系統會分配一定存儲位來記錄聲波的振幅,一般稱之為采樣分辯率或者采樣精度,采樣精度越高,聲音還原時就會越細膩。
數字音頻涉及到的概念非常多,對於在Linux下進行音頻編程的程序員來說,最重要的是理解聲音數字化的兩個關鍵步驟:采樣和量化。采樣就是每隔一定時間就讀一次聲音信號的幅度,而量化則是將采樣得到的聲音信號幅度轉換為數字值,從本質上講,采樣是時間上的數字化,而量化則是幅度上的數字化。
下面介紹幾個在進行音頻編程時經常需要用到的技術指標:
采樣頻率
采樣頻率是指將模擬聲音波形進行數字化時,每秒鐘抽取聲波幅度樣本的次數。采樣頻率的選擇應該遵循奈奎斯特(Harry Nyquist)采樣理論:如果對某一模擬信號進行采樣,則采樣後可還原的最高信號頻率只有采樣頻率的一半,或者說只要采樣頻率高於輸入信號最高頻率的兩倍,就能從采樣信號系列重構原始信號。正常人聽覺的頻率范圍大約在20Hz~20kHz之間,根據奈奎斯特采樣理論,為了保證聲音不失真,采樣頻率應該在40kHz左右。常用的音頻采樣頻率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果采用更高的采樣頻率,還可以達到DVD的音質。
量化位數
量化位數是對模擬音頻信號的幅度進行數字化,它決定了模擬信號數字化以後的動態范圍,常用的有8位、12位和16位。量化位越高,信號的動態范圍越大,數字化後的音頻信號就越可能接近原始信號,但所需要的存貯空間也越大。
聲道數
聲道數是反映音頻數字化質量的另一個重要因素,它有單聲道和雙聲道之分。雙聲道又稱為立體聲,在硬件中有兩條線路,音質和音色都要優於單聲道,但數字化後占據的存儲空間的大小要比單聲道多一倍。
(2)聲卡驅動
出於對安全性方面的考慮,Linux下的應用程序無法直接對聲卡這類硬件設備進行操作,而是必須通過內核提供的驅動程序才能完成。在Linux上進行音頻編程的本質就是要借助於驅動程序,來完成對聲卡的各種操作。
對硬件的控制涉及到寄存器中各個比特位的操作,通常這是與設備直接相關並且對時序的要求非常嚴格,如果這些工作都交由應用程序員來負責,那麼對聲卡的編程將變得異常復雜而困難起來,驅動程序的作用正是要屏蔽硬件的這些底層細節,從而簡化應用程序的編寫。目前Linux下常用的聲卡驅動程序主要有兩種:OSS和ALSA。
最早出現在Linux上的音頻編程接口是OSS(Open Sound System),它由一套完整的內核驅動程序模塊組成,可以為絕大多數聲卡提供統一的編程接口。OSS出現的歷史相對較長,這些內核模塊中的一部分(OSS/Free)是與Linux內核源碼共同免費發布的,另外一些則以二進制的形式由4Front Technologies公司提供。由於得到了商業公司的鼎力支持,OSS已經成為在Linux下進行音頻編程的事實標准,支持OSS的應用程序能夠在絕大多數聲卡上工作良好。
雖然OSS已經非常成熟,但它畢竟是一個沒有完全開放源代碼的商業產品,ALSA(Advanced Linux Sound Architecture)恰好彌補了這一空白,它是在Linux下進行音頻編程時另一個可供選擇的聲卡驅動程序。ALSA除了像OSS那樣提供了一組內核驅動程序模塊之外,還專門為簡化應用程序的編寫提供了相應的函數庫,與OSS提供的基於ioctl的原始編程接口相比,ALSA函數庫使用起來要更加方便一些。ALSA的主要特點有:
支持多種聲卡設備
模塊化的內核驅動程序
支持SMP和多線程
提供應用開發函數庫
兼容OSS應用程序
ALSA和OSS最大的不同之處在於ALSA是由志願者維護的自由項目,而OSS則是由公司提供的商業產品,因此在對硬件的適應程度上OSS要優於ALSA,它能夠支持的聲卡種類更多。ALSA雖然不及OSS運用得廣泛,但卻具有更加友好的編程接口,並且完全兼容於OSS,對應用程序員來講無疑是一個更佳的選擇。
(3)編程接口
Linux下的聲音設備編程比大多數人想象的要簡單得多。一般說來,www.linuxidc.com 我們常用的聲音設備是內部揚聲器和聲卡,它們都對應/dev目錄下的一個或多個設備文件,我們象打開普通文件一樣打開它們,用ioctl()函數設置一些參數,然後對這些打開的特殊文件進寫操作。
由於這些文件不是普通的文件,所以我們不能用ANSI C(標准C)的fopen、fclose等來操作文件,而應該使用系統文件I/O處理函數(open、read、write、lseek和close)來處理這些設備文件。ioctl()或許是Linux下最龐雜的函數,它可以控制各種文件的屬性,在Linux聲音設備編程中,最重要的就是使用此函數正確設置必要的參數。
如何對各種音頻設備進行操作是在Linux上進行音頻編程的關鍵,通過內核提供的一組系統調用,應用程序能夠訪問聲卡驅動程序提供的各種音頻設備接口,這是在Linux下進行音頻編程最簡單也是最直接的方法。
(A)訪問音頻設備
無論是OSS還是ALSA,都是以內核驅動程序的形式運行在Linux內核空間中的,應用程序要想訪問聲卡這一硬件設備,必須借助於Linux內核所提供的系統調用(system call)。從程序員的角度來說,對聲卡的操作在很大程度上等同於對磁盤文件的操作:首先使用open系統調用建立起與硬件間的聯系,此時返回的文件描述符將作為隨後操作的標識;接著使用read系統調用從設備接收數據,或者使用write系��調用向設備寫入數據,而其它所有不符合讀/寫這一基本模式的操作都可以由ioctl系統調用來完成;最後,使用close系統調用告訴Linux內核不會再對該設備做進一步的處理。
open系統調用
系統調用open可以獲得對聲卡的訪問權,同時還能為隨後的系統調用做好准備,其函數原型如下所示:
[cpp]參數pathname是將要被打開的設備文件的名稱,對於聲卡來講一般是/dev/dsp。參數flags用來指明應該以什麼方式打開設備文件,它可以是O_RDONLY、O_WRONLY或者O_RDWR,分別表示以只讀、只寫或者讀寫的方式打開設備文件;參數mode通常是可選的,它只有在指定的設備文件不存在時才會用到,指明新創建的文件應該具有怎樣的權限。
如果open系統調用能夠成功完成,它將返回一個正整數作為文件標識符,在隨後的系統調用中需要用到該標識符。如果open系統調用失敗,它將返回-1,同時還會設置全局變量errno,指明是什麼原因導致了錯誤的發生。
read系統調用
系統調用read用來從聲卡讀取數據,其函數原型如下所示:
參數fd是設備文件的標識符,它是通過之前的open系統調用獲得的;參數buf是指向緩沖區的字符指針,它用來保存從聲卡獲得的數據;參數count則用來限定從聲卡獲得的最大字節數。如果read系統調用成功完成,它將返回從聲卡實際讀取的字節數,通常情況會比count的值要小一些;如果read系統調用失敗,它將返回-1,同時還會設置全局變量errno,來指明是什麼原因導致了錯誤的發生。
write系統調用
系統調用write用來向聲卡寫入數據,其函數原型如下所示:
[cpp]系統調用write和系統調用read在很大程度是類似的,差別只在於write是向聲卡寫入數據,而read則是從聲卡讀入數據。參數fd同樣是設備文件的標識符,它也是通過之前的open系統調用獲得的;參數buf是指向緩沖區的字符指針,它保存著即將向聲卡寫入的數據;參數count則用來限定向聲卡寫入的最大字節數。
如果write系統調用成功完成,它將返回向聲卡實際寫入的字節數;如果read系統調用失敗,它將返回-1,同時還會設置全局變量errno,來指明是什麼原因導致了錯誤的發生。無論是read還是write,一旦調用之後Linux內核就會阻塞當前應用程序,直到數據成功地從聲卡讀出或者寫入為止。
ioctl系統調用
系統調用ioctl可以對聲卡進行控制,凡是對設備文件的操作不符合讀/寫基本模式的,都是通過ioctl來完成的,它可以影響設備的行為,或者返回設備的狀態,其函數原型如下所示:
[cpp]
參數fd是設備文件的標識符,它是在設備打開時獲得的;如果設備比較復雜,那麼對它的控制請求相應地也會有很多種,參數request的目的就是用來區分不同的控制請求;通常說來,在對設備進行控制時還需要有其它參數,這要根據不同的控制請求才能確定,並且可能是與硬件設備直接相關的。
close系統調用
當應用程序使用完聲卡之後,需要用close系統調用將其關閉,以便及時釋放占用的硬件資源,其函數原型如下所示:
[cpp]
參數fd是設備文件的標識符,它是在設備打開時獲得的。一旦應用程序調用了close系統調用,Linux內核就會釋放與之相關的各種資源,因此建議在不需要的時候盡量及時關閉已經打開的設備。