当前位置: 首页 > 工具软件 > Speex > 使用案例 >

Speex的移植和简单使用

强才捷
2023-12-01


前言

只是小白在学习过程中的记录,可能存在错误。。。
音频使用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

更多的东西有待验证,以下只是个人使用的一部分

  1. 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;
}
  1. 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

 类似资料: