Linux-音频应用编程

ALPHA I.MX6U 开发板支持音频,板上搭载了音频编解码芯片 WM8960,支持播放以及录音功能!本章我们来学习 Linux 下的音频应用编程,音频应用编程相比于前面几个章节所介绍的内容、其难度有所上升,但是笔者仅向大家介绍 Linux 音频应用编程中的基础知识,而更多细节、更加深入的内容需要大家自己去学习。

本章将会讨论如下主题内容。

⚫ Linux 下 ALSA 框架概述;

⚫ alsa-lib 库介绍;

⚫ alsa-lib 库移植;

⚫ alsa-lib 库的使用;

⚫ 音频应用编程之播放;

⚫ 音频应用编程之录音。


ALSA****概述

ALSA 是 Advanced Linux Sound Architecture(高级的 Linux 声音体系)的缩写,目前已经成为了 linux下的主流音频体系架构,提供了音频和 MIDI 的支持,替代了原先旧版本中的 OSS(开发声音系统);学习过 Linux 音频驱动开发的读者肯定知道这个;事实上,ALSA 是 Linux 系统下一套标准的、先进的音频驱动框架,那么这套框架的设计本身是比较复杂的,采用分离、分层思想设计而成,具体的细节便不给大家介绍了!作为音频应用编程,我们不用去研究这个。

在应用层,ALSA 为我们提供了一套标准的 API,应用程序只需要调用这些 API 就可完成对底层音频硬件设备的控制,譬如播放、录音等,这一套 API 称为 alsa-lib。如下图所示:


alsa-lib****简介

如上所述,alsa-lib 是一套 Linux 应用层的 C 语言函数库,为音频应用程序开发提供了一套统一、标准的接口,应用程序只需调用这一套 API 即可完成对底层声卡设备的操控,譬如播放与录音。

用户空间的 alsa-lib 对应用程序提供了统一的 API 接口,这样可以隐藏驱动层的实现细节,简化了应用程序的实现难度、无需应用程序开发人员直接去读写音频设备节点。所以本章,对于我们来说,学习音频应用编程其实就是学习 alsa-lib 库函数的使用、如何基于 alsa-lib 库函数开发音频应用程序。

ALSA 提供了关于 alsa-lib 的使用说明文档,其链接地址为:https://www.alsa-project.org/alsa-doc/alsa-lib/,

进入到该链接地址后,如下所示:

alsa-lib 库支持功能比较多,提供了丰富的 API 接口供应用程序开发人员调用,根据函数的功能、作用将这些 API 进行了分类,可以点击上图中 Modules 按钮查看其模块划分,如下所示:


一个分类就是一个模块(module),有些模块下可能该包含了子模块,譬如上图中,模块名称前面有三角箭头的表示该模块包含有子模块。

⚫ Global defines and functions:包括一些全局的定义,譬如函数、宏等;

⚫ Constants for Digital Audio Interfaces:数字音频接口相关的常量;

⚫ Input Interface:输入接口;

⚫ Output Interface:输出接口;

⚫ Error handling:错误处理相关接口;

⚫ Configuration Interface:配置接口;

⚫ Control Interface:控制接口;

⚫ PCM Interface:PCM 设备接口;

⚫ RawMidi Interface:RawMidi 接口;

⚫ Timer Interface:定时器接口;

⚫ Hardware Dependant Interface:硬件相关接口;

⚫ MIDI Sequencer:MIDI 音序器;

⚫ External PCM plugin SDK:外部 PCM 插件 SDK;

⚫ External Control Plugin SDK:外部控制插件 SDK;

⚫ Mixer Interface:混音器接口;

⚫ Use Case Interface:用例接口;

⚫ Topology Interface:拓扑接口。

可以看到,alsa-lib 提供的接口确实非常多、模块很多,以上所列举出来的这些模块,很多模块笔者也不是很清楚它们的具体功能、作用,但是本章我们仅涉及到三个模块下的 API 函数,包括:PCM Interface、Error Interface 以及 Mixer Interface。

PCM Interface
PCM Interface,提供了 PCM 设备相关的操作接口,譬如打开/关闭 PCM 设备、配置 PCM 设备硬件或软件参数、控制 PCM 设备(启动、暂停、恢复、写入/读取数据),该模块下还包含了一些子模块,如下所示:

点击模块名称可以查看到该模块提供的API接口有哪些以及相应的函数说明,这里就不给大家演示了!
Error Interface

该模块提供了关于错误处理相关的接口,譬如函数调用发生错误时,可调用该模块下提供的函数打印错误描述信息。

Mixer Interface

提供了关于混音器相关的一系列操作接口,譬如音量、声道控制、增益等等。


sound****设备节点

在 Linux 内核设备驱动层、基于 ALSA 音频驱动框架注册的 sound 设备会在/dev/snd 目录下生成相应的设备节点文件,譬如 ALPHA I.MX6U 开发板出厂系统/dev/snd 目录下有如下文件:

