ALSA子系统(三)------Audio测试工具(tinyalsa)

你好!这里是风筝的博客,
欢迎和我一起交流。

工欲善其事必先利其器,我们要测试音频时,一般有两个常见的工具:
amixer系列(amixer、aplay、arecord)和tinyalsa系列
amixer功能强大,基本啥都能配置。
tinyplay则比较简单,不足以实现全部功能。
但是!!!!!!
amixer系列工具需要使用alsa-lib和alsa-utils,经过一系列交叉编译后才能移植使用,tinyalsa则不需要依赖太多。

TinyAlsa是 Android 默认的 alsalib, 封装了内核 ALSA 的接口,用于简化用户空 间的 ALSA 编程。
与aplay相比,大大降低了编译和使用难度。
而在linux3.x的版本 android 4.0往后,已经使用TinyAlsa了,代码位置在external/tinyalsa。
libtinyals.so是由mixer.c和pcm.c文件生成的,基于这个库有3个工具可以使用:tinyplay,tinycap,tinymix,编译android系统后就能使用。

当然,我们也可以单独使用,源码在:https://github.com/tinyalsa/tinyalsa

仓库里有两个分支,master和google-origin
google-origin分支就是用在Android里面,不过我看这个分支一年多没人维护了,最新提交在2019年2月。
master分支则一直在维护,各大系统里面也是用的master分支。

我推荐使用google-origin分支,和Android同步。
将代码clone下来加上如下Makefile即可使用:

CFLAGS = -c -fPIC -Wall
INC = include
OBJECTS = mixer.o pcm.o
LIB = libtinyalsa.so
CROSS_COMPILE ?=
CC = $(CROSS_COMPILE)gcc

TARGET = tinyplay tinycap tinymix tinypcminfo

all: $(LIB) $(TARGET)
	rm *.o

tinyplay: $(LIB) tinyplay.o
	$(CC) tinyplay.o -L. -ltinyalsa -o tinyplay

tinycap: $(LIB) tinycap.o
	$(CC) tinycap.o -L. -ltinyalsa -o tinycap

tinymix: $(LIB) tinymix.o
	$(CC) tinymix.o -L. -ltinyalsa -o tinymix

tinypcminfo: $(LIB) tinypcminfo.o
	$(CC) tinypcminfo.o -L. -ltinyalsa -o tinypcminfo

$(LIB): $(OBJECTS)
	$(CC) -shared $(OBJECTS) -o $(LIB)

.c.o:
	$(CC) $(CFLAGS) $< -I$(INC)

.PHONY : clean
clean:
	-rm -f $(LIB) $(OBJECTS) $(TARGET) \
		*.o \

在tinyalsa里面,合理的pcm_config可以做到更好的低时延和功耗,移动设备的开发尤其敏感。

struct pcm_config {
    unsigned int channels;
    unsigned int rate;
    unsigned int period_size;
    unsigned int period_count;
    enum pcm_format format;
    unsigned int start_threshold;
    unsigned int stop_threshold;
    unsigned int silence_threshold;
    int avail_min;
};

解释一下结构中的各个参数,每个参数的单位都是frame(1帧 = 通道*采样位深):

  • period_size. 每次传输的数据长度。值越小,时延越小,cpu占用就越高。
  • period_count. 缓之冲区period的个数。缓冲区越大,发生XRUN的机会就越少。
  • format. 定义数据格式,如采样位深,大小端。
  • start_threshold. 缓冲区的数据超过该值时,硬件开始启动数据传输。如果太大, 从开始播放到声音出来时延太长,甚至可导致太短促的声音根本播不出来;如果太小, 又可能容易导致XRUN.
  • stop_threshold. 缓冲区空闲区大于该值时,硬件停止传输。默认情况下,这个数 为整个缓冲区的大小,即整个缓冲区空了,就停止传输。但偶尔的原因导致缓冲区空, 如CPU忙,增大该值,继续播放缓冲区的历史数据,而不关闭再启动硬件传输(一般此 时有明显的声音卡顿),可以达到更好的体验。
  • silence_threshold. 这个值本来是配合stop_threshold使用(当stop_threshold大于整个缓冲区大小时, 当DMA搬移完所有有效数据时, 读写指针就重叠了. 如果不停止DMA, 继续搬移, 就意味着会搬移一些写指针之后的旧数据, 此时我们就会听到一些旧声音. 如果不想听到旧声音, 我们可以把写指针之后的这段旧数据清零, 这就是所谓的silence模式),当DMA可搬移的数据小于该值时, 意味着马上就要搬完了, 此时内核开始往缓冲区填充静音 数据,这样就不会重播历史数据了。
  • avail_min. 缓冲区空闲区大于该值时,pcm_mmap_write()才往缓冲写数据。这个 值越大,往缓冲区写入数据的次数就越少,XRUN的机会就越大。Android samsung tuna 设备在screen_off时增大该值以减小功耗,在screen_on时减小该 值以减小XRUN的机会。

关于silence这块,还有三个参数要注意:

  • snd_pcm_uframes_t silence_size : 每次清零动作最多会清零多大的空间, 如果它被设为0, 则内核不会去做清零动作, 也就是说silence模式无效.
  • snd_pcm_uframes_t silence_start : 从哪个位置开始清零.
  • snd_pcm_uframes_t silence_filled : 已经清零了多少空间.
  • Frame:帧,构成一个完整的声音单元,它的大小等于 sample_bits * channels;
  • Peroid Size:周期大小,即每次 dma 运输处理音频数据的帧数。如果周期大小设定得较大,则单次处理的数据较多,这意味着单位时间内硬件中断的次数较少,CPU 也就有更多时间处理其他任务,功耗也更低,但这样也带来一个显著的弊端——数据处理的时延会增大。 再说说 period bytes,对于 dma 处理来说,它直接关心的是数据大小,而非 period_size(一个周期的帧数),有个转换关系:period_bytes = period_size * sample_bits * channels / 8

在不同的场景下,合理的参数就是在性能、时延、功耗等之间达到较好的平衡。


一个典型的声音程序(伪代码)包括:

  • 1 打开回放或录音接口
  • 2 设置硬件参数(访问模式,数据格式,信道数,采样率,等等)
  • 3 while 有数据要被处理:
  • 4 读PCM数据(录音) 或 写PCM数据(回放)
  • 5 关闭接口

在aplay中,使用函数宏 snd_pcm_hw_params_alloca分配出一个类型为snd_pcm_hw_param的变量,使用函数snd_pcm_hw_params_any来初始化这个变量。
对于采样率而言,声音硬件并不一定就精确地支持我们所定的采样率,但是我们可以使用函数snd_pcm_hw_params_set_rate_near来设置最接近我们指定的采样率的采样率。
只有当调用函数 snd_pcm_hw_params后,硬件参数才会起作用。

参考:【转】Alsa音频编程【精华】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值