[ 音频篇 ] 29 - 调试智能音箱中音频通路的回采(Ref信号)

2020-11-03 10:35:17 浏览数 (1)

项目场景:

项目基于BCM6755平台为基础,通过一系列的语音算法完成实现语音交互场景。这次遇到的问题主要是AEC效果差,如上图所示,设备播放音乐的场景,会出现唤醒困难的想象。实际的抓取录音数据发现录音和回采之间的数据延迟高达100ms,远远超过算法要求<30ms的要求。接下来需要定位延迟的问题。


问题描述:

通道1 为录音数据,通道2为回采数据,发现回采数据比录音数据还延后20ms,理论上回采数据应该比录音数据提前才对的。 最初以为可能是重采样延迟造成的,但实际验证结果不是的。


原因分析:

通过Audacity 生成1k的信号,前1s为空数据,后3s秒为1k信号,方便对比延迟。

通过NXP i.MX8M Mini 平台获取录音 回采数据如下:

通道1 为录音数据,通过Audacity可以看到开始时间为01s134ms。通道2为回采数据,可以看到开始时间为01s133ms。中间的延迟时间为1ms。满足AEC算法要求的时间。这样的录音数据是通过 ALSA multi plugs 1完成通道的合并。插件配置如下:

代码语言:javascript复制
// multi 插件完成两个声卡数据的叠加合并,相当于plughw:3,0 是6ch的音频数据,而plughw:1,0是2ch的音频数据,而通过arecord -Dsub_input 是8ch的音频数据。而bindings参数可以控制通道数据的调整。
pcm.sub_input {
    type multi
    slaves.a.pcm "plughw:3,0"
    slaves.a.channels 6
    slaves.b.pcm "plughw:1,0"
    slaves.b.channels 2
    bindings.0.slave a
    bindings.0.channel 0
    bindings.1.slave a
    bindings.1.channel 1
    bindings.2.slave a
    bindings.2.channel 2
    bindings.3.slave a
    bindings.3.channel 3
    bindings.4.slave a
    bindings.4.channel 4
    bindings.5.slave a
    bindings.5.channel 5
    bindings.6.slave b
    bindings.6.channel 0
    bindings.7.slave b
    bindings.7.channel 1
}

通过Broadcom BCM6755 平台获取录音 回采数据如下:

通道1 为录音数据,通道2为回采数据,发现回采数据比录音数据还延后20ms,理论上回采数据应该比录音数据提前才对的。 最初以为可能是重采样延迟造成的,但实际验证结果不是的。

实际的测试数据是通过VoiceProcess 进程导出的数据,而不是通过apaly xxx.wav | arecord xxx.wav 这种方式或gst-play-1.0 xxx.wav | arecord xxx.wav 因为发现这样测试在BCM6755 平台上有一些问题。

代码语言:javascript复制
# arecord -l 
card 1: MapleTree [MapleTree], device 0: Playback ad82584f-0 []
  Subdevices: 0/1
  Subdevice #0: subdevice #0
card 1: MapleTree [MapleTree], device 1: Capture ES7210 4CH ADC 0-1 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: Dummy [Dummy], device 0: DSP PCM [DSP PCM]
  Subdevices: 0/1
  Subdevice #0: subdevice #0

# aplay -l 
card 1: MapleTree [MapleTree], device 0: Playback ad82584f-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
# 设置系统音量
amixer -c 1 set Master 180 
# 一边播放一边录音
aplay -Dplughw:1,0  1ksine-48k-4s.wav | 
arecord  -D hw:1,0 -c 2 -r 48000 -f S32_LE -d 4 ref_48k_S32.wav

接下来是修改代码后的测试结果,可以发现回采数据比录音数据提前10ms,这样的数据就是比较正常的。

因为回采数据是走功放芯片ad82584f内部通路。而录音数据是通过麦克风采回来的。自然回采数据要快很多。

接下来看下代码的改动,主要还是ALSA API 初始化的参数配置2。

