DFT应用:计算线性卷积

目录

一、计算两个有限长序列的线性卷积示例

二、无限长序列和有限长序列的卷积(重叠相加法)

实验1:数据实验

实验2:纯净语音加混响(音效)

二、无限长序列和有限长序列的卷积(重叠保留法)

实验1:数据实验

三、小结


一、计算两个有限长序列的线性卷积示例

FFT计算代码:

baselib.cpp:

#include <stdio.h>
#include <math.h>
#include "common.h"

void FFT(double dataR[], double dataI[], double dataA[], int N, int M)
{
	int i,j,k,r;
	int p,L,B;
	unsigned int I,J,K,F0,F1,m,n;
	double Tr,Ti,temp;
	//01. 输入序列倒序
	for(I=0; I<N; I++) {
		/*获取下标I的反序J的数值*/
		J=0;
		for (k=0; k<(M/2+0.5); k++) {
			m=1; //m是最低位为1的二进制数
			n=(unsigned int)pow(2,M-1); //n是第M位为1的二进制数
			m <<= k; //对m左移k位
			n >>= k; //对n右移k位
			F0=I & n; //I与n按位与提取出前半部分第k位
			F1=I & m; //I与m按位与提取出F0对应的后半部分的低位
			if(F0) J=J | m; //J与m按位或使F0对应低位为1
			if(F1) J=J | n; //J与n按位或使F1对应高位为1
		}

		if (I<J) {
			//实数部分交换:
			temp = dataR[I];
			dataR[I] = dataR[J];
			dataR[J] = temp;
			//虚数部分交换
			temp = dataI[I];
			dataI[I] = dataI[J];
			dataI[J] = temp;
		}
	}

	//02. 进行FFT
	//FFT蝶形级数L从1--M
	for(L=1; L<=M;L++)
	{
		/*第L级的运算:
		 蝶形运算的种类数目等于间隔B: 有多少种蝶形运算就要需要循环多少次
		 随着循环的不同,旋转指数P也不同,P的增量为k=2^(M-L) */
		//先计算一下间隔 B = 2^(L-1);
		B = 1;
		B = (int)pow(2,L-1);
		//j = 0,1,2,...,2^(L-1) - 1
		/*同种蝶形运算*/
		for (j=0; j<=B-1; j++) {
			//先计算增量k=2^(M-L)
			k=1;
			k = (int)pow(2,M-L);
			//计算旋转指数p,增量为k时,则P=j*k
			p=1;
			p=j*k;
			/*同种蝶形运算的次数等于增量k=2^(M-L)
			  同种蝶形的运算次数等于蝶形运算的次数
			*/
			for (i=0; i<=k-1;i++) {
				//数组下标定为r
				r=1;
				r=j+2*B*i;
				Tr=dataR[r+B]*cos(2.0*PI*p/N) + dataI[r+B]*sin(2.0*PI*p/N);
				Ti=dataI[r+B]*cos(2.0*PI*p/N) - dataR[r+B]*sin(2.0*PI*p/N);
				dataR[r+B]=dataR[r]-Tr;
				dataI[r+B]=dataI[r]-Ti;
				dataR[r]=dataR[r]+Tr;
				dataI[r]=dataI[r]+Ti;
			}
		}
	}

	//计算幅值
	if (dataA!=NULL) {
		for (i=0; i<N; i++) {
			dataA[i] = sqrt(dataR[i]*dataR[i]+dataI[i]*dataI[i]);
		}
	}
}

