Fork me on GitHub
0%

ALSA

简介

官网:https://www.alsa-project.org/wiki/Main_Page

ALSA:Advanced Linux Sound Architecture,高级linux声音体系,它在Linux操作系统上提供了音频和MIDI(Musical Instrument Digital Interface,音乐设备数字化接口)的支持。

ALSA设备文件结构

1
2
3
4
5
6
7
8
# cd /dev/snd; ls -l;

crw-rw----+ 1 root audio 116, 8 2011-02-23 21:38 controlC0 用于声卡的控制,例如通道选择,混音,麦克风的控制等
crw-rw----+ 1 root audio 116, 4 2011-02-23 21:38 midiC0D0 用于播放midi音频
crw-rw----+ 1 root audio 116, 7 2011-02-23 21:39 pcmC0D0c 用于录音的pcm设备
crw-rw----+ 1 root audio 116, 6 2011-02-23 21:56 pcmC0D0p 用于播放的pcm设备
crw-rw----+ 1 root audio 116, 3 2011-02-23 21:38 seq 音序器
crw-rw----+ 1 root audio 116, 2 2011-02-23 21:38 timer 定时器

其中,C0D0代表的是声卡0中的设备0 ; pcmC0D0c最后一个c代表capture ; pcmC0D0p最后一个p代表playback。

alsa相关函数及文件

函数及结构体

  • AVFrame iFrame 存储非压缩的数据,输入
  • AVFrame swapFrame 存储非压缩的数据,重采样
  • AVPack oPacket 压缩即编码后的输入,输出
  1. snd_pcm_hw_params_get_rate( )

  2. snd_pcm_open( )

    • snd_config_update_ref

      这个函数作用是检查alsa配置文件是否发生了变化,包括文件名字和各个配置文件的内容是否修改,如果有修改,就重新加载配置文件树,刷新全局变量snd_config并增加一个snd_config的引用计数,调用snd_config_update_ref传入的top就是指向snd_config的指针,这个top当作参数传入snd_pcm_open_noupdate函数中。snd_config_unref接下来会减去snd_config的引用计数。

      首先创建顶层配置节点,然后打开/usr/share/alsa/alsa.conf,加载文件内容到顶层配置节点上,然后遍历所有的hooks,调用snd_config_hooks加载所有hooks,并调用相关的hooks函数打开对应的plugin动态库

    • snd_pcm_open_noupdate

  3. avcodec_open2( )

    • 检测指定的codec和context里面的codec是否匹配。

    • 分配空间。

    • 检测codec各个参数合法性,并给某些字段赋初值。

    • 调用codec的init初始化codec。

    • 释放资源,返回。

配置文件

  1. 参考网址 : https://www.cnblogs.com/lihaiping/p/alsaconfig.html

  2. 配置文件的调用过程

    alsa-lib库 : alsa-lib-1.2.1.2/src/pcm/pcm.c

    snd_pcm_open( ) 函数作为打开一个pcm的接口. 具体的调用过程是 : static snd_pcm_open_noupdate —-> static snd_pcm_open_conf( )

    解析asound.conf过程 : https://www.xuebuyuan.com/1043778.html

  3. snd_pcm_open内部直接解析的是usr/share/alsa/alsa.conf, 在这个文件中决定了etc/asound.conf和/.asoundrc这两个文件要不要调用

3.3 音频设备的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#  文件定义位置 : /system/core/include/system/audio.h
enum {
AUDIO_DEVICE_NONE = 0x0,
/* reserved bits */
AUDIO_DEVICE_BIT_IN = 0x80000000,
AUDIO_DEVICE_BIT_DEFAULT = 0x40000000,
/* output devices */
AUDIO_DEVICE_OUT_EARPIECE = 0x1, // 听筒
AUDIO_DEVICE_OUT_SPEAKER = 0x2, // 扬声器
AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, // 线控耳机,可以通过耳机控制远端播放、暂停、音量调节等功能的耳机
AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8, // 普通耳机,只能听,不能操控播放
AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10, // 单声道蓝牙耳机,十进制32
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20, // 车载免提蓝牙设备,十进制64
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40, // 立体声蓝牙耳机
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80, // 十进制128
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, // 十进制256
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, // 十进制512
AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, // 十进制1024
AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, // 十进制2048
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, // 十进制4096
};

alsa插件

asound.conf配置 https://www.alsa-project.org/main/index.php/Asoundrc

