翻看pjproject中的源码,发现它实现了一个回声消除的例子aectest.c,它主要依赖三种算法(1=speex, 2=echo suppress, 3=WebRtc),这是可选的,实际使用时选择其中的一种。

它调用的一个命令行为:aectest -d 100 -a 1 ../bin/orig8.wav ../bin/echo8.wav ../bin/result8.wav

-d选项是延迟,因为远端进来的参考声音信号被扬声器播放出来,再到麦克风拾取后读出来,存在一定的延时。

-a选项是选择算法类型,其中echo suppress是pjproject自己实现的,在源码echo_suppress.c中。而speex和WebRtc是从开源项目中搬过来的,放在了third_party目录下面。

这个例子对于学习和研究回声消除是比较有用的。

这里的echo suppress是回声抑制算法,这个依赖于双端发生检测(这是一个对二者电平进行不断统计的过程),就是将远端传过来的声音的电平与近端录制的声音的电平相比较,如果远端在发声,则将近端录制的声音进行抑制,抑制的过程也是一个平滑过渡的过程。

这种抑制的方法是非线性的,有时候会造成扬声器的播放断断续续,但也简单实用。

一份较老的代码echo_suppress.c实现:

  1. /* $Id: echo_suppress.c 1417 2007-08-16 10:11:44Z bennylp $ */
  2. /*
  3. * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18. */
  19. #include <pjmedia/types.h>
  20. #include <pjmedia/errno.h>
  21. #include <pjmedia/silencedet.h>
  22. #include <pj/assert.h>
  23. #include <pj/lock.h>
  24. #include <pj/log.h>
  25. #include <pj/os.h>
  26. #include <pj/pool.h>
  27. #include "echo_internal.h"
  28. #define THIS_FILE               "echo_suppress.c"
  29. /*
  30. * Simple echo suppresor
  31. */
  32. typedef struct echo_supp
  33. {
  34. pj_bool_t        suppressing;
  35. pjmedia_silence_det *sd;
  36. pj_time_val      last_signal;
  37. unsigned         samples_per_frame;
  38. unsigned         tail_ms;
  39. } echo_supp;
  40. /*
  41. * Create.
  42. */
  43. PJ_DEF(pj_status_t) echo_supp_create( pj_pool_t *pool,
  44. unsigned clock_rate,
  45. unsigned samples_per_frame,
  46. unsigned tail_ms,
  47. unsigned latency_ms,
  48. unsigned options,
  49. void **p_state )
  50. {
  51. echo_supp *ec;
  52. pj_status_t status;
  53. PJ_UNUSED_ARG(clock_rate);
  54. PJ_UNUSED_ARG(options);
  55. PJ_UNUSED_ARG(latency_ms);
  56. ec = PJ_POOL_ZALLOC_T(pool, struct echo_supp);
  57. ec->samples_per_frame = samples_per_frame;
  58. ec->tail_ms = tail_ms;
  59. status = pjmedia_silence_det_create(pool, clock_rate, samples_per_frame,
  60. &ec->sd);
  61. if (status != PJ_SUCCESS)
  62. return status;
  63. pjmedia_silence_det_set_name(ec->sd, "ecsu%p");
  64. pjmedia_silence_det_set_adaptive(ec->sd, PJMEDIA_ECHO_SUPPRESS_THRESHOLD);
  65. pjmedia_silence_det_set_params(ec->sd, 100, 500, 3000);
  66. *p_state = ec;
  67. return PJ_SUCCESS;
  68. }
  69. /*
  70. * Destroy.
  71. */
  72. PJ_DEF(pj_status_t) echo_supp_destroy(void *state)
  73. {
  74. PJ_UNUSED_ARG(state);
  75. return PJ_SUCCESS;
  76. }
  77. /*
  78. * Let the AEC knows that a frame has been played to the speaker.
  79. */
  80. PJ_DEF(pj_status_t) echo_supp_playback( void *state,
  81. pj_int16_t *play_frm )
  82. {
  83. echo_supp *ec = (echo_supp*) state;
  84. pj_bool_t silence;
  85. pj_bool_t last_suppressing = ec->suppressing;
  86. silence = pjmedia_silence_det_detect(ec->sd, play_frm,
  87. ec->samples_per_frame, NULL);
  88. ec->suppressing = !silence;
  89. if (ec->suppressing) {
  90. pj_gettimeofday(&ec->last_signal);
  91. }
  92. if (ec->suppressing!=0 && last_suppressing==0) {
  93. PJ_LOG(5,(THIS_FILE, "Start suppressing.."));
  94. } else if (ec->suppressing==0 && last_suppressing!=0) {
  95. PJ_LOG(5,(THIS_FILE, "Stop suppressing.."));
  96. }
  97. return PJ_SUCCESS;
  98. }
  99. /*
  100. * Let the AEC knows that a frame has been captured from the microphone.
  101. */
  102. PJ_DEF(pj_status_t) echo_supp_capture( void *state,
  103. pj_int16_t *rec_frm,
  104. unsigned options )
  105. {
  106. echo_supp *ec = (echo_supp*) state;
  107. pj_time_val now;
  108. unsigned delay_ms;
  109. PJ_UNUSED_ARG(options);
  110. pj_gettimeofday(&now);
  111. PJ_TIME_VAL_SUB(now, ec->last_signal);
  112. delay_ms = PJ_TIME_VAL_MSEC(now);
  113. if (delay_ms < ec->tail_ms) {
  114. #if defined(PJMEDIA_ECHO_SUPPRESS_FACTOR) && PJMEDIA_ECHO_SUPPRESS_FACTOR!=0
  115. unsigned i;
  116. for (i=0; i<ec->samples_per_frame; ++i) {
  117. rec_frm[i] = (pj_int16_t)(rec_frm[i] >>
  118. PJMEDIA_ECHO_SUPPRESS_FACTOR);
  119. }
  120. #else
  121. pjmedia_zero_samples(rec_frm, ec->samples_per_frame);
  122. #endif
  123. }
  124. return PJ_SUCCESS;
  125. }
  126. /*
  127. * Perform echo cancellation.
  128. */
  129. PJ_DEF(pj_status_t) echo_supp_cancel_echo( void *state,
  130. pj_int16_t *rec_frm,
  131. const pj_int16_t *play_frm,
  132. unsigned options,
  133. void *reserved )
  134. {
  135. echo_supp *ec = (echo_supp*) state;
  136. pj_bool_t silence;
  137. PJ_UNUSED_ARG(options);
  138. PJ_UNUSED_ARG(reserved);
  139. silence = pjmedia_silence_det_detect(ec->sd, play_frm,
  140. ec->samples_per_frame, NULL);
  141. if (!silence) {
  142. #if defined(PJMEDIA_ECHO_SUPPRESS_FACTOR) && PJMEDIA_ECHO_SUPPRESS_FACTOR!=0
  143. unsigned i;
  144. for (i=0; i<ec->samples_per_frame; ++i) {
  145. rec_frm[i] = (pj_int16_t)(rec_frm[i] >>
  146. PJMEDIA_ECHO_SUPPRESS_FACTOR);
  147. }
  148. #else
  149. pjmedia_zero_samples(rec_frm, ec->samples_per_frame);
  150. #endif
  151. }
  152. return PJ_SUCCESS;
  153. }