void IFFT(double dataR[], double dataI[], int N, int M)
{
	int i,j,k,r;
	int p,L,B;
	int I,J,K,F0,F1,m,n;
	double Tr,Ti,temp;
	//输入序列倒序
	for(I=0;I< N; I++)
	{
		/*获取下标I的反序J的数值*/
		J=0;
		for (k=0; k<(M/2+0.5); k++) {
			m=1;//m是最低位为1的二进制数
			n=(unsigned int)pow(2,M-1);//n是第M位为1的二进制数
			m <<= k; //对m左移k位
			n >>= k; //对n右移k位
			F0=I & n;//I与n按位与提取出前半部分第k位
			F1=I & m;//I与m按位与提取出F0对应的后半部分的低位
			if(F0) J=J | m; //J与m按位或使F0对应低位为1
			if(F1) J=J | n; //J与n按位或使F1对应高位为1
		}

		if (I<J) {
			temp = dataR[I];
			dataR[I] = dataR[J];
			dataR[J] = temp;
			temp = dataI[I];
			dataI[I] = dataI[J];
			dataI[J] = temp;
		}
	}

	//进行IFFT
	//FFT蝶形级数L从1--M
	for (L=1; L<=M; L++)
	{
		/*第L级的运算:
		 蝶形运算的种类数目等于间隔B: 有多少种蝶形运算就要需要循环多少次
		 随着循环的不同,旋转指数P也不同,P的增量为k=2^(M-L)*/
		//先计算一下间隔 B = 2^(L-1);
		B = 1;
		B = (int)pow(2,L-1);
		//j = 0,1,2,...,2^(L-1) - 1
		for (j=0; j<=B-1; j++)
		{	/*同种蝶形运算*/
			//先计算增量k=2^(M-L)
			k=1;
			k = (int)pow(2,M-L);
			//计算旋转指数p,增量为k时,则P=j*k
			p=1;
			p=j*k;
			/*同种蝶形运算的次数等于增量k=2^(M-L);
			 同种蝶形的运算次数等于蝶形运算的次数*/
			for (i=0; i<=k-1; i++) {
				//数组下标定为r
				r=1;
				r=j+2*B*i;
				Tr=dataR[r+B]*cos(2.0*PI*p/N) - dataI[r+B]*sin(2.0*PI*p/N);
				Ti=dataI[r+B]*cos(2.0*PI*p/N) + dataR[r+B]*sin(2.0*PI*p/N);
				dataR[r+B]=dataR[r]-Tr;dataR[r+B]=dataR[r+B]/2;
				dataI[r+B]=dataI[r]-Ti;dataI[r+B]=dataI[r+B]/2;
				dataR[r]=dataR[r]+Tr;dataR[r]=dataR[r]/2;
				dataI[r]=dataI[r]+Ti;dataI[r]=dataI[r]/2;
			}
		}
	}
}

baselib.h:

#ifndef __BASIC_LIB_H__
#define __BASIC_LIB_H__
#include "common.h"

void FFT(double dataR[], double dataI[], double dataA[], int N, int M);
void IFFT(double dataR[], double dataI[], int N, int M);

#endif /* __BASIC_LIB_H__ */

common.h:

#ifndef  __TYPEDEFS_H_
#define  __TYPEDEFS_H_

#define PI (3.141592653589793)

typedef struct {
	double real;
	double img;
}complex;

#endif  //__TYPEDEFS_H_

main.c:

#define _FFT_LEN (16)
#define _FFT_ORDER 4

#define SEQ1_LEN 8
#define SEQ2_LEN 5
int main(void)
{
	int i;

	//8+5-1=12<16[FFT的长度]
	double input_seq1[SEQ1_LEN]={1,2,3,4,5,4,3,2};
	double input_seq2[SEQ2_LEN]={1,1,1,1,1};

	double *res_seq_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *res_seq_i=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn1_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn1_i=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn2_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn2_i=(double *)calloc(_FFT_LEN, sizeof(double));

	//init input sequence
	for(i=0; i< SEQ1_LEN; i++) {
		xn1_r[i]=input_seq1[i];
	}

	for(i=0; i< SEQ2_LEN; i++) {
		xn2_r[i]=input_seq2[i];
	}

	FFT(xn1_r, xn1_i, NULL, _FFT_LEN, _FFT_ORDER);
	FFT(xn2_r, xn2_i, NULL, _FFT_LEN, _FFT_ORDER);

	//Multiplication in frequency domain
	for(i=0; i<_FFT_LEN; i++){
		res_seq_r[i]=xn1_r[i]*xn2_r[i] - xn1_i[i]*xn2_i[i];
		res_seq_i[i] =xn1_r[i]*xn2_i[i] + xn1_i[i]*xn2_r[i];
	}

	//iFFT
	IFFT(res_seq_r, res_seq_i, _FFT_LEN, _FFT_ORDER);

	for (i=0; i<SEQ1_LEN+SEQ2_LEN-1; i++) {
		printf("%f ", res_seq_r[i]);
	}
	printf("\n");
}

结果:

1.000000 3.000000 6.000000 10.000000 15.000000 18.000000 19.000000 18.000000 14.000000 9.000000 5.000000 2.000000

要注意的两个点:(1)圆周卷积的长度就是FFT的点数(2的整数倍次幂);(2)圆周卷积长度和线性卷积长度的关系。

二、无限长序列和有限长序列的卷积(重叠相加法)

实验1:数据实验

给出x(n)={1,2,3,4,5, 1,2,3,4,5, 1,2,3,4,5, …},0≤n≤29; h(n)={1,2,1}; 0≤n≤2; 求y(n)=x(n)*h(n)。