代码语言:javascript复制
snd_pcm_t *Open(std::string name, uint32_t rate, uint8_t channels,snd_pcm_format_t format,snd_pcm_stream_t streamType){
    uint32_t pcm;
    //snd_pcm_format_t format_;
    //uint8_t channels_;
    snd_pcm_t * handle_;
    snd_pcm_hw_params_t * hwParams_;
    snd_pcm_sw_params_t * swParams_;
    //snd_pcm_uframes_t buffer_size_ = BUFFER_SIZE;
    if( (pcm = snd_pcm_open(&handle_, name.c_str(), streamType, 0)) < 0)
    {
        std::cout << "ERROR! Cannot open " << name << std::endl;
        return nullptr;
    }
    snd_pcm_hw_params_alloca(&hwParams_);
    snd_pcm_sw_params_alloca(&swParams_);
    snd_pcm_sw_params_current(handle_, swParams_);
    snd_pcm_hw_params_any(handle_, hwParams_);

    if( (pcm = snd_pcm_hw_params_set_access(handle_, hwParams_, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
    {
        std::cout << "ERROR! Cannot set interleaved mode" << std::endl;
        return nullptr;
    }

    //format_ = format;
    if( (pcm = snd_pcm_hw_params_set_format(handle_, hwParams_, format)) < 0)
    {
        std::cout << "ERROR! Cannot set format" << std::endl;
        return nullptr;
    }

    //channels_ = channels;
    if( (pcm = snd_pcm_hw_params_set_channels(handle_, hwParams_, channels)) < 0)
    {
        std::cout << "ERROR! Cannot set number of channels" << std::endl;
        return nullptr;
    }

    if( (pcm = snd_pcm_hw_params_set_rate_near(handle_, hwParams_, &rate , 0)) < 0)
    {
        std::cout << "ERROR! Cannot set format" << std::endl;
        return nullptr;
    }

    /*  // 延迟的主要原因
    if( (pcm = snd_pcm_hw_params_set_buffer_size_near( handle_,hwParams_, &buffer_size_ )) < 0)
    {
        std::cout << "ERROR! Cannot set buffer" << std::endl;
        return nullptr;
    }
    */
    int dir;
    snd_pcm_uframes_t frames = (SAMPLE_RATE * PERIOD_TIME_MS)/ MS_IN_ONE_SECOND;
    snd_pcm_hw_params_set_period_size_near(handle_, hwParams_, &frames, &dir);
    if( (pcm  = snd_pcm_hw_params(handle_, hwParams_)) < 0)
    {
        std::cout << "ERROR! Cannot set hw parameters" << std::endl;
        return nullptr;
    }
    //frame_size_  = (format_ == SND_PCM_FORMAT_S32_LE ? 4 : 2) * channels_;
    return handle_;
}

进一步分析函数的原形:

代码语言:javascript复制
# alsa-lib-1.1.3/src/pcm/pcm.c
5652 /**
5653  * brief Restrict a configuration space to have buffer size nearest to a target
5654  * param pcm PCM handle
5655  * param params Configuration space
5656  * param val approximate target buffer size in frames / returned chosen approximate target buffer size in frames
5657  * return 0 otherwise a negative error code if configuration space is empty
5658  */
5659 #ifndef DOXYGEN
5660 int INTERNAL(snd_pcm_hw_params_set_buffer_size_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)
5661 #else
5662 int snd_pcm_hw_params_set_buffer_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)
5663 #endif
5664 {
5665 >---unsigned int _val = *val;
5666 >---int err = snd_pcm_hw_param_set_near(pcm, params, SND_PCM_HW_PARAM_BUFFER_SIZE, &_val, NULL);
5667 >---if (err >= 0)
5668 >--->---*val = _val;
5669 >---return err;
5670 }

0926 早上老化发现,长时间过后回采的数据还是比录音慢,对比数据看有35ms。

录音数据

回采数据

延迟

13.243s

12.277s

34ms

16.224s

16.259s

35ms

17.480.s

17.515ms

35ms

23.108s

23.137s

29ms

问题的根本原因隐藏的越来越深了,继续寻找问题的根本原因。

提示:这里填写问题的分析: 例如:Handler 发送消息有两种方式,分别是 Handler.obtainMessage()和 Handler.sendMessage(),其中 obtainMessage 方式当数据量过大时,由于 MessageQuene 大小也有限,所以当 message 处理不及时时,会造成先传的数据被覆盖,进而导致数据丢失。


解决方案:

提示:这里填写该问题的具体解决方案: 例如:新建一个 Message 对象,并将读取到的数据存入 Message,然后 mHandler.obtainMessage(READ_DATA, bytes, -1, buffer).sendToTarget();换成 mHandler.sendMessage()。

ALSA POLL 实现

Reading Microphone Data by Polling using ALSA [or V4L2]

check-alsa-poll.c

参考

Alsa音频编程【精华】 ALSA PCM Timestamping Audio Synchronization ALSA - PCM接口

Linux ALSA 音频系统:逻辑设备篇


  1. pcm_plugins ↩︎
  2. FramesPeriods ↩︎

0 人点赞