@[TOC]
8 ALSA應(yīng)用開發(fā)
8.1 音頻相關(guān)概念
? 音頻信號(hào)是一種連續(xù)變化的模擬信號(hào),但計(jì)算機(jī)只能處理和記錄二進(jìn)制的數(shù)字信號(hào),由自然音源得到的音頻信號(hào)必須經(jīng)過一定的變換,成為數(shù)字音頻信號(hào)之后,才能送到計(jì)算機(jī)中作進(jìn)一步的處理。
? 數(shù)字音頻系統(tǒng)通過將聲波的波型轉(zhuǎn)換成一系列二進(jìn)制數(shù)據(jù),來實(shí)現(xiàn)對(duì)原始聲音的重現(xiàn),實(shí)現(xiàn)這一步驟的設(shè)備常被稱為(A/D)。A/D轉(zhuǎn)換器以每秒鐘上萬次的速率對(duì)聲波進(jìn)行采樣,每個(gè)采樣點(diǎn)都記錄下了原始模擬聲波在某一時(shí)刻的狀態(tài),通常稱之為樣本(sample),而每一秒鐘所采樣的數(shù)目則稱為采樣頻率,通過將一串連續(xù)的樣本連接起來,就可以在計(jì)算機(jī)中描述一段聲音了。對(duì)于采樣過程中的每一個(gè)樣本來說,數(shù)字音頻系統(tǒng)會(huì)分配一定存儲(chǔ)位來記錄聲波的振幅,一般稱之為采樣分辯率或者采樣精度,采樣精度越高,聲音還原時(shí)就會(huì)越細(xì)膩。
? 數(shù)字音頻涉及到的概念非常多,對(duì)于在Linux下進(jìn)行音頻編程的程序員來說,最重要的是7406解聲音數(shù)字化的兩個(gè)關(guān)鍵步驟:采樣和量化。
-
采樣就是每隔一定時(shí)間就讀一次聲音信號(hào)的幅度,從本質(zhì)上講,采樣是時(shí)間上的數(shù)字化。
- 量化則是將采樣得到的聲音信號(hào)幅度轉(zhuǎn)換為數(shù)字值,從本質(zhì)上講,量化則是幅度上的數(shù)字化。
8.1.1 采樣頻率
? 采樣頻率是指將模擬聲音波形進(jìn)行數(shù)字化時(shí),每秒鐘抽取聲波幅度樣本的次數(shù)。采樣頻率的選擇應(yīng)該遵循奈奎斯特(Harry Nyquist)采樣理論:如果對(duì)某一模擬信號(hào)進(jìn)行采樣,則采樣后可還原的最高信號(hào)頻率只有采樣頻率的一半,或者說只要采樣頻率高于輸入信號(hào)最高頻率的兩倍,就能從采樣信號(hào)系列重構(gòu)原始信號(hào)。
? 如上圖所示 用40KHz的頻率去采樣20KHz的信號(hào)可以正確捕捉到原始信號(hào)。用30KHz的頻率去采樣20KHz的信號(hào)會(huì)出現(xiàn)混淆信號(hào)。
? 一般重建音樂信號(hào)時(shí)采用的最低采樣頻率為44.1KHz。在許多高品質(zhì)的系統(tǒng)中,采用的48KHz的采樣頻率。
系統(tǒng) | 采樣頻率 |
---|---|
電話 | 8000Hz |
CD | 44100Hz |
專業(yè)音頻 | 48000Hz |
DVD音頻 | 96000Hz |
8.1.2 量化位數(shù)
? 量化位數(shù)是對(duì)模擬音頻信號(hào)的幅度進(jìn)行數(shù)字化,它決定了模擬信號(hào)數(shù)字化以后的動(dòng)態(tài)范圍,常用的有8位、12位和16位。量化位越高,信號(hào)的動(dòng)態(tài)范圍越大,數(shù)字化后的音頻信號(hào)就越可能接近原始信號(hào),但所需要的存貯空間也越大。
? 音頻應(yīng)用中常用的數(shù)字表示方法為脈沖編碼調(diào)制(Pulse-Code-Modulated,PCM)信號(hào)。在這種表示方法中,每個(gè)采樣周期用一個(gè)數(shù)字電平對(duì)模擬信號(hào)的幅度進(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高級(jí)聲音體系。ALSA 是Linux內(nèi)核2.6后續(xù)版本中支持音頻系統(tǒng)的標(biāo)準(zhǔn)接口程序,由ALSA庫、內(nèi)核驅(qū)動(dòng)和相關(guān)測(cè) 試開發(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ù)庫更加便于使用。
? 對(duì)應(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è)備的測(cè)試程序,ALSA-Lib則是支持應(yīng)用API的中間層程序,ALSA-Util中的應(yīng)用程序中會(huì)調(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移植不需要修改源碼,只需要重新編譯庫代碼以支持自己的平臺(tái)。
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指定編譯后文件的安裝路徑,這樣安裝命令就還會(huì)指定的這個(gè)目錄中創(chuàng)建lib和include兩個(gè)目錄。
8.3.3 ALSA Util編譯
? ALSA Util可以生成用于播放,錄制,配置音頻的應(yīng)用可執(zhí)行文件,測(cè)試驅(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庫和工具移植入嵌入式平臺(tái)
? ALSA庫和測(cè)試工具的移植就是將相應(yīng)庫文件和可執(zhí)行文件放在目標(biāo)板上,以下文件 必須被拷貝至對(duì)應(yīng)位置 :
(1)ALSA Lib文件,放在/lib/中。
(2)配置文件放在/usr/local/share中,與編譯時(shí)指定的目錄相同。
(3)測(cè)試應(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 的一些工具,下面對(duì)幾個(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, 不指定的情況下會(huì)去讀取文件頭部作識(shí)別 |
-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, 不指定的情況下會(huì)去讀取文件頭部作識(shí)別 |
-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等對(duì)內(nèi)核層進(jìn)行訪問。內(nèi)核層的alsa-drivier驅(qū)動(dòng)再經(jīng)由sound core對(duì)硬件聲卡芯片進(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 接口說明請(qǐng)查閱:
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ì)
- 文件列表:
序號(hào) | 文件名 | 描述 |
---|---|---|
1 | AlsaVolume.h | 音量控制頭文件 |
2 | AlsaVolume.cpp | 音量控制程序 |
- 成員函數(shù)設(shè)計(jì):
序號(hào) | 函數(shù)名 | 參數(shù) | 參數(shù)描述 | 函數(shù)描述 |
---|---|---|---|---|
1 | setMasterVolume | long volume | 音量值 | 設(shè)置音量 |
2 | getCurrentVolume | 無 | 無 | 獲取當(dāng)前音量 |
3 | increaseVolume | 無 | 無 | 單步減小音量接口函數(shù) |
4 | decreaseVolume | 無 | 無 | 單步增加音量接口函數(shù) |
- 成員變量設(shè)計(jì):
序號(hào) | 成員變量名 | 類型 | 描述 |
---|---|---|---|
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 <alsa/asoundlib.h>
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ì)
- 文件列表:
序號(hào) | 文件名 | 描述 |
---|---|---|
1 | AlsaBase.h | ALSA基類頭文件 |
2 | AlsaBase.cpp | 基類的實(shí)現(xiàn)程序 |
- public成員變量:
序號(hào) | 成員變量名 | 類型 | 描述 |
---|---|---|---|
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成員變量:
序號(hào) | 成員變量名 | 類型 | 描述 |
---|---|---|---|
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ì)
- 文件列表
序號(hào) | 文件名 | 描述 |
---|---|---|
1 | AlsaPlayback.h | 音頻播放控制頭文件 |
2 | AlsaPlayback.cpp | 音頻播放程序 |
- 成員函數(shù)設(shè)計(jì)
序號(hào) | 函數(shù)名 | 參數(shù) | 參數(shù)描述 | 函數(shù)描述 |
---|---|---|---|---|
1 | playback | const char *input_buffer <br/> 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<char *>(_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ì)
- 文件列表
序號(hào) | 文件名 | 描述 |
---|---|---|
1 | AlsaCapture.h | 音頻錄制頭文件 |
2 | AlsaCapture.cpp | 音頻錄制程序 |
- 成員函數(shù)設(shè)計(jì)
序號(hào) | 函數(shù)名 | 參數(shù) | 參數(shù)描述 | 函數(shù)描述 |
---|---|---|---|---|
1 | capture | 無 | 錄制音頻 |
- 成員變量設(shè)計(jì)
序號(hào) | 成員變量名 | 類型 | 描述 |
---|---|---|---|
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;
}
}
}
本文摘自 :https://blog.51cto.com/w