小奥的学习笔记

  • Home
  • Learning & Working
    • Speech Enhancement Notes
    • Programming language
    • Computer & DL
    • MOOC
  • Life
    • Life Time
    • Thinking & Comprehension
    • Volunteer
    • Plan
    • Travel
  • Footprints
  • GuestBook
  • About
    • About Me
    • 个人履历
    • 隐私策略
  1. 首页
  2. Study-notes
  3. Computer & DL
  4. 语音处理学习笔记
  5. 正文

AEC个人学习串讲之AEC3:概述及非核心部分

2021年10月17日 3484点热度 8人点赞 0条评论

整体框图

参考信号:送往时延估计、AEC的状态计算
采集信号:送往时延估计、线性滤波器、AEC的状态计算
线性滤波器使用时延对齐后的参考信号和采集信号进行处理;线性滤波器处理后的结果送往NLP模块,最终得到处理结果。

类调用关系图

EchoCanceller3(入口)

AnalyzeRender

该部分只是将AudioBuffer类型的参考信号insert到render_transfer_queue_,这是一个swapqueue,数据从结尾insert,从开头remove,这里没有什么可以说的【当然insert之前会经过一个高通滤波器,滤除甚低频部分】。

ProcessCapture

核心处理过程

近端采集数据的处理主要分为以下几步:
  1. 设置固定的时延。如果config_里面有delay的数据(config_.delay.fixed_capture_delay_samples),则对capture设置时延(block_delay_buffer_.DelaySignal(capture))。
  2. 取出参考信号队列里面的数据,存入render_buffer_。【下面介绍】
  3. ProcessCaptureFrameContent处理,具体处理什么需要进一步梳理确定。该函数执行的两遍,差别在于第五个参数,第一个为0,第二个为1。该函数的参数列表如下:
变量名
数据类型
数据含义
linear_output
AudioBuffer*
线性处理部分的输出
capture
AudioBuffer*
近端采集信号
level_change
bool
level是否发生变化【即近端路径改变】
saturated_microphone_signal
bool
近端是否爆音
sub_frame_index
size_t
第几个子帧(两遍处理的区别在此)
capture_blocker
FrameBlocker*
暂不清楚作用。类型说明:Class for producing 64 sample multiband blocks from frames consisting of 2 subframes of 80 samples
linear_output_framer
BlockFramer*
暂不清楚作用。类型说明:Class for producing frames consisting of 2 subframes of 80 samples each from 64 sample blocks. The class is designed to work together with the FrameBlocker class which performs the reverse conversion. Used together with that, this class produces output frames are the same rate as frames are received by the FrameBlocker class. Note that the internal buffers will overrun if any other rate of packets insertion is used.
output_framer
BlockFramer*
同上
block_processor
BlockProcessor*
块处理的实例,核心处理内容
linear_output_block
std::vector<std::vector<std::vector<float>>>*
暂不确定作用
linear_output_sub_frame_view
std::vector<std::vector<rtc::ArrayView<float>>>*
同上
capture_block
std::vector<std::vector<std::vector<float>>>*
同上
capture_sub_frame_view
std::vector<std::vector<rtc::ArrayView<float>>>*
同上

EmptyRenderQueue

该函数的作用是将AnalyzeRender里面insert到swapqueue中的参考buffer remove出来存储到render_buffer_中。

BufferRenderFrameContent做两遍,实际上是分别将两个sub_frame_view(80个点为一个subframe)的数据存入sub_frame_view,然后将sub_frame_view数据存入2维的vector数组block【语句为render_blocker->InsertSubFrameAndExtractBlock(*sub_frame_view, block),它是从多频带帧中插入一个 80 采样的多频带子帧,并提取一个 64 采样的多频带块。下面执行本语句的相同】,然后去执行BlockProcessor的insert函数,将远端参考数据存入到render_buffer_。该函数的参数列表如下:
变量名
数据类型
数据含义
render_frame
std::vector<std::vector<std::vector<float>>>*
render_queue_output_frame_
sub_frame_index
size_t
第0或1个子帧
render_blocker
FrameBlocker*
用于将sub_frame_view数据存入2维的vector数组block
block_processor
BlockProcessor*
块处理的实例,核心处理内容
block
std::vector<std::vector<std::vector<float>>>*
用于插入render_buffer_的block
sub_frame_view
std::vector<std::vector<rtc::ArrayView<float>>>*
子帧
BufferRemainingRenderFrameContent执行了render_blocker->ExtractBlock(block)和block_processor->BufferRender(*block),与BufferRenderFrameContent作用类似,但为何多这样一个步骤,还需要进一步确定。

ProcessCaptureFrameContent

该函数的作用是将AudioBuffer类型的capture以及linear_output与std::vector<std::vector<rtc::ArrayView<float>>>*类型的子帧subframeview建立起地址间的绑定关系。然后拿着subframeview去转成std::vector<std::vector<std::vector<float>>>*类型的block,然后进行AEC处理后,再将block转为subframe。这样不需要再更新capture或linear_output就可以获取处理后的结果。

ProcessRemainingCaptureFrameContent

ProcessRemainingCaptureFrameContent的操作与ProcessCaptureFrameContent类似。但为何多这样一个步骤,还需要进一步确定。
至此,EchoCanceller3这个类主体内容以及分析完毕。后续操作就交给了BlockProcessor这个类来进行操作了。

BlockProcessorImpl::ProcessCapture

