首页 > 编程语言 >speexdsp库实现音频3A算法

speexdsp库实现音频3A算法

时间:2023-09-14 17:57:10浏览次数:41  
标签:rtcaec void state speexdsp aec context chw 3A 音频

speex是音频编解码库,speexdsp是附加的音频DSP库,是音频降噪库,也有回声抑制和自动增益控制功能,即通常说的音频3A算法。 现在音频编解码大部分都是使用opus库,很少使用speex进行音频编解码,但还是会使用speexdsp库的3A算法对音频数据进行处理。 本例是在ubuntu环境下,C/C++语言,使用Qt进行测试。   speexdsp库编译 源码下载:https://github.com/xiph/speexdsp/tags 我下载的版本:speexdsp-SpeexDSP-1.2.1.zip,解压缩。 编译步骤:   cd speexdsp-SpeexDSP-1.2.1/ ./autogen.sh #./autogen.sh: 11: autoreconf: not found报错,安装依赖 sudo apt-get install -y autoconf automake libtool ./configure --prefix=${BUILD_LIBS} --disable-oggtest make make install   在家目录的build_libs/文件夹下生成include头文件,和libspeexdsp.a静态库,libspeexdsp.so动态库。   使用speexdsp做音频去噪声demo 在Qt环境下测试,使用wav文件作为音频输入,把处理后的音频数据输出到本地wav文件,回声消除使用在语音通话场景,本例只给出接口样例。 把编译好的include和lib文件夹拷贝到过程目录下,pro文件添加如下内容:   INCLUDEPATH += $$PWD/include LIBS += -L$$PWD/lib -lspeexdsp -ldl   chwRtcAec.h   #ifndef chwAUDIOPROC_chwRtcAec_H_ #define chwAUDIOPROC_chwRtcAec_H_ #include <stdint.h> #include <stdlib.h> typedef struct{ void *session; void (*init)(void* session, int32_t sampleRate, int32_t pchannel,int32_t pframeSize,int32_t echopath); void (*closeAec)(void* session);   void (*echoCapture)(void* session,short *rec, short *out); void (*preprocessRun)(void* session,short *pcm); void (*echoStateReset)(void* session); void (*echoPlayback)(void* session,short *play); void (*echoCancellation)(void* session,const short *rec, const short *play, short *out); }chwRtcAec;   #define chw_delete(a) {if( (a)) {delete (a); (a) = NULL;}} #define chw_deleteA(a) {if( (a)) {delete[] (a); (a) = NULL;}} #define chw_free(a) {if( (a)) {free((a)); (a) = NULL;}}   #ifdef __cplusplus extern "C"{ #endif void chw_rtcaec_init(void* context, int32_t sampleRate, int32_t pchannel,int32_t pframeSize,int32_t echopath); void chw_rtcaec_preprocess_run(void* context,short *pcm); void chw_create_rtcaec(chwRtcAec* aec); void chw_destroy_rtcaec(chwRtcAec* aec); #ifdef __cplusplus } #endif #endif /* chwAUDIOPROC_chwAECSPEEX_H_ */     chwRtcAec.c   #include "chwRtcAec.h" #include <speex/speex_echo.h> #include <speex/speex_preprocess.h> #include <stdio.h> #include <string.h> typedef struct chwRtcAec{ int32_t channel; int32_t frameSize; int32_t sampleRate; int32_t echoPath; SpeexEchoState *state; SpeexPreprocessState *preprocState;   }chwRtcAecContext; ///m_aec->session,16000,1,320,10 void chw_rtcaec_init(void* context, int32_t sampleRate, int32_t pchannel,int32_t pframeSize,int32_t echopath) {     if(context==NULL) return;     chwRtcAecContext* aec=(chwRtcAecContext*)context;     aec->frameSize = pframeSize;     aec->sampleRate = sampleRate;     aec->channel = pchannel;     aec->echoPath=echopath;     int32_t frame_size = aec->frameSize;//(aec->frameSizeInMS * aec->sampleRate * 1.0) / 1000;     int32_t filter_length = aec->frameSize * aec->echoPath;//20;//30;//aec->echoPath;//(aec->frameSizeInMS * aec->sampleRate * 1.0) / 1000;       /** 创建一个新的多通道回波取消器状态      * @param frame_size 一次要处理的样本数(应对应10-20 ms)      * @param filter_length 要取消的回波样本数(一般应对应100-500 ms)      * @param aec->channel 麦克风通道数      * @param aec->channel 扬声器通道数      * @return 新创建的回波取消器状态      */     if(!aec->state) aec->state = speex_echo_state_init_mc(frame_size, filter_length, aec->channel,aec->channel);     //frame_size *= 2; // length read each time       /** 将创建一个新的预处理状态。您必须为每个已处理的通道创建一个状态。      * @param frame_size 一次要处理的样品数(应对应10-20 ms)。必须为:与用于剩余回声消除的回波消除器的值相同。      * @param sampling_rate 用于输入的采样率。      * @return 返回新创建的预处理器状态     */     if(!aec->preprocState) aec->preprocState = speex_preprocess_state_init(frame_size, aec->sampleRate);       speex_echo_ctl(aec->state, SPEEX_ECHO_SET_SAMPLING_RATE, &aec->sampleRate);//设置采样率     speex_preprocess_ctl(aec->preprocState, SPEEX_PREPROCESS_SET_ECHO_STATE,aec->state);//设置相应的回波消除器状态,以便可进行残余回波抑制(无残余回波抑制时为NULL)     int32_t i = 1;     //speex_preprocess_ctl(aec->preprocState, SPEEX_PREPROCESS_SET_VAD, &i);     int32_t noiseSuppress = -25;     i = 1;     speex_preprocess_ctl(aec->preprocState, SPEEX_PREPROCESS_SET_DENOISE, &i);//设置预处理器去噪状态     speex_preprocess_ctl(aec->preprocState, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &noiseSuppress);//设置噪声的最大衰减,单位为dB(负数)     i=0;     speex_preprocess_ctl(aec->preprocState, SPEEX_PREPROCESS_SET_AGC, &i);//设置预处理器自动增益控制状态     i=sampleRate;     speex_preprocess_ctl(aec->preprocState, SPEEX_PREPROCESS_SET_AGC_LEVEL, &i);//设置预处理器自动增益控制级别(浮动)     i=0;     speex_preprocess_ctl(aec->preprocState, SPEEX_PREPROCESS_SET_DEREVERB, &i);//设置预处理器dereverb状态     float f=.0;     speex_preprocess_ctl(aec->preprocState, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);//设置预处理器dereverb decay     f=.0;     speex_preprocess_ctl(aec->preprocState, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f);//设置EverB级别的预处理器 }   void chw_rtcaec_close(void* context){ if(context==NULL) return; chwRtcAecContext* aec=(chwRtcAecContext*)context;     if(aec->state) speex_echo_state_destroy(aec->state);//销毁回声消除状态     if(aec->preprocState) speex_preprocess_state_destroy(aec->preprocState);//销毁预处理器状态 aec->state = NULL; aec->preprocState = NULL; }   void chw_rtcaec_echo_capture(void* context,short *rec, short *out) { if(context==NULL) return; chwRtcAecContext* aec=(chwRtcAecContext*)context;     /** 使用内部回放缓冲区执行回声取消,这会延迟两帧      * 来解释大多数声卡引入的延迟(但它可能是关闭的!)      * @param aec->state 回声消除器状态      * @param rec 麦克风信号(近端+远端回声)      * @param out 返回去除回声的近端信号     */ if(aec->state) speex_echo_capture(aec->state, rec, out); }   void chw_rtcaec_preprocess_run(void* context,short *pcm) { if(context==NULL) return; chwRtcAecContext* aec=(chwRtcAecContext*)context;     /** 对帧进行预处理      * @param aec->preprocState 预处理器状态      * @param Pcm 音频样本向量(进和出)。必须与在speex_preprocess_state_init()中指定的大小相同。      * @return 语音业务的Bool值(1表示语音,0表示噪音/静音),仅当VAD打开时使用。     */ if(aec->preprocState) speex_preprocess_run(aec->preprocState, pcm); }   void chw_rtcaec_echo_state_reset(void* context) { if(context==NULL) return; chwRtcAecContext* aec=(chwRtcAecContext*)context;     /** 将回声取消器重置到其原始状态      * @param aec->state 回声消除状态      */ if(aec->state) speex_echo_state_reset(aec->state); } void chw_rtcaec_echo_playback(void* context,short *play) { if(context==NULL) return; chwRtcAecContext* aec=(chwRtcAecContext*)context;     /** 让回声取消器知道一个帧刚刚排队到声卡上      * @param aec->state 回声消除状态      * @param play 信号播放到扬声器(从远端接收)     */ if(aec->state) speex_echo_playback(aec->state, play); } void chw_rtcaec_echo_cancellation(void* context,const short *rec, const short *play, short *out) { if(context==NULL) return; chwRtcAecContext* aec=(chwRtcAecContext*)context;     /** 根据发送到扬声器的音频执行回声取消帧(没有添加延迟以此形式播放)      * @param aec->state 回声消除状态      * @param rec 来至麦克风的信号(近端+远端回声)      * @param play 信号播放到扬声器(从远端接收)      * @param out 返回消除回声的近端信号      */ if(aec->state) speex_echo_cancellation(aec->state, rec, play, out); }   void chw_create_rtcaec(chwRtcAec* aec){ if(aec==NULL) return; aec->session=(chwRtcAecContext*)calloc(sizeof(chwRtcAecContext),1); aec->init=chw_rtcaec_init; aec->closeAec=chw_rtcaec_close; aec->echoCancellation=chw_rtcaec_echo_cancellation; aec->echoCapture=chw_rtcaec_echo_capture; aec->echoPlayback=chw_rtcaec_echo_playback; aec->echoStateReset=chw_rtcaec_echo_state_reset; aec->preprocessRun=chw_rtcaec_preprocess_run; } void chw_destroy_rtcaec(chwRtcAec* aec){ if(aec==NULL) return;   chw_rtcaec_close((chwRtcAecContext*)aec->session); chw_free(aec->session); }       qt调用示例:   #include "spxmainwindow.h" #include "ui_spxmainwindow.h"   #include <QFile> #define HEADLEN 44 #define SAMPLE_RATE   (48000) #define SAMPLES_PER_FRAME  (1024) #define FRAME_SIZE   (SAMPLES_PER_FRAME * 1000/ SAMPLE_RATE) #define FRAME_BYTES  (SAMPLES_PER_FRAME)   spxMainWindow::spxMainWindow(QWidget *parent)     : QMainWindow(parent)     , ui(new Ui::spxMainWindow) {     ui->setupUi(this);       pcm=new short[4096/2];     m_aec = NULL;     m_aec=(chwRtcAec*)calloc(sizeof(chwRtcAec),1);     chw_create_rtcaec(m_aec);     chw_rtcaec_init(m_aec->session,44100,1,1024,10);//注意传参的采样率、帧大小、通道数一定要和采集时一致       dealAudio(); }   spxMainWindow::~spxMainWindow() {     chw_destroy_rtcaec(m_aec);     chw_free(m_aec);     chw_deleteA(pcm);     delete ui; } //消除噪声处理,以wav文件为例 void spxMainWindow::dealAudio() {     size_t len = 0;     QFile inFile, outFile;     inFile.setFileName("./test.wav");     outFile.setFileName("./outtest.wav");       char headBuf[HEADLEN];     char dataBuf[FRAME_BYTES * 2];       QByteArray readBuf;     if(inFile.open(QIODevice::ReadOnly))         inFile.read(headBuf,HEADLEN);//wav头部     if(outFile.open(QIODevice::WriteOnly))         outFile.write(headBuf,HEADLEN);       while(inFile.atEnd() == false)     {         len = inFile.read(dataBuf,SAMPLES_PER_FRAME);         if(len == 0) break;           chw_rtcaec_preprocess_run(m_aec->session,(short*)dataBuf);         outFile.write(dataBuf,SAMPLES_PER_FRAME);     }       inFile.flush();     inFile.close();     outFile.flush();     outFile.close(); } //回声消除,播放对方音频后调用 void spxMainWindow::dealEchoPlay(short* pbuf,int32_t plen) {     if(m_aec) m_aec->echoPlayback(m_aec->session,pbuf); } //回声采样和去噪,采集本端音频后,编码前调用 void spxMainWindow::putEchoBuffer( uint8_t *pBuffer,int32_t plen) {     if (m_aec)     {         m_aec->echoCapture(m_aec->session, (short*) pBuffer, pcm);         m_aec->preprocessRun(m_aec->session, pcm);         m_audioFrame.data = (uint8_t*)pcm;         m_audioFrame.len = plen; //        m_resample.resample(m_resample.context,&m_audioFrame);     } }     报错记录 使用回声消除时报如下几个警告:   waining:no playback frame available(your application is buggy and/or got xruns),#ifndef DISABLE_WARNINGS warning: Had to discard a playback frame (your application is buggy and/or got xruns) warning: Auto-filling the buffer (your application is buggy and/or got xruns)   基本不影响业务,看着不顺眼的可以修改libspeexdsp源码里的os_support.h文件,在里面添加下面定义,不打印WARNINGS信息,重新编译即可。   #define DISABLE_WARNINGS  

