ifft(outFFTData, g_fft_temp, inFFTData, g_twiddle_ifft, twiddle_stride, F_WLEN);//思考ifft输出的复数结果怎么给到硬件输出
之前一直关注FFT后信号奔赴到频域处理,那么频域处理完后,怎么重建信号呢?
给出一段FFT和IFFT函数源码
int nFrameRunCount = 0;#pragma section("ss_fw_code_fast")
void ToneChip_TestFFT_Process(float *Din, float *Dout, int nLen)
{float freqResol; //频点间隔,单位:Hzint i = 0;int j = 0;int k = 0;float *pDataIn = (float *)Din;float *pDataOut = (float *)Dout;int nDataInLen = nLen;int twiddle_stride = 1;float amp = 0.0f; //幅值float n2r = 2.0 / F_WLEN; //计算幅值时,相乘complex_float *inFFTData = (complex_float *)g_fft_input;complex_float *outFFTData = (complex_float *)g_fft_output;float *inFrameData = (float * )g_inFrameData;float fAmpMax = 0.0;freqResol = (SAMPLING_RATE_48K * 1.0) / (F_WLEN * 1.0); //得到频率分辨率memset(inFFTData, 0, sizeof(complex_float) * (F_WLEN) );memset(outFFTData, 0, sizeof(complex_float) * (F_WLEN) );// NHS处理帧旧数据的移动(最老的数据放在inFrameData[0],最新的数据放在了inFrameData[F_WLEN])fft_sp_blk_move((float *)&inFrameData[nDataInLen], (float *)inFrameData, (F_WLEN - nDataInLen));// 用新采样的数据更新NHS处理帧的sampleNumber个长度空间fft_sp_blk_move((float *)pDataIn, (float *)&inFrameData[F_WLEN - nDataInLen], nDataInLen);nFrameRunCount++;//前面4帧就不处理if(nFrameRunCount < F_WLEN / nDataInLen){return;}//2021年10月29日在加窗前对数据进行一次overlapfor(i = 0; i < F_WLEN; i++){inFFTData[i].re = inFrameData[i] * (g_win[i]); //数据加窗hanninginFFTData[i].im = 0.0f;}//在ADSP21489中调用FFT库函数cfft(inFFTData, g_fft_temp, outFFTData, g_twiddle_fft, twiddle_stride, F_WLEN);//如果inFFTData可以数据是临时性可以被重写,那么可以把g_fft_temp省出来
// cfft(inFFTData, NULL, outFFTData, g_twiddle_fft, twiddle_stride, F_WLEN);fAmpMax = -F_FLT_MAX;//计算频点的功率for(i = 1; i < F_WLEN / 2; i++){amp = sqrtf(outFFTData[i].re * outFFTData[i].re + outFFTData[i].im * outFFTData[i].im );amp = amp * n2r * 2.0;if (fAmpMax < amp){fAmpMax = amp; //找出频点中最大的值k = i;}}cfft_mag(outFFTData, g_fftSpectrum, F_WLEN);memset(inFFTData, 0, sizeof(complex_float) * F_WLEN);memset(g_fft_temp, 0, sizeof(complex_float) * F_WLEN);twiddle_stride = 1;ifft(outFFTData, g_fft_temp, inFFTData, g_twiddle_ifft, twiddle_stride, F_WLEN);//思考ifft输出的复数结果怎么给到硬件输出for(i = 0; i < nLen; i++){pDataOut[i] = inFFTData[i].re; //重建信号,只取实部
// pDataOut[i] = pDataIn[i]; //直通测试}}
原始波形
经过fft和iff处理后上述代码输出的波形
输出不正常,原因分析:
1.原始信号经过了hanning加窗,信号有衰减,经过加窗后,能量有所衰减。
查阅了相关资料后了解到
在做FFT计算时,有时候需要用到平顶汉宁窗,如下:
//Tukey window: for 20ms
/*
The Tukey window, also known as the cosine-tapered window,
can be regarded as a cosine lobe of width N*alpha/2 (spanning N*alpha/2 + 1 observations)
that is convolved with a rectangular window of width N(1 - alpha/2).
w(n) = 1/2 * (1 - cos(2*pi*n/(alpha*N))); 0 <= n <= alpha*N/2;
w(n) = 1; alpha*N/2 <= n <= N/2;
w(N-n) = w(n); 0 <= n <= N/2;
At α = 0 it becomes rectangular, and at α = 1 it becomes a Hann window.
*/
平顶汉宁窗的使用场景是:当2帧数据叠在一起时,当前帧大于上一帧,即当前帧所有数据和上一帧的部分数据一起叠帧时,此时就需要使用平顶汉宁窗函数。例如每一帧长320点,当前帧的320点和上一帧的后192点的数据一起叠帧,做512点的FFT,此时就需要使用平顶汉宁窗,其中平顶的数据长度为320-192+1(汉宁窗本身就有1个值为1).
添加的窗函数还必须满足另外一个条件,即重叠缓冲区的部分的窗口的平方和必须为1,
即:w(N)^2 + w(M+N)^2 = 1;
虽然加窗能减少频谱泄露,但是加窗也存在一个问题:加窗衰减了每帧信号的能量,特别是边界处。为了解决这一个问题,就引入了合成窗的概念。
在iSTFT中,合成窗是在iFFT之后,对时域信号做的。此次加窗,并不是为了减少频谱泄露,因为之后不再做FFT,不需要满足周期性序列条件。而是为解决分析窗导致能量衰减问题的。
总结就是要对IFFT后的数据进行重建,需要注意
根据完美重建公式,合成窗的选择,不仅跟分析窗有关,还和overlap有关。不同的overlap要采用不同的合成窗。
重点总结
对ifft后的数据要加一个合成窗函数,该函数满足重叠缓冲区的部分的窗口的平方和必须为1,即分析窗和合成窗平方和等于1,(另外测试如果对原始信号不加窗,那么ifft后可以得到正常信号)
参考文档:使用多特征建模分析语音噪声可能性的噪声抑制方法和装置.caj (专利文档:0059,0060)
2022.11.15
补充一下FFT之后取频域信号只取一半,而另一半却往往没注意是怎么分布的
下面截图看一下fft之后数据
因为我上图是作的1024点变换,那么看一下512点是什么
1024点FFT时,可以观察到的对称结构为
0点对应512点
511点对应的513点
1点则对应的是1023点
2点则对应的是1022点