[端侧驰算 · 踩坑记 03] 资源博弈:在树莓派 5 上构建“看、听、说”并发流水线01–听得到与讲得出

本文隶属于「端侧AI实战与系统架构」系列,专注嵌入式端侧AI,本篇涉及ALSA与 tinyalsa ,适合对端侧开发、车机、Android 底层音频感兴趣的新手阅读。

工欲善其事必先利其器,任何一个新的平台都应该先验证其硬件配置与基本功能。本文从树莓派 5 音频硬件识别开始,一步步打通“录音 + 播放”链路,并在无标准 ALSA 库环境下,使用 tinyalsa 完成耳返验证,为后续“看、听、说”并发流水线构建打下基础。如果需要展开ALSA深入讲解,请关注并私信我,我会根据实际情况在新的专栏中展开讲解。


目录


一、树莓派 5 麦克风到 /dev 节点的音频通路

1.1 查看音频硬件

cat /proc/asound/cards

执行结果:

0 [Audio          ]: USB-Audio - USB Audio
                      Generic USB Audio at usb-xhci-hcd.1-2, high speed
 1 [vc4hdmi0       ]: vc4-hdmi - vc4-hdmi-0
                      vc4-hdmi-0
 2 [vc4hdmi1       ]: vc4-hdmi - vc4-hdmi-1
                      vc4-hdmi-1

硬件设备查询结果

1.2 查看 PCM 设备节点

ls /dev/snd/

执行结果:

by-id  by-path  controlC0  controlC1  controlC2  pcmC0D0c  pcmC0D0p  pcmC1D0p  pcmC2D0p  seq  timer

节点结构说明:

/dev/snd/
├── controlC0     # USB Audio 控制接口
├── controlC1     # HDMI 0 控制接口
├── controlC2     # HDMI 1 控制接口
├── pcmC0D0c      # USB Audio - 录音设备 (capture)
├── pcmC0D0p      # USB Audio - 播放设备 (playback)
├── pcmC1D0p      # HDMI 0 - 仅播放
├── pcmC2D0p      # HDMI 1 - 仅播放
├── seq           # 音序器
└── timer         # 定时器

1.3 查看音频设备路由

cat /proc/asound/pcm

执行结果:

00-00: USB Audio : USB Audio : playback 1 : capture 1
01-00: MAI PCM i2s-hifi-0 : MAI PCM i2s-hifi-0 : playback 1
02-00: MAI PCM i2s-hifi-0 : MAI PCM i2s-hifi-0 : playback 1

查看完整 USB 音频路由拓扑:

cat /proc/asound/card0/stream0

执行结果:

Generic USB Audio at usb-xhci-hcd.1-2, high speed : USB Audio

Playback:
  Status: Stop
  Interface 2
    Altset 1
    Format: S16_LE
    Channels: 2
    Endpoint: 0x02 (2 OUT) (ADAPTIVE)
    Rates: 44100
    Data packet interval: 1000 us
    Bits: 16
    Channel map: FL FR
  Interface 2
    Altset 2
    Format: S24_3LE
    Channels: 2
    Endpoint: 0x02 (2 OUT) (ADAPTIVE)
    Rates: 44100
    Data packet interval: 1000 us
    Bits: 24
    Channel map: FL FR
  Interface 2
    Altset 3
    Format: S16_LE
    Channels: 2
    Endpoint: 0x02 (2 OUT) (ADAPTIVE)
    Rates: 48000
    Data packet interval: 1000 us
    Bits: 16
    Channel map: FL FR
  Interface 2
    Altset 4
    Format: S24_3LE
    Channels: 2
    Endpoint: 0x02 (2 OUT) (ADAPTIVE)
    Rates: 48000
    Data packet interval: 1000 us
    Bits: 24
    Channel map: FL FR
  Interface 2
    Altset 5
    Format: S16_LE
    Channels: 2
    Endpoint: 0x02 (2 OUT) (ADAPTIVE)
    Rates: 96000
    Data packet interval: 1000 us
    Bits: 16
    Channel map: FL FR
  Interface 2
    Altset 6
    Format: S24_3LE
    Channels: 2
    Endpoint: 0x02 (2 OUT) (ADAPTIVE)
    Rates: 96000
    Data packet interval: 1000 us
    Bits: 24
    Channel map: FL FR
  Interface 2
    Altset 7
    Format: S16_LE
    Channels: 2
    Endpoint: 0x02 (2 OUT) (ADAPTIVE)
    Rates: 192000
    Data packet interval: 1000 us
    Bits: 16
    Channel map: FL FR
  Interface 2
    Altset 8
    Format: S24_3LE
    Channels: 2
    Endpoint: 0x02 (2 OUT) (ADAPTIVE)
    Rates: 192000
    Data packet interval: 1000 us
    Bits: 24
    Channel map: FL FR

