百问网全志系列开发板音频ALSA配置步骤详解

2024-08-16 08:16:58 浏览数 (2)

8 ALSA

8.1 音频相关概念

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

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

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

  • 采样就是每隔一定时间就读一次声音信号的幅度,从本质上讲,采样是时间上的数字化。
  • 量化则是将采样得到的声音信号幅度转换为数字值,从本质上讲,量化则是幅度上的数字化。
8.1.1 采样频率

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

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

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

8.3 移植ALSA库及工具

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

  • alsa-lib:用户空间函数库, 封装驱动提供的抽象接口, 通过文件libasound.so提供API给应用程序使用。
  • alsa-utils:实用工具包,通过调用alsa-lib实现播放音频(aplay)、录音(arecord) 等工具。

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

如上图所示我们下载的版本为:

  • alsa-lib-1.2.2.tar.bz2
  • alsa-utils-1.2.2.tar.bz2
8.3.2 ALSA Lib编译

​ ALSA Lib移植不需要修改源码,只需要重新编译库代码以支持自己的平台。

代码语言:javascript复制
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可以生成用于播放,录制,配置音频的应用可执行文件,测试驱动代码时用处很大,编译过程如下:

代码语言:javascript复制
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库和测试工具的移植就是将相应库文件和可执行文件放在目标板上,以下文件 必须被拷贝至对应位置 : 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; }

代码语言:javascript复制
## 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类的定义

```c
#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类的构造函数
代码语言:javascript复制
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));
}
代码语言:javascript复制
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类的构造函数
代码语言:javascript复制
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;
        }
    }
}

0 人点赞