webrtc的回声消除延迟时间估算

news2024/12/25 0:47:12

叫回声消除的延迟时间估算不太合理,这里核心就是估算调用webrtc的条件边界,都知道webrtc回声消除的生效的前提就是一定要拿到远端声音的信息,然后拿近端声音和远端声音对齐,从近端声音中,结合远端声音模拟出远端声音在近端声音中的回声信息然后干掉回声信息,因此,若由于对齐的中保存的远端声音没有对应的声音,则webrtc的回声消除效果就会很差或者就没有效果,所以这个时间差,就很重要,需要结合硬件的情况进行综合权衡delay_time。

webrtc的回声抵消(aec、aecm)算法主要包括以下几个重要模块:

  • 回声时延估计
  • NLMS(归一化最小均方自适应算法)
  • NLP(非线性滤波)
  • CNG(舒适噪声产生)

回声时延估计

 

这张图很多东西可以无视,我们重点看T0,T1,T2三项。

  • T0代表着声音从扬声器传到麦克风的时间,这个时间可以忽略,因为一般来说话筒和扬声器之间距离不会太远,考虑到声音340米每秒的速度,这个时间都不会超过1毫秒。
  • T1代表远处传到你这来的声音,这个声音被传递到回声消除远端接口(WebRtcAec_BufferFarend)的到播放出来的时间。一般来说接收到的音频数据传入这个接口的时候也就是上层传入扬声器的时刻,所以可以理解成该声音防到播放队列中开始计时,到播放出来的时间。
  • T2代表一段声音被扬声器采集到,然后到被送到近端处理函数(WebRtcAec_Process)的时刻,由于声音被采集到马上会做回声消除处理,所以这个时间可以理解成麦克风采集到声音开始计时,然后到你的代码拿到音频PCM数据所用的时间。
  • delay=T0+T1+T2,其实也就是T1+T2。

一般来说,一个设备如果能找到合适的delay,那么这个设备再做回声消除处理就和降噪增益一样几乎没什么难度了。如iPhone的固定delay是60ms。不过这个要看代码所在位置,假如在芯片内部,时间还是比较少的,并且容易固定,假如在系统应用层软件,整个时间就不确定了。相对比较大了。apm_->set_stream_delay_ms(delay_ms_);

例如在android系统下的webrtc的封装调用:

#include "webrtc_apm.h"
//#include "webrtc/common_types.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/include/module_common_types.h"
#include "webrtc/api/audio/audio_frame.h"
#include "YuvConvert.h"

using namespace webrtc;
//using namespace cbase;

WebrtcAPM::WebrtcAPM(int process_smp, int reverse_smp)
: apm_(nullptr)
, far_frame_(new AudioFrame)
, near_frame_(new AudioFrame)
, ref_cnt_(0)
, sample_rate_(8000)
, samples_per_channel_(8000/100)
, channels_(1)
, frame_size_10ms_(8000/100*sizeof(int16_t))
, delay_ms_(60)
, process_sample_rate_(process_smp)//44100
, reverse_sample_rate_(reverse_smp)//48000
{
    audio_send_ = new unsigned char[kMaxDataSizeSamples_];
    audio_reverse_ = new unsigned char[kMaxDataSizeSamples_];

#if defined(__APPLE__)
    delay_ms_ = 60;
#endif

#if defined(VAD_TEST)
        //create webrtc vad
        ty_vad_create(8000, 1);
        ty_set_vad_level(vad_level::LOW);
        ty_vad_set_recordfile("/sdcard/vadfile.pcm");
#endif
}

WebrtcAPM::~WebrtcAPM()
{
    if(far_frame_)
    {
        delete far_frame_;
        far_frame_ = NULL ;
    }

    if (near_frame_) {
        delete near_frame_ ;
        near_frame_ = NULL ;
    }

    if (audio_send_) {
        delete[] audio_send_;
    }

    if (audio_reverse_) {
        delete[] audio_reverse_;
    }

#if defined(VAD_TEST)
        //destory webrtc vad
        ty_vad_destory();
#endif
}

void WebrtcAPM::set_sample_rate(int sample_rate)
{
    sample_rate_ = sample_rate;
    samples_per_channel_ = sample_rate_ / 100;
}

int WebrtcAPM::frame_size()
{
    return frame_size_10ms_;
}

void WebrtcAPM::set_ace_delay(int delay)
{
    LOGI("set aec delay to %d ms \n", delay);
    delay_ms_ = delay;
}