插件详解官网 https://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html

  1. 生成alsa配置文件 : alsactl store -f a.conf
  2. 对于耳机的controls需要指定-c 1,默认走的是card 0
  3. 播放音频的命令 arecord -D usbheadsetC -d 5 -f cd -r 48000 -c 2 -t wav test.wav
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//查看控件
# amixer -c 1 controls
numid=3,iface=MIXER,name='PCM Playback Switch'
numid=4,iface=MIXER,name='PCM Playback Volume'
numid=6,iface=MIXER,name='usbCapture_vol'
numid=5,iface=MIXER,name='usbPlay_vol'
numid=1,iface=PCM,name='Capture Channel Map'
numid=2,iface=PCM,name='Playback Channel Map'


//对控件进行配置
# amixer -c 1 sget usbCapture_vol
Simple mixer control 'usbCapture_vol',0
Capabilities: volume
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: 0 - 255
Front Left: 100 [39%]
Front Right: 100 [39%]
# amixer -c 1 cget numid=5,iface=MIXER,name='usbCapture_vol'
numid=5,iface=MIXER,name='usbCapture_vol'
; type=INTEGER,access=rw---RW-,values=2,min=0,max=255,step=0
: values=100,100
| dBscale-min=-20.00dB,step=0.20dB,mute=0


# arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: smartpenknot [smartpen_knot], device 0: i2s-ecodec nau8822-hifi-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: smartpenknot [smartpen_knot], device 1: dmic dmic-codec-hifi-1 []
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: smartpenknot [smartpen_knot], device 2: i2s-tloop pcm-dump-2 []
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: EP3C [Meizu EP3C], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
#
# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: smartpenknot [smartpen_knot], device 0: i2s-ecodec nau8822-hifi-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: EP3C [Meizu EP3C], device 0: USB Audio [USB Audio]
Subdevices: 0/1
Subdevice #0: subdevice #0

ALSA音频工具

对于以下工具的使用,都可以直接输入“工具名”或“工具名 –help” 获得详细使用方法

arecode 录音

  1. 查看录音设备 : arecode -l
  2. 命令实例 : arecord -D hw:0,1 -d 10 -f cd -r 44100 -c 2 -t wav test.wav
  3. 参数解析 :
    • -D 指定了录音设备,0,1 是card 0 device 1
    • -d 指定录音的时长,单位 : 秒
    • -f 指定采样格式,通过上面的信息知道只支持 cd cdr dat
    • -r 指定了采样率,单位 : Hz
    • -c 指定channel 个数
    • -t 指定生成的文件格式
1
2
3
4
5
6
7
8
lee@lee-virtual-machine:~$ arecord -l
**** CAPTURE 硬體裝置清單 ****
card 0: AudioPCI [Ensoniq AudioPCI], device 0: ES1371/1 [ES1371 DAC2/ADC]
子设备: 1/1
子设备 #0: subdevice #0
lee@lee-virtual-machine:~$ arecord -D hw:0,0 -d 10 -f cd -r 44100 -c 2 -t wav test.wav
正在录音 WAVE 'test.wav' : Signed 16 bit Little Endian, 频率44100Hz, Stereo
lee@lee-virtual-machine:~$

aplay 放音

  1. 查看放音设备 : aplay -l

  2. 命令实例 : aplay test.wav

    如果是直接播放pcm数据,则需要指定pcm格式 、采样率、channel个数等。

1
2
3
4
5
6
7
8
9
10
11
12
lee@lee-virtual-machine:~$ aplay -l
**** PLAYBACK 硬體裝置清單 ****
card 0: AudioPCI [Ensoniq AudioPCI], device 0: ES1371/1 [ES1371 DAC2/ADC]
子设备: 0/1
子设备 #0: subdevice #0
card 0: AudioPCI [Ensoniq AudioPCI], device 1: ES1371/2 [ES1371 DAC1]
子设备: 1/1
子设备 #0: subdevice #0
lee@lee-virtual-machine:~$ aplay test.wav
正在播放 WAVE 'test.wav' : Signed 16 bit Little Endian, 频率44100Hz, Stereo
lee@lee-virtual-machine:~$

amixer 配参