Tips:注意,Mini I.MX6U 开发板出厂系统/dev/snd 目录下是没有这些文件的,因为 Mini 板不支持音频、没有板载音频编解码芯片,所以本章实验例程无法在 Mini 板上进行测试,请悉知!
从上图可以看到有如下设备文件:
controlC0**:**用于声卡控制的设备节点,譬如通道选择、混音器、麦克风的控制等,C0 表示声卡 0(card0);

pcmC0D0c**:**用于录音的 PCM 设备节点。其中 C0 表示 card0,也就是声卡 0;而 D0 表示 device0,也就是设备 0;最后一个字母 c 是 capture 的缩写,表示录音;所以 pcmC0D0c 便是系统的声卡0 中的录音设备 0;

pcmC0D0p**:**用于播放(或叫放音、回放)的 PCM 设备节点。其中 C0 表示 card0,也就是声卡 0;而 D0 表示 device 0,也就是设备 0;最后一个字母 p 是 playback 的缩写,表示播放;所以 pcmC0D0p便是系统的声卡 0 中的播放设备 0;

pcmC0D1c**:**用于录音的 PCM 设备节点。对应系统的声卡 0 中的录音设备 1;

pcmC0D1p**:**用于播放的 PCM 设备节点。对应系统的声卡 0 中的播放设备 1。

timer**:**定时器。

本章我们编写的应用程序,虽然是调用 alsa-lib 库函数去控制底层音频硬件,但最终也是落实到对 sound设备节点的 I/O 操作,只不过 alsa-lib 已经帮我们封装好了。在 Linux 系统的/proc/asound 目录下,有很多的文件,这些文件记录了系统中声卡相关的信息,如下所示:


cards**:**

通过"cat /proc/asound/cards"命令、查看 cards 文件的内容,可列出系统中可用的、注册的声卡,如下所示:

cat /proc/asound/cards

我们的阿尔法板子上只有一个声卡(WM8960 音频编解码器),所以它的编号为 0,也就是 card0。系统中注册的所有声卡都会在/proc/asound/目录下存在一个相应的目录,该目录的命名方式为 cardX(X 表示 声卡的编号),譬如图 28.3.2 中的 card0;card0 目录下记录了声卡 0 相关的信息,譬如声卡的名字以及声卡注册的 PCM 设备,如下所示:

devices**:**
列出系统中所有声卡注册的设备,包括 control、pcm、timer、seq 等等。如下所示:

cat /proc/asound/devices


pcm**:**

列出系统中的所有 PCM 设备,包括 playback 和 capture:

cat /proc/asound/pcm


alsa-lib****移植

因为 alsa-lib 是 ALSA 提供的一套 Linux 下的 C 语言函数库,需要将 alsa-lib 移植到开发板上,这样基于 alsa-lib 编写的应用程序才能成功运行,除了移植 alsa-lib 库之外,通常还需要移植 alsa-utils,alsa-utils 包含了一些用于测试、配置声卡的工具。

事实上,ALPHA I.MX6U 开发板出厂系统中已经移植了 alsa-lib 和 alsa-utils,本章我们直接使用出厂系统移植好的 alsa-lib 和 alsa-utils 进行测试,笔者也就不再介绍移植过程了。其实它们的移植方法也非常简单,如果你想自己尝试移植,网上有很多参考,大家可以自己去看看。

alsa-utils 提供了一些用于测试、配置声卡的工具,譬如 aplay、arecord、alsactl、alsaloop、alsamixer、amixer 等,在开发板出厂系统上可以直接使用这些工具,这些应用程序也都是基于 alsa-lib 编写的。

aplay

aplay 是一个用于测试音频播放功能程序,可以使用 aplay 播放 wav 格式的音频文件,如下所示:


程序运行之后就会开始播放音乐,因为 ALPHA 开发板支持喇叭和耳机自动切换,如果不插耳机默认从喇叭播放音乐,插上耳机以后喇叭就会停止播放,切换为耳机播放音乐,这个大家可以自己进行测试。

需要注意的是,aplay 工具只能解析 wav 格式音频文件,不支持 mp3 格式解码,所以无法使用 aplay 工具播放 mp3 音频文件。稍后笔者会向大家介绍如何基于 alsa-lib 编写一个简单地音乐播放器,实现与 aplay相同的效果。

更多命令参考正电原子应用开发手册即可。

暂略。


编写一个简单的alsa-lib应用程序

本小节开始,我们来学习如何基于 alsa-lib 编写音频应用程序,alsa-lib 提供的库函数也别多,笔者肯定不会全部给大家介绍,只介绍基础的使用方法,关于更加深入、更加详细的使用方法需要大家自己去研究、学习。