void WebrtcAPM::set_reverse_stream(int reverse_sample_rate)
{
    std::lock_guard<std::mutex> guard(mutex_);

    reverse_sample_rate_ = reverse_sample_rate;
    if (resampleReverse) {
        delete resampleReverse;
        resampleReverse = NULL ;
    }

    resampleReverse = new webrtc::Resampler(reverse_sample_rate_,sample_rate_,channels_);
    int result = resampleReverse->Reset(reverse_sample_rate_,sample_rate_,channels_);
    if (result != 0) {
        LOGE("reset resampleReverse fail,%d!\n", result);
    }
}

int WebrtcAPM::init()
{
    std::lock_guard<std::mutex> guard(mutex_);

    resampleReverse = new webrtc::Resampler(reverse_sample_rate_,sample_rate_,channels_);
    int result = resampleReverse->Reset(reverse_sample_rate_,sample_rate_,channels_);
    if (result != 0) {
        LOGE("reset resampleReverse fail,%d!\n", result);
    }

    resampleIn = new webrtc::Resampler(sample_rate_,process_sample_rate_,channels_);
    result = resampleIn->Reset(sample_rate_,process_sample_rate_,channels_);
    if (result != 0) {
        LOGE("reset resampleIn fail,%d!\n", result);
    }

    initAPM();

    LOGI("initAPM success!");

    return 0;//crash without this
}

void WebrtcAPM::uninit()
{
    std::lock_guard<std::mutex> guard(mutex_);

    deInitAPM();
    safe_delete(resampleIn);
    safe_delete(resampleReverse);
}

void WebrtcAPM::reset_apm(){
    std::lock_guard<std::mutex> guard(mutex_);
    deInitAPM();
    initAPM();
}

int WebrtcAPM::initAPM(){
    apm_ = AudioProcessingBuilder().Create();
    if (apm_ == nullptr) {
        LOGE("AudioProcessing create failed");
        return -1;
    }

    AudioProcessing::Config config;
    config.echo_canceller.enabled = true;
    config.echo_canceller.mobile_mode = true;

    config.gain_controller1.enabled = true;
    config.gain_controller1.mode = AudioProcessing::Config::GainController1::kAdaptiveDigital;
    config.gain_controller1.analog_level_minimum = 0;
    config.gain_controller1.analog_level_maximum = 255;

    config.noise_suppression.enabled = true;
    config.noise_suppression.level = AudioProcessing::Config::NoiseSuppression::Level::kModerate;

    config.gain_controller2.enabled = true;
    config.high_pass_filter.enabled = true;
    config.voice_detection.enabled = true;

    apm_->ApplyConfig(config);
    LOGI("AudioProcessing initialize success \n");

    far_frame_->sample_rate_hz_ = sample_rate_;
    far_frame_->samples_per_channel_ = samples_per_channel_;
    far_frame_->num_channels_ = channels_;

    near_frame_->sample_rate_hz_ = sample_rate_;
    near_frame_->samples_per_channel_ = samples_per_channel_;
    near_frame_->num_channels_ = channels_;

    frame_size_10ms_ = samples_per_channel_ * channels_ * sizeof(int16_t);

    //LOGI("AudioProcessing initialize success end\n");
    return 0;

}

void WebrtcAPM::deInitAPM(){
    //std::lock_guard<std::mutex> guard(mutex_);
    //if (--ref_cnt_ == 0) {
        LOGI("destroy WebrtcAPM \n");
        safe_delete(apm_);
    //}
}

//8000->44100
void WebrtcAPM::process_stream(uint8_t *buffer,int bufferLength, uint8_t *bufferOut, int* pOutLen, bool bUseAEC)
{
    if (bUseAEC) {
        std::lock_guard<std::mutex> guard(mutex_);
        if (apm_) {
            int frame_count = bufferLength / frame_size_10ms_;
        
            for (int i = 0; i < frame_count; i++) {
                 apm_->set_stream_delay_ms(delay_ms_);
                 // webrtc apm process 10ms datas every time
                 memcpy((void*)near_frame_->data(), buffer + i*frame_size_10ms_, frame_size_10ms_);
                 int res = apm_->ProcessStream(near_frame_->data(),
                                        StreamConfig(near_frame_->sample_rate_hz_, near_frame_->num_channels_),
                                        StreamConfig(near_frame_->sample_rate_hz_, near_frame_->num_channels_),
                                        (int16_t * const)near_frame_->data());
                 if (res != 0) {
                     LOGE("ProcessStream failed, ret %d \n",res);
                 }

#if  defined(VAD_TEST)                        
            bool ret = ty_vad_process(buffer + i*frame_size_10ms_, frame_size_10ms_);
#endif

                 memcpy(buffer + i*frame_size_10ms_, near_frame_->data(), frame_size_10ms_);
            }
        }
    }

    if (resampleIn && apm_) {
        //resample
        size_t outlen = 0 ;
        int result = resampleIn->Push((int16_t*)buffer, bufferLength/sizeof(int16_t), (int16_t*)bufferOut, kMaxDataSizeSamples_/sizeof(int16_t), outlen);
        if (result != 0) {
            LOGE("resampleIn error, result = %d, outlen = %d\n", result, outlen);
        }
        *pOutLen = outlen;
    }
}

