DFT应用:计算线性卷积

news2024/11/18 13:59:40

目录

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

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

实验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等几个长度间的关系。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu 24.04 抢先体验换国内源 清华源 阿里源 中科大源 163源

Update 240307:Ubuntu 24.04 LTS 进入功能冻结期 预计4月25日正式发布。 Ubuntu22.04换源 Ubuntu 24.04重要升级daily版本下载换源步骤 (阿里源)清华源中科大源网易163源 Ubuntu 24.04 LTS&#xff0c;代号 「Noble Numbat」&#xff0c;即将与我们见面&#xff01; Canonica…

(学习日记)2024.03.07:UCOSIII第九节:时间戳

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

排序算法:冒泡排序和简单选择排序

一、冒泡排序 1.冒泡排序的基本原理 对存放原始数组的数据&#xff0c;按照从前往后的方向进行多次扫描&#xff0c;每次扫描都称为一趟。当发现相邻两个数据的大小次序不符合时&#xff0c;即将这两个数据进行互换&#xff0c;如果从小大小排序&#xff0c;这时较小的数据就…

【机器学习300问】28、什么是决策树?

〇、两个预测任务 &#xff08;1&#xff09;任务一&#xff1a;银行预测偿还能力 当前&#xff0c;某银行正致力于发掘潜在的放贷用户。他们掌握了每位用户的三个关键特征&#xff1a;房产状况、婚姻状况以及年收入。此外&#xff0c;银行还拥有过往这些用户的债务偿还能力的…

【办公类-39-03】批量下载微信公众号图片(三)-微信公众号链接的爬虫下载

背景需求&#xff1a; 测试两种公众号图片下载&#xff0c; 1、UIBOT下载速度慢&#xff0c;也需要有UIBOT软件 【办公类-39-01】批量下载微信公众号图片&#xff08;一&#xff09;UIBOT图片下载-CSDN博客文章浏览阅读289次。【办公类-39-01】批量下载微信公众号图片&#…

Paddle上手实战——NLP经典cls任务“推特文本情感13分类”

Paddle上手实战——NLP经典cls任务“推特文本情感13分类” 实战背景介绍 数据地址:https://www.heywhale.com/home/activity/detail/611cbe90ba12a0001753d1e9/content Twitter推文具备多重特性,首要之处在于其与Facebook的显著区别——其完全基于文本形式,通过Twitter接…

使用 Logstash 丰富你的 Elasticsearch 文档

作者&#xff1a;来自 Elastic David Pilato 我们在上一篇文章中看到&#xff0c;我们可以使用摄取管道中的 Elasticsearch Enrich Processor 在 Elasticsearch 中进行数据丰富。 但有时&#xff0c;你需要执行更复杂的任务&#xff0c;或者你的数据源不是 Elasticsearch&#…

Android14音频进阶:AIDL数据转换关键图解(五十九)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

光线追踪12 - Defocus Blur(虚焦模糊)

现在我们的最后一个特性是虚化模糊。注意&#xff0c;摄影师通常称之为景深&#xff0c;所以请确保在光线追踪的朋友中只使用虚化模糊这个术语。 真实相机具有虚化模糊是因为它们需要一个大孔&#xff08;而不仅仅是针孔&#xff09;来收集光线。一个大孔会导致所有物体失去焦点…

[HackMyVM]Quick 2

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 0a:00:27:00:00:05 (Un…

MySQl基础入门⑥

上一章知识内容 1.数据类型的属性 2.MySql的约束 mysql的约束时指对数据表中数据的一种约束行为&#xff0c;约束主要完成对数据的检验&#xff0c;如果有互相依赖数据&#xff0c;保证该数据不被删除。它能够帮助数据库管理员更好地管理数据库&#xff0c;并且能够确保数据库…

算法打卡day11|栈与队列篇03|Leetcode 239. 滑动窗口最大值、347.前 K 个高频元素

小顶堆和大顶堆 小顶堆&#xff08;Min Heap&#xff09;和大顶堆&#xff08;Max Heap&#xff09;是两种特殊的完全二叉树&#xff0c;它们遵循特定的堆属性&#xff0c;即父节点的值总是小于或等于&#xff08;小顶堆&#xff09;或者大于或等于&#xff08;大顶堆&#xf…

微信小程序开发系列(二十二)·wxml语法·双向数据绑定model:的用法

目录 1. 单向数据绑定 2. 双向数据绑定 3. 代码 在 WXML 中&#xff0c;普通属性的绑定是单向的&#xff0c;例如&#xff1a;<input value"((value))"/> 如果希望用户输入数据的同时改变 data 中的数据&#xff0c;可以借助简易双向绑定机制。在对应属性…

文心一言 VS 讯飞星火 VS chatgpt (210)-- 算法导论16.1 1题

一、根据递归式(16.2)为活动选择问题设计一个动态规划算法。算法应该按前文定义计算最大兼容活动集的大小 c[i,j]并生成最大集本身。假定输入的活动已按公式(16.1)排好序。比较你的算法和GREEDY-ACTIVITY-SELECTOR的运行时间。如何要写代码&#xff0c;请用go语言。 文心一言&…

阿里云和腾讯云区别价格表,云服务器费用对比2024年最新

2024年阿里云服务器和腾讯云服务器价格战已经打响&#xff0c;阿里云服务器优惠61元一年起&#xff0c;腾讯云服务器61元一年&#xff0c;2核2G3M、2核4G、4核8G、4核16G、8核16G、16核32G、16核64G等配置价格对比&#xff0c;阿腾云atengyun.com整理阿里云和腾讯云服务器详细配…

利用tree命令自动保存文件层级结构

tree命令的使用 为了将上图左侧的文件目录&#xff0c;生成上图右侧中的文件夹结构列表&#xff0c;保存在txt中&#xff0c;使用了如下cmd命令&#xff1a; C:\armadillo-12.8.0>tree .>list.txt以上tree命令分为3部分&#xff1a; tree 命令. 在当前目录>list.tx…

ChatGPT:人工智能的革命与未来

引言 随着人工智能技术的飞速发展&#xff0c;ChatGPT作为OpenAI推出的一款语言模型&#xff0c;已经引起了广泛的关注和讨论。它不仅改变了我们与机器交流的方式&#xff0c;还为众多行业的发展带来了革命性的影响。本文将深入探讨ChatGPT的技术原理、应用场景以及它对未来的…

一些硬件知识(六)

防反接设计&#xff1a; 同步电路和异步电路的区别: 同步电路:存储电路中所有触发器的时钟输入端都接同一个时钟脉冲源&#xff0c;因而所有触发器的状态的变化都与所加的时钟脉冲信号同步。 异步电路:电路没有统一的时钟&#xff0c;有些触发器的时钟输入端与时钟脉冲源相连…

【HarmonyOS】ArkTS-箭头函数

箭头函数 箭头函数是 比普通函数 更简洁 的一种函数写法 () > {}() > {// 函数体 }let 函数名 () > {// 函数体 }let 函数名 () > {// 函数体 } 函数名(实参1, 实参2)let 函数名 (形参1: 类型, 形参2: 类型) > {// 函数体 } 函数名(实参1, 实参2)let 函数名 …

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

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