0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux應用開發(fā)【第八章】ALSA應用開發(fā)

weidongshan ? 來源:weidongshan ? 作者:weidongshan ? 2021-12-10 19:19 ? 次閱讀

文章目錄

8 ALSA應用開發(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è)計

8.6.1 程序設(shè)計

8.6.2 AlsaVolume 類的定義

8.6.3 AlsaVolume類中成員函數(shù)的實現(xiàn)

8.7 ALSA基類的設(shè)計

8.7.1 程序設(shè)計

8.7.2 AlsaBase類中成員函數(shù)的實現(xiàn)

8.8 基于ALSA音頻的播放

8.8.1 程序設(shè)計

8.1.2 AlsaPlay類的定義

8.1.3 AlsaPlayback類中成員函數(shù)的實現(xiàn)

8.9 基于ALSA音頻的錄制

8.9.1 程序設(shè)計

8.9.2 AlsaPlay類的定義

8.9.3 AlsaCapture類中成員函數(shù)的實現(xiàn)

8 ALSA應用開發(fā)

8.1 音頻相關(guān)概念

? 音頻信號是一種連續(xù)變化的模擬信號,但計算機只能處理和記錄二進制的數(shù)字信號,由自然音源得到的音頻信號必須經(jīng)過一定的變換,成為數(shù)字音頻信號之后,才能送到計算機中作進一步的處理。

? 數(shù)字音頻系統(tǒng)通過將聲波的波型轉(zhuǎn)換成一系列二進制數(shù)據(jù),來實現(xiàn)對原始聲音的重現(xiàn),實現(xiàn)這一步驟的設(shè)備常被稱為(A/D)。A/D轉(zhuǎn)換器以每秒鐘上萬次的速率對聲波進行采樣,每個采樣點都記錄下了原始模擬聲波在某一時刻的狀態(tài),通常稱之為樣本(sample),而每一秒鐘所采樣的數(shù)目則稱為采樣頻率,通過將一串連續(xù)的樣本連接起來,就可以在計算機中描述一段聲音了。對于采樣過程中的每一個樣本來說,數(shù)字音頻系統(tǒng)會分配一定存儲位來記錄聲波的振幅,一般稱之為采樣分辯率或者采樣精度,采樣精度越高,聲音還原時就會越細膩。

pYYBAGGzN8yAa7DFAAHhL-PgCDc352.png

? 數(shù)字音頻涉及到的概念非常多,對于在Linux下進行音頻編程的程序員來說,最重要的是7406解聲音數(shù)字化的兩個關(guān)鍵步驟:采樣和量化。

采樣就是每隔一定時間就讀一次聲音信號的幅度,從本質(zhì)上講,采樣是時間上的數(shù)字化。

量化則是將采樣得到的聲音信號幅度轉(zhuǎn)換為數(shù)字值,從本質(zhì)上講,量化則是幅度上的數(shù)字化。

8.1.1 采樣頻率

? 采樣頻率是指將模擬聲音波形進行數(shù)字化時,每秒鐘抽取聲波幅度樣本的次數(shù)。采樣頻率的選擇應該遵循奈奎斯特(Harry Nyquist)采樣理論:如果對某一模擬信號進行采樣,則采樣后可還原的最高信號頻率只有采樣頻率的一半,或者說只要采樣頻率高于輸入信號最高頻率的兩倍,就能從采樣信號系列重構(gòu)原始信號。

poYBAGGzN9KALmKkAAF-JdmMXvs241.png

? 如上圖所示 用40KHz的頻率去采樣20KHz的信號可以正確捕捉到原始信號。用30KHz的頻率去采樣20KHz的信號會出現(xiàn)混淆信號。

? 一般重建音樂信號時采用的最低采樣頻率為44.1KHz。在許多高品質(zhì)的系統(tǒng)中,采用的48KHz的采樣頻率。

系統(tǒng) 采樣頻率
電話 8000Hz
CD 44100Hz
專業(yè)音頻 48000Hz
DVD音頻 96000Hz

8.1.2 量化位數(shù)

? 量化位數(shù)是對模擬音頻信號的幅度進行數(shù)字化,它決定了模擬信號數(shù)字化以后的動態(tài)范圍,常用的有8位、12位和16位。量化位越高,信號的動態(tài)范圍越大,數(shù)字化后的音頻信號就越可能接近原始信號,但所需要的存貯空間也越大。

? 音頻應用中常用的數(shù)字表示方法為脈沖編碼調(diào)制(Pulse-Code-Modulated,PCM)信號。在這種表示方法中,每個采樣周期用一個數(shù)字電平對模擬信號的幅度進行編碼。得到的數(shù)字波形是一組采樣自輸入模擬波形的近似值。由于所有A/D轉(zhuǎn)換器的分辨率都是有限的,所以在數(shù)字音頻系統(tǒng)中,A/D轉(zhuǎn)換器帶來的量化噪聲是不可避免的。