对于 alsa-lib 库的使用,ALSA 提供了一些参考资料来帮助应用程序开发人员快速上手 alsa-lib、基于alsa-lib 进行应用编程,以下笔者给出了链接:

https://users.suse.com/\~mana/alsa090_howto.html

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

第一份文档向用户介绍了如何使用 alsa-lib 编写简单的音频应用程序,包括 PCM 播放音频、PCM 录音等,笔者也是参考了这份文档来编写本章教程,对应初学者,建议大家看一看。

第二个链接地址是 ALSA 提供的一些示例代码,如下所示:


点击对应源文件即可查看源代码。

以上便是 ALSA 提供的帮助文档以及参考代码,链接地址已经给出了,大家有兴趣可以看一下。

本小节笔者将向大家介绍如何基于 alsa-lib 编写一个简单地音频应用程序,譬如播放音乐、录音等;但在此之前,首先我们需要先来了解一些基本的概念,为后面的学习打下一个坚实的基础!

一些基本概念

主要是与音频相关的基本概念,因为在 alsa-lib 应用编程中会涉及到这些概念,所以先给大家进行一个简单地介绍。

样本长度(Sample

样本是记录音频数据最基本的单元,样本长度就是采样位数,也称为位深度(Bit Depth、Sample Size、Sample Width)。是指计算机在采集和播放声音文件时,所使用数字声音信号的二进制位数,或者说每个采样样本所包含的位数(计算机对每个通道采样量化时数字比特位数),通常有 8bit、16bit、24bit 等。

声道数(channel

分为单声道(Mono)和双声道/立体声(Stereo)。1 表示单声道、2 表示立体声。

帧(frame

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

把所有声道中的数据加在一起叫做一帧,对于单声道:一帧 = 样本长度 * 1;双声道:一帧 = 样本长度 * 2。譬如对于样本长度为 16bit 的双声道来说,一帧的大小等于:16 * 2 / 8 = 4 个字节。
更多命令参考正电原子应用开发手册即可。

暂略。

关于音频,先了解吧。

后面如果要接触,再来深入学习。


补充

什么是PCM设备?

脉冲编码调制(Pulse Code Modulation,简称PCM)是一种将模拟信号转换为数字信号的技术。这种转换过程是通过测量模拟信号的特征点(例如电压或电流)并将其编码为二进制数字数据来实现的。

在通信系统中,PCM设备的作用主要体现在以下几个方面:

  • 低速业务转换:PCM设备可以将各种低速业务转换成数字信号,并装入64kbit/s通道。这些低速业务包括但不限于语音电话、热线电话、磁石电话等,以及2W/4W模拟音频、RS-232、RS-422、RS-485、V.35、G.703同向64kbit/s以太网等。

  • 多路复用:PCM设备具有将30路64kbit/s通道复接成2Mbit/s的能力,从而实现了多路复用的功能。这意味着在同一条物理线路上可以同时传输多路信号,提高了线路的利用率和通信效率。

  • 信号传输:在光纤通信系统中,PCM设备发挥着重要作用。光纤中传输的二进制光脉冲"0"码和"1"码,就是由二进制数字信号对光源进行通断调制产生的,而数字信号正是通过对连续变化的模拟信号进行抽样、量化和编码得到的,这就是PCM的过程。

  • 接口类型多样:PCM设备的接口类型丰富多样,包括环路中继接口、用户线接口、二线音频接口、四线音频接口、异步RS232/V.24接口、同步RS232、RS422接口、RS485接口、V.35接口、G.703 64Kb/s同向数据接口等。

总的来说,PCM设备在现代通信系统中扮演着重要角色,它不仅能够实现模拟信号到数字信号的转换,还能够通过多路复用技术提高通信效率,满足不同用户对数据传输速率的需求。

相关推荐
超喜欢下雨天10 分钟前
服务器安装 ros2时遇到底层库依赖冲突的问题
linux·运维·服务器·ros2
搬码临时工16 分钟前
小企业如何搭建本地私有云服务器,并设置内部网络地址提供互联网访问
运维·服务器
阿蒙Amon31 分钟前
【Python小工具】使用 OpenCV 获取视频时长的详细指南
python·opencv·音视频
tan77º1 小时前
【Linux网络编程】网络基础
linux·服务器·网络
18你磊哥1 小时前
Windows 本地安装部署 Apache Druid
运维·debian
笑衬人心。2 小时前
Ubuntu 22.04 + MySQL 8 无密码登录问题与 root 密码重置指南
linux·mysql·ubuntu
IT成长日记2 小时前
【Docker基础】Docker数据卷管理:docker volume inspect及其参数详解
运维·docker·容器·volume·inspect
ldj20202 小时前
Jenkins 构建过程常见错误
运维·jenkins
ladymorgana2 小时前
【Docker】如何设置 `wiredTigerCacheSizeGB` 和 `resources.limits.memory`
运维·docker·容器
aqi002 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体