音频文件在播放时出现声音断断续续,一卡一卡的或类似“爆破”(Pop-Click)杂音的现象,称之为 Xrun(可以是 underrun,也可以是 overrun)。
分析:
alsa driver使用了环形缓冲区对dma buffer进行管理,如下图。
播放时,应用程序把音频数据源源不断地写入dma buffer中,然后相应platform的dma操作则不停地从该buffer中取出数据,经dai送往codec中,当写入的数据慢,播放的数据快时声音会出现断断续续,一卡一卡的现象。
录音时,codec源源不断地把A/D转换好的音频数据经过dai送入dma buffer中,而应用程序则不断地从该buffer中读走音频数据,当写入的数据快,播放的数据慢时,当数据量较多buffer有可能被冲掉,声音会出现类似“爆破”(Pop-Click)杂音的现象。
[RK3288][Android5.1] 调试如下:
1、播放的数据快,写入的数据慢时,触发DMA中断,将音频数据写入dma buffer中,出现一卡一卡的现象!检查codec和machine的采样频率,设置ASoc框架的Codec驱动和Machine驱动的输出freq = 12288000。
//设置codec dai的主时钟,采样率
static int xvf3500_set_dai_sysclk(struct snd_soc_dai *codec_dai,int clk_id, unsigned int freq, int dir)
{
...
freq = 12288000;
switch (freq) {
...
case 12288000:
case 16934400:
case 24576000:
case 33868800:
xvf3500->sysclk_constraints = &constraints_12288;
xvf3500->sysclk = freq;
return 0;
...
}
return -EINVAL;
}
//在machine设置codec dai采样率,保持codec驱动采样率一致
static int rk29_xvf3500_init(struct snd_soc_pcm_runtime *rtd)
{
...
ret = snd_soc_dai_set_sysclk(codec_dai, 0,12288000, SND_SOC_CLOCK_IN);
if (ret < 0) {
printk(KERN_ERR "Failed to set xvf3500 SYSCLK: %d\n", ret);
return ret;
}
...
return 0;
}
2、当写入的数据快,播放的数据慢时,当数据量较多buffer有可能被冲掉,声音会出现类似“爆破”或“呲呲”杂音的现象。通过调整硬件抽象层的period_size和period_count,来改变dma的传输数据量。
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;
};
合理的pcm_config可以做到更好的低时延和功耗。解释一下结构中的各个参数,每个参数的单位都是frame(1帧 = 通道*采样位深):
在不同的场景下,合理的参数就是在性能、时延、功耗等之间达到较好的平衡。
//修改HAL层的period_size 和 period_count路径...hardware/rockchip/audio/tinyalsa_hal/audio_hw.h
//播放
struct pcm_config pcm_config = {
.channels = 2,
.rate = 48000,
.period_size = 2048,
.period_count = 4,
.format = PCM_FORMAT_S16_LE,
};
//录音
struct pcm_config pcm_config_in = {
.channels = 2,
.rate = 48000,
.period_size = 128,//1024
.period_count = 32,//4
.format = PCM_FORMAT_S16_LE,
};
调试技巧:实时录音会有呲呲噪音问题,修改pcm_config_in的period size和period_count大小,使其period_size * period_count的乘积为4096不变。
总结:
这种方法通过增加或减少音频数据的 period_count或period_size来进行补偿。但这样也会使音频播放/录音的数据准备时间变长,增加音频操作的延迟。