对比:先直接进行计算,代码如下:

main.c:

#define _FFT_LEN (32)
#define _FFT_ORDER 5

#define SEQ1_LEN 30
#define SEQ2_LEN 3
int main(void)
{
	int i;

	//30+3-1=32<=32[FFT的长度]
	double input_seq1[SEQ1_LEN]={1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5};
	double input_seq2[SEQ2_LEN]={1,2,1};

	double *res_seq_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *res_seq_i=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn1_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn1_i=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn2_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn2_i=(double *)calloc(_FFT_LEN, sizeof(double));

	//init input sequence
	for(i=0; i< SEQ1_LEN; i++) {
		xn1_r[i]=input_seq1[i];
	}

	for(i=0; i< SEQ2_LEN; i++) {
		xn2_r[i]=input_seq2[i];
	}

	FFT(xn1_r, xn1_i, NULL, _FFT_LEN, _FFT_ORDER);
	FFT(xn2_r, xn2_i, NULL, _FFT_LEN, _FFT_ORDER);

	//Multiplication in frequency domain
	for(i=0; i<_FFT_LEN; i++){
		res_seq_r[i]=xn1_r[i]*xn2_r[i] - xn1_i[i]*xn2_i[i];
		res_seq_i[i] =xn1_r[i]*xn2_i[i] + xn1_i[i]*xn2_r[i];
	}

	//iFFT
	IFFT(res_seq_r, res_seq_i, _FFT_LEN, _FFT_ORDER);

	for (i=0; i<SEQ1_LEN+SEQ2_LEN-1; i++) {
		printf("%f ", res_seq_r[i]);
	}
	printf("\n");
}

结果如下:

1.000000 4.000000 8.000000 12.000000 16.000000 15.000000 9.000000 8.000000 12.000000 16.000000 15.000000 9.000000 8.000000 12.000000 16.000000 15.000000 9.000000 8.000000 12.000000 16.000000 15.000000 9.000000 8.000000 12.000000 16.000000 15.000000 9.000000 8.000000 12.000000 16.000000 14.000000 5.000000

利用重叠相加法(长序列进行均匀分段),短序列长度M=3,长序列分段后N=5,则计算如下:

#define _FFT_LEN (8)
#define _FFT_ORDER 3

#define SEQ1_LEN 30
#define SEQ2_LEN 3

//重叠相加法
#define M SEQ2_LEN
#define N 5 /* 长序列均匀分段的每一段长度 */

int main(void)
{
	int i, j, k;

	double *res_seq_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *res_seq_i=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn1_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn1_i=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn2_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn2_i=(double *)calloc(_FFT_LEN, sizeof(double));

	//30+3-1=32<=32[FFT的长度]
	double input_seq1[SEQ1_LEN]={1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5};
//	double input_seq1[SEQ1_LEN];
//	for(i=0; i<30; i++)
//		input_seq1[i]=i;

	double input_seq2[SEQ2_LEN]={1,2,1};
	double xk[N];
	double overlap[M-1];

	for(k=2; k<SEQ1_LEN/N; k++) {
		memset(xn1_r, 0, _FFT_LEN*sizeof(double));
		memset(xn1_i, 0, _FFT_LEN*sizeof(double));
		memset(xn2_r, 0, _FFT_LEN*sizeof(double));
		memset(xn2_i, 0, _FFT_LEN*sizeof(double));
		memset(res_seq_r, 0, _FFT_LEN*sizeof(double));
		memset(res_seq_i, 0, _FFT_LEN*sizeof(double));

		for(j=0; j<N; j++) {
			xk[j]=input_seq1[k*N+j];
		}

		//init input sequence
		for(i=0; i< N; i++) {
			xn1_r[i]=xk[i];
		}

		//5+3-1=7,所以每小段做8个点的FFT即可。
		for(i=0; i< M; i++) {
			xn2_r[i]=input_seq2[i];
		}


		FFT(xn1_r, xn1_i, NULL, _FFT_LEN, _FFT_ORDER);
		FFT(xn2_r, xn2_i, NULL, _FFT_LEN, _FFT_ORDER);

		//Multiplication in frequency domain
		for(i=0; i<_FFT_LEN; i++){
			res_seq_r[i]=xn1_r[i]*xn2_r[i] - xn1_i[i]*xn2_i[i];
			res_seq_i[i]=xn1_r[i]*xn2_i[i] + xn1_i[i]*xn2_r[i];
		}

		//iFFT
		IFFT(res_seq_r, res_seq_i, _FFT_LEN, _FFT_ORDER);

		for (i=0; i<N+M-1; i++) {
			printf("%f ", res_seq_r[i]);
		}
		printf("\n");
	}
}

