前言 只是小白在学习过程中的记录,可能存在错误。。。 音频使用3A算法,Speex也支持。。 3A:声学回声消除(AEC)、背景噪声抑制(ANS)、自动增益控制(AGC)三种音频算法的合称 个人尝试之后发现,背景噪声消除效果较好,AEC和AGC似乎没什么用。。 一、speex、speexdsp的简单介绍 Speex:开源免费、无专利保护、针对语音而设计,支持音频编解码和3A算法处理 Speexdsp:就是在speex上提取的3A算法而已 官网:https://www.speex.org/ 下载源码:https://www.speex.org/downloads/ 手册:http://www.speex.org/docs/api/speex-api-reference/index.html http://maemo.org/api_refs/5.0/5.0-final/speex/group__SpeexPreprocessState.html 二、下载移植 1.下载 直接进入官网,选择需要的包进行下载即可 2.Linux下编译安装 Speex的编译安装和大部分开源包都是一样的,解压、配置、编译、安装 ./configure --prefix="安装目录" --host="安装平台(arm-linux)" --enable-shared 生成共享库 --enable-static 生成静态库 --enable-sse 支持使用SSE指令 CC= 需要使用的交叉编译链(注意此处要为绝对路径,否则编译失败) ./configure --prefix="/home/y/workfile/speexdsp-install" --host="arm-linux" --enable-shared --enable-static --enable-sse CC=/opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc 编译:make 安装:make install 生成文件存放与prefix指定目录下 同理可以编译安装speex speexdsp ogg 个人测试demo 更多的东西有待验证,以下只是个人使用的一部分 speex-demo.c //main函数参数一是原始pcm文件名,参数二是去噪后的pcm文件名 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "speex_preprocess.h" #include <stdio.h> #include <string.h> #define FRAME_SIZE 1024 //1152 #define FRAME_SAMPLERATE 32000 #define DENOISE_DB (-90) int main(int argn, char* argv[]) { char* szInFilename = NULL; char* szOutFilename = NULL; FILE* pInFileHandle = NULL; FILE* pOutFileHandle = NULL; short in[FRAME_SAMPLERATE]; int i; SpeexPreprocessState *st; int count=0; float f; printf("starting....\r\n"); if(argn != 3){ printf("please input 2 parameters\r\n"); return -1; } //memset((void*)empty, 0, sizeof(empty)); szInFilename = argv[1]; szOutFilename = argv[2]; pInFileHandle = fopen(szInFilename, "rb"); //打开原始文件 if(!pInFileHandle){ printf("open file %s error\r\n", szInFilename); return -2; } pOutFileHandle = fopen(szOutFilename, "wb"); //转换后的文件 if(!pOutFileHandle){ printf("open file %s error\r\n", szOutFilename); fclose(pInFileHandle); return -3; } /** 创建预处理器 SpeexPreprocessState SpeexPreprocessState* preprocess_state = speex_preprocess_state_init(frame_size,sampling_rate); frame_size:每次的预处理数 sampling_rate:采样率 **/ st = speex_preprocess_state_init(FRAME_SIZE, FRAME_SAMPLERATE); int denoise = 1; int noiseSuppress = DENOISE_DB; /**预处理属性设置,类似于ioctl,节省cpu**/ speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DENOISE, &denoise); //去噪开关设置 1打开 2关闭 speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &noiseSuppress); //设置噪声的最大衰减值 i=0; //0 speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC, &i); // Set maximal gain increase in dB/second (int32) i=8000; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC_LEVEL, &i); //AGC级别 Set preprocessor Automatic Gain Control level (float) i=0; //0 speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB, &i); //Set preprocessor dereverb state f=.0; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);//Set preprocessor dereverb decay f=.0; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f); //Set preprocessor dereverb level int vad = 1; int vadProbStart = 80; int vadProbContinue = 65; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_VAD, &vad); //静音检测 VAD 1on 2off speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_PROB_START , &vadProbStart); //设置VAD从静音到声音的概率 Set probability required for the VAD to go from silence to voice speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &vadProbContinue); //设置VAD保持语音状态所需的概率(整数百分比) Set probability required for the VAD to stay in the voice state (integer percent) while (1) { int vad; int iLen = fread(in, sizeof(short), FRAME_SIZE, pInFileHandle); if(iLen <= 0){ break; } if (feof(pInFileHandle)) //用feof()去判断文件是否结束,若结束,返回非零,若文档未结束,则返回零 break; /** int speex_preprocess_run(SpeexPreprocessState *st, spx_int16_t *x); SpeexPreprocessState:预处理器 x:要处理的数据 大小要和init时相同 return: 1---语音 0---静音/噪音 **/ vad = speex_preprocess_run(st, in); if(vad != 0){ printf("speech.\r\n"); fwrite(in, sizeof(short), FRAME_SIZE, pOutFileHandle); }else{ printf("slience############################\r\n"); fwrite(in, sizeof(short), FRAME_SIZE, pOutFileHandle); } count++; } /**销毁预处理器**/ speex_preprocess_state_destroy(st); fclose(pInFileHandle); fclose(pOutFileHandle); return 0; } Makefile LIBDIR=./lib INCLUDEDIR=./include/speex SRCS=$(wildcard *.c) TARGET = ./speex-demo #HAVE_STDINT_H HAVE_CONFIG_H CFLAGS += -Wall -O -g -D HAVE_CONFIG_H CFLAGS += -pthread -lm -O2 -lspeexdsp CFLAGS += -L $(LIBDIR) CFLAGS += -I $(INCLUDEDIR) CC=arm-linux-gnueabihf-gcc .PHONE : clean all all:$(TARGET) $(TARGET):$(SRCS) $(CC) $(CFLAGS) -o $@ $^ clean: @rm -f $(TARGET) 个人在工程中使用的部分代码 1.speexdsp.c #include "speexdsp.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "speex_preprocess.h" #include "speex_echo.h" #include <stdio.h> #include <string.h> #define FRAME_SIZE 2048 //1152 #define IS_PRINT 0 #define FRAME_SIZE_MS 2048 //should correspond to 10-20 ms #define FILTER_LEN_MS 256 //32000*4/1000 //should generally correspond to 100-500 ms #define SPEEX_NUM 10 #define DENOISE_DB (-90) static int init_falg[SPEEX_NUM] = {0}; static int g_samplerate[SPEEX_NUM] = {0}; typedef struct { int DB_SWITCH; int NOISE_SUPPRESS; //降噪值 int AGC; int AGC_LEVEL; int DEREVERB; float F; }SPEEX_ARG_S; SpeexPreprocessState *sp[SPEEX_NUM]; SpeexEchoState *echo_sp[SPEEX_NUM]; void * speexdsp_init_run(int channel_id,int data_size,int samplerate,AUDIO_CODEC_TYPE audio_type) { if(init_falg[channel_id]) return 0; SPEEX_ARG_S speexArg = { .DB_SWITCH = 1,//是否打开噪音抑制 .NOISE_SUPPRESS=-100,//DENOISE_DB,//噪音的最大程度衰减的分贝值,负值 越小越强 .AGC = 0,//自动增益控制 .AGC_LEVEL=8000,//自动增益控制默认8000 .DEREVERB=1,//是否消除混响 .F=20};//消除混响的等级 消除混响的衰减 int ret = 0,i; if(audio_type != AUDIO_CODEC_PCM) { speexdsp_destroy_resource(channel_id); sp[channel_id] = NULL; echo_sp[channel_id] = NULL; // ec = NULL; if(IS_PRINT)printf("#############NOT NOT NOT PCM channel_id[%d]!!!!!\n",channel_id); return NULL; } else if(g_samplerate[channel_id] != samplerate) { speexdsp_destroy_resource(channel_id); g_samplerate[channel_id] = samplerate; sp[channel_id] = NULL; echo_sp[channel_id] = NULL; // ec = NULL; if(IS_PRINT)printf("#############samplerate different different different:%d channel_id[%d]!!!!!\n",samplerate,channel_id); } else { speexdsp_destroy_resource(channel_id); sp[channel_id] = NULL; if(IS_PRINT)printf("#############is is is is PCM channel_id[%d]!!!!!\n",channel_id); } if(IS_PRINT)printf("----------------********---------------------samplerate:%d data_size:%d channel_id[%d]\n",samplerate,data_size,channel_id); sp[channel_id] = speex_preprocess_state_init(data_size, samplerate); // i =1;//设置是否打开Speex预处理器句柄的残余回音消除,开启后应在回音消除后再进行预处理 // speex_echo_ctl(echo_sp[channel_id],SPEEX_PREPROCESS_SET_ECHO_STATE,&i); i = -40;//设置残余回音消除时,残余回音的最大程度衰减的分贝值 speex_preprocess_ctl(sp[channel_id],SPEEX_PREPROCESS_SET_ECHO_SUPPRESS,&i); i=-1;//设置残余回音消除时,接近末尾的残余回音的最大程度衰减的分贝值 speex_preprocess_ctl(sp[channel_id],SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE,&i); // speex_preprocess_ctl(sp[channel_id], SPEEX_PREPROCESS_SET_ECHO_STATE, echo_sp[channel_id]); ret = speex_preprocess_ctl(sp[channel_id], SPEEX_PREPROCESS_SET_DENOISE, &speexArg.DB_SWITCH); if(ret < 0) printf("[SPEEXDSP] SPEEX_PREPROCESS_SET_DENOISE failed!!\n"); ret = speex_preprocess_ctl(sp[channel_id], SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &speexArg.NOISE_SUPPRESS); //设置降噪值 if(ret < 0) printf("SPEEX_PREPROCESS_SET_NOISE_SUPPRESS failed!!\n"); ret = speex_preprocess_ctl(sp[channel_id], SPEEX_PREPROCESS_SET_AGC, &speexArg.AGC); if(ret < 0) printf("SPEEX_PREPROCESS_SET_AGC failed!!\n"); ret = speex_preprocess_ctl(sp[channel_id], SPEEX_PREPROCESS_SET_AGC_LEVEL, &speexArg.AGC_LEVEL); if(ret < 0) printf("SPEEX_PREPROCESS_SET_AGC_LEVEL failed!!\n"); ret = speex_preprocess_ctl(sp[channel_id], SPEEX_PREPROCESS_SET_DEREVERB, &speexArg.DEREVERB); if(ret < 0) printf("SPEEX_PREPROCESS_SET_DEREVERB failed!!\n"); ret = speex_preprocess_ctl(sp[channel_id], SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &speexArg.F); if(ret < 0) printf("SPEEX_PREPROCESS_SET_DEREVERB_DECAY failed!!\n"); ret = speex_preprocess_ctl(sp[channel_id], SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &speexArg.F); if(ret < 0) printf("SPEEX_PREPROCESS_SET_DEREVERB_LEVEL failed!!\n"); init_falg[channel_id] = 1; if(IS_PRINT)printf("-----------------------speex_preprocess_state_init--channel_id[%d]---OK OK OK-------------------------------------------\n",channel_id); return sp[channel_id]; } int speexdsp_preprocess_audio(int channel_id,short *data) { if(!init_falg[channel_id] || sp[channel_id] == NULL) return 0; if(sp[channel_id] == NULL){ printf("channel_id[%d] sp is NULL\n",channel_id); return -1; } speex_preprocess_run(sp[channel_id], data); // printf("speex_preprocess_run !\n"); return 0; } int speexdsp_echo_cancellatio(int channel_id ,short *micro,short *speaker,short *out) { if(!init_falg[channel_id] || sp[channel_id] == NULL || echo_sp[channel_id] == NULL) return 0; if(micro == NULL)printf("micro is NULL\n"); if(speaker == NULL)printf("speaker is NULL"); printf("#########speexdsp_echo_cancellatio\n"); speex_echo_cancellation(echo_sp[channel_id], micro, speaker, out); return 0; } int speexdsp_echo_play(int channel_id,short *frame) { if(!init_falg[channel_id] || sp[channel_id] == NULL || echo_sp[channel_id] == NULL) return 0; speex_echo_playback(echo_sp[channel_id], frame); return 0; } int speexdsp_echo_captrue(int channel_id,short *input_frame,short *output_frame) { if(!init_falg[channel_id] || sp[channel_id] == NULL || echo_sp[channel_id] == NULL) return 0; speex_echo_capture(echo_sp[channel_id], input_frame,output_frame); return 0; } int speexdsp_destroy_resource(int channel_id) { if(!init_falg[channel_id]){ if(IS_PRINT)printf("-----------------------speexdsp_destroy_resource-----000000000000000000----channel_id[%d]-----------------\n",channel_id); return 0; } if(echo_sp[channel_id] != NULL){ printf("*****************speex_echo_state_destroy******echo_sp[%d]********\n",channel_id); speex_echo_state_destroy(echo_sp[channel_id]); } if(sp[channel_id] != NULL){ if(IS_PRINT)printf("*****************speexdsp_destroy_resource******channel_id[%d]********\n",channel_id); speex_preprocess_state_destroy(sp[channel_id]); sleep(1); } init_falg[channel_id] = 0; sp[channel_id] = NULL; echo_sp[channel_id] = NULL; if(IS_PRINT)printf("-----------------------speexdsp_destroy_resource----------channel_id[%d]-------------------------------------\n",channel_id); return 0; } 2.speexdsp.h #ifndef SPEEXDSP_H__ #define SPEEXDSP_H__ typedef enum { AUDIO_CODEC_PCM, AUDIO_CODEC_G711A, AUDIO_CODEC_G711U, AUDIO_CODEC_MP3, AUDIO_CODEC_AAC }AUDIO_CODEC_TYPE; void * speexdsp_init_run(int channel_id,int data_size,int samplerate,AUDIO_CODEC_TYPE audio_type); int speexdsp_preprocess_audio(int channel_id,short *data); int speexdsp_destroy_resource(int channel_id); #endif
标签:Speex,speex,SPEEX,int,sp,移植,简单,id,channel From: https://www.cnblogs.com/kn-zheng/p/17703019.html