amixer,是alsamixer的文本模式(命令行模式)用于配置音频的各个参数

  1. 使用方法 :

    1. 用 # amixer controls 列出所有控制项。( 显示接口函数 )

    2. 如果对控制项不熟悉或者想查看当前的所有配置可以使用 # amixer contents 命令。(显示当前配置)

    3. 查看获取某个设置项的值用 # amixer cget + 控制参数 命令。 这里控制参数直接复制 # amixer controls 打印出来的数据就可以

    4. 根据控制参数类型设置控制参数使用 # amixer cset + 控制参数 + 设置参数 命令。

    5. values 表示数值对于 volume 之类的是具体的数字,在上下文菜单中有显示数值的范围,对于 switch 之类的表示开关一般是 on 或 off。

      另外一sget/sset系列的命令, 与cget/cset的使用相同

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      //使用例子:
      //给的参数看value需要几个 需要什么类型进行相应的配置即可
      / # amixer cget numid=43,iface=MIXER,name='Speaker Playback Volum'
      numid=43,iface=MIXER,name='Speaker Playback Volume'
      ; type=INTEGER,access=rw---R--,values=2,min=0,max=63,step=0
      : values=63,63
      | dBscale-min=-57.00dB,step=1.00dB,mute=0
      / #
      / #
      / #
      / # amixer cset numid=43,iface=MIXER,name='Speaker Playback Volum' 30 30
      numid=43,iface=MIXER,name='Speaker Playback Volume'
      ; type=INTEGER,access=rw---R--,values=2,min=0,max=63,step=0
      : values=30,30
      | dBscale-min=-57.00dB,step=1.00dB,mute=0
      / #
  2. 保存及恢复方法

    对声卡信息状态进行保存 : alsactl store -f /var/lib/alsa/asound.state

    声卡状态恢复命令 : alsactl restore -f /var/lib/alsa/asound.state

Linux音频分析工具

  1. audacity

    • 功能:查看各种格式音频的波形,并有裁剪、转换格式、生成特定音频数据等诸多功能

    • 下载方法 : sudo apt install audacity -o Acquire::http::proxy=”http://192.168.4.15:8080/"( -o 参数指定代理)

    • 使用方法:命令行输入audacity,添加音频文件即可

  2. play

    • 由于aplay播放数据格式有限,play对于音频数据格式进行了较多的支持
    • play播放裸数据命令 : play -t raw -r 16000 -e signed-integer -b 16 -c 1 test.wav

API使用

代码链接:https://github.com/yangLieee/audioplayer

Q&A

  1. Q:数据存储出来只有78字节,但是写函数已经执行完毕?

    A:这只是写了一个文件头,而最终文件没有写入。原因是:avio_open之后没有关闭,调用closep函数之后数据正常写入;

    ​ 调试流程:首先定位问题位置。在alsa录音完成之后将数据写入buffer中存储来看是否是alsa部分的录音问题。之后根据流程进行检查。一定要注意open close / new delete / 等配套的使用

  2. 数据已经写结束 但是还是会再次执行一次其上面的程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    if(fmtContext)                                                                    
    av_write_trailer(fmtContext);
    if(fmtContext != NULL){
    if( avio_closep(&fmtContext->pb) < 0)
    printf("avio_closep error!");
    avformat_free_context(fmtContext);
    }

    /*
    av_write_trailer的作用
    1. 循环调用interleave_packet()以及write_packet(),将还未输出的AVPacket输出出来。
    2. 调用AVOutputFormat的write_trailer(),输出文件尾。
    avio_closep的作用
    关闭输出文件的上下文,与avio_open相对应使用

    这两个的使用需要保证av_write_trailer函数调用早于avio_closep
    如果不调用这两个函数会导致最终文件不能写出
    */
  3. Q:重采样时 channel 可以正常转换, sample_rate采样率变化不能转换?

    A:在做采样率的重采样时, 对输出的context配置需要将采样点也做一下转换,否则已经转换成功但是生成的文件大小还是按照以前的输出进行.
    采样点转换函数 : av_rescale_rnd(iFrame->nb_samples, oSampleRate, iSampleRate, AV_ROUND_UP);

  4. Q : 录音之后声音太小, 甚至有时候听不到, 需要如何调整?

    A : 通过amixer参数进行相应的配置

    1
    2
    3
    4
    5
    6
    /storage/recode # amixer cset numid=53,iface=MIXER,name='DMIC GAIN' 15
    numid=53,iface=MIXER,name='DMIC GAIN'
    ; type=INTEGER,access=rw---R--,values=1,min=0,max=31,step=0
    : values=15
    | dBscale-min=0.00dB,step=3.00dB,mute=0
    //这个步骤的作用是, 将dmic的增益调整为15
  5. Q: 耳机音量小但是amixer没有相关可以配置的参数?

    A : 通过asound.conf配置新的插件,

    https://alsa.opensrc.org/How_to_use_softvol_to_control_the_master_volume

参考资料(包含FFmpeg)

注:如果后续如涉及到FFmpeg文章,再将该部分内容移除,以下文章是在工作的开发中有帮助到自己