结果:重叠的点有M-1个,长序列的长度为N,线性卷积后,结果长度为N+(M-1),M-1就是多出来的重叠的点。

1.000000 4.000000 8.000000 12.000000 16.000000 14.000000 5.000000 

1.000000 4.000000 8.000000 12.000000 16.000000 14.000000 5.000000

1.000000 4.000000 8.000000 12.000000 16.000000 14.000000 5.000000

1.000000 4.000000 8.000000 12.000000 16.000000 14.000000 5.000000

接下来处理重叠的部分(相加):

#define _FFT_LEN (8)
#define _FFT_ORDER 3

#define SEQ1_LEN 30
#define SEQ2_LEN 3

//重叠相加法
#define M SEQ2_LEN
#define N 5 /* 长序列均匀分段的每一段长度 */

int main(void)
{
	int i, j, k;

	double *res_seq_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *res_seq_i=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn1_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn1_i=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn2_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn2_i=(double *)calloc(_FFT_LEN, sizeof(double));

	//30+3-1=32<=32[FFT的长度]
	double input_seq[SEQ1_LEN]={1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5};
	double output_seq[SEQ1_LEN+SEQ2_LEN-1];

//	double input_seq1[SEQ1_LEN];
//	for(i=0; i<30; i++)
//		input_seq1[i]=i;

	double input_seq2[SEQ2_LEN]={1,2,1};
	double xk[N];
	double overlap[M-1];
	memset(overlap, 0, (M-1)*sizeof(double));

	for(k=0; k<SEQ1_LEN/N; k++)
	{
		memset(xn1_r, 0, _FFT_LEN*sizeof(double));
		memset(xn1_i, 0, _FFT_LEN*sizeof(double));
		memset(xn2_r, 0, _FFT_LEN*sizeof(double));
		memset(xn2_i, 0, _FFT_LEN*sizeof(double));
		memset(res_seq_r, 0, _FFT_LEN*sizeof(double));
		memset(res_seq_i, 0, _FFT_LEN*sizeof(double));

		for(j=0; j<N; j++) {
			xk[j]=input_seq[k*N+j];
		}

		//init input sequence
		for(i=0; i< N; i++) {
			xn1_r[i]=xk[i];
		}

		//5+3-1=7,所以每小段做8个点的FFT即可。
		for(i=0; i< M; i++) {
			xn2_r[i]=input_seq2[i];
		}


		FFT(xn1_r, xn1_i, NULL, _FFT_LEN, _FFT_ORDER);
		FFT(xn2_r, xn2_i, NULL, _FFT_LEN, _FFT_ORDER);

		//Multiplication in frequency domain
		for(i=0; i<_FFT_LEN; i++){
			res_seq_r[i]=xn1_r[i]*xn2_r[i] - xn1_i[i]*xn2_i[i];
			res_seq_i[i]=xn1_r[i]*xn2_i[i] + xn1_i[i]*xn2_r[i];
		}

		//iFFT
		IFFT(res_seq_r, res_seq_i, _FFT_LEN, _FFT_ORDER);

		//overlap add
		for(i=0; i<M-1; i++) {
			res_seq_r[i]=overlap[i]+res_seq_r[i];
			overlap[i]=res_seq_r[i+N];
		}

		if(k!=SEQ1_LEN/N-1)
		{
			for (i=0; i<N; i++) {
				output_seq[k*N+i]=res_seq_r[i];
				printf("%f ", output_seq[k*N+i]);
			}
		} else {
			for (i=0; i<N+M-1; i++)
				output_seq[k*N+i]=res_seq_r[i];
		}
	}

	for (i=0; i<SEQ1_LEN+SEQ2_LEN-1; i++) {
			printf("%f ", output_seq[i]);
	}
	printf("\n");
}

结果:分段计算和整体直接计算的结果是一样的

1.000000 4.000000 8.000000 12.000000 16.000000 15.000000 9.000000 8.000000 12.000000 16.000000 15.000000 9.000000 8.000000 12.000000 16.000000 15.000000 9.000000 8.000000 12.000000 16.000000 15.000000 9.000000 8.000000 12.000000 16.000000 15.000000 9.000000 8.000000 12.000000 16.000000 14.000000 5.000000

实验2:纯净语音加混响(音效)

给出短序列(有限长序列)为房间冲击响应,长序列是一段纯净语音序列。