Capture:
  Status: Stop
  Interface 1
    Altset 1
    Format: S16_LE
    Channels: 2
    Endpoint: 0x81 (1 IN) (ASYNC)
    Rates: 44100
    Data packet interval: 1000 us
    Bits: 16
    Channel map: FL FR
  Interface 1
    Altset 2
    Format: S24_3LE
    Channels: 2
    Endpoint: 0x81 (1 IN) (ASYNC)
    Rates: 44100
    Data packet interval: 1000 us
    Bits: 24
    Channel map: FL FR
  Interface 1
    Altset 3
    Format: S16_LE
    Channels: 2
    Endpoint: 0x81 (1 IN) (ASYNC)
    Rates: 48000
    Data packet interval: 1000 us
    Bits: 16
    Channel map: FL FR
  Interface 1
    Altset 4
    Format: S24_3LE
    Channels: 2
    Endpoint: 0x81 (1 IN) (ASYNC)
    Rates: 48000
    Data packet interval: 1000 us
    Bits: 24
    Channel map: FL FR
  Interface 1
    Altset 5
    Format: S16_LE
    Channels: 2
    Endpoint: 0x81 (1 IN) (ASYNC)
    Rates: 96000
    Data packet interval: 1000 us
    Bits: 16
    Channel map: FL FR
  Interface 1
    Altset 6
    Format: S24_3LE
    Channels: 2
    Endpoint: 0x81 (1 IN) (ASYNC)
    Rates: 96000
    Data packet interval: 1000 us
    Bits: 24
    Channel map: FL FR
  Interface 1
    Altset 7
    Format: S16_LE
    Channels: 2
    Endpoint: 0x81 (1 IN) (ASYNC)
    Rates: 192000
    Data packet interval: 1000 us
    Bits: 16
    Channel map: FL FR
  Interface 1
    Altset 8
    Format: S24_3LE
    Channels: 2
    Endpoint: 0x81 (1 IN) (ASYNC)
    Rates: 192000
    Data packet interval: 1000 us
    Bits: 24
    Channel map: FL FR

1.4 音频数据流通路(美观版流程图)

🎤 录音通路(USB 麦克风 → 应用程序)

┌─────────────────┐
│  USB 麦克风硬件  │
└────────┬────────┘
         ↓
┌─────────────────┐
│  USB 物理接口   │
│  xhci-hcd.1-2   │
└────────┬────────┘
         ↓
┌─────────────────┐
│ USB Audio 驱动  │
│  snd-usb-audio  │
└────────┬────────┘
         ↓
┌─────────────────┐
│   ALSA 核心层   │
└────────┬────────┘
         ↓
┌─────────────────┐
│  设备节点文件   │
│ /dev/snd/pcmC0D0c │
└────────┬────────┘
         ↓
┌─────────────────┐
│   用户应用程序  │
└─────────────────┘

🔊 播放通路(应用程序 → 音箱/HDMI)

┌─────────────────┐
│   用户应用程序  │
└────────┬────────┘
         ↓
┌───────────────────────────────┐
│ 设备节点(三选一)            │
│ • /dev/snd/pcmC0D0p  USB 输出 │
│ • /dev/snd/pcmC1D0p  HDMI 0   │
│ • /dev/snd/pcmC2D0p  HDMI 1   │
└───────────────┬───────────────┘
                ↓
┌─────────────────┐
│  对应硬件驱动   │
└────────┬────────┘
         ↓
┌─────────────────┐
│ USB 音箱 / HDMI │
└─────────────────┘

🔁 完整音频分层流转架构

┌─────────────────────────────────────────────────────┐
│                  硬件层                             │
│            USB Audio Class 2.0 设备                  │
└───────────────────────────┬─────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────┐
│                  USB 驱动层                          │
│             xhci-hcd → snd-usb-audio                 │
└───────────────────────────┬─────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────┐
│                   内核缓冲层                         │
│                DMA 环形缓冲区 & 中断                  │
└───────────────────────────┬─────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────┐
│                   ALSA 中间层                        │
│            格式转换 · 声道映射 · 采样率规整           │
└───────────────────────────┬─────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────┐
│                    路由混音层                        │
│              多路复用 · 混音 · 通路控制              │
└───────────────────────────┬─────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────┐
│                   设备节点层                          │
│               /dev/snd/controlC* / pcmC*D*           │
└───────────────────────────┬─────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────┐
│                     用户空间                         │
│              tinycap / tinyplay / 业务APP            │
└─────────────────────────────────────────────────────┘

二、环境问题:无音频库,直接操作 PCM 节点失败

直接 open /dev/snd/pcmC0D0c 这类节点是无法直接录音播放的,必须完成 ALSA 底层初始化流程,否则会出现设备无法打开、参数配置失败等问题。

