DFT应用:计算线性卷积

目录

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

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

实验1:数据实验

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

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

实验1:数据实验

三、小结


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

FFT计算代码:

baselib.cpp:

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:

cpp 复制代码
#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:

cpp 复制代码
#ifndef  __TYPEDEFS_H_
#define  __TYPEDEFS_H_

#define PI (3.141592653589793)

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

#endif  //__TYPEDEFS_H_

main.c:

cpp 复制代码
#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:

cpp 复制代码
#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,则计算如下:

cpp 复制代码
#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 |

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

cpp 复制代码
#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

cpp 复制代码
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:

cpp 复制代码
#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等几个长度间的关系。

相关推荐
limingade3 小时前
手机实时提取SIM卡打电话的信令和声音-新的篇章(一、可行的方案探讨)
物联网·算法·智能手机·数据分析·信息与通信
jiao000015 小时前
数据结构——队列
c语言·数据结构·算法
迷迭所归处6 小时前
C++ —— 关于vector
开发语言·c++·算法
leon6257 小时前
优化算法(一)—遗传算法(Genetic Algorithm)附MATLAB程序
开发语言·算法·matlab
CV工程师小林7 小时前
【算法】BFS 系列之边权为 1 的最短路问题
数据结构·c++·算法·leetcode·宽度优先
Navigator_Z7 小时前
数据结构C //线性表(链表)ADT结构及相关函数
c语言·数据结构·算法·链表
Aic山鱼7 小时前
【如何高效学习数据结构:构建编程的坚实基石】
数据结构·学习·算法
天玑y8 小时前
算法设计与分析(背包问题
c++·经验分享·笔记·学习·算法·leetcode·蓝桥杯
sjsjs118 小时前
【数据结构-一维差分】力扣1893. 检查是否区域内所有整数都被覆盖
数据结构·算法·leetcode
redcocal8 小时前
地平线秋招
python·嵌入式硬件·算法·fpga开发·求职招聘