分析:纯净语音序列长度是72000,房间冲击响应是4096个点。短序列长度M=4096,长序列均匀分段的每段长度定义为N=4097,那对于每一段来说:线性卷积的长度就是N+M-1=8192,选取圆周卷积的长度为8192(同时也是FFT的长度),那圆周卷积的结果就是线性卷积的结果(通过圆周卷积来计算线性卷积)。如下图:

代码:

main.c

void conv_overlap(double *long_seq, int long_seq_len, double *short_seq, int short_seq_len, double *outputdata);

int main(void)
{
	int count, i=0;
	//open input and output file
	FILE *fpin, *fpout;
	fpin=fopen("audio.raw", "rb");
	if (NULL == fpin) {
		printf("open source file error!\n");
		fclose(fpin);
		return -1;
	}

	fpout=fopen("audio_out.raw", "wb");
	if (NULL == fpin) {
		printf("open output file error!\n");
		fclose(fpin);
		fclose(fpout);
		return -1;
	}

	//get date length of input audio file
	//Note:没有处理wav格式文件的文件头
	fseek(fpin, 0, SEEK_END);
	int rir_length = sizeof(rir)/sizeof(double);//4096
	int inputdata_length=ftell(fpin);
	inputdata_length = inputdata_length/2;
	printf("len of rir:%d\n", rir_length);//4096
	printf("input voice length:%d\n", inputdata_length);//72000

	rewind(fpin);

	short *inputdata = (short *)malloc(inputdata_length * sizeof(short));
	short *outputdata = (short *)malloc((inputdata_length+rir_length-1) * sizeof(short));

	count = fread(inputdata, sizeof(short), inputdata_length, fpin);

	//add rir
	conv_overlap_voice(inputdata, inputdata_length, rir, rir_length, outputdata);

	//save output
	fwrite(outputdata, sizeof(short), inputdata_length, fpout);

	free(inputdata);
	free(outputdata);
	fclose(fpin);
	fclose(fpout);

	return 0;
}

#define _FFT_LEN (8192)
#define _FFT_ORDER 13

//重叠相加法
#define M 4096
#define N 4097 /* 长序列均匀分段的每一段长度 */

void conv_overlap_voice(short *long_seq, long long_seq_len, double *short_seq, long short_seq_len, short *outputdata)
{
	int i, j, k;

	double *res_seq_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *res_seq_i=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn1_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn1_i=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn2_r=(double *)calloc(_FFT_LEN, sizeof(double));
	double *xn2_i=(double *)calloc(_FFT_LEN, sizeof(double));

	short *long_seq_ptr=long_seq;
	short *outputdata_ptr=outputdata;

	/* 4096+4097-1=8192<=8192[FFT的长度] */
//	SEQ1_LEN=long_seq_len;
//	SEQ2_LEN=short_seq_len;
//	input_seq=long_seq;
//	input_seq2=short_seq;

	double xk[N];
	double overlap[M-1];
	memset(overlap, 0, (M-1)*sizeof(double));

	for(k=0; k<long_seq_len/N; k++)
	{
		memset(xn1_r, 0, _FFT_LEN*sizeof(double));
		memset(xn1_i, 0, _FFT_LEN*sizeof(double));
		memset(xn2_r, 0, _FFT_LEN*sizeof(double));
		memset(xn2_i, 0, _FFT_LEN*sizeof(double));
		memset(res_seq_r, 0, _FFT_LEN*sizeof(double));
		memset(res_seq_i, 0, _FFT_LEN*sizeof(double));

		for(j=0; j<N; j++) {
			xk[j]=(double)(long_seq_ptr[k*N+j]/32767.0);
		}

		//init input sequence
		for(i=0; i< N; i++) {
			xn1_r[i]=xk[i];
		}

		for(i=0; i< M; i++) {
			xn2_r[i]=short_seq[i];
		}


		FFT(xn1_r, xn1_i, NULL, _FFT_LEN, _FFT_ORDER);
		FFT(xn2_r, xn2_i, NULL, _FFT_LEN, _FFT_ORDER);

		//Multiplication in frequency domain
		for(i=0; i<_FFT_LEN; i++){
			res_seq_r[i]=xn1_r[i]*xn2_r[i] - xn1_i[i]*xn2_i[i];
			res_seq_i[i]=xn1_r[i]*xn2_i[i] + xn1_i[i]*xn2_r[i];
		}

		//iFFT
		IFFT(res_seq_r, res_seq_i, _FFT_LEN, _FFT_ORDER);

		//overlap add
		for(i=0; i<M-1; i++) {
			res_seq_r[i]=overlap[i]+res_seq_r[i];
			overlap[i]=res_seq_r[i+N];
		}

		if(k!=long_seq_len/N-1)
		{
			for (i=0; i<N; i++) {
				outputdata_ptr[k*N+i]=(short)(res_seq_r[i]*32767.0);
				//printf("%f ", output_seq[k*N+i]);
			}
		} else { //最后一段
			for (i=0; i<N+M-1; i++)
				outputdata_ptr[k*N+i]=(short)(res_seq_r[i]*32767.0);
		}
	}

//	for (i=0; i<long_seq_len+short_seq_len-1; i++) {
//			printf("%f ", outputdata[i]);
//	}
//	printf("\n");

	free(xn1_r);
	free(xn1_i);
	free(xn2_r);
	free(xn2_i);
	free(res_seq_r);
	free(res_seq_i);

	printf("process done\n");
}