可供初学者一睹其原貌,但最新的pjproject中的实现就比较复杂。

浅谈回声消除中的回声抑制(echo suppress)相关推荐

  1. 回声消除中的自适应滤波算法综述

    作者:凌逆战 博客园地址:https://www.cnblogs.com/LXP-Never/p/11773190.html 自适应回声消除原理 声学回声是指扬声器播出的声音在接受者听到的同时,也通过 ...

  2. 运用计算机优化教学的方法,浅谈计算机教学中多种教学方法优化运用.doc

    浅谈计算机教学中多种教学方法优化运用 浅谈计算机教学中多种教学方法优化运用 摘 要:每一种教学方法各有其优越性,也各有其局限性,正如一把钥匙不能打开所有的锁一样,所以对于不同特点的学生群体,不同特点的 ...

  3. mysql declare与set的区别_浅谈MySQL存储过程中declare和set定义变量的区别

    在存储过程中常看到declare定义的变量和@set定义的变量.简单的来说,declare定义的类似是局部变量,@set定义的类似全局变量. 1.declare定义的变量类似java类中的局部变量,仅 ...

  4. 浅谈单片机程序设计中的“分层思想”!

    浅谈单片机程序设计中的"分层思想",并不是什么神秘的东西,事实上很多做项目的工程师本身自己也会在用.看了不少帖子都发现没有提及这个东西,然而分层结构确是很有用的东西,参透后会有一种 ...

  5. 浅谈网络爬虫中广度优先算法和代码实现

    前几天给大家分享了网络爬虫中深度优先算法的介绍及其代码实现过程,没来得及上车的小伙伴们可以戳这篇文章--浅谈网络爬虫中深度优先算法和简单代码实现.今天小编给大家分享网络爬虫中广度优先算法的介绍及其代码 ...

  6. 浅谈软件性能测试中关键指标的监控与分析(转)

    浅谈软件性能测试中关键指标的监控与分析 一.软件性能测试需要监控哪些关键指标? 软件性能测试的目的主要有以下三点: Ø  评价系统当前性能,判断系统是否满足预期的性能需求. Ø  寻找软件系统可能存在 ...

  7. php hasmany,浅谈laravel orm 中的一对多关系 hasMany

    个人对于laravel orm 中对于一对多关系的理解 文章表 article,文章自然可以评论,表 comment 记录文章的评论,文章和评论的关系就是一对多,一篇文章可以有多个评论. 在 comm ...

  8. mysql key_len_浅谈mysql explain中key_len的计算方法

    mysql的explain命令可以分析sql的性能,其中有一项是key_len(索引的长度)的统计.本文将分析mysql explain中key_len的计算方法. 1.创建测试表及数据 CREATE ...

  9. 事物日志恢复 mysql_浅谈SQL Server中的事务日志(五)----日志在高可用和灾难恢复中的作用...

    本篇文章是系列文章中的第五篇,是对前一个日志系列的补充篇.如果您对日志的基本概念还没有一个比较系统的了解,可以参看本系列之前的文章: 浅谈SQL Server中的事务日志(一)----事务日志的物理和 ...

