Android音频学习(二十一)——ALSA简介

ALSA(Advanced Linux Sound Architecture)Linux标准音频驱动框架, 对于android系统来说使用的是一个精简版的ALSA架构,即TinyALSA, 有一部分ALSA的接口是在用户空间,供上层调用来连接kernel。

根据音频数据的流向再把音频内核分为以下三个层次:

  • Tinyalsa
  • ALSA Core
  • ASoC

1. TinyAlsa

1.1tinyAlsa简介

TinyAlsa 是 Android 社区为了满足移动嵌入式设备需求而开发的一个轻量级、精简的 ALSA 用户空间库和工具集 。它的核心目标是提供一个最小化的 ALSA API 实现,用于替代 GNU/Linux 发行版中常见的、功能庞大而复杂的 alsa-lib

您可以把它理解为 ALSA 的"嵌入式版本"

在标准的 Linux 桌面发行版中,应用程序通过 alsa-lib 这个庞大的库来访问音频设备。alsa-lib 功能非常全面,包括:

  • 复杂的插件系统(用于格式转换、重采样、多路复用等)

  • 复杂的配置文件和解析器 (alsa.conf)

  • 支持众多不常用的音频格式和功能

对于 Android 这样的嵌入式系统,alsa-lib 显得过于臃肿, tinyalsa与alsa对比如下:

特性 标准 alsa-lib TinyAlsa
体积和依赖 庞大,依赖多 极其轻量,代码量小,依赖少
配置 支持复杂的配置文件 无配置文件或非常简单,通常通过代码硬编码或传递参数
功能 功能全面,插件丰富 功能精简,只保留最基本、必需的 PCM 和 Control 操作
目标平台 通用桌面/服务器 Linux Android 及其他嵌入式 Linux
设计哲学 "大而全" **"小而美",满足基本需求即可

因此,TinyAlsa 应运而生,它剥离了 alsa-lib 中绝大多数嵌入式设备用不到的高级功能和插件,只保留了最核心的与内核 ALSA 驱动交互的能力。

1.2 TinyAlsa核心组件

tinyalsa在android的external/tinyalsa目录中,TinyAlsa 主要包含两部分:

(1)库文件 (libtinyalsa.so)

这是一个轻量级的动态库,提供了操作音频设备的核心 API。主要头文件包括:

  • tinyalsa/pcm.h:用于播放和录制 PCM 音频数据。

    • 关键结构体:struct pcm

    • 关键函数:pcm_open, pcm_write, pcm_read, pcm_close, pcm_get_params 等。

  • tinyalsa/mixer.h:用于控制音频路径、音量等。

    • 关键结构体:struct mixer, struct mixer_ctl

    • 关键函数:mixer_open, mixer_get_ctl_by_name, ctl_set_value 等。

(2)命令工具集

这些工具对于开发和调试音频问题至关重要,通常位于 Android 设备的 /system/bin/ 下:

  • tinyplay: 用于播放原始 PCM 音频文件。

    • 用法:tinyplay /sdcard/test_44100_s16le.wav
  • tinycap: 用于录制音频并保存为原始 PCM 文件。

    • 用法:tinycap /sdcard/recording.wav -r 48000 -c 2
  • tinymix : 用于查看和设置混音器控件,是调试音频路由和音量的神器

    • 用法:tinymix (查看所有控件), tinymix "Playback Path" 1 (设置某个控件的值)
  • tinypcminfo: 用于查看 PCM 设备的能力和参数,如支持的采样率、声道数、格式等。

    • 用法:tinypcminfo -D 0

1.3.TinyAlsa 在 Android 音频架构中的位置