运行结果对比:

原纯净语音

加了混响后:

二、无限长序列和有限长序列的卷积(重叠保留法)

实验1:数据实验

给出x(n)={1,2,3,4,5, 1,2,3,4,5, 1,2,3,4,5, …},0≤n≤29; h(n)={1,2,1}; 0≤n≤2; 利用重叠保留法计算y(n)=x(n)*h(n)。

main.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common.h"
#include "baselib.h"

#define _FFT_LEN (8)
#define _FFT_ORDER 3

#define SEQ1_LEN 30
#define SEQ2_LEN 3

//重叠相加法
#define M SEQ2_LEN
#define N 5 /* 长序列均匀分段的每一段长度 */

int main(void)
{
	int i, j, k;

	double* res_seq_r = (double*)calloc(_FFT_LEN, sizeof(double));
	double* res_seq_i = (double*)calloc(_FFT_LEN, sizeof(double));
	double* xn1_r = (double*)calloc(_FFT_LEN, sizeof(double));
	double* xn1_i = (double*)calloc(_FFT_LEN, sizeof(double));
	double* xn2_r = (double*)calloc(_FFT_LEN, sizeof(double));
	double* xn2_i = (double*)calloc(_FFT_LEN, sizeof(double));

	double input_seq[SEQ1_LEN] = { 1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5 };
	double input_seq2[SEQ2_LEN] = { 1,2,1 };
	double output_seq[SEQ1_LEN];
	
	double xk[M-1+N]; //输入的子序列的长(参与圆周卷积)
	
	double overlap[M - 1];
	memset(overlap, 0, (M - 1) * sizeof(double));

	for (k = 0; k <=SEQ1_LEN / N; k++)
	{
		memset(xn1_r, 0, _FFT_LEN * sizeof(double));
		memset(xn1_i, 0, _FFT_LEN * sizeof(double));
		memset(xn2_r, 0, _FFT_LEN * sizeof(double));
		memset(xn2_i, 0, _FFT_LEN * sizeof(double));
		memset(res_seq_r, 0, _FFT_LEN * sizeof(double));
		memset(res_seq_i, 0, _FFT_LEN * sizeof(double));
		memset(xk, 0, (M-1+N) * sizeof(double));
		
		if(k==0) { //第一个子段
			for (j = 0; j < N; j++)
				xk[j+M-1] = input_seq[k * N + j];
		} else if(k == SEQ1_LEN / N){ //最后一个子段
			for(i=0;i<M-1;i++)
				xk[i]=input_seq[SEQ1_LEN-M+1+i];
			for (j = 0; j < N; j++)
				xk[j+M-1]=0;
		} else {
			for (i = 0; i < M - 1; i++)
				xk[i] = input_seq[k*N - M + 1+i];//3 4
			for (i = 0; i <N; i++)
				xk[i+M-1] = input_seq[k*N+i];
		}
		//for (i = 0; i < M - 1 + N; i++) {
			//printf("%f ", xk[i]);
		//}
		//printf("\n");

		//init input sequence(len=M-1+N)
		for (i = 0; i < M-1+N; i++) {//补一个0,达到FFT的长度8
			xn1_r[i] = xk[i];
		}
		
		//补5个0,达到FFT的长度
		for (i = 0; i < M; i++) {
			xn2_r[i] = input_seq2[i];
		}

		FFT(xn1_r, xn1_i, NULL, _FFT_LEN, _FFT_ORDER);
		FFT(xn2_r, xn2_i, NULL, _FFT_LEN, _FFT_ORDER);

		//Multiplication in frequency domain
		for (i = 0; i < _FFT_LEN; i++) {
			res_seq_r[i] = xn1_r[i] * xn2_r[i] - xn1_i[i] * xn2_i[i];
			res_seq_i[i] = xn1_r[i] * xn2_i[i] + xn1_i[i] * xn2_r[i];
		}

		//iFFT
		IFFT(res_seq_r, res_seq_i, _FFT_LEN, _FFT_ORDER);

		//舍掉输出序列的前M-1个点后,连续取N个点,后面的点舍掉
        //后面的点是因为FFT的长度大于圆周卷积的长度M-1+N而计算出来的
		for (i = M-1; i < M-1+N; i++) {
		//for (i = 0; i < _FFT_LEN; i++) {
			printf("%f ", res_seq_r[i]);
		}
		printf("\n");
		//break;
	}
}