最新文章

  1. ELK 搭建 TB 级海量日志监控系统,这个太强了!
  2. 合并果子(NOIP2004)
  3. 【Python】编程笔记6
  4. linux 系统安装mongodb数据库---方法2
  5. leetcode 480. 滑动窗口中位数(堆+滑动窗口)
  6. [react-router] 在history模式中push和replace有什么区别?
  7. 41%的网络保险理赔和勒索软件有关
  8. NYOJ455 - 黑色帽子
  9. 8.确保0对于值类型是有效的
  10. 《编写可维护的 JavaScript》读书笔记第7章:事件处理
  11. 【Gym-100889 H】Hitting Points【凸包三分】
  12. openGauss企业级开源数据库获第十届中国电子信息博览会金奖
  13. Java面向对象通讯录程序
  14. Java Wbe开发快速入门
  15. 贝叶斯公式:通俗的理解
  16. 海量数据处理技巧-转载
  17. 硬件知识:固态硬盘和机械硬盘区别
  18. matlab的数字图像处理,基于MATLAB的数字图像处理分析及应用.pdf
  19. MarkdownPad2行内公式如何用`$$`替代`\\(\\)`---MathJax风格化配置
  20. 基于python机票预定系统_机票预订系统课程设计.doc

热门文章

  1. android百度地图:卫星地图普通地图交通地图切换
  2. 火狐浏览器支持跨域调试
  3. Linux命令——history及其优化
  4. linux防火墙相关操作,永久关闭防火墙
  5. Sigmastar与Novatek联咏IPC芯片规格对比_整理不易_先赞后存
  6. 更多的资本更少的初创企业 以色列网络安全行业走向成熟
  7. HTTP XMLHttp Ajax WebSocket
  8. [案例分享]激活电力价值,八大案例深度解析电力大数据应用
  9. 005.python科学计算库pandas(下)
  10. bearychat和trello的协同工作