[What]linux -> ALSA 调试

记录ALSA的驱动和上层调试。

ALSA自带调试输出

在内核的子系统中已经自带了很多调试信息,而不需要我们在这些子系统代码中再手动的插入。

通过查看文件 include/sound/core.h 发现有宏 CONFIG_SND_DEBUG , CONFIG_SND_DEBUG_VERBOSE, CONFIG_SND_VERBOSE_PRINTK, 通过在 menuconfig 中搜索发现其路径为:

Device Drivers -> Sound card support -> Advanced Linux Sound Architecture 
  • CONFIG_SND_DEBUG : 输出基本的调试信息
  • CONFIG_SND_VERBOSE_PRINTK : 在调试信息头前加上文件及位置标记
  • CONFIG_SND_DEBUG_VERBOSE: 更多的调试信息输出
    • 在选中 Debug 后才会出现 More verbose debug 的选项。

数据验证

上层的数据是会通过 wirte 类函数发送下来,可以在驱动中打印缓冲中的值以确认数据对齐操作。

比较常见的方法就是写一个递增的数据,下层来依次匹配。

注意I2S协议

除了标准的I2S协议外,还有左右对齐两种方式,如果这个没有配置与codec芯片一致,那么听到的声音就会有类似过载的效果。(因为上一个通道的低位数据被采用到了下一个通道的高位上去)

codec配置

有些芯片的配置默认是静音,有些复杂的芯片(比如AD1761)内部还有很多通路,这些都可以先通过 amixer 命令配置, 再确定参数后再使用程序配置。

  • 首先使用 amixer contents 列出其默认配置
  • 根据芯片的datasheet来查看其中一些不合适的配置罗列出来
  • 根据需要配置的id号(numid=xx),使用命令配置 amixer cset numid=xx <value>
    • 当有几个音频芯片时,为了找出对应芯片的设置,可以在芯片驱动中找到对应字符串并进行反向搜寻

ALSA中的生产者和消费者模型

问题描述

一般来说是需要消费者的平均速度大于生产者的平均数据才能保证数据不丢失,但是在音频 这种情况下,为了播放的声音和产生的声音一致,那就需要生产者和消费者的速度理论上一致。

在播放一个音频文件时,可以保证消费者一直可以不间断的获取数据,但在其他场合(比如先解调再产生音频数据), 生产者需要一定时间才会产生有效数据,这就从宏观上导致消费者会间歇的等待生产者。 在这种情况下上层便会产生 Broken pipe 也就是 underrun 错误。

解决方案(自我思考)

ALSA出现此错误的根本原因就是这个特殊的消费者需要时刻都有数据,那么解决思路就是:

方案一

播放上一次处理后的数据,在播放的同时(一般通过DMA送出),处理当前接收的数据.

这种方式有一个条件: 处理一帧数据的时间要低于播放完一帧的时间

方案二

当处理一帧的时间长于播放完一帧的时间时,方案一就会出现杂音,为了避免这种情况就需要进行重采样,相当于增大发送给播放器的数据量,延长其播放时间。

至于这个插值取多少,要根据当前的数据速率和消费者产生数据的时间消耗来算。

  比如当前数据速率是 44100 Hz,那么一个frame的时间就是23微秒,而上层在接收到采样原始数据后
  进行解调的时间需要5毫秒,那么这段插值数据至少要包含218个frame才可以保证与解调算法持平。
  加上线程的切换时间取300个frame的空间才比较合适。

真实解决方案

音频这方面处理应该是一个很普遍的需求,肯定有很多高人处理了这个问题并开源出来了,待我搜他一搜…..

目前发现有两个库都具有重采样的功能:

  1. liquid
  2. libsamplerate

在实际应用中发现,重采样只是解决了其中一环,还有其他环!

让我们来好好梳理一下在类似于解调产生的音频数据这种应用:

  1. 在数据送往ALSA接口前,需要进行以下两步:
    • 接收数据
    • 解调
    • 重采样
  2. 为了不让ALSA接收不到数据(只要中间没有接收到数据,最终听到的声音里面就会有咔、咔、咔…),需要:
    • 接收数据和解调数据以及重采样的一周期 所消耗的时间小于ALSA播放完一周期的时间
  3. 仅仅这样还不够,因为ALSA内部具有环形缓存,ALSA将一个period发送给DMA后会立即返回接收下一帧数据(如果缓存有空位的话)
    • 所以需要将解调数据输出到一个中间环形缓存并且延时启动ALSA
      • 延时是为了让缓存中有足够的数据可以满足ALSA前期的突发读取
  4. 经过以上3步,从宏观上来看,解调数据会写满缓存,这个时候还需要抽样丢掉数据(就是间隔一段数据丢掉一小帧)
    • 因为音频芯片内部有滤波器,所以当抽样丢数据时,对听声音不会有影响。但如果连续丢数据,也会咔、咔、咔…

这样整个方案就如下图所示:

audio_play.jpg
Last Updated 2018-11-05 Mon 07:36.
Render by hexo-renderer-org with Emacs 26.1 (Org mode 9.1.14)