结果:

1.000000 4.000000 8.000000 12.000000 16.000000

15.000000 9.000000 8.000000 12.000000 16.000000

15.000000 9.000000 8.000000 12.000000 16.000000

15.000000 9.000000 8.000000 12.000000 16.000000

15.000000 9.000000 8.000000 12.000000 16.000000

15.000000 9.000000 8.000000 12.000000 16.000000

14.000000 5.000000 0.000000 0.000000 0.000000

三、小结

1. 以上测试代码只是理论公式的验证,仿真用的

2. 可以优化的点:比如短序列一般是提前知道的,可以事先计算其FFT,减少实时运算过程的运算量;代码流程上的优化;空间数据buffer的优化;FFT算法的优化;或者可以转为定点运算...。

3. 注意对比两种算法:分段有无重叠,输出结果有无重叠;均匀分段如何取值,线性卷积、循环卷积、FFT等几个长度间的关系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/438846.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

吴恩达机器学习笔记十五 什么是导数 计算图 大型神经网络案例

假设函数 J(w)w^2&#xff0c;当 w3 时&#xff0c; J(w)3*39 当我们给w增加一个很小的量时&#xff0c;观察J(w)如何变化。 例如 w30.001&#xff0c; 则J&#xff08;w&#xff09;9.006001&#xff0c;因此当w3且增加一个变化量 ε 时&#xff0c;J(w)将会增加 6ε&#x…

非线形优化 Matlab和Python (含01规划)

MATLAB&#xff1a;fmincon 在matlab中&#xff0c;一般使用fmincon来解决非线性优化问题 [x,fval,exitflag,output,lambda,grad,hessian]fmincon(fun,x0,A,b,Aeq,beq,lb,ub,nonlcon,options) 一般使用&#xff1a; [x,fval,exitflag]fmincon(fun,x0,A,b,Aeq,beq,lb,ub,non…

RestTemplate解析响应数据中文字符出现Unicode编码问题解决和源码剖析

问题 基于上篇文章&#xff0c;开发过程中又遇到一个restTemplate问题&#xff1a; restTemplate请求接口返回响应数据为json时&#xff0c;解析其中的中文字符出现Unicode编码 测试 接口如下&#xff1a; 测试代码&#xff1a; 觉得很奇怪&#xff0c;我的restTemplate配置…

排序算法——梳理总结

✨冒泡 ✨选择 ✨插入  ✨标准写法  &#x1f3ad;不同写法 ✨希尔排序——标准写法 ✨快排 ✨归并 ✨堆排 ✨冒泡 void Bubble(vector<int>& nums) {// 冒泡排序只能先确定最右边的结果&#xff0c;不能先确定最左边的结果for (int i 0; i < nums.size(); i){…

Effective C++ 学习笔记 条款16 成对使用new和delete时要采取相同形式

以下动作有什么错&#xff1f; std::string *stringArray new std::string[100]; // ... delete stringArray;每件事看起来都井然有序&#xff0c;使用了new&#xff0c;也搭配了对应的delete。但还是有某样东西完全错误&#xff1a;你的程序行为未定义。至少&#xff0c;str…

自律篇001-养成自律的秘密武器1-目标规划表

&#x1f680;以前在某书上看到一些博主非常自律&#xff0c;比如每天5点多起床看书&#xff0c;或者每天坚持健身&#xff0c;直到练出马甲线&#xff0c;还有一边工作一边考研等等&#xff0c;自己也曾尝试过做一些目标规划&#xff0c;但结果都不尽人意。写计划的时候往往信…

阿里云k8s环境下,因slb限额导致的发布事故

一、背景 阿里云k8s容器&#xff0c;在发布java应用程序的时候&#xff0c;客户端访问出现500错误。 后端服务是健康且可用的&#xff0c;网关层大量500错误请求&#xff0c;slb没有流入和流出流量。 经过回滚&#xff0c;仍未能解决错误。可谓是一次血的教训&#xff0c;特…

UI学习 一

