音符起始點檢測(音頻節奏檢測)(5)

原文鏈接:https://www.badlogicgames.com/wordpress/?p=154

原文鏈接:Onset Detection Part 5: The (Discrete) Fourier Transform

警告:這是我理解離散傅里葉變換的方法,在一些地方可能是錯誤的。對於傅里葉變換的正確解釋,我建議閱讀

http://www.dspguide.com/ch8.htm

(譯註:上面的鏈接我沒打開,個人推薦看下面這一篇)

http://www.opticsjournal.net/Mobile/postdetails/PT160728000122iOlRn?code=3&from=singlemessage&isappinstalled=0

最後我們來看看可怕的傅里葉變換。讓我們複習一些我們已經知道的東西。首先要記住的是,我們的音頻數據是從某種錄音設備中獲取的,或者是數字合成的。在任何情況下,我們所擁有的是測量離散時間點的振幅,我們稱之爲樣本。我們可以有單聲道樣本也可以有立體聲樣本,在後一種情況下,每個時間點有兩個樣本而不是一個。採樣頻率是我們每秒測量的採樣數(對於立體聲,我們將左右通道的採樣同時計算爲一個)。以下是音符 A (440Hz)的1024個樣本,採樣頻率爲44100Hz:

圖片來自原文,誰能告訴我咋把水印去了?

注意:左邊的垂直線不是實際圖像的一部分。是wordpress搞砸了

採樣速率爲44100Hz的1024個樣本的時間跨度爲4307ms。從這張圖中我們可以看到,這些樣本形成了類似於正弦波的東西。事實上,這些樣本是使用Math.sin()生成的,就像我們在本系列第一篇文章中所做的那樣,它證實了聲音只不過是不同頻率和振幅的正弦波的混合物。

一個叫傅里葉的聰明數學家曾經提出一個定理,所有可能的函數都可以用一系列所謂的正弦曲線來近似,正弦曲線是具有不同頻率和振幅的正弦波(實際上高斯已經知道了這一點,但傅里葉得到了名聲)。他爲所謂的連續函數做了所有這些魔術你們會在學校的分析課上記得(你們記得分析,對吧?)它被推廣到離散函數中,你猜怎麼着,我們的樣本就是,一個時間上的離散函數。現在,傅里葉變換把一個信號分解成一組不同頻率和振幅的正弦信號。如果我們對離散音頻信號進行傅里葉變換我們就得到了時域音頻信號的頻域表示。這個變換是可逆的,所以我們可以這樣或那樣做。從現在開始,我們只考慮音頻信號上的離散傅里葉變換,以免使問題過於複雜。

時域信號的頻域告訴我們什麼?簡單地說,它告訴我們頻率對時域信號有多大的影響。頻域信號可以告訴我們音頻信號中是否有440Hz的音符A以及這個音符相對於整首曲子有多高。這是非常棒的,因爲它允許我們調查特定的頻率。從上一篇文章的頻率圖中我們知道各種樂器可以產生不同的頻率。如果我們想專注於唱歌我們可以看看頻率範圍比如80Hz到1000Hz。當然,樂器的頻率範圍是重疊的,這可能會導致一些問題。但是我們稍後會講到。

現在,我們用離散傅里葉變換來分析頻率。給定一個離散時間信號,例如以特定採樣率測量的樣本,我們得到一個離散頻域。作爲一箇中間階段,我們得到了頻域的實部和虛部。最後一句中有一些可怕的術語,我們快速過一遍。傅里葉變換將時間信號分解爲正弦信號的係數。實部包含特定頻率餘弦的傅里葉係數(餘弦和正弦相等)以及相同頻率正弦的傅里葉係數。對於我們的用例來說,這種表示並不那麼重要。我們要用的是所謂的頻譜它是由原始傅里葉變換的實部和虛部計算出來的。頻譜告訴我們每個頻率對原始時域音頻信號的貢獻。從現在開始,我們假設已經計算了這個頻譜,這個頻譜在框架中是由一個名爲FFT(取自Minim)的類來完成的,它可以減輕一些你當下的痛苦。如果你想了解如何從傅里葉變換的實部和虛部轉換到頻譜,請閱讀本篇開始時我提供的鏈接。

