音频ALSA架构简介

article/2025/10/3 3:21:01

一、ALSA架构

ALSA(Advanced Linux Sound Architecture)即高级 Linux 声音架构。
嵌入式移动设备的音频子系统目前主要是ALSA 驱动 asoc 框架,其中包括 codec driver、 platform driver、 machine driver 等。 codec driver只关心 codec 本身; platform driver 主要包括平台 cpu dai( 如 i2s) , dma 等部分;machine driver 主要将 platform driver 和 codec driver 连接起来。 platform 和 dai driver 一般修改较少, 主要修改machine 和 codec driver,这也将是我们后续分析的重点。

ALSA架构图如下:
ALSA框架图
可以看看之前写的文章进行对比参照:
Linux—ALSA音频工具arecord、aplay、amixer使用
ALSA驱动asoc框架之machine
ALSA驱动asoc框架之Codec
ALSA驱动asoc框架之Platform

二、其他说明

1.PCM接口

最简单的音频接口是PCM(脉冲编码调制)接口,该接口由时钟脉冲(BCLK)、帧同步信号(FS)及接收数据(DR)和发送数据(DX)组成。在FS信号的上升沿数据传输从MSB开始,FS频率等于采样频率。FS信号之后开始数据字的传输,单个数据位按顺序进行传输,一个时钟周期传输一个数据字。PCM接口很容易实现,原则上能够支持任何数据方案和任何采样频率,但需要每个音频通道获得一个独立的数据队列。

2.IIS接口

IIS接口在20世纪80年代首先被PHILIPS用于消费音频产品,并在一个称为LRCLK(Left/Right CLOCK)的信号机制中经过转换,将两路音频变成单一的数据队列。当LRCLK为高时,左声道数据被传输;LRCLK为低时,右声道数据被传输。与CPM相比,IIS更适合于立体声系统,当然,IIS的变体也支持多通道的时分复用,因此可以支持多通道。

3.linux ALSA音频设备驱动

ALSA系统包括包括驱动包alsa-driver、开发包alsa-libs、开发包插件alsa-libplugins、设置管理工具包alsa-utils、其他声音相关处理小程序包alsa-tools、特殊音频固件支持包alsa-firmware、OSS接口兼容模拟层工具alsa-oss供7个子项目,其中只有驱动包是必需的。

目前ALSA内核提供给用户空间的接口有:

(1)设备信息接口(/proc/asound)
(2)设备控制接口(/dev/snd/controlCX)
(3)混音器设备接口(/dev/snd/mixerCXDX)
(4)PCM设备接口(/dev/snd/pcmCXDX)
(5)原始MIDI(迷笛)设备接口(/dev/snd/midiCXDX)
(6)声音合成(synthesizer)设备接口(/dev/snd/seq)
(7)定时器接口(/dev/snd/timer)
这些接口被提供给alsa-lib使用,而不是给应用程序使用,应用程序最好使用alsa-lib,或者更高级的接口比如jack提供的接口。

4.linux ASoC音频设备驱动

ASoC是ALSA在SoC方面的发展和演变,它的本质仍然属于ALSA,但是在ALSA架构基础上对CPU相关的代码和Codec相关的代码进行了分离,其原因是采用传统ALSA架构情况下,同一型号的Codec工作于不同的CPU时,需要不同的驱动,这是不符合代码重用的要求的。

ASoC主要由3部分组成:
(1)Codec驱动,这一部分只关系Codec本身,与CPU相关的特性不由此部分操作
(2)平台驱动,这一部分只关心CPU本身,不关系Codec,它主要处理了两个问题:DMA引擎和SoC解除的PCM、IIS或AC’97数字接口控制。
(3)板驱动,这一部分将平台驱动和Codec驱动绑定在一起,描述了板一级的硬件特征

以上3部分中,1和2基本都可以仍然是通用的驱动了,即Codec驱动认为自己可以连接任意CPU,而CPU的IIS、PCM、或AC’97接口对应的平台驱动则认为自己可以连接符号其接口类型的Codec,只有3是不通用的,由特定的电路板上具体的CPU和Codec确定,因此它很像一个插座,上面插着Codec和平台这两个插头。ASoC的用户空间编程方法与ALSA完全一致。

5.card声卡和组件管理

