4

ALSA(一)

 2 years ago
source link: http://antkillerfarm.github.io/technology/2015/11/13/alsa.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

ALSA(一)

2015-11-13

ALSA(Advanced Linux Sound Architecture)是Linux内核默认的音频驱动框架。以下是一些参考资料:

http://www.alsa-project.org/main/index.php/Tutorials_and_Presentations

包含官方提供的若干教程。

http://blog.sina.com.cn/s/blog_5707eebf0100tjbe.html

这篇文章介绍了ALSA的代码文件结构。

http://blog.csdn.net/droidphone/article/details/6271122

系列文章共8篇,详细介绍了ALSA和ASOC。该博客还有7篇与DAPM有关的博文。

http://blog.chinaunix.net/uid-20602659-id-2981336.html

系列文章共2篇。主要偏重于ASOC驱动的具体实现,而不是背后的机制原理。

https://www.alsa-project.org/alsa-doc/alsa-lib/

C library reference

http://equalarea.com/paul/alsa-audio.html

A Tutorial on Using the ALSA Audio API

关于ALSA的应用层

查找PC本机驱动

首先使用lspci -v列出所有的硬件设备,其中当然也包括了音频设备。例如我的PC上是这样的:

Audio device: Intel Corporation 6 Series/C200 Series Chipset Family High Definition Audio Controller (rev 05)
	Subsystem: ASUSTeK Computer Inc. Device 1b73
	Flags: bus master, fast devsel, latency 0, IRQ 54
	Memory at df000000 (64-bit, non-prefetchable) [size=16K]
	Capabilities: <access denied>
	Kernel driver in use: snd_hda_intel

从驱动名snd_hda_intel可以很轻松的发现,这个驱动的源代码是sound/pci/hda/hda_intel.c。

在/dev/snd下,可以看到声卡的子设备。

对于ASoC驱动来说,因为它是platform驱动,所以可以在/sys/bus/platform下,找到驱动的相关痕迹。

alsa-utils

alsa-utils是ALSA项目提供的工具软件集,以下是一些测试样例:

播放wave文件

aplay /mnt/nfs/test.wav

变频播放(以44 KHz来播放音频)

aplay -D rate_44k /mnt/nfs/test.wav

录音,以20秒的间隔(-d 20),立体声(-c 2),频率是 8000Hz来录制Wave格式音频

arecord -d 20 -c 2 -t wav -r 8000 -f "Signed 16 bit Little Endian" /mnt/nfs/test.wav

测试混音播放(先是播放test1.wav,然后再同时播放test2.wav)

aplay -D plug:dmix_44k /mnt/nfs/test1.wav & aplay -D plug:dmix_44k /mnt/nfs/test2.wav

设置放音增益(0 to 3)

amixer set Master 1

设置录音音量(0-31)

amixer set Line 10

列出可用的ALSA设备

aplay -L

ALSA System on Chip (ASoC) layer

这是专为嵌入式设备设计的ALSA框架。官网是:

http://www.alsa-project.org/main/index.php/ASoC

上图是TI某平台的ASOC架构图。其中的ASoC layer可分为三个不同的部分:

1.Codec driver。这部分是平台无关的,包括了音频接口配置、音频控制、电源管理和其他IO功能。所谓平台是若干使用相同SOC的机型的集合。

2.Platform driver。这部分主要是平台相关的音频接口驱动和相关的DMA驱动。

3.Machine driver。这部分用于前两部分之间的协调,起到粘合剂的作用。它通常是与具体使用的机器相关的。

https://processors.wiki.ti.com/index.php/Sitara_Linux_Audio_Driver_Overview

Sitara Linux Audio Driver Overview


以三星设备为例,Platform driver和Machine driver在sound/soc/samsung下,该文件夹的Makefile节选如下:

# S3c24XX Platform Support
snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o

obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o
obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o

# S3C24XX Machine Support
snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o