标签:rtcaec,void,state,speexdsp,aec,context,chw,3A,音频
From: https://www.cnblogs.com/kn-zheng/p/17703054.html

相关文章

  • 爆肝总结2023Android面试,看完学会它,公司追着给你offer
    前言想要在面试中脱颖而出吗?想要在最短的时间内快速掌握Android的核心知识点吗?想要成为一位优秀的Android工程师吗?本篇文章能助你一臂之力!金九银十,目前正值招聘求职旺季,很多朋友对一些新技术名词都能侃侃而谈,但对一些核心原理理解的不够透彻,特别是对Android的一些核心基础知识点掌......
  • java获得音频比特率
    <dependency> <groupId>it.sauronsoftware</groupId> <artifactId>jave</artifactId> <version>1.0.2</version> </dependency>maven下载不成功的话,可以手动下载,下载地址:http://www.sauronsoftware.it/projects/ja......
  • 大声场对讲设备的音频算法调试总结
    事由 上周应客户之约,去客户那边调试对讲设备的回声消除和降噪。通过现场的调试和与客户的沟通,算是把产品的效果和竞品各有千秋了。通过这次,笔者本身也对行业和需求有了更深的了解。更清楚了一些里面的技术细节和方向。这里做个总结吧。 问题 客户拿到我们的样品模块之后,装......
  • FFmpeg进阶: 音频滤镜大全
    在做音频处理模块的时候,为了对声音进行优化处理,我很多时候会使用各种算法对音频进行变换,效果包括变音变调、声音降噪等等。其实FFmpeg库里的滤镜模块包含了很多有用的音频滤镜算法,这对于提升开发效率避免重复造轮子是很有帮助的。这里翻译了一下FFmpeg官方文档的所有音频滤镜的介......
  • Matlab 2023a图文安装教程及下载
    MATLAB是由美国MathWorks公司出品的专业数学软件,用于算法开发,数据可视化,数据分析以及数值计算的高级技术计算语言和交互式环境,MATLAB是矩阵和实验室两个词的组合,意为矩阵工厂(矩阵实验室),主要包括MATLAB和Simulink两大部分。它将数值分析,矩阵计算,科学数据可视化以及非线性动态系统的......
  • 基于live555研发的EasyRTSPServer嵌入式流媒体服务器解决对接海康NVR时G726音频不能正
    我们在基于live555研发实现对接海康NVR时候,发现视频正常,但音频不能正常播放。基于live555研发的EasyRTSPServer在接入输出视频编码为H.264,音频编码格式为G726,当接入视频源都来自海康的摄像机时,摄像机直接接入海康NVR是可以正常播放的,但接入live555研发的EasyRTSPServer是有问题的......
  • ffmpeg 采集音频数据
    音视频数据采集的步骤:设备注册设置对应的采集方式,avfoundation、dshow、alas打开设备具体的例子:#include<stdio.h>extern"C"{  #include<libavutil/avutil.h>  #include<libavdevice/avdevice.h>  #include<libavformat/avformat.h>  #include<libavcod......
  • ffmpeg之采集音频
     一、打开音频设备设备可以是多媒体文件和物理设备#ifndefTESTC_H#defineTESTC_H#include<stdio.h>#include"libavutil/avutil.h"#include"libavdevice/avdevice.h"//打开音频设备相关的头文件#include"libavformat/avformat.h"//ffmpeg下的所有文......
  • 干货贴|免费AI数据标注工具-多功能语音音频标注软件
    图像标注实际应用比较广泛,因此前几期我们分享了不少图像标注的内容,不过大家也有反馈希望对文本标注、语音标注有一些学习,小A那么宠粉,必须安排起来。工欲善其事,必先利其器。标注工具是数据标注行业的基础,一款好用的标注工具是提升标注效率与产出高质量标注数据的关键。之前我也分享......
  • uni-app实现音频播放器(ios)
    在uni-app中,ios有自带的音频播放器,和网易云一样,安卓需要自己写1.全局定义constbgAudioManager=uni.getBackgroundAudioManager();bgAudioManager.title=this.info.name;//音频名称bgAudioManager.singer='';//作者bgAudioManager.coverImgUrl=this.info.pic;/......