常见环境缺失表现:

  • 系统中无 aplayarecord 等基础音频工具

  • 缺失 libasound2-dev 等 ALSA 开发库,无法编译依赖 ALSA 的程序

  • 嵌入式/样机环境禁止联网,无法通过 apt 命令安装任何依赖

环境检查常用命令:

# 查看是否有 aplay 工具
which aplay

# 查看 tinyalsa 头文件是否存在
ls /usr/include/tinyalsa/ 2>/dev/null

# 查看系统是否有 ALSA 相关库文件
ls /usr/lib/*asound* 2>/dev/null

# 全局查找 arecord 工具
find / -name "arecord" 2>/dev/null

# 查找系统中所有音频相关工具
find /system /vendor -name "*audio*" 2>/dev/null

三、解决办法与取舍

多数教程都会直接让你执行以下命令,快速安装 ALSA 开发库,从而完成音频验证:

sudo apt install libasound2-dev

但在真实量产、车机样机、嵌入式离线环境中,这种方式是不现实的——设备往往无外网权限,且不允许随意安装第三方库。我在面试端侧音频方向候选人时,也会重点询问 ALSA 底层细节,核心目的就是识别候选人能否脱离“教程式安装”,直接上手解决真实环境的问题,而非背诵八股文。

方案对比与取舍:

路径 A:直接调用内核 ALSA API

  • 优点:最底层、零依赖,完全脱离第三方库,适配所有 Linux 嵌入式环境

  • 缺点:接口复杂,需深入理解 ALSA 内核的 ioctl 控制机制,调试难度高、耗时久

  • 结论:适合专门花时间深入研究 ALSA 内核原理,现阶段为了快速打通音频链路,不使用此方案。

  • 如果对希望博主对ALSA进行深入讲解,请关注和私信。

路径 B:使用 tinyalsa(推荐)

  • 优势:体积小、无冗余依赖,大量 Android 设备、车机黑盒样机默认内置;接口简洁,易上手,可静态编译,一次编译可在多台设备复用;贴近真实生产环境,同时能为后续深入学习 ALSA 内核打下基础

  • 结论:优先选择,既能快速打通“录音+播放”链路,又能贴合实际开发场景,性价比最高


四、tinyalsa 源码编译安装

推荐通过源码编译的方式安装 tinyalsa,可灵活适配树莓派 5 环境,同时便于后续查看源码、深入学习:

git clone https://github.com/tinyalsa/tinyalsa.git
cd tinyalsa
make

编译完成后,会在项目的 utils/ 目录下生成两个核心工具:

  • tinycap:用于录制 PCM 原始音频数据

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

小贴士:tinyalsa 源码是学习 ALSA 机制的优质素材,可配合 AI 辅助理解源码逻辑,但切勿完全依赖 AI 结论,需结合实际调试验证。如果对AI在嵌入式开发中的使用感兴趣的朋友可以关注和私信我。


五、功能实测:录音 + 播放

编译完成后,通过 tinycap 和 tinyplay 工具,可快速实现功能,验证音频链路是否完全打通。

5.1 录制 PCM 原始音频

./utils/tinycap test.raw -D 0 -d 0 -c 2 -r 44100 -b 16 -t 5

参数说明(重点标注):

  • -D 0:指定声卡卡号为 0(对应前文查到的 USB Audio 设备)

  • -d 0:指定设备号为 0

  • -c 2:设置录音声道为双声道(立体声)

  • -r 44100:设置采样率为 44.1kHz(主流音频采样率,适配大多数设备)

  • -b 16:设置音频位深为 16bit(兼顾音质与存储)

  • -t 5:设置录制时长为 5 秒(可根据需求调整)

5.2 播放音频

# 播放刚才录制的 PCM 原始音频文件
./utils/tinyplay test.raw -D 0 -d 0 -c 2 -r 44100 -b 16

# 若有 WAV 文件,可直接播放(无需指定格式参数)
./utils/tinyplay test.wav -D 0 -d 0

执行成功后,即可通过音箱/HDMI 设备听到刚才录制的声音,证明树莓派 5 的音频“录音+播放”链路已完全打通。


六、查看 PCM 设备状态信息

在调试音频问题(如设备占用、格式不匹配)时,可通过以下命令查看 PCM 设备的详细状态信息,辅助定位问题:

cat /proc/asound/card0/pcm0c/info  # 查看录音设备(pcm0c)状态
cat /proc/asound/card0/pcm0p/info  # 查看播放设备(pcm0p)状态

七、下期预告

至此,音频的录音和播放已经验证完成。你是否也在思考路径A该如何实现?在评论区发表你的意见吧。
下期继续主线:
[端侧驰算 · 踩坑记 03] 资源博弈:在树莓派 5 上构建“看、听、说”并发流水线02–听得懂

标签#树莓派5 #嵌入式开发 #ALSA #音频 #端侧AI

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