/* 对于每个声卡,必须创建一个card实例,该函数用于创建card参数idx为索引号参数id为标识的字符串参数module一般指向THIS_MODULE参数extra_size是要分配的额外的数据的大小,分配的extra_size大小的内存将作为card->private_data*/struct snd_card *snd_card_new(int idx, const char *id,struct module *module, int extra_size)/* 注册声卡 */
int snd_card_register(struct snd_card *card)/* 释放(注销)声卡 */
int snd_card_free(struct snd_card *card)/* 创建一个ALSA设备部件参数type为设备类型,查看宏SNDRV_DEV_XXX*/
int snd_device_new(struct snd_card *card, snd_device_type_t type,void *device_data, struct snd_device_ops *ops)/* 释放声卡的设备参数device_data指向要设备的私有数据*/
int snd_device_free(struct snd_card *card, void *device_data)

6.PCM 设备接口

/* 创建PCM实例参数card指向声卡参数id是标识字符串参数device为PCM设备引索(0表示第1个PCM设备)参数playback_count为播放设备的子流数参数capture_count为录音设备的子流数参数指向构造的PCM实例*/
int snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count,struct snd_pcm ** rpcm)/* 设置PCM操作函数 参数direction,查看宏SNDRV_PCM_STREAM_XXX*/
void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops)/* 分配DMA缓冲区,仅当DMA缓冲区已预分配的情况下才可调用该函数 */
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)/* 释放由snd_pcm_lib_malloc_pages函数分配的一致性DMA缓冲区 */
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)/* 分配缓冲区的最简单的方法是调用该函数type的取值可查看宏SNDRV_DMA_TYPE_* */
int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,int type, void *data,size_t size, size_t max)

7.控制接口说明

/* 创建一个control实例----struct snd_kcontrol结构体参数ncontrol为初始化记录private_data为设置的私有数据 */
struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, void *private_data)/* 为声卡添加一个控制实例 */
int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)/* 驱动程序可中断服务程序中调用该函数来改变或更新一个control*/
void snd_ctl_notify(struct snd_card *card, unsigned int mask,struct snd_ctl_elem_id *id)

8.基于ALSA音频框架的驱动程序设计

1:struct snd_card *snd_card_new(int idx, const char *id,struct module *module, int extra_size);/* 创建一个声卡 */2:static struct snd_device_ops ops = {.dev_free =     xxx_free,};3:struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,void *private_data); /*  创建control实例 */4:int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol);    /* 为声卡添加控制实例 */5:int snd_device_new(struct snd_card *card, SNDRV_DEV_CODEC,void *device_data, struct snd_device_ops *ops);  /* 创建一个ALSA设备部件-----编解码设备 */6:int snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count,struct snd_pcm ** rpcm) /*  创建PCM实例 */8:int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,int type, void *data,size_t size, size_t max)  /* 分配缓冲区 */9:void snd_pcm_set_ops(struct snd_pcm *pcm, SNDRV_PCM_STREAM_PLAYBACK, struct snd_pcm_ops *ops)  /* 设置PCM操作函数----播放  */10:void snd_pcm_set_ops(struct snd_pcm *pcm, SNDRV_PCM_STREAM_CAPTURE, struct snd_pcm_ops *ops)  /* 设置PCM操作函数 ----录音 */11:/*音频相关的初始化:音频流相关、引脚等相关的初始化*/12:DMA相关的设置13:int snd_card_register(struct snd_card *card);  /* 注册声卡 */IRQ_AC97

9.ASoC音频驱动重要结构体

/**1:ASoC Codec驱动:*/struct snd_soc_dai { /* 数字音频接口,描述了Codec DAI(数字音频接口---Digital Audio Interface)和PCM配置 *//* DAI description */char *name;  /* 名字 */unsigned int id;  /* ID */int ac97_control;struct device *dev;/* DAI callbacks */int (*probe)(struct platform_device *pdev,struct snd_soc_dai *dai);void (*remove)(struct platform_device *pdev,struct snd_soc_dai *dai);int (*suspend)(struct snd_soc_dai *dai);int (*resume)(struct snd_soc_dai *dai);/* ops */struct snd_soc_dai_ops *ops;  /* DAI操作函数 *//* DAI capabilities */struct snd_soc_pcm_stream capture;  /* 录音流 */struct snd_soc_pcm_stream playback; /* 播放流 *//* DAI runtime info */struct snd_pcm_runtime *runtime;  /* PCM运行时 */struct snd_soc_codec *codec;  /* 编解码器 */unsigned int active;unsigned char pop_wait:1;void *dma_data;/* DAI private data */void *private_data;/* parent codec/platform */union {struct snd_soc_codec *codec;  /* 编解码器 */struct snd_soc_platform *platform;  /* 平台驱动----CPU */};struct list_head list; /* 用于形成链表 */
};struct snd_soc_codec { /* SoC音频编解码器---IO操作、动态音频电源管理以及时钟、PLL等控制 */char *name;  /* 名字 */struct module *owner; /* THIS_MODULE */struct mutex mutex;struct device *dev;struct list_head list; /* 用于形成链表 *//* callbacks */int (*set_bias_level)(struct snd_soc_codec *,enum snd_soc_bias_level level); /* 回调函数 *//* runtime */struct snd_card *card;  /* 声卡 */struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */ /* AC97设备 */unsigned int active;unsigned int pcm_devs;void *private_data;/* codec IO */void *control_data; /* codec control (i2c/3wire) data */unsigned int (*read)(struct snd_soc_codec *, unsigned int);int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);int (*display_register)(struct snd_soc_codec *, char *,size_t, unsigned int);hw_write_t hw_write;hw_read_t hw_read;void *reg_cache;short reg_cache_size;short reg_cache_step;/* dapm */u32 pop_time;struct list_head dapm_widgets;struct list_head dapm_paths;enum snd_soc_bias_level bias_level;enum snd_soc_bias_level suspend_bias_level;struct delayed_work delayed_work;/* codec DAI's */struct snd_soc_dai *dai; /* SoC层接口 */unsigned int num_dai;#ifdef CONFIG_DEBUG_FSstruct dentry *debugfs_reg;struct dentry *debugfs_pop_time;
#endif
};struct snd_soc_dai_ops { /* 数字音频接口DAI操作函数集 *//** DAI clocking configuration, all optional.* Called by soc_card drivers, normally in their hw_params.*/int (*set_sysclk)(struct snd_soc_dai *dai,int clk_id, unsigned int freq, int dir); /* 设置系统时钟 */int (*set_pll)(struct snd_soc_dai *dai,int pll_id, unsigned int freq_in, unsigned int freq_out); /* 设置PLL */int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);  /* 设置时钟分频 *//** DAI format configuration* Called by soc_card drivers, normally in their hw_params.*//* DAI格式配置 */int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt); /* 设置格式 */int (*set_tdm_slot)(struct snd_soc_dai *dai,unsigned int mask, int slots);int (*set_tristate)(struct snd_soc_dai *dai, int tristate);/** DAI digital mute - optional.* Called by soc-core to minimise any pops.*/int (*digital_mute)(struct snd_soc_dai *dai, int mute); /* 数字静音 *//** ALSA PCM audio operations - all optional.* Called by soc-core during audio PCM operations.*//* ALSA PCM音频操作 */int (*startup)(struct snd_pcm_substream *,struct snd_soc_dai *);void (*shutdown)(struct snd_pcm_substream *,struct snd_soc_dai *);int (*hw_params)(struct snd_pcm_substream *,struct snd_pcm_hw_params *, struct snd_soc_dai *);int (*hw_free)(struct snd_pcm_substream *,struct snd_soc_dai *);int (*prepare)(struct snd_pcm_substream *,struct snd_soc_dai *);int (*trigger)(struct snd_pcm_substream *, int,struct snd_soc_dai *);
};struct snd_soc_ops { /* SoC操作函数----Codec音频操作 */int (*startup)(struct snd_pcm_substream *);void (*shutdown)(struct snd_pcm_substream *);int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);int (*hw_free)(struct snd_pcm_substream *);int (*prepare)(struct snd_pcm_substream *);int (*trigger)(struct snd_pcm_substream *, int);
};/**2:ASoC平台驱动:**在ASoC平台驱动部分,同样存在着Codec驱动中的snd_soc_dai、snd_soc_dai_ops、snd_soc_ops这三个结构体的实例用于描述*DAI和DAI的操作,不过不同的是,在平台驱动中,它们只描述CPU相关的部分而不描述Codec。除此之外,在ASoC*平台驱动中,必须实现完整的DMA驱动,即传统ALSA的and_pcm_ops结构体成员函数trigger()、pointer()等,因此ASoC平台*驱动通常由DAI和DMA两部分组成。*//**ASoC板驱动:*在板驱动的模块初始化函数中,会通过platform_device_add()注册一个名为"soc-audio"的platform设备,这是因为soc-core.c*注册了一个名为"soc-audio"的platform驱动*/struct snd_soc_device { /* SoC设备 */struct device *dev; /* 内嵌的设备模型的设备 */struct snd_soc_card *card; /* SoC卡 */struct snd_soc_codec_device *codec_dev;  /* SoC编解码设备 */void *codec_data;  /* 编解码设备使用的数据 */
};struct snd_soc_dai_link  { /* 绑定ASoC Codec驱动和CPU端的平台驱动数据结构 */char *name;			/* Codec name */ /* 编解码器的名字 */char *stream_name;		/* Stream name */ /* 流的名字 *//* DAI */struct snd_soc_dai *codec_dai; /* SoC层的接口----编解码器端 */struct snd_soc_dai *cpu_dai;     /* SoC层的接口----CPU端*//* machine stream operations */struct snd_soc_ops *ops; /* SoC操作函数----流操作函数 *//* codec/machine specific init - e.g. add machine controls */int (*init)(struct snd_soc_codec *codec);  /* 初始化 *//* DAI pcm */struct snd_pcm *pcm; /* 指向pcm */
};

http://chatgpt.dhexx.cn/article/013nkpij.shtml

相关文章

Linux ALSA 之一:ALSA 架构简介

一、概述 ALSA是 Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构。 在 Linux 内核设备驱动层,ALSA 提供了 alsa-driver,在应用层,ALSA 为我们提供了 alsa-lib,故在其支持下&#…

ALSA (高级Linux声音架构)、ASOC基础知识

目录 第一节:什么是ALSA和ASOC 第二节:ALSA框架 第三节:ALSA的使用 第四节:ASOC的硬件框架 第四节:ASOC的软件框架 第一节:什么是ALSA和ASOC ALSA是Advanced Linux Sound Architecture,高级…

动态链接库dlopen的函数的使用

转自&#xff1a;http://blog.const.net.cn/a/17154.htm 编译时候要加入 -ldl (指定dl库) dlopen 基本定义 功能&#xff1a;打开一个动态链接库 [喝小酒的网摘]http://blog.const.net.cn/a/17154.htm 包含头文件&#xff1a; #include <dlfcn.h> 函数定义&#xff…

dlopen的用法

1、前言 为了使程序方便扩展&#xff0c;具备通用性&#xff0c;可以采用插件形式。采用异步事件驱动模型&#xff0c;保证主程序逻辑不变&#xff0c;将各个业务已动态链接库的形式加载进来&#xff0c;这就是所谓的插件。linux提供了加载和处理动态链接库的系统调用&#xff…

织梦上一篇下一篇没有了改为英文

织梦上一篇下一篇没有了改为英文 网站根目录找到 include/arc.archives.class.php 文件 打开找到 上一篇改为 Previous上一篇后面的“没有了” 改为 No Previous原图修改后 接着放下翻&#xff0c;紧贴着下面的“下一篇&#xff0c;没有了” 找到 下一篇改为 在这里插入…

在wordpress文章页中显示上一篇和下一篇文章

查看原文&#xff1a;http://www.hellonet8.com/1162.html 今天博主在看别人博客的时候发现他们的文章末尾会有显示上一篇和下一篇的文章链接&#xff0c;所以也想在自己的博客中添加这个功能。这么做顺便可以增加文章之间的相关性&#xff0c;对搜索引擎的蜘蛛也会友好些。废话…

js 实现 点击上一篇、下一篇功能

列表界面&#xff1a; 详细界面&#xff1a; 思路&#xff1a; 1. 首先目录列表渲染的数据是通过接口调用取到的值&#xff0c;然后点击具体某一条数据的时候&#xff0c;获取到他的 ID&#xff0c;然后通过路由跳转的时候带到详细信息页面。 2. 在详细页面中&#xff0c;先再…

Vue中 实现上一篇下一篇的功能

效果&#xff1a; 看下html页面 <div class"NewsDetails_cont_footer"><!-- 使用三目运算符判断 按钮是否可以点击 --><div click"last" :class"lastNoShow ? noClick : btn"><img src"../assets/img/newsDetail/公共…

java实现上一篇下一篇功能

根据文章类型查询&#xff0c;实现上一篇、下一篇的效果 自定义实体Dto(这里只放出扩展字段) Getter Setter public class OsArticleDto extends BaseDto {/** */private static final long serialVersionUID 1L;/** 上一篇文章id*/private String beforeId;/*** 上一篇文章…

vue实现上一篇下一篇

先来看一下效果图 请求回来所有文章&#xff0c;根据索引进行上一篇下一篇的判断 首先为两个按钮绑定点击事件 <buttonclick"last(lastId, columnId)":disabled"isLast":class"{ disClick: isLast true }">上一篇:{{ lastTitle }}</b…

Django针对上一篇和下一篇文章标题的实现逻辑

1、要求显示效果 2、前端html内容 <div><nav aria-label"..."><ul class"pager"><li><a href"/blog/detail/{{ previous_article.article_id }}">上一篇&#xff1a;{{ previous_article.title }}</a><…

程序员,我们应该如何去学习

IT技术的发展日新月异&#xff0c;新技术层出不穷&#xff0c;具有良好的学习能力&#xff0c;能及时获取新知识、随时补充和丰富自己&#xff0c;已成为程序员职业发展的核心竞争力。本文中&#xff0c;作者结合多年的学习经验总结出了提高程序员学习能力的三个要点。 众所周知…

程序员该如何学习新知识

想必大家都不是张无忌&#xff0c;人家三十年才可以练成的乾坤大挪移&#xff0c;张无忌大侠两个时辰就可以搞定&#xff0c;作为一个普通的程序员&#xff0c;经常遇到很多新技术和新知识&#xff0c;it界就是这样&#xff0c;日新月异&#xff0c; 那么我们如何学习一门技术和…

@程序员,这四个学习建议值得收藏

在我看来&#xff0c;学习能力应该是一个人最重要的能力之一。因为我们赖以生存的所有技能&#xff0c;无一例外都是通过学习获得的。那些优秀的人&#xff0c;也不过是学习能力或者学习效率比一般人强而已。 这样的观点被很多人论证过&#xff0c;商业理论家阿里德赫斯&#…

程序员学习视频教程汇总

转载请注明出处:http://blog.csdn.net/lowprofile_coding/article/details/51059080 在IT这个节凑快的行业&#xff0c;我们每天都需要学习&#xff0c;需要get新技能&#xff0c;才能不被淘汰&#xff0c;成功的人总是贵在坚持&#xff0c;我觉得有一句话说的很好&#xff1a;…

想自学一下程序员,该学些什么?

程序员是一门职业&#xff08;手动滑稽&#xff09;&#xff0c;需要自学的是编程哦。 编程分为一个方向&#xff0c;方向不同需要学习的东西也大不相同 大数据前端开发后端开发移动端开发移动开发市场游戏开发人工智能服务器开发等等 前端开发难度较高&#xff0c;需要人员…

程序员,这四个学习建议值得收藏

大家好&#xff0c;我是本周的值班编辑 江南一点雨 &#xff0c;本周将由我为大家排版并送出技术干货&#xff0c;大家可以在公众号后台回复“springboot”&#xff0c;获取最新版 Spring Boot2.1.6 视频教程试看。 在我看来&#xff0c;学习能力应该是一个人最重要的能力之一。…

做程序员,需要学习哪些专业知识?

一、两大能力 1、学习能力 为什么需要学习能力,因为技术不会一直停着不动,可能当你刚在学校出来的时候,是公司的佼佼者,但是如果你不学习,当别人会HTML6/7/8的时候,你还只是在HTML5上徘徊。而且当你遇到困难的时候,如果是有大牛给你解决了问题,但是你只是照搬并没有真…

AJAX-设置同步

AJAX-设置同步 一.同步方式与异步方式的区别&#xff1a; 1.1.同步方式发送请求&#xff1a;发送一个请求&#xff0c;需要等待响应返回&#xff0c;然后才能够发送下一个请求&#xff0c;如果该请求没有响应&#xff0c;不能发送下一个请求&#xff0c;客户端会一直处于等待…

js设置ajax执行顺序,ajax同步处理(使得JS按顺序执行)

在项目中碰到一个问题: 图一: 图二: 函数1代码:这里是因为有ajax请求,默认的是异步的 //点击分页页码,请求后台返回对应页码的数据 function getdata(fewPage,flag,content){$.getJSON(getUrl()+/myAccount/getMyOrders.do?curPage=+fewPage+&flag=+flag+&conte…