format (格式)
常见格式:
S16_LE- 有符号16位小端整数 (一个采样点16位,占2个字节)。两个值分别为0x1234和0xABCD的采样点在内存中表示为:
| 0x34 | 0x12 | 0xCD | 0xAB |
|---|
S24_LE- 有符号24位小端整数 (一个采样点24位,占4个字节,高3字节有效,低1字节填充0)。两个值分别为0x123456和0xABCDEF的采样点在内存中表示为:
| 0x00 | 0x56 | 0x34 | 0x12 | 0x00 | 0xEF | 0xCD | 0xAB |
|---|
S24_3LE- 有符号24位小端整数 (一个采样点24位,占3个字节)。两个值分别为0x123456和0xABCDEF的采样点在内存中表示为:
| 0x56 | 0x34 | 0x12 | 0xEF | 0xCD | 0xAB |
|---|
S32_LE- 有符号32位小端整数 (一个采样点32位,占4个字节)。两个值匀为0x12345678的采样点在内存中表示为:
| 0x78 | 0x56 | 0x34 | 0x12 | 0x78 | 0x56 | 0x34 | 0x12 |
|---|
FLOAT_LE- 小端浮点数 (一个采样点32位,占4个字节)
问:为什么要区别大端小端?
答:处理器分大端处理器和小端处理器。大端处理器只能理解大端数据(即对大端数据做运算),小端处理器只能理解小端数据(即对小端数据做运算)。同样一个采样点,大端处理器把它写入内存或文件时,便形成了大端数据;小端处理器同理。想让大端处理器处理小端数据,那么处理前要先把小端数据转换为大端;小端处理器处理大端数据同理。
frame (帧)
帧是ALSA最基本的数据传输单元。所有声道在同一个时刻产生的1个采样点组成1帧。
单声道情况下的4帧:
| L1 | L2 | L3 | L4 | … | … | … | … | … |
|---|
双声道(交织)情况下的4帧:
| L1 | R1 | L2 | R2 | L3 | R3 | L4 | R4 | … |
|---|
举个例子:采样率48kHz,位宽16位,2声道情况下:1 帧 = (16 / 8) * 2 = 4 字节,1秒钟传输48000个帧,也就是192000字节,比特率=192000*8=1536kbps。
问:为什么帧(frame)是最基本的传输单元,而非采样点(sample)或字节(byte)?
答:首先不能是字节,因为例如16位采样点只取一个字节即半个采样点,相当于数据被破坏,所以一个字节没有任何意义。然后不能是一个声道的一个采样点,因为在多声道情况下,只处理同一时刻一个声道的一个采样点而忽略其他声道在这个时刻的采样点,相当于这个时刻的采样点数据不完整。所以应该要把所有声道在同一个时刻产生的1个采样点(即1帧)作为最基本最原子的传输单位。
问:"ALSA帧"跟音频算法中常说的"帧"是同一个概念吗?
答:不是。音频算法在处理数据时,都是以一小段一小段等长的数据处理的。这一小段数据就叫做一帧,这一帧可以是10ms数据也可以是20ms数据等。以48kHz采样率为例,一个算法帧如果是20ms,那么就对应到48000*0.02=960个ALSA帧。
period (周期)
一个period指的是若干个ALSA帧,类似于一个算法帧。period_size指一个period里包含多少ALSA帧。period_count指缓冲区里有多少个period。
例如一个period_size=1024, period_count=4 的缓冲区:
| 1024 ALSA帧 | 1024 ALSA帧 | 1024 ALSA帧 | 1024 ALSA帧 |
|---|
问:一个period应该设定多长?
答:不能太长也不能太短。太长的话意味着延时就长,在延时敏感的应用场景下会不能接受。例如通话时,当一端用400毫秒长的period录音,就意味麦克风硬件采集到声音并把数据存放到内存时,说话的声音至少400毫秒后才被软件读走,即使将网络发送到对方再从对方的喇叭播放出来的时间忽略不计,对方也至少要400毫秒后才能听到这端的说话声音,这样通话的体验是非常差的。而period太短的话也有问题,举个最极端的例子,若一个period的长度等于一个ALSA帧,这意味着每隔一个ALSA帧(以48000Hz采样率为例,一个ALSA帧就是1/48000=20微妙)软件就要处理一次,这样会极大地增加系统开销。所以一个period应该设定多长取决于应用场景和系统负载。
xrun
xrun对于播放来说即underrun,对于录音来说即overrun。underrun指缓冲区里已经没有数据给硬件去消耗了,但硬件还想要继续消耗数据。overrun指缓冲区数据已满,没有空位给硬件去填新的数据了,但硬件还想继续往里填新数据。