• 當前位置:首頁 > IT技術 > 系統服務 > 正文

    Linux應用開發【第八章】ALSA應用開發
    2021-12-13 17:47:38

    @[TOC]

    8 ALSA應用開發

    8.1 音頻相關概念

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

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

    Linux應用開發【第八章】ALSA應用開發

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

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

    • 量化則是將采樣得到的聲音信號幅度轉換為數字值,從本質上講,量化則是幅度上的數字化。

    8.1.1 采樣頻率

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

    Linux應用開發【第八章】ALSA應用開發

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

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

    系統 采樣頻率
    電話 8000Hz
    CD 44100Hz
    專業音頻 48000Hz
    DVD音頻 96000Hz

    8.1.2 量化位數

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

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

    Linux應用開發【第八章】ALSA應用開發

    8.2 ALSA架構

    ? ALSA全稱是Advanced Linux Sound Architecture,中文音譯是Linux高級聲音體系。ALSA 是Linux內核2.6后續版本中支持音頻系統的標準接口程序,由ALSA庫、內核驅動和相關測 試開發工具組成,更好的管理Linux中音頻系統。

    ? 本小節將介紹ALSA的架構。

    8.2.1 ALSA架構介紹

    ? ALSA是Linux系統中為聲卡提供驅動的內核組件。它提供了專門的庫函數來簡化相應應用程序的編寫。相較于OSS的編程接口,ALSA的函數庫更加便于使用。

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

    ? ALSA系統包括7個子項目:

    • 驅動包alsa-driver
    • 開發包alsa-libs
    • 開發包插件alsa-libplugins
    • 設置管理工具包alsa-utils
    • OSS接口兼容模擬層工具alsa-oss
    • 特殊音頻固件支持包alsa-finnware
    • 其他聲音相關處理小程序包alsa-tools

    ALSA聲卡驅動與用戶空間體系結構交互如下圖所示:

    Linux應用開發【第八章】ALSA應用開發

    8.3 移植ALSA庫及工具

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

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

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

    Linux應用開發【第八章】ALSA應用開發

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

    8.3.1 ALSA庫下載

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

    Linux應用開發【第八章】ALSA應用開發

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

    • 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指定編譯器,這里指定為交叉編譯器,運行本配置命令前務必保證編譯器已經可以在Shell下可以直接執行了。

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

    8.3.3 ALSA Util編譯

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

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

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

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

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

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

    ? 在LINUX系統中,每個設備文件都是文件。音頻設備也是一樣,它的設備文件被放在/dev/snd目錄下,我們來看下這些設備文件:

    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:音頻控制設備文件,例如通道選擇,混音,麥克風的控制等;

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

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

    (4)timer:定時器設置。

    8.4 ALSA的調試

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

    8.4.1 amixer

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

    使用方法:

    • 常用選項
    選項 功能
    -D,--device 指定聲卡設備, 默認使用card0
    • 常用命令
    命令 功能
    controls 列出指定聲卡的所有控件
    contents 列出指定聲卡的所有控件的具體信息
    get 獲取指定控件的信息
    set 設定指定控件的值

    舉例:

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

    8.4.2 aplay

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

    選項 功能
    -D,--device 指定聲卡設備, 默認使用 default
    -l,--list-devices 列出當前所有聲卡
    -t,--file-type 指定播放文件的格式, 如 voc,wav,raw, 不指定的情況下會去讀取文件頭部作識別
    -c,--channels 指定通道數
    -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 聲卡驅動的錄音工具,用于錄音功能。
    使用方法:

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

    舉例:

    錄制5s,通道數為2, 采樣率為16000, 采樣精度為16bit, 保存為wav文件
    arecord -Dhw:audiocodec -f S16_LE -r 16000 -c 2 -d 5 /mnt/UDISK/test.wav

    8.5 常用接口說明

    ? 從代碼角度體現了alsa-lib和alsa-driver及hardwared的交互關系。用戶層的alsa-lib通過操作alsa-driver創建的設備文件/dev/snd/pcmC0D0p等對內核層進行訪問。內核層的alsa-drivier驅動再經由sound core對硬件聲卡芯片進行訪問。

    Linux應用開發【第八章】ALSA應用開發

    8.5.1 PCM接口

    ? 為了方便操作訪問, alsa-lib 中封裝了相關接口, 通過 pcmCXDXp/pcmCXDXc 節點 (/dev/snd/pcmCXDXx) 去實現播放、錄音功能。

    ? 主要涉及到的接口:

    函數名 解釋
    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的音量控制程序設計

    8.6.1 程序設計

    • 文件列表:
    序號 文件名 描述
    1 AlsaVolume.h 音量控制頭文件
    2 AlsaVolume.cpp 音量控制程序
    • 成員函數設計:
    序號 函數名 參數 參數描述 函數描述
    1 setMasterVolume long volume 音量值 設置音量
    2 getCurrentVolume 獲取當前音量
    3 increaseVolume 單步減小音量接口函數
    4 decreaseVolume 單步增加音量接口函數
    • 成員變量設計:
    序號 成員變量名 類型 描述
    1 _VOLUMECHANGE const float 音量調節步進大小
    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類中成員函數的實現

    • AlsaVolume類的構造函數
    AlsaVolume::AlsaVolume()
    {
        snd_mixer_selem_id_t* sid = NULL;
        const char* card = "default";
        const char* selem_name = "Playback";
        //1. 打開混音設備
        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. 設置元素ID的位置
    snd_mixer_selem_id_set_index(sid, 0);
    
        //7. 設置元素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);
    }
    • 設置音量函數
    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;
    }
    • 獲取當前音量函數
    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);
    }
    • 音量步進減少函數
    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;
    }
    • 音量步進增加函數
    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基類的設計

    8.7.1 程序設計

    • 文件列表:
    序號 文件名 描述
    1 AlsaBase.h ALSA基類頭文件
    2 AlsaBase.cpp 基類的實現程序
    • public成員變量:
    序號 成員變量名 類型 描述
    1 rate int 碼率
    2 channels int 通道數
    3 bits_per_frame mutable int 每幀數據大小
    4 default_output_buffer_size int 默認輸出緩存大小
    5 frames snd_pcm_uframes_t 幀數
    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類中成員函數的實現

    • AlsaBase類的構造函數
    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;
        // 分配硬件參數空間
        snd_pcm_hw_params_alloca(&params);
    
        //1、以默認值填充硬件參數
        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、設置存取方式
        if ((err = snd_pcm_hw_params_set_access(handle, params, access_type)) < 0) {
            return err;
        }
    
        //4、設置格式,S16_LE等
        if ((err = snd_pcm_hw_params_set_format(handle, params, format)) < 0) {
            return err;
        }
    
        //5 設置通道
        if ((err = snd_pcm_hw_params_set_channels(handle, params, channels)) < 0) {
            return err;
        }
    
        //6 設置碼率
        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);
    
        // 將參數寫入設備
        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 程序設計

    • 文件列表
    序號 文件名 描述
    1 AlsaPlayback.h 音頻播放控制頭文件
    2 AlsaPlayback.cpp 音頻播放程序
    • 成員函數設計
    序號 函數名 參數 參數描述 函數描述
    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類中成員函數的實現

    • AlsaPlayback類的構造函數
    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函數的實現
    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 程序設計

    • 文件列表
    序號 文件名 描述
    1 AlsaCapture.h 音頻錄制頭文件
    2 AlsaCapture.cpp 音頻錄制程序
    • 成員函數設計
    序號 函數名 參數 參數描述 函數描述
    1 capture 錄制音頻
    • 成員變量設計
    序號 成員變量名 類型 描述
    1 _VOLUMECHANGE const float 音量調節步進大小
    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:
        // 輸出數據緩存
        char *output_buffer;
        // 輸出緩存大小
        unsigned int output_buffer_size;
        // int frames_to_read;
        // 用于返回已讀的幀數
        int frames_readed;
    
        AlsaCapture(const std::string &dev);
        ~AlsaCapture();
        int open_device();
        int capture();
      private:
        int err;
    };
    
    }

    8.9.3 AlsaCapture類中成員函數的實現

    • AlsaCapture類的構造函數
    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類的構造函數
    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

    開通會員,享受整站包年服務
    国产呦精品一区二区三区网站|久久www免费人咸|精品无码人妻一区二区|久99久热只有精品国产15|中文字幕亚洲无线码