0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux应用开发【第八章】ALSA应用开发

weidongshan 来源:weidongshan 作者:weidongshan 2021-12-10 19:19 次阅读

文章目录

8 ALSA应用开发

8.1 音频相关概念

8.1.1 采样频率

8.1.2 量化位数

8.2 ALSA架构

8.2.1 ALSA架构介绍

8.3 移植ALSA库及工具

8.3.1 ALSA库下载

8.3.2 ALSA Lib编译

8.3.3 ALSA Util编译

8.3.4 ALSA库和工具移植入嵌入式平台

8.4 ALSA的调试

8.4.1 amixer

8.4.2 aplay

8.4.3 arecord

8.5 常用接口说明

8.5.1 PCM接口

8.6 基于ALSA的音量控制程序设计

8.6.1 程序设计

8.6.2 AlsaVolume 类的定义

8.6.3 AlsaVolume类中成员函数的实现

8.7 ALSA基类的设计

8.7.1 程序设计

8.7.2 AlsaBase类中成员函数的实现

8.8 基于ALSA音频的播放

8.8.1 程序设计

8.1.2 AlsaPlay类的定义

8.1.3 AlsaPlayback类中成员函数的实现

8.9 基于ALSA音频的录制

8.9.1 程序设计

8.9.2 AlsaPlay类的定义

8.9.3 AlsaCapture类中成员函数的实现

8 ALSA应用开发

8.1 音频相关概念

​ 音频信号是一种连续变化的interwetten与威廉的赔率体系 信号,但计算机只能处理和记录二进制的数字信号,由自然音源得到的音频信号必须经过一定的变换,成为数字音频信号之后,才能送到计算机中作进一步的处理。

​ 数字音频系统通过将声波的波型转换成一系列二进制数据,来实现对原始声音的重现,实现这一步骤的设备常被称为(A/D)。A/D转换器以每秒钟上万次的速率对声波进行采样,每个采样点都记录下了原始模拟声波在某一时刻的状态,通常称之为样本(sample),而每一秒钟所采样的数目则称为采样频率,通过将一串连续的样本连接起来,就可以在计算机中描述一段声音了。对于采样过程中的每一个样本来说,数字音频系统会分配一定存储位来记录声波的振幅,一般称之为采样分辩率或者采样精度,采样精度越高,声音还原时就会越细腻。

pYYBAGGzN8yAa7DFAAHhL-PgCDc352.png

​ 数字音频涉及到的概念非常多,对于在Linux下进行音频编程的程序员来说,最重要的是7406解声音数字化的两个关键步骤:采样和量化。

采样就是每隔一定时间就读一次声音信号的幅度,从本质上讲,采样是时间上的数字化。

量化则是将采样得到的声音信号幅度转换为数字值,从本质上讲,量化则是幅度上的数字化。

8.1.1 采样频率

​ 采样频率是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。采样频率的选择应该遵循奈奎斯特(Harry Nyquist)采样理论:如果对某一模拟信号进行采样,则采样后可还原的最高信号频率只有采样频率的一半,或者说只要采样频率高于输入信号最高频率的两倍,就能从采样信号系列重构原始信号。

poYBAGGzN9KALmKkAAF-JdmMXvs241.png

​ 如上图所示 用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转换器带来的量化噪声是不可避免的。

pYYBAGGzN9iAD4LOAABa2a3SjWs378.png

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声卡驱动与用户空间体系结构交互如下图所示:

pYYBAGGzN9iAXW2zAABz2tRukJI787.png

8.3 移植ALSA库及工具

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

alsa-lib:用户空间函数库, 封装驱动提供的抽象接口, 通过文件libasound.so提供API给应用程序使用。

alsa-utils:实用工具包,通过调用alsa-lib实现播放音频(aplay)、录音(arecord) 等工具。

pYYBAGGzN9mAI4fuAAApW48q0wA225.png

​ 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。

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指定编译器,这里指定为交叉编译器,运行本配置命令前务必保证编译器已经可以在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对硬件声卡芯片进行访问。

pYYBAGGzN9mAcvLNAACUIVprOZ0576.png

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 
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(¶ms);

    //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
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(_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;
        }
    }
}

审核编辑 黄昊宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11296

    浏览量

    209359
  • alsa
    +关注

    关注

    0

    文章

    19

    浏览量

    3616
收藏 人收藏

    评论

    相关推荐

    《测控电路》习题完整参考答案(第八章

    《测控电路》习题完整参考答案(第八章
    发表于 05-07 11:39

    C语言编程入门(linux环境)

    消息管理 …………………………………………………………………………39第七 线程操作 …………………………………………………………………………49第八章 网络编程 …………………………………………………………………………54第九
    发表于 12-19 13:57

    自动控制原理—北京航天航空大学精品课件(八章全)

    第一:拉氏变换第二:数学模型第三:时域分析法第四:根轨迹法第五:频率法第六:系统校正
    发表于 10-11 13:58

    【正点原子FPGA连载】第八章Linux基础外设的使用-领航者ZYNQ之linux开发指南

    原子公众号,获取最新资料第八章Linux基础外设的使用通过对Petalinux和XSDK的学习我们了解了如何使用Petalinux定制Linux和如何使用XSDK开发
    发表于 09-10 17:51

    嵌入式Linux开发流程中的各个步骤

    基础第三 ARM体系架构第四 嵌入式编程开发入门篇第五 软硬件开发环境第六 交叉编译工具
    发表于 11-04 07:37

    I.MX6U嵌入式Linux驱动开发指南

    【正点原子Linux连载】第八章汇编LED灯试验--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0`:---------:`第八章
    发表于 01-26 08:26

    波形的产生与变换电路 第八章PPT

    波形的产生与变换电路 第八章
    发表于 04-20 09:33 19次下载
    波形的产生与变换电路 <b class='flag-5'>第八章</b>PPT

    信号发生电路基础 第八章

    信号发生电路基础 第八章 8.1正弦波振荡电路1、振荡电路:是一种不需要外接输入信号就能将直流能源转换成具有一定频率、一定幅度和一定波形的交流能量
    发表于 04-20 13:58 49次下载

    第八章 线性离散控制系统分析

    第八章 线性离散控制系统分析 数字控制系统是一种以数字计算机为控制器去控制具有连续工作状态的被控对象的闭环控制系统。
    发表于 05-27 15:50 0次下载

    51单片机第八章素材

    单片机第八章素材练习,主要讲解单片机的初步应用只是,配合protues使用,加强对单片机有关知识的理解。
    发表于 11-16 18:53 1次下载

    电子威廉希尔官方网站 基础模拟部分第五版_第八章习题答案.pdf

    电子威廉希尔官方网站 基础模拟部分第五版_第八章习题答案.pdf,作业题解答!
    发表于 04-11 17:44 0次下载

    《测控电路》习题完整参考答案(第八章

    《测控电路》习题完整参考答案(第八章
    发表于 02-14 17:02 0次下载

    电路《电路原理》邱关源---第八章 相量法

    电路《电路原理》邱关源---第八章 相量法
    发表于 01-18 11:37 0次下载

    【正点原子Linux连载】第八章汇编LED灯试验--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    【正点原子Linux连载】第八章汇编LED灯试验--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
    发表于 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>驱动<b class='flag-5'>开发</b>指南V1.0

    IC工艺和版图设计第八章Latch-up和GuardRing设计

    IC工艺和版图设计第八章Latch-up和GuardRing设计
    发表于 02-10 18:11 1次下载