该函数被ProcessCaptureFrameContent和ProcessRemainingCaptureFrameContent调用。参数列表如下:
变量名
数据类型
数据含义
echo_path_gain_change
bool
回声路径是否发生改变
capture_signal_saturation
bool
是否为爆音信号
linear_output
std::vector<std::vector<std::vector<float>>>*
线性输出的block
capture_block
std::vector<std::vector<std::vector<float>>>*
近端输入的block
用到的结构体:
struct EchoPathVariability {
enum class DelayAdjustment {//delay_change的一个状态
kNone,//初始状态
kBufferReadjustment,//buffer重新调整
kBufferFlush,//刷新了buffer
kDelayReset,//delay被reset
kNewDetectedDelay//新检测到延迟
};
EchoPathVariability(bool gain_change,
DelayAdjustment delay_change,
bool clock_drift);
bool AudioPathChanged() const {
return gain_change || delay_change != DelayAdjustment::kNone;
}
bool gain_change;
DelayAdjustment delay_change;
bool clock_drift;
};
如果接收参考信号帧已经开始(render_properly_started_==true):说明需要做AEC,则接下来判断采集开始状态是否为false(capture_properly_started_==false),若为false,则代表AEC的采集帧还没过来,就将capture_properly_started_置为true,render_buffer_给清空,delay_controller_给reset掉;若为true,说明之前AEC就开始了,则继续进行就好了。若render_properly_started_==false,说明远端没有参考信号,则不需要做AEC,直接return就好。
新建一个EchoPathVariability类型的echo_path_variability,用echo_path_gain_change、EchoPathVariability::DelayAdjustment::kNone(delay_change的初始状态)和false来初始化。
接下来就是看render_buffer有没有满(如果满了的话,在BufferRender会将render_event_置为kRenderOverrun),如果render_buffer满了且render_properly_started_是true(肯定是有参考信号才行),则需要将render_buffer清空、delay_controller_清空,这个时候需要将echo_path_variability的delay_change置为kBufferFlush,代表刷新了。无论render_buffer有没有满,之后都要将render_event_置为kNone。
再接下来就是对render_buffer进行预处理得到一个buffer_event(PrepareCaptureProcessing)。若buffer_event为underrun,则代表render_buffer空了,则需要将delay_controller_reset掉,传入false。否则的话就继续往下进行。
然后根据config判断是否有额外输入的delay数据,如果有,则直接根据额外的delay数据进行对齐(render_buffer_->AlignFromExternalDelay());否则的话,使用内建的GetDelay()函数得到estimated_delay_,然后对齐参考信号和输入采集信号,如果delay发生改变,则将echo_path_variability.delay_change修改为kNewDetectedDelay。
然后就是最关键的一步,根据有没有使用内建的getdelay()得到时延(has_delay_estimator)或者 render_buffer_->HasReceivedBufferDelay()判断是否进行核心的回声消除,若两者有一个,则执行(echo_remover_->ProcessCapture()),否则不执行。
最后更新下metrics(metrics_.UpdateCapture(false);)。
这里面最关键是的使用匹配滤波器得到时延的GetDelay()和进行回声消除的echo_remover_->ProcessCapture()。接下来细细讲来。
以上是第一部分内容,接下来将是第二部分内容,在下一篇文章更新。由于博客格式问题较多,想更好的学习或者有什么问题,欢迎到飞书文档进行浏览和留言。点击这里:飞书文档链接: https://qouwscohey.feishu.cn/docs/doccnUcSGDg8ehjisuw1ww5KmGi,访问密码可通过关注我的公众号,并回复“AEC3”获得。
本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: AEC AEC3 WebRTC
最后更新:2021年10月17日

davidcheung

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

搜索
欢迎关注我的个人公众号
最新 热点 随机
最新 热点 随机
DEEPFILTERNET:一种基于深度滤波的全频带音频低复杂度语音增强框架 奥地利匈牙利九日游旅程 论文阅读之Study of the General Kalman Filter for Echo Cancellation 小奥看房之鸿荣源珈誉府 杭州往返旅途及西溪喜来登和万怡的体验报告 2022年的第一篇碎碎念
奥地利匈牙利九日游旅程小奥看房之鸿荣源珈誉府论文阅读之Study of the General Kalman Filter for Echo CancellationDEEPFILTERNET:一种基于深度滤波的全频带音频低复杂度语音增强框架
莱芜市2010年普通高中招生说明(摘要)2 能否在我污秽的思想之中找到一点纯洁的东西? C++ Primer Plus(第五版)第5章编程题答案 世园会水舞(夜景) ULTRAMAN X-ERO制作前记 关于2011年莱芜市中小学校寒暑假安排有关事宜的通知
标签聚合
leetcode 算法 生活 鸟哥的linux私房菜 学习 高中 python学习 Java Python linux
最近评论
davidcheung 发布于 5 个月前(02月09日) The problem has been fixed. May I ask if you can s...
tk88 发布于 5 个月前(02月07日) Hmm is anyone else having problems with the pictur...
cuicui 发布于 8 个月前(10月20日) :wink:
niming 发布于 10 个月前(09月19日) 同级校友,能刷到太巧了
davidcheung 发布于 2 年前(08月16日) 我得找一下我之前整理的word文档看一下,如果找到了我就更新一下这篇文章。
Nolan 发布于 2 年前(07月25日) 您的笔记非常有帮助。贴图不显示了,可以更新一下吗?
davidcheung 发布于 3 年前(06月19日) 到没有看webrtc的代码。现在主要在看我们公司的代码了。。。只是偶尔看一看webrtc的东西。。。
aobai 发布于 3 年前(03月13日) gain_change_hangover_ 应该是每三个block 只能够调整一次,这样保证每帧...
匿名 发布于 5 年前(12月30日) 烫
小奥 发布于 5 年前(12月12日) webRTC里面的NS本身我记得就是在C++里面呀

COPYRIGHT © 2025 小奥的学习笔记. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

陕ICP备19003234号-1

鲁公网安备37120202000100号