1. ALSA既包括各种声卡的Kernel驱动,同时也提供libasound用户空间开发库,可以帮助开发者调用更高级的接口,而不用直接和ioctl打交道,并且可以提高兼容性,另外,alsa还提供了Plugin能力,可以扩展很多功能。同时,alsa还提供了一系列基于命令行的工具集,例如mixer, aplay, arecord等。sound设备的目录为/dev/snd/
其中:C0D0代表的是声卡0中的设备0,pcmC0D0c最后一个c代表capture,pcmC0D0p最后一个p代表playback。
controlC0:用于声卡的控制,例如通道选择,混音,麦克风的控制等;
midiC0D0:用于播放midi音频;
pcmC0D0c : 用于录音的pcm设备;
pcmC0D0p :用于播放的pcm设备;
seq :音序器;
timer :定时器;
如果没有上面这些设备的话,应该是对应的driver没有加载,去看一下对应的board目录的下的dts文件,看看codec的型号,然后去code中kernel/linux5.4/sound/soc/codecs/目录下找到对应的驱动文件,在menuconfig_public_linux中选中,这里注意不要make clean ,因为menuconfig_public_linux生成的.config文件是放在out目录下的。
2. ALSA API可以分为以下几个主要部分:
- control接口:一个通用功能,用来管理声卡的寄存器以及查询可用的设备。
- PCM接口:管理audio capture和playback的接口,这也是audio最常用的接口
- Raw MIDI接口: 支持MIDI(电子音乐设备的标准),提供了对声卡MIDI bus的访问,Raw接口由MIDI events驱动,程序负责管理协议和计时。
- Timer接口:提供对声卡上计时硬件的访问,用于同步声音事件。
- Sequencer interface:一个MIDI编程和声音同步接口,比raw MIDI接口级别更高,它管理大部分MIDI协议和计时。
- Mixer接口:控制声卡上的信号路由和音量调节的设备。它是建立在control接口之上的。
- device name:
API操作的是逻辑设备名而不是设备文件,设备名可以是真正的硬件设备或者插件。声卡设备会创建多个逻辑设备,用于control, capture, playback等,这些设备可以在/dev/snd目录下看到这些逻辑设备,同时在/sys/class/sound目录下也创建了同样的节点, 声卡的创建的code是在/kernel/sound/arm目录下,这些code会根据dts文件来创建声卡设备. 硬件设备使用hw:i,j这种格式,i是卡号而j是在这个卡上的设备。第一个声音设备是hw:0,0。第一个sound设备的别名为defalut,本文后面的例子都使用default。Plugins使用另外一种命名模式:例如plughw:是一个插件除了提供对硬件设备的访问还提供某种功能,比如软件实现采样率转换,因为硬件不支持这种操作。dmix插件允许合成几路数据, dshare插件允许把单路数据动态分配到不同的应用中。 - Sound Buffers and Data Transfer
声卡有一个硬件buffer。在录音时(capture)存储录音的采样值,当buffer填满后声卡生成一个中断,kernel sound驱动使用DMA传输采样数据到内存buffer。类似的在playback时,应用buffer数据通过DMA传输给sound card的硬件buffer。
这个硬件buffer是一个ring buffers,意味着当到达buffer末端后,就会重新回到起始端。一个指针用来维护硬件buffer和应用buffer的当前位置。在内核外部,仅能访问application buffer,所以在这里我们主要讨论application buffer。
buffer的尺寸可以通过ALSA库函数调用指定。buffer可以非常的大,一次传输完整个buffer的数据可能导致无法接受的延迟,所以,ALSA把这个buffer分割为一系列periods,以period做为传输数据的单位。
Period由多个frames组成,每一个frames包含在一个时间点的采样值,对于立体声设备来说,一个frames包含两个channels的采用,图1演示了一个buffer, period,sample之间的关系,在这里,左右声道的数据存储在一帧中,这种模式称为interleaved。对于non-interleaved 模式,所有的左声道数据存放在一起,然后所有的右声道保存在一起。 - Over and Under Run
当一个声音设备被激活,数据持续的在硬件和应用buffer间传送。在录音情况下(capture),如果应用没有快速的从buffer中读取数据,环状buffer(声卡硬件buffer)将被新到的数据覆盖,导致数据丢失,我们称之为overrun。在playback情况下,如果应用无法快速传送数据到buffer中,那么导致hardware无数据可播放,这种情况我们称之为underrun。ALSA文档有时把这两种情况统称为XRUN。正确设计的应用最小化XRUN的发生并且在XRUN发生后能够恢复操作。
6. 下面提供一段伪代码来展示最常用的PCM接口编程模式,也就是capture或者playback模式:
open interface for capture or playback
set hardware parameters(access mode, data format, channels, rate, etc.)
while there is data to be processed:
read PCM data(capture) or write PCM data(playback)
close interface
- 合理的pcm_config可以做到更好的低时延和功耗。
struct pcm_config {
unsigned int channels;
unsigned int rate;
unsigned int period_size;
unsigned int period_count;
enum pcm_format format;
unsigned int start_threshold;
unsigned int stop_threshold;
unsigned int silence_threshold;
int avail_min;
};
(1)结构中的每个参数的单位都是frame(1帧 = 通道*采样位深):
period_size. 每次传输的数据长度。值越小,时延越小,cpu占用就越高。
(2)period_count. 缓冲区period的个数。缓冲区越大,发生XRUN的机会就越少。
(3)format. 定义数据格式,如采样位深,大小端。
(4)start_threshold. 缓冲区的数据超过该值时,硬件开始启动数据传输。如果太大, 从开始播放到声音出来时延太长,甚至可导致太短促的声音根本播不出来;如果太小, 又可能容易导致XRUN.
(5)stop_threshold. 缓冲区空闲区大于该值时,硬件停止传输。默认情况下,这个数为整个缓冲区的大小,即整个缓冲区空了,就停止传输。但偶尔的原因导致缓冲区空, 如CPU忙,增大该值,继续播放缓冲区的历史数据,而不关闭再启动硬件传输(一般此时有明显的声音卡顿),可以达到更好的体验。
(6)silence_threshold. 这个值本来是配合stop_threshold使用,往缓冲区填充静音数据,这样就不会重播历史数据了。
(7)avail_min. 缓冲区空闲区大于该值时,pcm_mmap_write()才往缓冲写数据。这个值越大,往缓冲区写入数据的次数就越少,面临XRUN的机会就越大.
在不同的场景下,合理的参数就是在性能、时延、功耗等之间达到较好的平衡。
- 在linux_kernel/sound/soc目录下有各家soc的实现,这里实现了本平台的一些设备的管理比如i2s, dmic等。
- 配置文件:
/etc/asound.conf
asound.conf允许对声卡或者设备进行更高级的控制,提供访问alsa-lib中的pcm插件方法,允许你做更多的复杂的控制,比如可以把声卡组合成一个或者多声卡访问多个I/O, 并且可以定义一些逻辑设备名称,该逻辑设备是在哪个声卡上。
参考资料 https://blog.csdn.net/weixin_41965270/article/details/81272710
/usr/share/alsa/alsa.conf为alsa-api的主要入口点
- 这里记录一些alsa里面的一些概念的意义:
frame : 一般frame是指一个采样点,包括所有的channel. 并不是指一段时间的采样点。所以frame size 就是一个frame的字节数,比如sample length 是16 bit, channel = 2, 则frame size = (16 / 8) * channel
参考文档:
https://blog.csdn.net/zyuanyun 这里有三篇博客介绍的非常详细,值得学习