obj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC) += snd-soc-s3c24xx-simtec.o

Codec driver在sound/soc/codecs下,该文件夹的Makefile节选如下:

snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o

obj-$(CONFIG_SND_SOC_UDA134X)	+= snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o

snd_soc_register_codec函数用来注册Codec driver。

snd_soc_register_component函数用来注册外设接口驱动(例如I2S)。

snd_soc_register_platform函数用来注册Platform driver。

Machine driver通过snd_soc_dai_link结构的codec_name和platform_name成员,指定匹配的Codec driver和Platform driver。

三部分的加载顺序:

1.驱动对象的加载,一般在模块加载时进行。各对象的先后顺序无关紧要。

2.设备对象的加载,一般在驱动对象加载之后,而在ALSA加载之前。由于模块加载的初始化优先级是device_initcall,而alsa_sound_last_init函数的初始化优先级是late_initcall_sync,因此设备对象的初始化优先级设为late_initcall是比较妥当的。

3.由于Machine driver在Codec driver和Platform driver之间起着桥梁作用,因此需要在加载了Codec和Platform的设备对象之后,再进行Machine设备对象的加载。

PCM, I2S, DMA

无论何种形式的音频,最终都要转换成PCM格式的数据(有时也被称作原始音频数据),然后才能发送给相关的硬件。CPU和音频芯片之间的数据接口,在PC上主要是AC97接口,而在嵌入式设备中,则多为I2S接口。

通常使用DMA方式传输原始音频数据。在3.X内核中,一般将PCM的DMA功能设置为Platform driver,而将I2S功能设置为该driver的一个component。

从DMA的实现方式来看,主要分两种:

1.Platform driver包含i2s和pcm两个文件。它的核心是设置snd_pcm_ops数据结构。例如pxa2xx-i2s.c和pxa2xx-pcm.c。

2.Platform driver只包含i2s一个文件。它主要调用与dmaengine_pcm_platform结构相关的函数。例如s3c24xx-i2s.c。

ASoC术语表

DAI: Digital Audio Interface

DAPM:Dynamic Audio Power Management

ALSA从2.6.X到3.X的变化细节

网上的文章有不少都是针对2.6.X的老内核的,现将3.X引入的变化,罗列如下:(自己摘录,仅供备忘,非官方内容)

1.snd_card_create改为snd_card_new。

http://www.rosoo.net/a/201203/15847.html

ALSA SOC在Linux3.1上的一些改进

从Gstreamer到ALSA

Gstreamer通过alsasink和alsasrc这两个插件访问ALSA API。这两个插件在gst-plugins-base代码的ext/alsa文件夹下。

gst-plugins-base的git地址是:

git://anongit.freedesktop.org/gstreamer/gst-plugins-base

ALSA API的代码在alsa-lib中,它的git地址是:

git://git.alsa-project.org/alsa-lib.git

ALSA API会调用ALSA driver,而driver的代码肯定在linux内核中。

alsa-lib中的很多常用功能,被做成了动态链接库,例如:alsa-lib/libasound_module_pcm_pulse.so,这些链接库的代码在alsa-plugins中。alsa-plugins的git地址是:

git://git.alsa-project.org/alsa-plugins.git

aplay等ALSA工具的代码在alsa-utils中,其地址如下:

git://git.alsa-project.org/alsa-utils.git

1.open操作

gst-plugins-base/ext/alsa/gstalsasink.c: gst_alsasink_open

这一步之后,进入alsa-lib作用域。

alsa-lib/src/pcm/pcm.c: snd_pcm_open

alsa-lib/src/pcm/pcm.c: snd_pcm_open_noupdate

alsa-lib/src/pcm/pcm.c: snd_pcm_open_conf

alsa-lib中pcm有很多插件,提供诸如硬件、复制、线性变换等操作。详见alsa-lib/include/pcm_plugin.h。