Application\] -\> \[Java/Native Framework\] -\> \[AudioFlinger\] -\> \[Audio HAL

|

v

TinyAlsa (libtinyalsa.so)\] \<-- 在这里! \| v \[Kernel ALSA Driver \& /dev/snd/ nodes

它的主要使用者是 Audio HAL 的实现。

设备制造商在编写他们的 audio.primary.[platform].so(即 Primary Audio HAL)时,通常会选择链接 libtinyalsa.so,然后使用 TinyAlsa 的 API 来:

  • 打开/关闭 PCM 设备 (pcm_open, pcm_close)

  • 写入播放数据 (pcm_write) 或读取录制数据 (pcm_read)

  • 通过 Mixer API 设置音量、切换音频路径(如从扬声器切换到耳机)

1.4代码示例

cpp 复制代码
#include <tinyalsa/pcm.h>

int play_audio(const char *data, size_t size) {
    // 1. 配置 PCM 参数
    struct pcm_config config = {
        .channels = 2,          // 立体声
        .rate = 44100,          // 采样率 44.1kHz
        .format = PCM_FORMAT_S16_LE, // 采样格式:有符号16位小端
        .period_size = 1024,    // 周期大小(帧数)
        .period_count = 4,      // 周期个数
        .start_threshold = 0,
        .stop_threshold = 0,
        .silence_threshold = 0,
    };

    // 2. 打开 PCM 设备。0 是声卡号,0 是设备号(0代表播放)
    struct pcm *pcm = pcm_open(0, 0, PCM_OUT, &config);
    if (!pcm || !pcm_is_ready(pcm)) {
        // 处理错误
        return -1;
    }

    // 3. 写入音频数据
    int ret = pcm_write(pcm, data, size);

    // 4. 关闭设备,释放资源
    pcm_close(pcm);
    return ret;
}

2. ALSA Core

ALSA Core 指的是 Linux 内核中的 ALSA 子系统核心,它是整个 ALSA 架构的心脏和基石。虽然用户通常接触的是 ALSA 的用户空间库和工具,但真正驱动硬件、处理音频数据流的是这个内核模块。AlSA Core提供了:

  • 统一的音频设备驱动框架。

  • 音频数据流和控制的抽象模型。

  • 与用户空间交互的标准接口。

它的主要目标是为所有音频设备(从简单的板载声卡到复杂的专业音频接口)提供一个一致、高效的驱动模型。

2.1核心组件

(1)声卡 (struct snd_card)

  • 这是 ALSA 驱动中最顶层的抽象,代表一个物理或虚拟的音频设备。

  • 一个 snd_card 可以包含多个组件(如 PCM、Control、Mixer 等)。

  • /proc/asound/ 目录下,每个注册的声卡都会有一个对应的条目(如 card0)。、

(2)PCM 设备 (struct snd_pcm)

  • PCM(脉冲编码调制) 是处理数字音频流的核心接口,负责播放(playback)录制(capture)

  • 它管理着音频数据流的生命周期:

    • 硬件参数: 采样率、位深、声道数、缓冲区大小、周期大小。

    • DMA 缓冲区管理 : 负责在内核空间与音频硬件之间传输数据,通常使用环形缓冲区

    • 中断处理: 当一个音频周期(period)的数据传输完成时,硬件会产生中断,PCM 核心据此来更新缓冲区指针并唤醒用户空间应用程序。

  • 在用户空间,PCM 设备表现为 /dev/snd/pcmC0D0p(Card 0, Device 0, playback)这样的设备节点。

(3)控制接口 (struct snd_ctl)

  • 用于处理所有非音频流的控制命令,例如:

    • 音量控制

    • 通道开关

    • 输入源选择(如麦克风 vs 线路输入)

    • 各种开关(静音、幻象电源等)

  • 用户空间的混音器程序(如 alsamixer, tinymix)就是通过这个接口与驱动通信的。

(4)原始 MIDI 接口 (struct snd_rawmidi)

  • 提供对 MIDI 设备的支持,用于传输音乐控制信息(如音符开关、控制改变等)。

(5)定时器 (struct snd_timer)

  • 为音频应用提供高精度的定时服务,特别是在处理 MIDI 序列和同步时非常关键。

(6)序列器 (struct snd_seq)

  • 一个更高级的 MIDI 子系统,支持多端口、队列和连接,用于复杂的音乐软件。

2.2 软件层次结构

+-------------------------------------------------------+

| User Space Applications |

| (e.g., aplay, tinyplay, Audio HAL, Media Player) |

+-------------------------------------------------------+

| ALSA User Library (alsa-lib) |

+-------------------------------------------------------+

| System Call Interface (ioctl, read, write) |

+-------------------------------------------------------+

| +---------------------------------------------------+ |

| | ALSA Core (Kernel) | |

| +---------------------------------------------------+ |

| | PCM | Control | MIDI | Timer | ... | | |

| | (Abstraction Layer & Core Logic) | |

| +---------------------------------------------------+ |

| | ALSA Driver for Specific Hardware | |

| | (e.g., snd_hda_intel, snd_soc_xxx, snd_usb_audio)| |

| +---------------------------------------------------+ |

+-------------------------------------------------------+

| Hardware (Sound Card) |

+-------------------------------------------------------+

(1) 硬件特定驱动层

  • 这是直接与硬件交互的代码,由芯片厂商或社区开发。

  • 例如:snd_hda_intel(用于 Intel HDA 声卡),snd_usb_audio(用于 USB 音频设备),以及各种 SoC 音频驱动。

(2)ALSA Core 抽象层

  • 这一层提供了统一的 API 和数据结构(如 snd_pcm_ops, snd_ctl_ops),让硬件驱动开发者能够专注于硬件操作,而无需重复实现通用的音频逻辑。

  • 它处理了缓冲区管理、电源管理、设备文件创建、proc/sysfs 接口等通用任务。

(3)用户空间接口层

  • ALSA Core 通过 /dev/snd/ 下的设备节点和 ioctl 调用向用户空间暴露接口。

2.3数据流

让我们跟踪一段音频数据从用户空间到硬件的旅程,以理解 ALSA Core 的作用:

  1. 用户空间调用 :应用程序(通过 alsa-lib)调用 write 系统调用,将 PCM 数据写入 /dev/snd/pcmC0D0p

  2. 驱动入口write 调用进入内核,最终到达该 PCM 设备文件对应的驱动函数(在 ALSA Core 中定义)。

  3. 数据拷贝 :驱动将用户空间的数据拷贝到内核中预先分配好的 DMA 缓冲区。这个缓冲区是音频硬件可以直接访问的内存区域。

  4. 启动流 :当缓冲区中有足够的数据(达到一个 period 的大小)时,驱动启动硬件开始传输。

  5. DMA 传输 :音频硬件通过 DMA(直接内存访问) 自动从 DMA 缓冲区中读取数据,并将其发送到数字模拟转换器(DAC),整个过程无需 CPU 参与

  6. 周期中断 :当硬件处理完一个 period 的数据后,它会触发一个硬件中断

  7. 中断处理:CPU 响应中断,执行对应的 ALSA 中断处理程序。该程序会:

    • 更新缓冲区指针,标记已传输的数据空间为可用。

    • 如果驱动支持,触发高精度定时器来提供更精确的时序。

    • 唤醒正在等待可用缓冲区的用户空间应用程序。

  8. 循环往复:应用程序被唤醒后,继续写入新的音频数据,整个过程重复进行,直到音频流停止。

2.4 ASoC Core的关键特性

  1. 内核级支持:作为 Linux 内核的一部分,提供高效的、直接访问硬件的途径。

  2. 完整的框架:提供了 PCM、Control、Mixer、MIDI、Timer 等完整的音频组件模型。

  3. 硬件抽象:通过统一的 Ops(操作函数集)结构体,屏蔽了不同音频硬件的差异。

  4. 高效的数据传输:基于 DMA 和中断机制,实现了低 CPU 占用率的音频数据传输。

  5. 可扩展性:支持从简单的 UAC(USB Audio Class)到复杂的多通道专业声卡。

  6. 嵌入式优化:通过 ASoC 子框架,为嵌入式 SoC 提供了强大的支持。

3.ASoC

ASoC是ALSA系统在嵌入式系统或SoC(System on Chip)上的一个子框架和扩展。它的目标是为嵌入式平台上的音频提供更好的支持,尤其是那些集成了音频功能的SoC(如手机处理器、平板电脑处理器等)。

在嵌入式系统中,音频系统通常由两部分组成:一是SoC内部集成的音频控制器(如I2S、PCM接口),二是外部的音频编解码器(Codec)。此外,还可能有一些复杂的板级配置(如连接多个Codec、复杂的时钟和电源管理)。传统的ALSA驱动模型在这种情况下显得不够灵活,因此ASoC应运而生。

ASoC将嵌入式音频系统分为三个部分:

  1. Platform Driver:负责SoC这边的音频接口,比如DMA控制器、I2S/PCM接口等。

  2. Codec Driver:负责外部的编解码器,通常通过I2C/SPI等总线控制,并提供音频的采集和播放功能。

  3. Machine Driver:作为"胶水"将Platform和Codec驱动结合起来,并处理板级特定的配置(如音频路由、时钟、电源等)。

这样分离的好处是:

  • 代码复用:同一个Codec驱动可以在不同的SoC平台上使用,同一个SoC的Platform驱动也可以搭配不同的Codec。

  • 逻辑清晰:各司其职,便于维护和调试。

下面分别介绍下这三部分

3.1Platform Driver

Platform Driver 处理 SoC 平台本身的音频功能,主要分为两部分:

  • CPU DAI (Digital Audio Interface) Driver:

    • 驱动 SoC 内部的数字音频接口控制器,如 I²S、PCM、AC97 等。

    • 配置接口参数:主/从模式、时钟、帧同步信号、数据格式等。

    • 通常对应 snd_soc_dai_driver

  • Audio DMA Driver:

    • 驱动 SoC 内部的 DMA 控制器,负责在内存和音频接口之间搬运音频数据。

    • 管理 DMA 缓冲区、周期中断等。

    • 通常对应 snd_soc_platform_driver(新内核中已整合到 snd_soc_component_driver)。

示例:高通的 SoC 会有自己的 Platform Driver,负责其 Hexagon DSP 和 I²S 接口。

3.2Codec Driver

Codec Driver 驱动外部内部 的音频编解码器芯片,负责所有与音频信号处理相关的功能:

  • DAC/ADC:数字/模拟转换。

  • Mixer:音频路由和混音。

  • PGA (Programmable Gain Amplifier):可编程增益放大器,控制音量。

  • BIAS 和电源管理:为麦克风等提供偏置电压。

  • Codec DAI:描述 Codec 侧的数字音频接口。

  • Controls:暴露各种控制项(如 "Playback Volume", "Capture Switch")给用户空间。

Codec Driver 是平台无关的。同一个 WM8994 Codec 驱动既可以用在三星的手机上,也可以用在其他使用该 Codec 的开发板上。

3.3Machine Driver

Machine Driver 是连接 Platform 和 Codec 的 "胶水代码" ,它包含了板级特定的配置信息:

  • 定义 DAI Link:指定使用哪个 Platform 的 CPU DAI 和哪个 Codec 的 Codec DAI 进行连接。

  • 音频路由:定义音频路径,例如:

    • "MIC1 -> ADC1"(录音路径)

    • "DAC1 -> HP Out"(播放路径)

  • 时钟配置:配置主时钟(MCLK)、位时钟(BCLK)等。

  • 初始化配置:设置上电时的默认音量、上电/下电序列等。

示例:一个具体的手机主板,其 Machine Driver 会指明 SoC 的 I²S0 接口连接到 Cirrus Logic CS42L73 Codec,并定义耳机插入时音频路由如何切换。

3.4ASoC 的关键特性与机制

DAPM (Dynamic Audio Power Management)

DAPM 是 ASoC 框架中一个极其重要的特性,它的核心目标是根据音频路径动态管理电源,实现最低功耗

  • 工作原理

    1. 将音频系统中的每个部件(Mixer、ADC、DAC、放大器等)建模为一个 "widget"

    2. 每个 widget 有电源状态(开/关)。

    3. DAPM 根据用户空间的音频流设置(如 tinymix)和预定义的音频路由,自动计算出一条完整的、从源(如麦克风)到目的(如扬声器)的完整路径

    4. 只打开这条路径上的所有 widget,其他无关的 widget 全部保持关闭

  • 举例:当播放音乐到扬声器时,DAPM 会自动打开 DAC、扬声器放大器等,但会关闭耳机放大器、ADC、麦克风偏置等。一旦播放停止,整个路径会自动断电。

DAI (Digital Audio Interface) 和 DAI Link

  • DAI:代表一个数字音频接口。无论是 SoC 端的 I²S 还是 Codec 端的 I²S,都抽象为一个 DAI。

  • DAI Link :在 Machine Driver 中定义,用于连接一个 CPU DAI 和一个 Codec DAI,形成一个逻辑上的音频子系统的"管道"。

音频路由与控件

  • 路由:在设备树(Device Tree)或 Machine Driver 代码中静态定义,描述了信号在 Codec 内部或板级上的可能路径。

  • 控件 :通过 ALSA Control 接口暴露给用户空间,让 alsamixertinymix 等工具可以动态改变路由和设置参数。

3.5ASoC的特性

(1)模块化与代码复用:Platform、Codec、Machine 三者分离,极大地提高了驱动代码的复用性。

(2)强大的电源管理:DAPM 机制在不牺牲功能的前提下,最大限度地降低了音频子系统的功耗,这对移动设备至关重要。

(3)统一的框架:为各种复杂的嵌入式音频硬件提供了一个标准、统一的驱动模型。

(4)易于调试和扩展:清晰的分层使得定位问题和添加新硬件支持变得更加容易。

相关推荐
Brian Xia3 小时前
Social-Auto-Upload - 多平台社交媒体视频自动化上传工具
自动化·音视频·媒体
come112343 小时前
ptyhon 基础语法学习(对比php)
android·学习
倔强菜鸟3 小时前
2025.8.10-学习C++(一)
开发语言·c++·学习
奶糖 肥晨4 小时前
视频抽帧完全指南:使用PowerShell批量提取与优化图片序列
音视频
蓝桉~MLGT4 小时前
Python学习历程——组织结构(包含for、if、while等等)
开发语言·python·学习
西猫雷婶4 小时前
pytorch基本运算-torch.normal()函数生成的随机数据添加噪声
人工智能·pytorch·python·深度学习·学习·线性代数·机器学习
Rousson4 小时前
硬件学习笔记--78 MCU复位电路简介
笔记·学习
chengooooooo5 小时前
微服务架构:从单机到分布式的革命性升级
学习·微服务·架构
小狗爱吃黄桃罐头6 小时前
正点原子【第四期】Linux之驱动开发学习笔记-5.1 设备树下的LED驱动实验
linux·驱动开发·学习