文章目錄
8 ALSA應(yīng)用開發(fā)
8.1 音頻相關(guān)概念
8.1.1 采樣頻率
8.1.2 量化位數(shù)
8.2 ALSA架構(gòu)
8.2.1 ALSA架構(gòu)介紹
8.3 移植ALSA庫及工具
8.3.1 ALSA庫下載
8.3.2 ALSA Lib編譯
8.3.3 ALSA Util編譯
8.3.4 ALSA庫和工具移植入嵌入式平臺
8.4 ALSA的調(diào)試
8.4.1 amixer
8.4.2 aplay
8.4.3 arecord
8.5 常用接口說明
8.5.1 PCM接口
8.6 基于ALSA的音量控制程序設(shè)計(jì)
8.6.1 程序設(shè)計(jì)
8.6.2 AlsaVolume 類的定義
8.6.3 AlsaVolume類中成員函數(shù)的實(shí)現(xiàn)
8.7 ALSA基類的設(shè)計(jì)
8.7.1 程序設(shè)計(jì)
8.7.2 AlsaBase類中成員函數(shù)的實(shí)現(xiàn)
8.8 基于ALSA音頻的播放
8.8.1 程序設(shè)計(jì)
8.1.2 AlsaPlay類的定義
8.1.3 AlsaPlayback類中成員函數(shù)的實(shí)現(xiàn)
8.9 基于ALSA音頻的錄制
8.9.1 程序設(shè)計(jì)
8.9.2 AlsaPlay類的定義
8.9.3 AlsaCapture類中成員函數(shù)的實(shí)現(xiàn)
8 ALSA應(yīng)用開發(fā)
8.1 音頻相關(guān)概念
? 音頻信號是一種連續(xù)變化的模擬信號,但計(jì)算機(jī)只能處理和記錄二進(jìn)制的數(shù)字信號,由自然音源得到的音頻信號必須經(jīng)過一定的變換,成為數(shù)字音頻信號之后,才能送到計(jì)算機(jī)中作進(jìn)一步的處理。
? 數(shù)字音頻系統(tǒng)通過將聲波的波型轉(zhuǎn)換成一系列二進(jìn)制數(shù)據(jù),來實(shí)現(xiàn)對原始聲音的重現(xiàn),實(shí)現(xiàn)這一步驟的設(shè)備常被稱為(A/D)。A/D轉(zhuǎn)換器以每秒鐘上萬次的速率對聲波進(jìn)行采樣,每個(gè)采樣點(diǎn)都記錄下了原始模擬聲波在某一時(shí)刻的狀態(tài),通常稱之為樣本(sample),而每一秒鐘所采樣的數(shù)目則稱為采樣頻率,通過將一串連續(xù)的樣本連接起來,就可以在計(jì)算機(jī)中描述一段聲音了。對于采樣過程中的每一個(gè)樣本來說,數(shù)字音頻系統(tǒng)會分配一定存儲位來記錄聲波的振幅,一般稱之為采樣分辯率或者采樣精度,采樣精度越高,聲音還原時(shí)就會越細(xì)膩。
? 數(shù)字音頻涉及到的概念非常多,對于在Linux下進(jìn)行音頻編程的程序員來說,最重要的是7406解聲音數(shù)字化的兩個(gè)關(guān)鍵步驟:采樣和量化。
采樣就是每隔一定時(shí)間就讀一次聲音信號的幅度,從本質(zhì)上講,采樣是時(shí)間上的數(shù)字化。
量化則是將采樣得到的聲音信號幅度轉(zhuǎn)換為數(shù)字值,從本質(zhì)上講,量化則是幅度上的數(shù)字化。
8.1.1 采樣頻率
? 采樣頻率是指將模擬聲音波形進(jìn)行數(shù)字化時(shí),每秒鐘抽取聲波幅度樣本的次數(shù)。采樣頻率的選擇應(yīng)該遵循奈奎斯特(Harry Nyquist)采樣理論:如果對某一模擬信號進(jìn)行采樣,則采樣后可還原的最高信號頻率只有采樣頻率的一半,或者說只要采樣頻率高于輸入信號最高頻率的兩倍,就能從采樣信號系列重構(gòu)原始信號。
? 如上圖所示 用40KHz的頻率去采樣20KHz的信號可以正確捕捉到原始信號。用30KHz的頻率去采樣20KHz的信號會出現(xiàn)混淆信號。
? 一般重建音樂信號時(shí)采用的最低采樣頻率為44.1KHz。在許多高品質(zhì)的系統(tǒng)中,采用的48KHz的采樣頻率。
系統(tǒng) | 采樣頻率 |
---|---|
電話 | 8000Hz |
CD | 44100Hz |
專業(yè)音頻 | 48000Hz |
DVD音頻 | 96000Hz |
8.1.2 量化位數(shù)
? 量化位數(shù)是對模擬音頻信號的幅度進(jìn)行數(shù)字化,它決定了模擬信號數(shù)字化以后的動(dòng)態(tài)范圍,常用的有8位、12位和16位。量化位越高,信號的動(dòng)態(tài)范圍越大,數(shù)字化后的音頻信號就越可能接近原始信號,但所需要的存貯空間也越大。
? 音頻應(yīng)用中常用的數(shù)字表示方法為脈沖編碼調(diào)制(Pulse-Code-Modulated,PCM)信號。在這種表示方法中,每個(gè)采樣周期用一個(gè)數(shù)字電平對模擬信號的幅度進(jìn)行編碼。得到的數(shù)字波形是一組采樣自輸入模擬波形的近似值。由于所有A/D轉(zhuǎn)換器的分辨率都是有限的,所以在數(shù)字音頻系統(tǒng)中,A/D轉(zhuǎn)換器帶來的量化噪聲是不可避免的。
8.2 ALSA架構(gòu)
? ALSA全稱是Advanced Linux Sound Architecture,中文音譯是Linux高級聲音體系。ALSA 是Linux內(nèi)核2.6后續(xù)版本中支持音頻系統(tǒng)的標(biāo)準(zhǔn)接口程序,由ALSA庫、內(nèi)核驅(qū)動(dòng)和相關(guān)測 試開發(fā)工具組成,更好的管理Linux中音頻系統(tǒng)。
? 本小節(jié)將介紹ALSA的架構(gòu)。
8.2.1 ALSA架構(gòu)介紹
? ALSA是Linux系統(tǒng)中為聲卡提供驅(qū)動(dòng)的內(nèi)核組件。它提供了專門的庫函數(shù)來簡化相應(yīng)應(yīng)用程序的編寫。相較于OSS的編程接口,ALSA的函數(shù)庫更加便于使用。
? 對應(yīng)用程序而言ALSA無疑是一個(gè)更佳的選擇,因?yàn)樗哂懈佑押玫木幊探涌?并且完全兼容于OSS。
? ALSA系統(tǒng)包括7個(gè)子項(xiàng)目:
驅(qū)動(dòng)包alsa-driver
開發(fā)包alsa-libs
開發(fā)包插件alsa-libplugins
設(shè)置管理工具包alsa-utils
OSS接口兼容模擬層工具alsa-oss
特殊音頻固件支持包alsa-finnware
其他聲音相關(guān)處理小程序包alsa-tools
ALSA聲卡驅(qū)動(dòng)與用戶空間體系結(jié)構(gòu)交互如下圖所示:
8.3 移植ALSA庫及工具
移植ALSA主要是移植alsa-Ub和alsa-utils。
alsa-lib:用戶空間函數(shù)庫, 封裝驅(qū)動(dòng)提供的抽象接口, 通過文件libasound.so提供API給應(yīng)用程序使用。
alsa-utils:實(shí)用工具包,通過調(diào)用alsa-lib實(shí)現(xiàn)播放音頻(aplay)、錄音(arecord) 等工具。
? ALSA Util是純應(yīng)用層的軟件,相當(dāng)于ALSA設(shè)備的測試程序,ALSA-Lib則是支持應(yīng)用API的中間層程序,ALSA-Util中的應(yīng)用程序中會調(diào)用到ALSA-Lib中的接口來操作到我們的音頻編解碼芯片的寄存器,而lib中接口就是依賴于最底層驅(qū)動(dòng)代碼,因此移植ALSA程序的順序就是先后移植Driver,Lib,Util。
8.3.1 ALSA庫下載
? ALSA首先需要在ALSA的官網(wǎng)上下載官網(wǎng)http://www.alsa-project.org下載alsa-lib和alsa-utils。
如上圖所示我們下載的版本為:
alsa-lib-1.2.2.tar.bz2
alsa-utils-1.2.2.tar.bz2
8.3.2 ALSA Lib編譯
? ALSA Lib移植不需要修改源碼,只需要重新編譯庫代碼以支持自己的平臺。
tar -xvf alsa-lib-1.0.27.2.tar.bz2 cd alsa-lib-1.0.27.2 CC=arm-none-linux-gnueabi-gcc ./configure --host=arm-linux --prefix=/home/m/3rd/alsa/install/ make make install
? 在上述命令中./configure配置的幾個(gè)重要的配置選項(xiàng)解釋如下:
–host指定編譯器,這里指定為交叉編譯器,運(yùn)行本配置命令前務(wù)必保證編譯器已經(jīng)可以在Shell下可以直接執(zhí)行了。
–prefix指定編譯后文件的安裝路徑,這樣安裝命令就還會指定的這個(gè)目錄中創(chuàng)建lib和include兩個(gè)目錄。
8.3.3 ALSA Util編譯
? ALSA Util可以生成用于播放,錄制,配置音頻的應(yīng)用可執(zhí)行文件,測試驅(qū)動(dòng)代碼時(shí)用處很大,編譯過程如下:
tar -xvf alsa-utils-1.0.27.2.tar.bz2 cd alsa-utils-1.0.27.2 CC=arm-none-linux-gnueabi-gcc ./configure --prefix=/home/m/3rd/alsa/install/ --host=arm-linux --with-alsa-inc-prefix=/home/m/3rd/alsa/install/include --with-alsa-prefix=/home/m/3rd/alsa/install/lib --disable-alsamixer --disable-xmlto --disable-nls make
8.3.4 ALSA庫和工具移植入嵌入式平臺
? ALSA庫和測試工具的移植就是將相應(yīng)庫文件和可執(zhí)行文件放在目標(biāo)板上,以下文件 必須被拷貝至對應(yīng)位置 :
(1)ALSA Lib文件,放在/lib/中。
(2)配置文件放在/usr/local/share中,與編譯時(shí)指定的目錄相同。
(3)測試應(yīng)用文件,ALSA Util能產(chǎn)生aplay、amixer、arecord,我們可以把這些可執(zhí)行文件放在/usr/sbin中。
(4)內(nèi)核目錄中保證有/dev/snd/目錄,這個(gè)目錄下存放controlC0,pcmC0D0,/usr/sbintimer,timer這些設(shè)備文件,如果這些設(shè)備文件已經(jīng)在/dev目錄下,可手動(dòng)拷貝到/snd目錄中。
? 在LINUX系統(tǒng)中,每個(gè)設(shè)備文件都是文件。音頻設(shè)備也是一樣,它的設(shè)備文件被放在/dev/snd目錄下,我們來看下這些設(shè)備文件:
ls /dev/snd -l crw-rw----+ 1 root audio 116, 2 5月 19 21:24 controlC0 用于聲卡的 crw-rw----+ 1 root audio 116, 4 6月 6 19:31 pcmC0D0c crw-rw----+ 1 root audio 116, 3 6月 11 11:53 pcmC0D0p crw-rw----+ 1 root audio 116, 33 5月 19 21:24 timer
(1)controlC0:音頻控制設(shè)備文件,例如通道選擇,混音,麥克風(fēng)的控制等;
(2)pcmC0D0c:聲卡0設(shè)備0的錄音設(shè)備,c表示capter;
(3)pcmC0D0p:聲卡0設(shè)備0的播音設(shè)備,p表示play;
(4)timer:定時(shí)器設(shè)置。
8.4 ALSA的調(diào)試
? 本小節(jié)將著重講解tinyalsa工具使用,tinyalsa 是 alsa-lib 的一個(gè)簡化版。它提供了 pcm 和 control 的基本接口;沒有太多太復(fù)雜的操作、功能??梢园葱枋褂媒涌凇?tinyalsa-utils 是基于 tinyalsa 的一些工具,下面對幾個(gè)常用的工具作介紹。
8.4.1 amixer
? 與 amixer 作用類似,用于操作 mixer control。
使用方法:
常用選項(xiàng)
選項(xiàng) | 功能 |
---|---|
-D,–device | 指定聲卡設(shè)備, 默認(rèn)使用card0 |
常用命令
命令 | 功能 |
---|---|
controls | 列出指定聲卡的所有控件 |
contents | 列出指定聲卡的所有控件的具體信息 |
get | 獲取指定控件的信息 |
set | 設(shè)定指定控件的值 |
舉例:
獲取audiocodec聲卡的所有控件名 amixer -Dhw:audiocodec controls 獲取當(dāng)前硬件音量 amixer -Dhw:audiocodec cget name='LINEOUT volume' 設(shè)置當(dāng)前硬件音量 amixer -Dhw:audiocodec cget name='LINEOUT volume' 25
8.4.2 aplay
? aplay 是命令行的 ALSA 聲卡驅(qū)動(dòng)的播放工具,用于播放功能。
使用方法:
選項(xiàng) | 功能 |
---|---|
-D,–device | 指定聲卡設(shè)備, 默認(rèn)使用 default |
-l,–list-devices | 列出當(dāng)前所有聲卡 |
-t,–file-type | 指定播放文件的格式, 如 voc,wav,raw, 不指定的情況下會去讀取文件頭部作識別 |
-c,–channels | 指定通道數(shù) |
-f,–format | 指定采樣格式 |
-r,–rate | 采樣率 |
-d,–duration | 指定播放的時(shí)間 |
–period-size | 指定 period size |
–buffer-size | 指定 buffer size |
舉例:
aplay -Dhw:audiocodec /mnt/UDISK/test.wav
8.4.3 arecord
? arecord 是命令行的 ALSA 聲卡驅(qū)動(dòng)的錄音工具,用于錄音功能。
使用方法:
選項(xiàng) | 功能 |
---|---|
-D,–device | 指定聲卡設(shè)備, 默認(rèn)使用 default |
-l,–list-devices | 列出當(dāng)前所有聲卡 |
-t,–file-type | 指定播放文件的格式, 如 voc,wav,raw, 不指定的情況下會去讀取文件頭部作識別 |
-c,–channels | 指定通道數(shù) |
-f,–format | 指定采樣格式 |
-r,–rate | 采樣率 |
-d,–duration | 指定播放的時(shí)間 |
–period-size | 指定 period size |
–buffer-size | 指定 buffer size |
舉例:
錄制5s,通道數(shù)為2, 采樣率為16000, 采樣精度為16bit, 保存為wav文件 arecord -Dhw:audiocodec -f S16_LE -r 16000 -c 2 -d 5 /mnt/UDISK/test.wav
8.5 常用接口說明
? 從代碼角度體現(xiàn)了alsa-lib和alsa-driver及hardwared的交互關(guān)系。用戶層的alsa-lib通過操作alsa-driver創(chuàng)建的設(shè)備文件/dev/snd/pcmC0D0p等對內(nèi)核層進(jìn)行訪問。內(nèi)核層的alsa-drivier驅(qū)動(dòng)再經(jīng)由sound core對硬件聲卡芯片進(jìn)行訪問。
8.5.1 PCM接口
? 為了方便操作訪問, alsa-lib 中封裝了相關(guān)接口, 通過 pcmCXDXp/pcmCXDXc 節(jié)點(diǎn) (/dev/snd/pcmCXDXx) 去實(shí)現(xiàn)播放、錄音功能。
? 主要涉及到的接口:
函數(shù)名 | 解釋 |
---|---|
snd_pcm_open | |
snd_pcm_info | |
snd_pcm_hw_params_any | |
snd_pcm_hw_params_set_access | |
snd_pcm_hw_params_set_format | |
snd_pcm_hw_params_set_channels | |
snd_pcm_hw_params_set_rate_near | |
snd_pcm_hw_params_set_buffer_size_near | |
snd_pcm_hw_params | |
snd_pcm_sw_params_current | |
snd_pcm_sw_params | |
snd_pcm_readi | |
snd_pcm_writei | |
snd_pcm_close |
? 詳細(xì) pcm 接口說明請查閱:
https://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html
https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html
8.6 基于ALSA的音量控制程序設(shè)計(jì)
8.6.1 程序設(shè)計(jì)
文件列表:
序號 | 文件名 | 描述 |
---|---|---|
1 | AlsaVolume.h | 音量控制頭文件 |
2 | AlsaVolume.cpp | 音量控制程序 |
成員函數(shù)設(shè)計(jì):
序號 | 函數(shù)名 | 參數(shù) | 參數(shù)描述 | 函數(shù)描述 |
---|---|---|---|---|
1 | setMasterVolume | long volume | 音量值 | 設(shè)置音量 |
2 | getCurrentVolume | 無 | 無 | 獲取當(dāng)前音量 |
3 | increaseVolume | 無 | 無 | 單步減小音量接口函數(shù) |
4 | decreaseVolume | 無 | 無 | 單步增加音量接口函數(shù) |
成員變量設(shè)計(jì):
序號 | 成員變量名 | 類型 | 描述 |
---|---|---|---|
1 | _VOLUMECHANGE | const float | 音量調(diào)節(jié)步進(jìn)大小 |
2 | handle | snd_mixer_t* | Mixer handle |
3 | element_handle | snd_mixer_elem_t* | Mixer element handle |
4 | minVolume | long | 最小音量 |
5 | maxVolume | long | 最大音量 |
8.6.2 AlsaVolume 類的定義
#pragma once #include namespace rv1108_audio{ class AlsaVolume { public: AlsaVolume(); ~AlsaVolume(); int setMasterVolume(long volume); long getCurrentVolume(); long increaseVolume(); long decreaseVolume(); protected: const float _VOLUMECHANGE = 5; private: snd_mixer_t* handle = nullptr; snd_mixer_elem_t* element_handle = nullptr; long minVolume,maxVolume; }; }// namespace rv1108_camera
8.6.3 AlsaVolume類中成員函數(shù)的實(shí)現(xiàn)
AlsaVolume類的構(gòu)造函數(shù)
AlsaVolume::AlsaVolume() { snd_mixer_selem_id_t* sid = NULL; const char* card = "default"; const char* selem_name = "Playback"; //1. 打開混音設(shè)備 auto res = snd_mixer_open(&handle, 0); //2. attach HCTL to open mixer res = snd_mixer_attach(handle, card); //3. Register mixer simple element class. snd_mixer_selem_register(handle, NULL, NULL); //4. 取得第一個(gè) element,也就是 Master snd_mixer_load(handle); //5. allocate an invalid snd_mixer_selem_id_t using standard alloca snd_mixer_selem_id_alloca(&sid); //6. 設(shè)置元素ID的位置 snd_mixer_selem_id_set_index(sid, 0); //7. 設(shè)置元素ID的名字 snd_mixer_selem_id_set_name(sid, selem_name); //8. 查找元素 element_handle = snd_mixer_find_selem(handle, sid); res = snd_mixer_selem_get_playback_volume_range(element_handle, &minVolume, &maxVolume); }
設(shè)置音量函數(shù)
int AlsaVolume::setMasterVolume(long volume) { long alsaVolume = volume * (maxVolume - minVolume) / 100 ; if(snd_mixer_selem_set_playback_volume_all(element_handle, alsaVolume) < 0){ if(handle) snd_mixer_close(handle); return -1; } return 0; }
獲取當(dāng)前音量函數(shù)
long AlsaVolume::getCurrentVolume() { long alsaVolume; if(snd_mixer_selem_get_playback_volume(element_handle, SND_MIXER_SCHN_MONO, &alsaVolume) < 0){ if(handle) snd_mixer_close(handle); return -1; } return (alsaVolume*100)/(maxVolume - minVolume); }
音量步進(jìn)減少函數(shù)
long AlsaVolume::decreaseVolume() { long newVolume = 0; if (getCurrentVolume() >= 0 + _VOLUMECHANGE) // check that we won't go below minimum volume newVolume = getCurrentVolume() - _VOLUMECHANGE; else newVolume = 0; setMasterVolume(newVolume); return newVolume; }
音量步進(jìn)增加函數(shù)
long AlsaVolume::increaseVolume() { long newVolume = 0; if (getCurrentVolume() <= 100 - _VOLUMECHANGE) // check that we don't go above the max volume newVolume = getCurrentVolume() + _VOLUMECHANGE; else newVolume = 100; setMasterVolume(newVolume); return newVolume; }
8.7 ALSA基類的設(shè)計(jì)
8.7.1 程序設(shè)計(jì)
文件列表:
序號 | 文件名 | 描述 |
---|---|---|
1 | AlsaBase.h | ALSA基類頭文件 |
2 | AlsaBase.cpp | 基類的實(shí)現(xiàn)程序 |
public成員變量:
序號 | 成員變量名 | 類型 | 描述 |
---|---|---|---|
1 | rate | int | 碼率 |
2 | channels | int | 通道數(shù) |
3 | bits_per_frame | mutable int | 每幀數(shù)據(jù)大小 |
4 | default_output_buffer_size | int | 默認(rèn)輸出緩存大小 |
5 | frames | snd_pcm_uframes_t | 幀數(shù) |
6 | buffer_size | snd_pcm_uframes_t | 緩存大小 |
7 | buffer_frames | snd_pcm_uframes_t | 緩存大小 |
8 | period_size | snd_pcm_uframes_t | 時(shí)間段大小 |
9 | period_frames | snd_pcm_uframes_t | |
10 | period_time | unsigned int | |
11 | buffer_time | unsigned int | |
12 | bits_per_sample | size_t |
protected成員變量:
序號 | 成員變量名 | 類型 | 描述 |
---|---|---|---|
1 | device | const char * | |
2 | handle | snd_pcm_t * | |
3 | params | snd_pcm_hw_params_t * | |
4 | format | snd_pcm_format_t | |
5 | access_type | snd_pcm_access_t | |
6 | DEVICE_OPENED | bool | |
7 | PARAMS_SETED | bool |
8.7.2 AlsaBase類中成員函數(shù)的實(shí)現(xiàn)
AlsaBase類的構(gòu)造函數(shù)
AlsaBase::AlsaBase(const std::string &dev) { device = dev.c_str(); rate = 8000; channels = 2; format = SND_PCM_FORMAT_S16_LE; access_type = SND_PCM_ACCESS_RW_INTERLEAVED; frames = 480; DEVICE_OPENED = false; PARAMS_SETED = false; bits_per_sample = snd_pcm_format_physical_width(format); bits_per_frame = (bits_per_sample >> 3) * channels; default_output_buffer_size = frames * bits_per_frame / 8; // in byte buffer_frames = frames * 8; buffer_time = 0; period_frames = buffer_frames / 4; period_time = 0; } AlsaBase::~AlsaBase() { if (DEVICE_OPENED){ if((err = snd_pcm_close(handle)) < 0){ ; }else{ ; } } } int AlsaBase::set_params() { if (!DEVICE_OPENED) return -1; // 分配硬件參數(shù)空間 snd_pcm_hw_params_alloca(?ms); //1、以默認(rèn)值填充硬件參數(shù) if ((err = snd_pcm_hw_params_any(handle, params)) < 0) { return err; } //2、 Restrict a configuration space to contain only real hardware rates. if ((err = snd_pcm_hw_params_set_rate_resample(handle, params, 0)) < 0) { return err; } //3、設(shè)置存取方式 if ((err = snd_pcm_hw_params_set_access(handle, params, access_type)) < 0) { return err; } //4、設(shè)置格式,S16_LE等 if ((err = snd_pcm_hw_params_set_format(handle, params, format)) < 0) { return err; } //5 設(shè)置通道 if ((err = snd_pcm_hw_params_set_channels(handle, params, channels)) < 0) { return err; } //6 設(shè)置碼率 unsigned int rrate; rrate =rate; if ((err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, NULL)) < 0) { return err; } //7 if (buffer_time == 0 && buffer_frames == 0) { err = snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0); assert(err >= 0); if (buffer_time > 500000) buffer_time = 500000; } //8 if (period_time == 0 && period_frames == 0) { if (buffer_time > 0) period_time = buffer_time / 4; else period_frames = buffer_frames / 4; } //9 if (period_time > 0) { err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, 0); } else { err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_frames, 0); } assert(err >= 0); //10 if (buffer_time > 0) { err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, 0); } else { err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_frames); } assert(err >= 0); // 將參數(shù)寫入設(shè)備 if ((err = snd_pcm_hw_params(handle, params)) < 0) { return -1; } else { PARAMS_SETED = true; } snd_pcm_uframes_t t_buffer_frames; snd_pcm_hw_params_get_buffer_size(params, &t_buffer_frames); buffer_frames = t_buffer_frames; snd_pcm_uframes_t t_period_frames; snd_pcm_hw_params_get_period_size(params, &t_period_frames, 0); period_frames = t_period_frames; return 0; }
8.8 基于ALSA音頻的播放
8.8.1 程序設(shè)計(jì)
文件列表
序號 | 文件名 | 描述 |
---|---|---|
1 | AlsaPlayback.h | 音頻播放控制頭文件 |
2 | AlsaPlayback.cpp | 音頻播放程序 |
成員函數(shù)設(shè)計(jì)
序號 | 函數(shù)名 | 參數(shù) | 參數(shù)描述 | 函數(shù)描述 |
---|---|---|---|---|
1 | playback |
const char *input_buffer const long input_buffer_size |
播放音頻 |
8.1.2 AlsaPlay類的定義
#pragma once #include "AlsaBase.h" namespace rv1108_audio{ class AlsaPlayback : public AlsaBase { public: AlsaPlayback(const std::string &dev); ~AlsaPlayback(); int open_device(); int playback(const char *input_buffer, const long input_buffer_size) const; private: int err; }; }
8.1.3 AlsaPlayback類中成員函數(shù)的實(shí)現(xiàn)
AlsaPlayback類的構(gòu)造函數(shù)
AlsaPlayback::AlsaPlayback(const std::string &dev) : AlsaBase(dev) { if (!DEVICE_OPENED) open_device(); }
int AlsaPlayback::open_device() { if(snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0) < 0) { DEVICE_OPENED = false; } else { DEVICE_OPENED = true; } return 0; }
playback函數(shù)的實(shí)現(xiàn)
int AlsaPlayback::playback(const char *_input_buffer, const long input_buffer_size) const { int res = -1; char *input_buffer = const_cast(_input_buffer); long r = input_buffer_size / bits_per_frame * 8; AUDIO_DEV_LOCK; while (r > 0) { snd_pcm_wait(handle, 100); do { res = snd_pcm_writei(handle, input_buffer, frames); if (res == -EPIPE){ AUDIO_DEV_UNLOCK; snd_pcm_prepare(handle); continue; } }while (res < 0); r -= err; input_buffer += res * bits_per_frame / 8; } return 0; }
8.9 基于ALSA音頻的錄制
8.9.1 程序設(shè)計(jì)
文件列表
序號 | 文件名 | 描述 |
---|---|---|
1 | AlsaCapture.h | 音頻錄制頭文件 |
2 | AlsaCapture.cpp | 音頻錄制程序 |
成員函數(shù)設(shè)計(jì)
序號 | 函數(shù)名 | 參數(shù) | 參數(shù)描述 | 函數(shù)描述 |
---|---|---|---|---|
1 | capture | 無 | 錄制音頻 |
成員變量設(shè)計(jì)
序號 | 成員變量名 | 類型 | 描述 |
---|---|---|---|
1 | _VOLUMECHANGE | const float | 音量調(diào)節(jié)步進(jìn)大小 |
2 | handle | snd_mixer_t* | Mixer handle |
3 | element_handle | snd_mixer_elem_t* | Mixer element handle |
4 | minVolume | long | 最小音量 |
5 | maxVolume | long | 最大音量 |
8.9.2 AlsaPlay類的定義
#pragma once #include "AlsaBase.h" namespace rv1108_audio{ class AlsaCapture : public AlsaBase { public: // 輸出數(shù)據(jù)緩存 char *output_buffer; // 輸出緩存大小 unsigned int output_buffer_size; // int frames_to_read; // 用于返回已讀的幀數(shù) int frames_readed; AlsaCapture(const std::string &dev); ~AlsaCapture(); int open_device(); int capture(); private: int err; }; }
8.9.3 AlsaCapture類中成員函數(shù)的實(shí)現(xiàn)
AlsaCapture類的構(gòu)造函數(shù)
AlsaCapture::AlsaCapture(const std::string &dev) : AlsaBase(dev) { if (!DEVICE_OPENED) open_device(); if (!PARAMS_SETED) set_params(); output_buffer_size = default_output_buffer_size; output_buffer = (char *)calloc(output_buffer_size, sizeof(char)); }
int AlsaCapture::open_device() { if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) { DEVICE_OPENED = false; return -1; } else { DEVICE_OPENED = true; } return 0; }
AlsaCapture類的構(gòu)造函數(shù)
int AlsaCapture::capture() { while (1) { int err; if ((frames_readed = snd_pcm_readi(handle, output_buffer, frames)) < 0) { // Overrun happened if (frames_readed == -EPIPE) { snd_pcm_prepare(handle); continue; } return -1; } else { return frames_readed; } } } 審核編輯 黃昊宇
-
Linux
+關(guān)注
關(guān)注
87文章
11304瀏覽量
209542 -
alsa
+關(guān)注
關(guān)注
0文章
19瀏覽量
3618
發(fā)布評論請先 登錄
相關(guān)推薦
評論