alsa-lib/src/pcm/pcm_hw.c: _snd_pcm_hw_open

alsa-lib/src/pcm/pcm_hw.c: snd_pcm_hw_open

alsa-lib/include/local.h: snd_open_device

这个函数调用open系统调用,进入内核空间。在内核空间中根据driver模型的不同,进入linux-kernel/sound/core/pcm_native.c: snd_pcm_open(ALSA)或linux-kernel/sound/soc/soc-pcm.c: soc_pcm_open(ASOC),注意这里的snd_pcm_open和alsa-lib中的snd_pcm_open名称相同,参数却不同。

2.write操作

gst-plugins-base/ext/alsa/gstalsasink.c: gst_alsasink_write

这一步之后,进入alsa-lib作用域。

alsa-lib/src/pcm/pcm.c: snd_pcm_writei

alsa-lib有两种写操作:snd_pcm_writei和snd_pcm_writen。snd_pcm_writei表示写入交织的PCM数据,而snd_pcm_writen表示写入非交织的PCM数据。

alsa-lib/src/pcm/pcm_local.h: _snd_pcm_writei

alsa-lib/src/pcm/pcm_hw.c: snd_pcm_hw_writei

这个函数调用ioctl系统调用,参数为SNDRV_PCM_IOCTL_WRITEI_FRAMES,pcm数据的buffer,放在snd_xferi结构中,传入内核空间。

linux-kernel/sound/core/pcm_native.c: snd_pcm_playback_ioctl1

linux-kernel/sound/core/pcm_lib.c: snd_pcm_lib_write

linux-kernel/sound/core/pcm_lib.c: snd_pcm_lib_write_transfer

这里会有两个分支,以下仅讨论DMA方式的处理。pcm数据放在snd_pcm_substream.runtime->dma_area指向的缓冲区。open的时候,使用mmap做好snd_pcm_substream.runtime->dma_area和snd_pcm_substream.runtime->dma_addr之间的映射。底层驱动只须操作dma_addr即可。

3.音量操作

音量操作虽然在Gstreamer和ALSA中都有,但彼此并无调用关系。Gstreamer中的音量调整是用软件改变PCM数据实现的,可称为软音量。与之相对的是音频硬件功放经扬声器所产生的音量,是为硬音量。ALSA的音量可以是软音量,也可以是硬音量。

Gstreamer的音量操作步骤:

gst-plugins-base/gst/playback/gstplaysink.c: gst_play_sink_set_volume

这个函数主要是通过设置playsink的volume属性来实现操作。

Gstreamer软音量的实现代码在gst-plugins-base/gst/volume/gstvolume.c中。

除此之外,还有流媒体音量设置gst_stream_volume_set_volume。详见gst-plugins-base/gst-libs/gst/audio/streamvolume.c。

ALSA设置音量的代码片段如下(注意,该片段没有异常处理):

void SetAlsaMasterVolume(long volume)
{
    long min, max;
    snd_mixer_t *handle;
    snd_mixer_selem_id_t *sid;
    const char *card = "default";
    const char *selem_name = "Master";

    snd_mixer_open(&handle, 0);
    snd_mixer_attach(handle, card);
    snd_mixer_selem_register(handle, NULL, NULL);
    snd_mixer_load(handle);

    snd_mixer_selem_id_alloca(&sid);
    snd_mixer_selem_id_set_index(sid, 0);
    snd_mixer_selem_id_set_name(sid, selem_name);
    snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);

    snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
    snd_mixer_selem_set_playback_volume_all(elem, volume * max / 100);

    snd_mixer_close(handle);
}

ALSA的音量操作步骤:(只讨论硬音量)

alsa-lib/src/mixer/simple.c: snd_mixer_selem_set_playback_volume

alsa-lib/src/mixer/simple_none.c: _snd_mixer_selem_set_volume

这一步将volume数据放到selem_none_t.str.vol结构中。后面都是针对这个结构的写操作。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK