【音頻處理】離散傅里葉變換

前言

最近復現音樂驅動舞蹈的文章《Dancing-to-Music Character Animation》,用到了與傅里葉變換很相似的稱爲常Q變換的方法去分割音樂,所以對傅里葉變換做了一個小了解,本文不深入各種亂糟糟的理論,比如什麼蝶形算法啥的,單純討論離散傅里葉變換(DFT),我們常見的是快速傅里葉變換(FFT),其實FFT是對DFT的一個計算優化,主要是剔除DFT裏面有週期性之類的被冗餘計算,但是FFT的算法有點小複雜,有興趣深入理論的請移步如下幾篇博文:

如何理解和掌握快速傅里葉變換的計算和概念?

如何通俗地解釋什麼是離散傅里葉變換?

傅里葉分析之掐死教程(完整版)更新於2014.06.06

傅里葉變換

快速傅里葉變換

第三章 離散傅里葉變換(DFT) 及其快速算法(FFT)

傅里葉級數和傅里葉變換是什麼關係?

如何理解傅里葉變換公式?

An Introduction to the Fast Fourier Transform

FFT的詳細解釋,相信你看了就明白了

如果想仔細學習FFT,最好是看完上述推薦的博文並額外找資料,我是不想看了,因爲我看着看着發現自己掉頭髮了o(╯□╰)o

簡介

傅里葉變換意思就是任何一個輸入信號都可以使用多個餘弦波疊加而成,簡單的說就是把時序信號轉換成頻域信息。我們最終需要的就是找到這些餘弦波的相關參數:幅值,相位。

複習一下三角函數的標準式:

y=Acos(wx+b)+k

A 代表振幅,函數週期是2πw ,頻率是週期的倒數w2πb 是函數初相位,k 在信號處理中稱爲直流分量。

在很多工具的實現中,餘弦波的個數就是信號長度,但是在理論公式中會出現一個參數N,是採樣點,通常稱爲N點FFT。

X(k)=n=1Nx(n)exp(12π(k1)(n1)/N),1<=k<=N.

上述公式就是DFT的求解方法了,不考慮它的優化方法FFT,我們直接在matlab中碼公式,最後與FFT的結果做對比即可驗證寫的算法對不對。

實例

假設我們的輸入信號是函數是

S=0.2+0.7cos(2π50t+20180π)+0.2cos(2π100t+70180π)

可以發現直流分量是0.2,以及兩個餘弦函數的疊加,餘弦函數的幅值分別爲0.7和0.2,頻率分別爲50和100,初相位分別爲20度和70度。

畫出信號圖:

Fs = 1000;            % Sampling frequency
T = 1/Fs;             % Sampling period
L = 1000;             % Length of signal
t = (0:L-1)*T;        % Time vector
S = 0.2+0.7*cos(2*pi*50*t+20/180*pi) + 0.2*cos(2*pi*100*t+70/180*pi) ;
plot(1000*t(1:50),S(1:50))
title('Signal Corrupted with Zero-Mean Random Noise')
xlabel('t (milliseconds)')
ylabel('X(t)')

這裏寫圖片描述

FFT變換

先使用matlab默認的快速傅里葉變換函數FFT求解疊加餘弦的各參數

Y = fft(S);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);

首先直接對原始信號進行傅里葉變換得到Y ,它包含實部與虛部,然後求解歸一化後Y 各項的模得到P2 ,由於matlab求解的結果包含對稱的兩個頻譜,稱爲雙側頻譜,我們只需要取一半得到P1 ,此時只需要將除第一個元素外的元素乘以2即可得到幅值信息,其實求解的根本就是來源於YY 有多少項,就說明求解了多少個疊加的餘弦波,接下來解釋如何求解各參數:

  • 直流分量:就是Y的第一個值除以採樣頻率,一般來說是非複數
  • 頻率:(1) ,本例中採樣頻率是1000,長度也是1000,那麼Y 的第二項頻率就是1,第三項頻率是2,其實最終情況下,我們選取頻率不接近0的值。
  • 幅值:2abs(Y)
  • 初相位:atan2(YY) 轉角度製表示

這裏寫圖片描述

P1 的圖中,我們很容易看出來幅值不接近0的位置分別是0,50,100附近,其實我們去看具體的位置發現是1,51,101,此三個位置的Y 值分別爲:

>> Y(1)

ans =

  200.0000

>> Y(51)

ans =

   3.2889e+02 + 1.1971e+02i

>> Y(101)

ans =

  34.2020 +93.9693i

那麼按照描述,我們得到:

  • 直流分量:Y(1)Fs=0.2

  • 頻率:第51項和101項的頻率分別爲50和100

  • 幅值:2abs(Y(51)L)=0.7 同理2abs(Y(101)L)=0.2

  • 初相位:

    >> rad2deg(atan2(imag(Y(51)),real(Y(51))))
    
    ans =
    
     20.0000
    
    >> rad2deg(atan2(imag(Y(101)),real(Y(101))))
    
    ans =
    
     70.0000

【注1】: 在實際應用中,一般讓餘弦波的的數量與信號長度相同,如果不相同,那就是理論中常說的N點FFT

【注2】:幅值是通過模求解的,但是模一般都是正數,如果疊加信號的幅值是複數呢?不要忘記了cos(x)=cos(xπ) ,也就是說如果幅值是複數,那麼只需要在正數幅值的基礎上變化一初相位。比如把例子的函數換成:

S=0.20.7cos(2π50t+20180π)+0.2cos(2π100t+70180π)

那麼求解頻率50對應餘弦波的賦值爲+0.7,初相位爲-160

IFFT變換

顧名思義,IFFT就是逆傅里葉變換,用Y重構信號,其實我們通過Y都已經分析出了原始信號所需的餘弦波的各參數,肯定能重構原始數據,這裏還是做個實驗吧,用IFFT函數:

figure
pred_X=ifft(Y);
plot(pred_X,'r-')
hold on
plot(S,'b*')

這裏寫圖片描述

DFT變換

按照公式手擼DFT,看看計算得到Ymatlab自帶的FFT結果是否一致

X(k)=n=1Nx(n)exp(12π(k1)(n1)/N),1<=k<=N.
%% DFT變換
%                    N
%      X(k) =       sum  x(n)*exp(-j*2*pi*(k-1)*(n-1)/N), 1 <= k <= N.
%                   n=1
DFT_X=zeros(1,L);
N=L;
for k=1:L
    for n=1:N
        DFT_X(k)=DFT_X(k)+S(n)*exp(-1j*2*pi*(k-1)*(n-1)/N);
    end
end
P2=abs(DFT_X/L);
P1 = 2*P2(1:L/2+1);
f = Fs*(0:(L/2))/L;
figure
plot(f,P1)
xlabel('頻率')
ylabel('振幅')
title('DFT變換')

這裏寫圖片描述
再計算與FFT求解的結果的誤差

%% FFT變換
FFT_X=fft(S);
figure
plot(abs(FFT_X-DFT_X))
title('DFT和FFT誤差')

這裏寫圖片描述

IDFT

同樣按照公式手擼逆離散傅里葉變換

x(n)=1Nk=1NX(k)exp(12π(k1)(n1)/N),1<=n<=N.
%% IDFT變換
%                    N
%      x(n) = (1/N) sum  X(k)*exp( j*2*pi*(k-1)*(n-1)/N), 1 <= n <= N.
%                   k=1
DFT_rec_x=zeros(1,k);
for n=1:L
    for k=1:L
        DFT_rec_x(n)=DFT_rec_x(n)+DFT_X(k)*exp( 1j*2*pi*(k-1)*(n-1)/N);
    end
end
DFT_rec_x=DFT_rec_x./N;
rec_err=real(DFT_rec_x)-S;
figure
plot(rec_err)
title('IFFT數據重構誤差')

這裏寫圖片描述

IFFT的結果對比一下:

%% IFFT變換
FFT_rec_x=ifft(FFT_X);
figure
plot(abs(DFT_rec_x-FFT_rec_x))
title('IDFT和IFFT誤差')

這裏寫圖片描述

後記

折騰了這麼多,其實就是爲了一個字:懶。爲了避免複雜的FFT的理論理解,我直接按照DFT的公式碼代碼,取得了與FFT一樣的結果,對於不喜歡複雜理論的同志,可以在代碼實現FFT的時候直接採用DFT的代碼作爲替代品,雖然時間複雜度增大很多,但是理論理解起來簡單很多倍不是。

貼一下文章代碼,此外還有一個FFT的手動實現:鏈接:https://pan.baidu.com/s/1mUdclEQ3tgQUvZQ4XffN3g 密碼:08tg

等我不掉頭髮了,再去糾結FFT的蝶形算法^_^

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章