pYYBAGGzN9iAD4LOAABa2a3SjWs378.png

8.2 ALSA架構(gòu)

? ALSA全稱是Advanced Linux Sound Architecture,中文音譯是Linux高級聲音體系。ALSA 是Linux內(nèi)核2.6后續(xù)版本中支持音頻系統(tǒng)的標準接口程序,由ALSA庫、內(nèi)核驅(qū)動和相關(guān)測 試開發(fā)工具組成,更好的管理Linux中音頻系統(tǒng)。

? 本小節(jié)將介紹ALSA的架構(gòu)。

8.2.1 ALSA架構(gòu)介紹

? ALSA是Linux系統(tǒng)中為聲卡提供驅(qū)動的內(nèi)核組件。它提供了專門的庫函數(shù)來簡化相應應用程序的編寫。相較于OSS的編程接口,ALSA的函數(shù)庫更加便于使用。

? 對應用程序而言ALSA無疑是一個更佳的選擇,因為它具有更加友好的編程接口,并且完全兼容于OSS。

? ALSA系統(tǒng)包括7個子項目:

驅(qū)動包alsa-driver

開發(fā)包alsa-libs

開發(fā)包插件alsa-libplugins

設(shè)置管理工具包alsa-utils

OSS接口兼容模擬層工具alsa-oss

特殊音頻固件支持包alsa-finnware

其他聲音相關(guān)處理小程序包alsa-tools

ALSA聲卡驅(qū)動與用戶空間體系結(jié)構(gòu)交互如下圖所示:

pYYBAGGzN9iAXW2zAABz2tRukJI787.png

8.3 移植ALSA庫及工具

移植ALSA主要是移植alsa-Ub和alsa-utils。

alsa-lib:用戶空間函數(shù)庫, 封裝驅(qū)動提供的抽象接口, 通過文件libasound.so提供API給應用程序使用。

alsa-utils:實用工具包,通過調(diào)用alsa-lib實現(xiàn)播放音頻(aplay)、錄音(arecord) 等工具。

pYYBAGGzN9mAI4fuAAApW48q0wA225.png

? ALSA Util是純應用層的軟件,相當于ALSA設(shè)備的測試程序,ALSA-Lib則是支持應用API的中間層程序,ALSA-Util中的應用程序中會調(diào)用到ALSA-Lib中的接口來操作到我們的音頻編解碼芯片寄存器,而lib中接口就是依賴于最底層驅(qū)動代碼,因此移植ALSA程序的順序就是先后移植Driver,Lib,Util。

8.3.1 ALSA庫下載

? ALSA首先需要在ALSA的官網(wǎng)上下載官網(wǎng)http://www.alsa-project.org下載alsa-lib和alsa-utils。

poYBAGGzN9mAS1jrAAHvSDeCrDQ488.png

如上圖所示我們下載的版本為:

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配置的幾個重要的配置選項解釋如下:

–host指定編譯器,這里指定為交叉編譯器,運行本配置命令前務必保證編譯器已經(jīng)可以在Shell下可以直接執(zhí)行了。

–prefix指定編譯后文件的安裝路徑,這樣安裝命令就還會指定的這個目錄中創(chuàng)建lib和include兩個目錄。

8.3.3 ALSA Util編譯

? ALSA Util可以生成用于播放,錄制,配置音頻的應用可執(zhí)行文件,測試驅(qū)動代碼時用處很大,編譯過程如下:

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庫和測試工具的移植就是將相應庫文件和可執(zhí)行文件放在目標板上,以下文件 必須被拷貝至對應位置 :

(1)ALSA Lib文件,放在/lib/中。

(2)配置文件放在/usr/local/share中,與編譯時指定的目錄相同。

(3)測試應用文件,ALSA Util能產(chǎn)生aplay、amixer、arecord,我們可以把這些可執(zhí)行文件放在/usr/sbin中。

(4)內(nèi)核目錄中保證有/dev/snd/目錄,這個目錄下存放controlC0,pcmC0D0,/usr/sbintimer,timer這些設(shè)備文件,如果這些設(shè)備文件已經(jīng)在/dev目錄下,可手動拷貝到/snd目錄中。

? 在LINUX系統(tǒng)中,每個設(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è)備文件,例如通道選擇,混音,麥克風的控制等;

(2)pcmC0D0c:聲卡0設(shè)備0的錄音設(shè)備,c表示capter;

(3)pcmC0D0p:聲卡0設(shè)備0的播音設(shè)備,p表示play;

(4)timer:定時器設(shè)置。

8.4 ALSA的調(diào)試

