ALSA应用层编程播放音乐

article/2025/10/3 2:51:49

关于ALSA,网上也有介绍,但是我在看的时候看的也是一脸懵逼,不是介绍的不好,是因为我之前对于嵌入式软件这一块实在没什么了解,之前一直学的JAVA,整个体系跟JAVA还是有很大的区别,要学的也完全是我之前没了解过的,所以以下有说错的请及时纠正。

功能

实现在linux中通过编程.C文件播放一个.wav格式的音频文件

播放: 将音频文件进行解码(Decode)生成PCM数据, 并将其送入音频设备中播出.

录音: 本程序暂时不涉及录音功能

ALSA

关于ALSA我不过多介绍,这篇笔记主要是记录我如何成功播放音乐,主要是怕误导别人,在我看来就是向上提供了接口供我们使用,向下控制了硬件播放音乐,跟JAVA中的JDK提供的接口函数类似,你只管使用就可以了。

  • Linux ALSA声卡驱动之一:ALSA架构简介

还有一个是ALSA的官方的教程好像是,播放音乐整体顺序我也是参考的这篇文章来写的。

  • ALSA Programming HOWTO

术语

关于ALSA中有一些术语着实是让我懵逼的一批,有几个术语我到现在还不知道理解的对不对,所以在正式编程之前一定要先知道,等自己编程的时候再理解一下。

因为声音是连续模拟量,计算机将它离散化之后用数字表示,就有了以下几个名词术语

  • 样本长度(sample):样本是记录音频数据最基本的单位,计算机对每个通道采样量化时数字比特位数,常见的有8位和16位。

这个样本长度后面编程时会用到的,按照字面意思理解的话,就是取出来8bit或者16bit的数据做样本;就理解成一个样本就可以,只不过一个样本的大小是8bit或者16bit,或者其他大小。

  • 通道(channel):该参数为1表示单声道,2则是立体声。

这个术语也好理解,单声道应该就是只有一个左耳机或右出声音,而立体就是左右耳机都出声的意思;每一个通道都有一个样本长度,单声道的数据就是一个样本长度(样本),立体声道的话2个样本长度(样本)。

  • 帧(frame):帧记录了一个声音单元,其长度为样本长度与通道数的乘积,一段音频数据就是由苦干帧组成的。

帧单位,把所有通道中的数据加在一起叫做一帧,所以单声道:一帧 = 样本长度 * 1;双声道:一帧 = 样本长度 * 2

  • 采样率(rate):每秒钟采样次数,该次数是针对帧而言,常用的采样率如8KHz的人声,44.1KHz的mp3音乐, 96Khz的蓝光音频。

这个在编程时也比较好设置,没什么混淆的,固定的值也就那么几个,一般没有随便乱设置的数值

  • 周期(period):音频设备一次处理所需要的桢数,对于音频设备的数据访问以及音频数据的存储,都是以此为单位。

一个周期用来存放若干个帧的单元,ALSA函数是以一个周期为单位来读取音频数据的,其实这个周期现在我也没很好的理解到底什么个意思,
是说ALSA向硬件输入数据时,是以时间为周期,在这个时间周期中输入若干个帧呢?
还是说就是一个固定存储空间大小叫做周期,然后这个周期大小的空间放入若干个帧? 真是让人头大!!!!!!!

  • 缓冲区(buffer): 用来存放将要被输入到硬件的数据,我这么理解也不知道对还是不对

一个缓冲区一般有两个周期,缓冲区是循环读取的,比如一个缓冲区有两个周期,那么硬件在读取一个缓冲区时便会产生两次中断,当第一个周期的音频数据被取走就准备取第二个周期的音频数据,同时第三个周期的音频数据会填充到第一个音频数据的位置,以此循环

  • 交错模式(interleaved):是一种音频数据的记录方式,分为交错模式和非交错模式

交错模式与非交错模式只是音频数据存放在缓冲区时的一种方式,
在交错模式下,数据以连续桢的形式存放,即首先记录完桢1的左声道样本和右声道样本(假设为立体声格式),再开始桢2的记录。
而在非交错模式下,首先记录的是一个周期内所有桢的左声道样本,再记录右声道样本,数据是以连续通道的方式存储。

看到上面的术语真是让人头大,但是不理解这些,等编程的时候也是瞎子摸象–一通乱摸,所以务必要先理解了这些术语,如果实在看不下去这术语,可以编程的时候走到哪一步时再回过来一一对照理解也可以。

ALSA编程

在用户空间使用ALSA需要安装依赖

alsa-lib: 用户空间函数库, 封装驱动提供的抽象接口, 通过文件libasound.so提供API给应用程序使用

alsa-utils: 实用工具包,通过调用alsa-lib实现播放音频(aplay)、录音(arecord) 等工具

安装

ubuntu 安装

$ sudo apt-get update
$ sudo apt-get install alsa-lib alsa-utils

Arch 安装

$ sudo pacman -Sy alsa-lib alsa-utils glibc

