android、ios、windows等平台回声消除的解决方案

2024-04-09 06:32

本文主要是介绍android、ios、windows等平台回声消除的解决方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

回声消除介绍

http://www.qttaudio.com/aecjsgs.html

在即时通讯应用中,需要进行双方,或是多方的实时语音交流,在要求较高的场合,通常都是采用外置音箱放音,这样必然会产生回音,即一方说话后,通过对方的音箱放音,然后又被对方的Mic采集到回传给自己。如果不对回音进行处理,将会影响通话质量和用户体验,更严重的还会形成震荡,产生啸叫。

​回声消除就是在Mic采集到声音之后,将本地音箱播放出来的声音从Mic采集的声音数据中消除掉,使得Mic录制的声音只有本地用户说话的声音。

传统的回声消除都是采用硬件方式,在硬件电路上集成DSP处理芯片,如我们常用的固定电话、手机等都有专门的回音消除处理电路,而采用软件方式实现回声消除一直存在技术难点。

回声消除已经成为即时通讯中提供全双工语音的标准方法。声学回声消除是通过消除或者移除本地话筒中拾取到的远端的音频信号来阻止远端的声音返回去的一种处理方法。这种音频的移除都是通过数字信号处理来完成的。回声消除技术是数字信号处理的典型应用之一。

Android平台回声消除方法

 

QttAudio
  1. QttAudio解决了什么问题?
  • Android、iOS、Windows、(嵌入式)Linux系统的回音消除问题:通话再无回声啸叫,高清音质
  • 多语音流混音问题:多人会议模式很简单
  • 内置多种语音编解码格式:摆脱烦人的编解码开发工作量
  • 跨平台,接口一致性,集成简单:合四为一,加速应用开发,助您抢占先机
  • 与服务器无关:摆脱云服务绑架,私有云部署,更安全,更划算
  1. 一个代码小例子

Java(Android )

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);try {//初始化QttAudioEngineQttAudioEngine.me().init(this, "your appkey");//创建QttAudioStreamQttAudioStream stream = QttAudioEngine.me().createStream();//设置mic的编码参数,pcm,单声道,48000hz采样率QttAudioEngine.me().setMicCodecParams("pcm", 1, 48000, 0);//设置mic采集回调函数QttAudioEngine.me().setMicCaptureCb(new QttCaptureCallbak() {@Overridepublic void onCapture(byte[] buf, int bufLength, Object userdata) {QttAudioStream tmpStream = (QttAudioStream) userdata;//将mic采集到的播放出来,实现音频回放功能tmpStream.writePayload(buf, 0, bufLength);}}, stream);//启动stream,开始工作stream.start();} catch (QttException e) {e.printStackTrace();}}
}

C (iOS、Linux、Windows)

static void onCapture(char *buf, size_t len, void *userdata) {QttAudioStream *stream = (QttAudioStream *)userdata;//将mic采集到的播放出来,实现音频回放功能qtt_stream_write_payload(stream, buf, len);
}int main() {//创建QttAudioEngineQttAudioEngine *engine = qtt_engine_init("your appkey");if (engine == NULL) {fprintf(stderr, "qtt_engine_init fail\n");return -1;}//创建QttAudioStreamQttAudioStream *stream = qtt_engine_create_stream(engine);if (stream == NULL) {fprintf(stderr, "qtt_engine_create_stream fail\n");return -1;}//设置mic的编码参数,pcm,单声道,48000hz采样率qtt_engine_set_mic_codec_params(engine, "pcm", 1, 48000, 0);//设置mic采集回调函数qtt_engine_set_mic_capture_callback(engine, onCapture, stream);//启动stream,开始工作qtt_stream_start(stream);char c = getchar();qtt_stream_stop(stream);qtt_stream_free(stream);qtt_engine_free(engine);return 0;
}
系统API

Android在4.1(API level 16)的时候增加了一个API:AcousticEchoCanceler

下面简单的介绍下AcousticEchoCanceler的使用方法:

private AcousticEchoCanceler canceler;
//判断当前机型是否支持AEC
public boolean isDeviceSupportAEC()
{return AcousticEchoCanceler.isAvailable();
}
//初始化AEC
public boolean initAEC(int audioSession)
{if (canceler != null){return false;}canceler = AcousticEchoCanceler.create(audioSession);canceler.setEnabled(true);return canceler.getEnabled();
}public boolean setAECEnabled(boolean enable)
{if (null == canceler){return false;}canceler.setEnabled(enable);return canceler.getEnabled();
}public boolean release()
{if (null == canceler){return false;}canceler.setEnabled(false);canceler.release();return true;
}

从以上代码可知,AcousticEchoCanceler的初始化需要一个sessionid,这个sessionid由初始化好的AudioRecord对象获得:

sessionid = audioRecord.getAudioSessionId();

初始化AudioTrack时,也需要额外的处理sessionid:

audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL, frequency, channelOUT, audioEncoding, tmpSize, AudioTrack.MODE_STREAM, sessionid);

为什么AcousticEchoCanceler和AudioTrack初始化都需要AudioRecord对象的sessionid呢?这牵涉到理解回声消除的原理,请见这篇文章。

Speex

Speex提供开源的的回音消除算法,从提供的文档(https://speex.org/docs/manual/speex-manual/node7.html#SECTION00740000000000000000)和测试用例看,效果还是非常棒的。下面简单介绍speex echo cancellation api的使用方法,官方示例文件testecho.c:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "speex/speex_echo.h"
#include "speex/speex_preprocess.h"#define NN 128
#define TAIL 1024int main(int argc, char **argv)
{FILE *echo_fd, *ref_fd, *e_fd;short echo_buf[NN], ref_buf[NN], e_buf[NN];SpeexEchoState *st;SpeexPreprocessState *den;int sampleRate = 8000;if (argc != 4){//mic_signal是mic实时采集到的音频数据(包含speaker_signal),speaker_signal是由喇叭放出去的音频数据(也可称为远端数据),output是将mic_signal中去掉speaker_signal的数据,就得到了干净无回声的数据fprintf(stderr, "testecho mic_signal.sw speaker_signal.sw output.sw\n");exit(1);}echo_fd = fopen(argv[2], "rb");ref_fd  = fopen(argv[1],  "rb");e_fd    = fopen(argv[3], "wb");st = speex_echo_state_init(NN, TAIL);den = speex_preprocess_state_init(NN, sampleRate);speex_echo_ctl(st, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate);speex_preprocess_ctl(den, SPEEX_PREPROCESS_SET_ECHO_STATE, st);while (!feof(ref_fd) && !feof(echo_fd)){fread(ref_buf, sizeof(short), NN, ref_fd);fread(echo_buf, sizeof(short), NN, echo_fd);//ref_buf是采集到的声音,至少包含两部分:我们的说话声和喇叭放出来的声音(echo_buf)//echo_buf是喇叭放出来的声音//e_buf是干净的声音speex_echo_cancellation(st, ref_buf, echo_buf, e_buf);speex_preprocess_run(den, e_buf);fwrite(e_buf, sizeof(short), NN, e_fd);}speex_echo_state_destroy(st);speex_preprocess_state_destroy(den);fclose(e_fd);fclose(echo_fd);fclose(ref_fd);return 0;
}

 

以上代码是处理文件,而在实际的VOIP通话中,难点在于NN和TAIL的指定,也就是frame_size和tail_length,因为像android\ios、windows或linux等非实时系统,用户调用采集或者播放的接口,到实际硬件真正采集和播放声音,根据各硬件平台以及系统的不一样,这中间是有几十至几百毫秒的延迟,这对于回声处理增加了非常大的难度。

WebRTC

WebRTC,名称源自网页实时通信(Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的技术。

WebRTC源代码中设计了两个回声消除模块,AEC(Acoustic Echo Canceller)和AECM(Acoustic Echo Canceller Mobile),AEC是在电脑端使用的回声消除器,而AECM是在移动端使用。由于电脑与移动设备的差别比较明显,在处理速度上,编解码器的性能上和内存方面都有着较大的差异。

在VoIP实时语音通话中,之所以会产生声学回声的原因是,近端通话者的声音被自己的麦克风拾取后通过网络传到远端,远端扬声器播放出来的声音被麦克风拾取后通过网络又重新发回近端。加上网络和数据处理等各种延迟的影响,使得近端通话者能够从扬声器中听到自己的刚才所说的话,就产生了回声。

下面主要来简单介绍一下AECM模块 中echo_control_mobile.c的两个函数:

//farend 是远端传过来的音频数据,也就是将要从喇叭放出来的声音,类似于speex的echo_buf
WebRtcAecm_BufferFarend(void *aecmInst, const int16_t *farend, int16_t nrOfSamples)
//nearendNoisy 是从mic采集到的声音,类似于speex的ref_buf
//nearendClean 可以放入经过Noise Reduction的音频数据
//msInSndCarBuf 该值需要特别计算才能够取得良好的去回音效果,也即将听筒播放出来的声音被mic采集到时的一个精确的值,才能使AECM达到良好的回音消除效果
WebRtcAecm_Process(void *aecmInst, const int16_t *nearendNoisy, const int16_t *nearendClean, int16_t *out, int16_t nrOfSamples, int16_t msInSndCardBuf)
转自:http://www.qttaudio.com/android-aec.html

这篇关于android、ios、windows等平台回声消除的解决方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/887412

相关文章

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

Java.lang.InterruptedException被中止异常的原因及解决方案

《Java.lang.InterruptedException被中止异常的原因及解决方案》Java.lang.InterruptedException是线程被中断时抛出的异常,用于协作停止执行,常见于... 目录报错问题报错原因解决方法Java.lang.InterruptedException 是 Jav

kkFileView在线预览office的常见问题以及解决方案

《kkFileView在线预览office的常见问题以及解决方案》kkFileView在线预览Office常见问题包括base64编码配置、Office组件安装、乱码处理及水印添加,解决方案涉及版本适... 目录kkFileView在线预览office的常见问题1.base642.提示找不到OFFICE组件

SpringBoot监控API请求耗时的6中解决解决方案

《SpringBoot监控API请求耗时的6中解决解决方案》本文介绍SpringBoot中记录API请求耗时的6种方案,包括手动埋点、AOP切面、拦截器、Filter、事件监听、Micrometer+... 目录1. 简介2.实战案例2.1 手动记录2.2 自定义AOP记录2.3 拦截器技术2.4 使用Fi

Linux之platform平台设备驱动详解

《Linux之platform平台设备驱动详解》Linux设备驱动模型中,Platform总线作为虚拟总线统一管理无物理总线依赖的嵌入式设备,通过platform_driver和platform_de... 目录platform驱动注册platform设备注册设备树Platform驱动和设备的关系总结在 l

WinForm跨线程访问UI及UI卡死的解决方案

《WinForm跨线程访问UI及UI卡死的解决方案》在WinForm开发过程中,跨线程访问UI控件和界面卡死是常见的技术难题,由于Windows窗体应用程序的UI控件默认只能在主线程(UI线程)上操作... 目录前言正文案例1:直接线程操作(无UI访问)案例2:BeginInvoke访问UI(错误用法)案例

Spring Security常见问题及解决方案

《SpringSecurity常见问题及解决方案》SpringSecurity是Spring生态的安全框架,提供认证、授权及攻击防护,支持JWT、OAuth2集成,适用于保护Spring应用,需配置... 目录Spring Security 简介Spring Security 核心概念1. ​Securit

MySQL逻辑删除与唯一索引冲突解决方案

《MySQL逻辑删除与唯一索引冲突解决方案》本文探讨MySQL逻辑删除与唯一索引冲突问题,提出四种解决方案:复合索引+时间戳、修改唯一字段、历史表、业务层校验,推荐方案1和方案3,适用于不同场景,感兴... 目录问题背景问题复现解决方案解决方案1.复合唯一索引 + 时间戳删除字段解决方案2:删除后修改唯一字

Windows环境下解决Matplotlib中文字体显示问题的详细教程

《Windows环境下解决Matplotlib中文字体显示问题的详细教程》本文详细介绍了在Windows下解决Matplotlib中文显示问题的方法,包括安装字体、更新缓存、配置文件设置及编码調整,并... 目录引言问题分析解决方案详解1. 检查系统已安装字体2. 手动添加中文字体(以SimHei为例)步骤