//48000->8000
void WebrtcAPM::process_reverse_10ms_stream(uint8_t *bufferIn, int bufferLength, uint8_t *bufferOut, int* pOutLen, bool bUseAEC)
{
    size_t outlen = 0 ;
    if (resampleReverse && apm_) {
        //resample
        int result = resampleReverse->Push((int16_t*)bufferIn, bufferLength/sizeof(int16_t), (int16_t*)audio_reverse_, kMaxDataSizeSamples_/sizeof(int16_t), outlen);
        if (result != 0) {
            LOGE("resampleReverse error, result = %d, outlen = %d\n", result, outlen);
        }
    }
    else {
		memcpy(audio_reverse_, bufferIn, bufferLength);
		outlen = bufferLength;
	}

    *pOutLen = outlen;

    if (!bUseAEC){
        //copy data and return
        memcpy(bufferOut, audio_reverse_, frame_size_10ms_);
        return;
    }

    std::lock_guard<std::mutex> guard(mutex_);
    if (apm_) {
        memcpy((void*)far_frame_->data(), audio_reverse_, frame_size_10ms_);
        int res = apm_->ProcessReverseStream(far_frame_->data(),
                                        StreamConfig(far_frame_->sample_rate_hz_, far_frame_->num_channels_),
                                        StreamConfig(far_frame_->sample_rate_hz_, far_frame_->num_channels_),
                                        (int16_t * const)far_frame_->data());
        if (res != 0) {
            LOGE("ProcessReverseStream failed, ret %d \n",res);
        }
        memcpy(bufferOut, audio_reverse_, frame_size_10ms_);//far_frame_->data()
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/825102.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL安装 找不到 MSVCP100.dll

安装mysql5.6.51时&#xff0c;出现由于找不到 MSVCP100.dll&#xff0c;无法继续执行代码。重新安装程序可能会解决此问题。 这应该是缺少VS运行库文件导致的&#xff0c;运行库就是支持大部分程序运行的基础&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编…

Toolformer :让AI学会使用工具

paper: 《Toolformer: Language Models Can Teach Themselves to Use Tools 》 核心思想&#xff0c; 1. Sampling API Calls &#xff1a;设计设计prompt,让模型生成含API调用的文本&#xff08;如图3&#xff09;&#xff0c;只保留K个概率最高的API调用 2. Executing API …

nvm安装和切换node版本

1、nvm list查看已安装的node版本 2、查看当前使用的npm和node版本 3、安装某版本的node 4、 切换node版本

一行命令删除tag为<none>的镜像

sudo docker images | grep none | awk {print $3;} | xargs sudo docker rmi

理解APP开发的法规和合规性问题

随着移动应用程序的快速普及&#xff0c;越来越多的用户选择使用这些应用程序。但它们也会带来一些问题&#xff0c;其中许多是由于应用程序开发过程中存在的法规和合规性问题。用户需要了解这些问题&#xff0c;以及如何解决它们。 APP开发是一个不断变化的领域&#xff0c;其…

我的创作纪念日——256天

机缘 最开始我写博客没有什么特别的原因&#xff0c;主要是因为以下几点&#xff1a; 练习自己的语言组织能力 记录自己学习生活中学到的知识 为和我同一个学习阶段的朋友提供帮助 事实上最开始我根本不指望我的博客有多少人看&#xff0c;主要是想找一个好的保存 Markdown 笔…

穷举深搜暴搜回溯剪枝(2)

一)电话号码的字母组合 17. 电话号码的字母组合 - 力扣&#xff08;LeetCode&#xff09; 1)画出决策树:只是需要对最终的决策树做一个深度优先遍历 把图画出来&#xff0c;知道每一层在干什么&#xff0c;代码就能写出来了 2)定义全局变量: 1)定义一个哈希表来处理数字和字符串…

【freespace】YOLOP: You Only Look Once for Panoptic Driving Perception论文解读

目录 Abstract 摘要 1、简介 2、 相关工作 2.1. 交通目标检测 2.2. 可驾驶区域分割 2.3. 车道检测 2.4. 多任务的方法 3. 方法 3.1. 编码器 3.1.1 骨干 3.1.2 脖子 3.2. 解码器 3.2.1 探测头 3.2.2 可驾驶区域分段头&车道线分段头 3.3. 损失函数 3.4. 训练模…

Windows同时安装两个版本的JDK并随时切换,以JDK6和JDK8为例,并解决相关存在的问题(亲测有效)

Windows同时安装两个版本的JDK并随时切换&#xff0c;以JDK6和JDK8为例&#xff0c;并解决相关存在的问题&#xff08;亲测有效&#xff09; 1.下载不同版本JDK 这里给出JDK6和JDK的百度网盘地址&#xff0c;具体安装过程&#xff0c;傻瓜式安装即可。 链接&#xff1a;http…

华为OD机试真题 JavaScript 实现【机器人活动区域】【2023Q1 200分】,附详细解题思路

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、JavaScript算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试&am…

EXCEL,多条件查询数字/文本内容的4种方法

目录 1 问题&#xff1a;如何根据多条件查询到想要的内容 2 方法总结 2.1 方法1&#xff1a; sumif() 和sumifs() 适合查找符合条件的多个数值之和 2.2 方法2&#xff1a;使用lookup(1,0/((区域1条件1)*(区域2条件2)*....),结果查询区域) 2.3 方法3&#xff1a;使用 ind…

rknn模型在rv1126开发板上跑

在前面&#xff0c;已经将 onnx模型转为 rknn模型。 yolov5 onnx模型 转为 rknn模型_爱钓鱼的歪猴的博客-CSDN博客 这里探讨的是&#xff1a;rknn模型在rv1126开发板上运行 目录 1、rknn模型在PC端进行推理测试&#xff0c;评估模型精度 2、模型预编译 3、rknn模型部署到r…

【LeetCode】根据二叉树创建字符串

根据二叉树创建字符串 题目描述算法分析编程代码 链接: 根据二叉树创建字符串 题目描述 算法分析 当单纯的按照前序遍历输出后&#xff0c;我们只要对&#xff08;&#xff09;进行一些修改就好 编程代码 /*** Definition for a binary tree node.* struct TreeNode {* …

偷懒神器-->花样的代码生成工具

1、CRUD代码生成&#xff1a; 根据MyBatisPlus逆向工程改造而来&#xff0c;添加了showDoc文档生成&#xff0c;数据库脚本生成&#xff0c;增删改查文件生成&#xff0c;Po、Vo、Request对象生成等。普通的增删改查一般搞定。并预调了部份判断逻辑。 效果示例&#xff1a; p…

0801|IO进程线程day4 文件IO函数-目录相关函数

1&#xff09;opendir 功能&#xff1a;打开一个目录文件; 原型&#xff1a; #include <sys/types.h>#include <dirent.h>DIR *opendir(const char *name); 参数&#xff1a; char *name&#xff1a;指定要打开的目录的路径以及名字; 返回值&#xff1a; 成功&…

OpenCV实战:从图像处理到深度学习的全面指南

文章目录 1. OpenCV简介什么是OpenCV&#xff1f;OpenCV的历史与发展OpenCV的应用领域 2. OpenCV的安装与配置OpenCV在Windows系统下的安装OpenCV在Linux系统下的安装OpenCV在Mac OS系统下的安装配置Python环境使用OpenCV 3. OpenCV基础图像的载入、显示和保存图像的基础操作图…

java 企业工程管理系统软件源码+Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis em

&#xfeff; 工程项目管理软件&#xff08;工程项目管理系统&#xff09;对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营&#xff0c;全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据…

linux(一)-----osi七层模型

1.七层模型的定义 1&#xff09;物理层 &#xff08;指的是网卡&#xff0c;网线等物理层面的硬件&#xff09; 2&#xff09;数据链路层 前两层可以概括为物理链路层&#xff0c;以二进制的数据形式在物理媒介上进行传输数据&#xff08;传输协议ISO2100协议&#xff09; …

微信小程序 - 解析富文本插件版们

一、html2wxml 插件版 https://gitee.com/qwqoffice/html2wxml 申请使用注意事项 插件版本解析服务是由 QwqOffice 完成&#xff0c;存在不稳定因素&#xff0c;如对稳定性有很高的要求&#xff0c;请自行搭建解析服务&#xff0c;或在自家服务器上直接完成解析。对于有关插…