只是小白在学习过程中的记录,可能存在错误。。。
音频使用3A算法,Speex也支持。。
3A:声学回声消除(AEC)、背景噪声抑制(ANS)、自动增益控制(AGC)三种音频算法的合称
个人尝试之后发现,背景噪声消除效果较好,AEC和AGC似乎没什么用。。
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
直接进入官网,选择需要的包进行下载即可
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
更多的东西有待验证,以下只是个人使用的一部分
//main函数参数一是原始pcm文件名,参数二是去噪后的pcm文件名
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "speex_preprocess.h"
#include
#include
#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;
}
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
#include
#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