? 本小節(jié)將著重講解tinyalsa工具使用,tinyalsa 是 alsa-lib 的一個簡化版。它提供了 pcm 和 control 的基本接口;沒有太多太復雜的操作、功能??梢园葱枋褂媒涌凇?tinyalsa-utils 是基于 tinyalsa 的一些工具,下面對幾個常用的工具作介紹。

8.4.1 amixer

? 與 amixer 作用類似,用于操作 mixer control。

使用方法:

常用選項

選項 功能
-D,–device 指定聲卡設(shè)備, 默認使用card0

常用命令

命令 功能
controls 列出指定聲卡的所有控件
contents 列出指定聲卡的所有控件的具體信息
get 獲取指定控件的信息
set 設(shè)定指定控件的值

舉例:

獲取audiocodec聲卡的所有控件名
amixer -Dhw:audiocodec controls
獲取當前硬件音量
amixer -Dhw:audiocodec cget name='LINEOUT volume'
設(shè)置當前硬件音量
amixer -Dhw:audiocodec cget name='LINEOUT volume' 25 

8.4.2 aplay

? aplay 是命令行的 ALSA 聲卡驅(qū)動的播放工具,用于播放功能。
使用方法:

選項 功能
-D,–device 指定聲卡設(shè)備, 默認使用 default
-l,–list-devices 列出當前所有聲卡
-t,–file-type 指定播放文件的格式, 如 voc,wav,raw, 不指定的情況下會去讀取文件頭部作識別
-c,–channels 指定通道數(shù)
-f,–format 指定采樣格式
-r,–rate 采樣率
-d,–duration 指定播放的時間
–period-size 指定 period size
–buffer-size 指定 buffer size

舉例:

aplay -Dhw:audiocodec /mnt/UDISK/test.wav

8.4.3 arecord

? arecord 是命令行的 ALSA 聲卡驅(qū)動的錄音工具,用于錄音功能。
使用方法:

選項 功能
-D,–device 指定聲卡設(shè)備, 默認使用 default
-l,–list-devices 列出當前所有聲卡
-t,–file-type 指定播放文件的格式, 如 voc,wav,raw, 不指定的情況下會去讀取文件頭部作識別
-c,–channels 指定通道數(shù)
-f,–format 指定采樣格式
-r,–rate 采樣率
-d,–duration 指定播放的時間
–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)核層進行訪問。內(nèi)核層的alsa-drivier驅(qū)動再經(jīng)由sound core對硬件聲卡芯片進行訪問。

pYYBAGGzN9mAcvLNAACUIVprOZ0576.png

8.5.1 PCM接口

? 為了方便操作訪問, alsa-lib 中封裝了相關(guān)接口, 通過 pcmCXDXp/pcmCXDXc 節(jié)點 (/dev/snd/pcmCXDXx) 去實現(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

? 詳細 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è)計

8.6.1 程序設(shè)計

文件列表:

序號 文件名 描述
1 AlsaVolume.h 音量控制頭文件
2 AlsaVolume.cpp 音量控制程序

成員函數(shù)設(shè)計:

序號 函數(shù)名 參數(shù) 參數(shù)描述 函數(shù)描述
1 setMasterVolume long volume 音量值 設(shè)置音量
2 getCurrentVolume 獲取當前音量
3 increaseVolume 單步減小音量接口函數(shù)
4 decreaseVolume 單步增加音量接口函數(shù)

成員變量設(shè)計:

序號 成員變量名 類型 描述
1 _VOLUMECHANGE const float 音量調(diào)節(jié)步進大小
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ù)的實現(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. 取得第一個 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;
}

獲取當前音量函數(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);
}

音量步進減少函數(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;
}

音量步進增加函數(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è)計

8.7.1 程序設(shè)計

文件列表:

序號 文件名 描述
1 AlsaBase.h ALSA基類頭文件
2 AlsaBase.cpp 基類的實現(xiàn)程序

public成員變量:

序號 成員變量名 類型 描述
1 rate int 碼率
2 channels int 通道數(shù)
3 bits_per_frame mutable int 每幀數(shù)據(jù)大小
4 default_output_buffer_size int 默認輸出緩存大小
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 時間段大小
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ù)的實現(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、以默認值填充硬件參數(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è)計

文件列表

序號 文件名 描述
1 AlsaPlayback.h 音頻播放控制頭文件
2 AlsaPlayback.cpp 音頻播放程序

成員函數(shù)設(shè)計

序號 函數(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ù)的實現(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ù)的實現(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è)計

文件列表

序號 文件名 描述
1 AlsaCapture.h 音頻錄制頭文件
2 AlsaCapture.cpp 音頻錄制程序

成員函數(shù)設(shè)計

序號 函數(shù)名 參數(shù) 參數(shù)描述 函數(shù)描述
1 capture 錄制音頻

成員變量設(shè)計

序號 成員變量名 類型 描述
1 _VOLUMECHANGE const float 音量調(diào)節(jié)步進大小
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ù)的實現(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;
        }
    }
}