教程&#xff1a;Accessibility – Material Design 3 需要科学上网&#xff0c;否则图片显示不出来。设计教程没有图片说明&#xff0c;不容易理解。 优化UI方向 清晰可见的元素足够的对比度和尺寸重要性的明确等级一眼就能辨别的关键信息 传达某一事物的相对重要性 将重…

简单了解Stable Diffusion里面sampling methods(采样方法)每种方法的效果

在 Stable Diffusion 模型中&#xff0c;采样方法&#xff08;Sampling Methods&#xff09;是指在生成图像时用于从模型的概率分布中抽取样本的算法。这些方法对于生成图像的质量、多样性和速度都有重要影响&#xff0c;以下是一些 Stable Diffusion 中常见的采样方法。 那么…

STM32day2

1.思维导图 个人暂时的学后感&#xff0c;不一定对&#xff0c;没什么东西&#xff0c;为做项目奔波中。。。1.使用ADC采样光敏电阻数值&#xff0c;如何根据这个数值调节LED灯亮度。 while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */adc_val HAL_ADC_GetValue(&a…

2575. 找出字符串的可整除数组(Go语言)

https://leetcode.cn/problems/find-the-divisibility-array-of-a-string/ 在看题解之前&#xff0c;我的代码是以下这样&#xff1a; package mainimport ("fmt" )func main() {fmt.Println(divisibilityArray("998244353", 3)) }func divisibilityArray…

基于LSTM实现春联上联对下联

按照阿光的项目做出了学习笔记&#xff0c;pytorch深度学习实战项目100例 基于LSTM实现春联上联对下联 基于LSTM&#xff08;长短期记忆网络&#xff09;实现春联上联对下联是一种有趣且具有挑战性的任务&#xff0c;它涉及到自然语言处理&#xff08;NLP&#xff09;中的序列…

【嵌入式】嵌入式系统稳定性建设:静态代码扫描的稳定性提升术

1. 概述 在嵌入式系统开发过程中&#xff0c;代码的稳定性和可靠性至关重要。静态代码扫描工具作为一种自动化的代码质量检查手段&#xff0c;能够帮助开发者在编译前发现潜在的缺陷和错误&#xff0c;从而增强系统的稳定性。本文将介绍如何在嵌入式C/C开发中使用静态代码扫描…

【数据结构】栈和队列的应用——括号匹配 + 表达式求值 + 表达式转换 +栈的递归应用+队列在计算机系统中的应用

文章目录 3.栈的应用3.1 括号匹配问题3.2 表达式求值3.2.1 三种算术表达式3.2.2 后缀表达式A.中缀转后缀B.后缀表达式的计算 3.2.3 前缀表达式A.中缀转前缀B.前缀表达式的计算 3.2.4 中缀表达式的求值 3.3 递归中栈的应用 4.队列的应用 栈基础知识&#xff1a;【数据结构】栈 顺…

react tab选项卡吸顶实现

react tab选项卡吸顶实现&#xff0c;直接上代码&#xff08;代码有注释&#xff09; tsx代码 /* eslint-disable react-hooks/exhaustive-deps */ import React, { useEffect, useState } from "react"; import DocumentTitle from react-document-title import s…

python界面开发 - Menu (popupmenu) 右键菜单

文章目录 1. python图形界面开发1.1. Python图形界面开发——Tkinter1.2. Python图形界面开发——PyQt1.3. Python图形界面开发——wxPython1.4. Python图形界面开发—— PyGTK&#xff1a;基于GTK1.5. Python图形界面开发—— Kivy1.6. Python图形界面开发——可视化工具1.7. …

交友盲盒系统PHP开源的盲盒源码

源码介绍&#xff1a; 交友盲盒系统是一款基于PHP开发的开源免费盲盒系统&#xff0c;旨在为用户提供一个充满乐趣和惊喜的社交体验。该系统具有丰富的功能和灵活的扩展性&#xff0c;可以轻松地满足各种线上交友、抽奖活动等场景的需求。 安装说明&#xff1a; PHP版本&…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:CheckboxGroup)

多选框群组&#xff0c;用于控制多选框全选或者不全选状态。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 CheckboxGroup(options?: CheckboxGroupOptions) 创建多选框群组…

以人为本的AI技术升级

我们需要以人为本的技术来提高生产力和投资回报率。 通过在数据标注流程中融合机器学习辅助技术&#xff0c;可以减少数据标注所需的时间、资金和人力。 有很多方法可以防止标注员被模型的预测误导。 在传统的机器学习&#xff08;Machine Learning&#xff09;方法下&#…

阿珊比较Vue和React:两大前端框架的较量

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…