安装好以后需要运行 aplay -l 确认当前用户可以使用声卡设备

$ aplay -l

应用层声卡设备

如果没有显示图片中的内容,可以切换到root用户试一下,或者把当前用户加入到音频组也可以

$ gpasswd -a [user_name] audio
# 记得注销一下,或者切换用户后再切换回来,通过下面的命令可以查看当前用户是否已经加入audio组
$ groups [user_name]
# 再运行查看声卡设备是否有了
$ aplay -l

编程

代码我已经上传到github,可自行下载后运行。

  • GIthub ALSA 源码

文件分为:

  • Makefile:make 的配置文件
  • 1.wav:音频文件
  • alsa-myself-rw-wav-file-paly-music.c:C源码文件
  • const.h:C头文件

C源码文件中我每一步都有注释,使用的函数都可以在参考中的函数接口文档中找到。

在源码中我有一些自己的理解,如果觉得我的注释对你起不到辅助作用可以删除。

buff_size 和 frame 的关系

alsa中有这么两个函数 snd_pcm_hw_params_set_buffer_size函数 和 snd_pcm_writei 函数,在这之前我对这两个函数的第三个参数就特别混淆,不知道该设置什么,现在我有点明白了

首先这两个函数的第三个参数都是传入以 帧 为单位的值,所以根本就是同一个值,第一个函数设置的是每次硬件可以向多大的缓存中拿数据,第二个是应用层向多大的缓存输入多少数据,这里的数据都是 帧,所以我输入的就是你拿的数据,那可不就是一个东西嘛

所以buff_size大小就无所谓了,通常为 buffer_size = period_size * periods

最主要的是第三个参数frame如何计算,也特别好计算,就好像 RMB和美元的换算是一个道理

buffer_size = RMB

frame(帧) = 美元

钱的是1:7,在计算机中按照字节换算,首先要知道一帧(frame)等于到少字节,才能换算,以下使用16bit采样长度换算,如果是其他采样长度换掉采样长度即可。

# 这里除以8 是因为 1字节 = 8bit
1 帧(frame) = 采样长度 * 通道数 = 16 * 2 / 8 = 4
# 知道一帧的大小后,就可以计算总共的帧大小了,右移两位相当于除以4
frames = buffer_size >> 2;

经过计算的frames 就是要传入两个函数的第三个参数的值了。

使用

我自己是在树莓派4上编译使用的,进入到文件夹后,直接运行 make 命令即可在本目录出现可执行文件 alsa

$ git clone https://github.com/ywhs/rpi-arm64.git
$ cd ALSA
$ make
# -m 参数的意思音乐文件,-f 是术语中提到的样本长度,-r 是术语中提到的采样率
$ ./alsa -m 1.wav -f 161 -r 44
# 161 是 S16_LE 162 是 S16_BE,以此类推,或者直接运行 ./alsa 查看都可以设置什么值
$ ./alsa
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Either 1'st, 2'nd, 3'th or all parameters were missing 1'st : -m [music_filename] music_filename.wav 2'nd : -f [format 241bit or 16bit or 32bit] 161 for S16_LE, 162 for S16_BE 241 for S24_LE, 242 for S24_BE 2431 for S24_3LE, 2432 for S24_3BE 321 for S32_LE, 322 for S32_BE 3'th : -r [rate,44 or 88] 44 for 44100hz 82 for 88200hz For example: alsa -m 1.wav -f 161 -r 44 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++

一般不报错的话把耳机插入树莓派的耳机插口就可以听到音乐了

参考

  • ALSA project C 函数接口文档
  • Linux音频编程

http://chatgpt.dhexx.cn/article/usJxt92r.shtml

相关文章

ALSA系统简析

一 音频架构 如图所示 是 嵌入式系统的音频连接 音频编解码器将数字音频信号 转换成 扬声器播放所需要的模拟声音信号。而通过麦克风时,则执行相反的过程。 数字音频信号通过 PCM技术对模拟信号以某个比特率采样得到的,编解码器的任务就是以支持的PCM…

ALSA编程精华

https://www.cnblogs.com/cslunatic/p/3677729.html 一、前序 这里了解一下各个参数的含义以及一些基本概念。 声音是连续模拟量,计算机将它离散化之后用数字表示,就有了以下几个名词术语。 样本长度(sample):样本是记录音频数据最基本的…

ALSA学习笔记

文章目录 一、概述二、系统架构三、常用操作命令1、安装ALSA2、查看音频设备3、列出音频设备4、音量控制器 四、常见问题1、cannot open mixer: 没有那个文件或目录 一、概述 ALSA(Advanced Linux Sound Architecture),高级Linux声音架构的简…

音频ALSA架构简介

一、ALSA架构 ALSA(Advanced Linux Sound Architecture)即高级 Linux 声音架构。 嵌入式移动设备的音频子系统目前主要是ALSA 驱动 asoc 框架,其中包括 codec driver、 platform driver、 machine driver 等。 codec driver只关心 codec 本身…

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;需要人员…