審核編輯 黃昊宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207913
  • alsa
    +關(guān)注

    關(guān)注

    0

    文章

    19

    瀏覽量

    3583
收藏 人收藏

    評論

    相關(guān)推薦

    《測控電路》習題完整參考答案(第八章

    《測控電路》習題完整參考答案(第八章
    發(fā)表于 05-07 11:39

    C語言編程入門(linux環(huán)境)

    消息管理 …………………………………………………………………………39第七 線程操作 …………………………………………………………………………49第八章 網(wǎng)絡編程 …………………………………………………………………………54第九
    發(fā)表于 12-19 13:57

    自動控制原理—北京航天航空大學精品課件(八章全)

    第一:拉氏變換第二:數(shù)學模型第三:時域分析法第四:根軌跡法第五:頻率法第六:系統(tǒng)校正
    發(fā)表于 10-11 13:58

    【正點原子FPGA連載】第八章Linux基礎(chǔ)外設(shè)的使用-領(lǐng)航者ZYNQ之linux開發(fā)指南

    原子公眾號,獲取最新資料第八章Linux基礎(chǔ)外設(shè)的使用通過對Petalinux和XSDK的學習我們了解了如何使用Petalinux定制Linux和如何使用XSDK開發(fā)
    發(fā)表于 09-10 17:51

    嵌入式Linux開發(fā)流程中的各個步驟

    基礎(chǔ)第三 ARM體系架構(gòu)第四 嵌入式編程開發(fā)入門篇第五 軟硬件開發(fā)環(huán)境第六 交叉編譯工具
    發(fā)表于 11-04 07:37

    I.MX6U嵌入式Linux驅(qū)動開發(fā)指南

    【正點原子Linux連載】第八章匯編LED燈試驗--摘自【正點原子】I.MX6U嵌入式Linux驅(qū)動開發(fā)指南V1.0`:---------:`第八章
    發(fā)表于 01-26 08:26

    波形的產(chǎn)生與變換電路 第八章PPT

    波形的產(chǎn)生與變換電路 第八章
    發(fā)表于 04-20 09:33 ?19次下載
    波形的產(chǎn)生與變換電路 <b class='flag-5'>第八章</b>PPT

    信號發(fā)生電路基礎(chǔ) 第八章

    信號發(fā)生電路基礎(chǔ) 第八章 8.1正弦波振蕩電路1、振蕩電路:是一種不需要外接輸入信號就能將直流能源轉(zhuǎn)換成具有一定頻率、一定幅度和一定波形的交流能量
    發(fā)表于 04-20 13:58 ?49次下載

    第八章 線性離散控制系統(tǒng)分析

    第八章 線性離散控制系統(tǒng)分析 數(shù)字控制系統(tǒng)是一種以數(shù)字計算機為控制器去控制具有連續(xù)工作狀態(tài)的被控對象的閉環(huán)控制系統(tǒng)。
    發(fā)表于 05-27 15:50 ?0次下載

    51單片機第八章素材

    單片機第八章素材練習,主要講解單片機的初步應用只是,配合protues使用,加強對單片機有關(guān)知識的理解。
    發(fā)表于 11-16 18:53 ?1次下載

    電子技術(shù)基礎(chǔ)模擬部分第五版_第八章習題答案.pdf

    電子技術(shù)基礎(chǔ)模擬部分第五版_第八章習題答案.pdf,作業(yè)題解答!
    發(fā)表于 04-11 17:44 ?0次下載

    《測控電路》習題完整參考答案(第八章

    《測控電路》習題完整參考答案(第八章
    發(fā)表于 02-14 17:02 ?0次下載

    電路《電路原理》邱關(guān)源---第八章 相量法

    電路《電路原理》邱關(guān)源---第八章 相量法
    發(fā)表于 01-18 11:37 ?0次下載

    【正點原子Linux連載】第八章匯編LED燈試驗--摘自【正點原子】I.MX6U嵌入式Linux驅(qū)動開發(fā)指南V1.0

    【正點原子Linux連載】第八章匯編LED燈試驗--摘自【正點原子】I.MX6U嵌入式Linux驅(qū)動開發(fā)指南V1.0
    發(fā)表于 12-01 19:21 ?10次下載
    【正點原子<b class='flag-5'>Linux</b>連載】<b class='flag-5'>第八章</b>匯編LED燈試驗--摘自【正點原子】I.MX6U嵌入式<b class='flag-5'>Linux</b>驅(qū)動<b class='flag-5'>開發(fā)</b>指南V1.0

    IC工藝和版圖設(shè)計第八章Latch-up和GuardRing設(shè)計

    IC工藝和版圖設(shè)計第八章Latch-up和GuardRing設(shè)計
    發(fā)表于 02-10 18:11 ?0次下載