由於變換的離散性,並非所有可能的頻率都會出現在頻譜中。頻率被綁定,也就是說多個頻率被合併成一個單獨的值。離散傅里葉變換能給出的最大頻率也就是奈奎斯特頻率。它等於時域信號採樣率的一半。假設我們有一個採樣頻率爲44100Hz的音頻信號,尼奎斯特頻率爲22050Hz。當我們把信號轉換到頻域,我們得到頻率箱(frequency bins)高達22050Hz。有多少個這樣的箱?樣本數的一半加一。當我們對1024個樣本進行變換時,我們得到513個頻率箱,每個頻率箱的帶寬(頻率範圍)爲奈奎斯特頻率,除以除第一個和最後一個有一半帶寬的桶外的桶數。假設我們有1024個樣本以44100Hz採樣。第一個存儲箱將表示0到22050 / 513 / 2~ 21.5Hz的頻率(請記住,第一個存儲箱只有正常帶寬的一半)。下一個方框表示21.5Hz到21.5Hz的頻率加上22050 / 513 ~ 43Hz == 64.5Hz(再一次21.5Hz到64.5Hz,帶寬爲43Hz)。下一個範圍是64.5Hz到107.5Hz,以此類推。

當我們對音頻數據進行離散傅里葉變換時,我們對樣本窗口進行離散傅里葉變換,例如1024個樣本是一個常見的窗口大小。窗口的大小決定了得到的頻譜的分辨率,即頻譜中頻率箱的數量將隨着我們變換樣本的數量線性增加。樣本窗口越大,容器的粒度越細,它們的帶寬就越小。爲了計算頻譜,我們使用了一種特殊的算法,稱爲快速傅里葉變換(FFT),它運行在O(n log n)中,與採用O(n*n)的樸素傅里葉變換相比,它非常快。幾乎所有的FFT實現都要求示例窗口的大小爲2的整數次冪。256 512 1024 2048可以,273不行。我們在框架中使用的FFT實現也有這個“限制”。

因此,無需贅言,我將向您展示上圖中描述的樣本的光譜

注意:左右的垂直線不是實際圖像的一部分。是wordpress搞砸了

是的,就是這樣。x軸表示頻率箱,y軸表示頻率箱的振幅。我們清楚地看到在左邊的一個peek,其餘的頻譜是零。這個峯值對應於包含頻率440Hz的頻率庫,我們爲其中的音符A生成1024個樣本。我們還看到它不是一個乾淨的峯值,左邊和右邊的箱子也接收到一些振幅。這叫做泄漏,是我們無法解決的問題。在我們的例子中,這不是一個大問題,但在其他應用程序場景中,它可能是一個大問題。下面是生成上述兩幅圖像的程序:

public class FourierTransformPlot 
{
   public static void main( String[] argv )
   {
      final float frequency = 440; // Note A		
      float increment = (float)(2*Math.PI) * frequency / 44100;		
      float angle = 0;		
      float samples[] = new float[1024];      

      for( int i = 0; i < samples.length; i++ )
      {
         samples[i] = ((float)Math.sin( angle ));
         angle += increment;			
      }
		
      FFT fft = new FFT( 1024, 44100 );
      fft.forward( samples );

      Plot plotSamples = new Plot( "Samples", 512, 512 );
      plotSamples.plot( samples, 2, Color.white );

      Plot plotSpectrum = new Plot( "Spectrum", 512, 512);
      plotSpectrum.plot(fft.getSpectrum(), 1, Color.white );
   }
}

頭幾行應該很熟悉,我們只是以44000Hz的採樣率生成1024個樣本。實際上這個程序中只有兩行有趣的代碼。第一個是實例化FFT對象的地方。構造函數需要兩個參數,我們用來生成頻譜的採樣率 和樣本窗口大小。下一行執行傅里葉變換和頻譜計算。我們所要做的就是傳入一個浮點數組,其中包含的樣本大小與我們在FFT構造函數中指定的大小相同。這就是我們需要做的來得到我們寶貴的頻率箱。剩下的是繪製樣本和光譜。注意對fft.getSpectrum()的調用。這個方法將返回我們調用FFT.forward()所做的上一個傅里葉變換的頻譜。

哎呀,那太噁心了。我建議大家多讀一下離散傅里葉變換。它是所有音頻工程師必備的數學工具。雖然大多數音頻工程師會像我們一樣使用工具箱,但熟悉周圍的環境並沒有壞處。

現在我們已經裝備好了所有我們需要的東西來啓動我們的起點/節奏檢測器。請繼續關注下一篇文章。

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