關於ALSA,網上也有介紹,可是我在看的時候看的也是一臉懵逼,不是介紹的很差,是由於我以前對於嵌入式軟件這一塊實在沒什麼瞭解,以前一直學的JAVA,整個體系跟JAVA仍是有很大的區別,要學的也徹底是我以前沒了解過的,因此如下有說錯的請及時糾正。html
實如今linux中經過編程.C文件播放一個.wav格式的音頻文件linux
播放: 將音頻文件進行解碼(Decode)生成PCM數據, 並將其送入音頻設備中播出.git
錄音: 本程序暫時不涉及錄音功能github
關於ALSA我不過多介紹,這篇筆記主要是記錄我如何成功播放音樂,主要是怕誤導別人,在我看來就是向上提供了接口供咱們使用,向下控制了硬件播放音樂,跟JAVA中的JDK提供的接口函數相似,你只管使用就能夠了。編程
還有一個是ALSA的官方的教程好像是,播放音樂總體順序我也是參考的這篇文章來寫的。ubuntu
關於ALSA中有一些術語着實是讓我懵逼的一批,有幾個術語我到如今還不知道理解的對不對,因此在正式編程以前必定要先知道,等本身編程的時候再理解一下。緩存
由於聲音是連續模擬量,計算機將它離散化以後用數字表示,就有了如下幾個名詞術語bash
這個樣本長度後面編程時會用到的,按照字面意思理解的話,就是取出來8bit或者16bit的數據作樣本;就理解成一個樣本就能夠,只不過一個樣本的大小是8bit或者16bit,或者其餘大小。架構
這個術語也好理解,單聲道應該就是隻有一個左耳機或右出聲音,而立體就是左右耳機都出聲的意思;每個通道都有一個樣本長度,單聲道的數據就是一個樣本長度(樣本),立體聲道的話2個樣本長度(樣本)。函數
幀單位,把全部通道中的數據加在一塊兒叫作一幀,因此單聲道:一幀 = 樣本長度 * 1;雙聲道:一幀 = 樣本長度 * 2
這個在編程時也比較好設置,沒什麼混淆的,固定的值也就那麼幾個,通常沒有隨便亂設置的數值
一個週期用來存放若干個幀的單元,ALSA函數是以一個週期爲單位來讀取音頻數據的,其實這個週期如今我也沒很好的理解到底什麼個意思, 是說ALSA向硬件輸入數據時,是以時間爲週期,在這個時間週期中輸入若干個幀呢? 仍是說就是一個固定存儲空間大小叫作週期,而後這個週期大小的空間放入若干個幀? 真是讓人頭大!!!!!!!
一個緩衝區通常有兩個週期,緩衝區是循環讀取的,好比一個緩衝區有兩個週期,那麼硬件在讀取一個緩衝區時便會產生兩次中斷,當第一個週期的音頻數據被取走就準備取第二個週期的音頻數據,同時第三個週期的音頻數據會填充到第一個音頻數據的位置,以此循環
交錯模式與非交錯模式只是音頻數據存放在緩衝區時的一種方式, 在交錯模式下,數據以連續楨的形式存放,即首先記錄完楨1的左聲道樣本和右聲道樣本(假設爲立體聲格式),再開始楨2的記錄。 而在非交錯模式下,首先記錄的是一個週期內全部楨的左聲道樣本,再記錄右聲道樣本,數據是以連續通道的方式存儲。
看到上面的術語真是讓人頭大,可是不理解這些,等編程的時候也是瞎子摸象--一通亂摸,因此務必要先理解了這些術語,若是實在看不下去這術語,能夠編程的時候走到哪一步時再回過來一一對照理解也能夠。
在用戶空間使用ALSA須要安裝依賴
alsa-lib: 用戶空間函數庫, 封裝驅動提供的抽象接口, 經過文件libasound.so提供API給應用程序使用
alsa-utils: 實用工具包,經過調用alsa-lib實現播放音頻(aplay)、錄音(arecord) 等工具
ubuntu 安裝
$ sudo apt-get update
$ sudo apt-get install alsa-lib alsa-utils
複製代碼
Arch 安裝
$ sudo pacman -Sy alsa-lib alsa-utils glibc
複製代碼
安裝好之後須要運行 aplay -l 確認當前用戶可使用聲卡設備
$ aplay -l
複製代碼
若是沒有顯示圖片中的內容,能夠切換到root用戶試一下,或者把當前用戶加入到音頻組也能夠
$ gpasswd -a [user_name] audio
# 記得註銷一下,或者切換用戶後再切換回來,經過下面的命令能夠查看當前用戶是否已經加入audio組
$ groups [user_name]
# 再運行查看聲卡設備是否有了
$ aplay -l
複製代碼
代碼我已經上傳到github,可自行下載後運行。
文件分爲:
在C源碼文件中我每一步都有註釋,使用的函數均可以在參考中的函數接口文檔中找到。
在源碼中我有一些本身的理解,若是以爲個人註釋對你起不到輔助做用能夠刪除。
在alsa中有這麼兩個函數 snd_pcm_hw_params_set_buffer_size函數 和 snd_pcm_writei 函數,在這以前我對這兩個函數的第三個參數就特別混淆,不知道該設置什麼,如今我有點明白了
首先這兩個函數的第三個參數都是傳入以 幀 爲單位的值,因此根本就是同一個值,第一個函數設置的是每次硬件能夠向多大的緩存中拿數據,第二個是應用層向多大的緩存輸入多少數據,這裏的數據都是 幀,因此我輸入的就是你拿的數據,那可不就是一個東西嘛
因此buff_size大小就無所謂了,一般爲 buffer_size = period_size * periods
最主要的是第三個參數frame如何計算,也特別好計算,就好像 RMB和美圓的換算是一個道理
buffer_size = RMB
frame(幀) = 美圓
錢的是1:7,在計算機中按照字節換算,首先要知道一幀(frame)等於到少字節,才能換算,如下使用16bit採樣長度換算,若是是其餘採樣長度換掉採樣長度便可。
# 這裏除以8 是由於 1字節 = 8bit
1 幀(frame) = 採樣長度 * 通道數 = 16 * 2 / 8 = 4
# 知道一幀的大小後,就能夠計算總共的幀大小了,右移兩位至關於除以4
frames = buffer_size >> 2;
複製代碼
通過計算的frames 就是要傳入兩個函數的第三個參數的值了。
我本身是在樹莓派4上編譯使用的,進入到文件夾後,直接運行 make 命令便可在本目錄出現可執行文件 alsa
$ git clone https://github.com/ywhs/rpi-arm64.git
$ cd ALSA
$ make
# -m 參數的意思音樂文件,-f 是術語中提到的樣本長度,-r 是術語中提到的採樣率
$ ./alsa -m 1.wav -f 161 -r 44
# 161 是 S16_LE 162 是 S16_BE,以此類推,或者直接運行 ./alsa 查看均可以設置什麼值
$ ./alsa
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Either 1'st, 2'nd, 3'th or all parameters were missing 1'st : -m [music_filename]
music_filename.wav
2'nd : -f [format 241bit or 16bit or 32bit] 161 for S16_LE, 162 for S16_BE 241 for S24_LE, 242 for S24_BE 2431 for S24_3LE, 2432 for S24_3BE 321 for S32_LE, 322 for S32_BE 3'th : -r [rate,44 or 88]
44 for 44100hz
82 for 88200hz
For example: alsa -m 1.wav -f 161 -r 44
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
複製代碼
通常不報錯的話把耳機插入樹莓派的耳機插口就能夠聽到音樂了