Hızlı Fourier Dönüşümü kullanarak sesi analiz edin


109

Python'da bir grafik spektrum analizörü oluşturmaya çalışıyorum.

Şu anda 16 bit çift kanallı 44,100 Hz örnek hızındaki ses akışının 1024 baytını okuyorum ve 2 kanalın genliğinin ortalamasını alıyorum. Şimdi 256 imzalı şortum var. Şimdi, numpy gibi bir modül kullanarak bu dizide bir fft önceden oluşturmak ve sonucu, başlangıçta sadece 32 bar olacak grafik spektrum analizörünü oluşturmak için kullanmak istiyorum.

Hızlı Fourier Dönüşümü ve Ayrık Fourier Dönüşümü hakkındaki Wikipedia makalelerini okudum, ancak ortaya çıkan dizinin neyi temsil ettiğinden hala emin değilim. Numpy kullanarak dizimde bir fft oluşturduktan sonra dizi şöyle görünür:

   [ -3.37260500e+05 +0.00000000e+00j   7.11787022e+05 +1.70667403e+04j
   4.10040193e+05 +3.28653370e+05j   9.90933073e+04 +1.60555003e+05j
   2.28787050e+05 +3.24141951e+05j   2.09781047e+04 +2.31063376e+05j
  -2.15941453e+05 +1.63773851e+05j  -7.07833051e+04 +1.52467334e+05j
  -1.37440802e+05 +6.28107674e+04j  -7.07536614e+03 +5.55634993e+03j
  -4.31009964e+04 -1.74891657e+05j   1.39384348e+05 +1.95956947e+04j
   1.73613033e+05 +1.16883207e+05j   1.15610357e+05 -2.62619884e+04j
  -2.05469722e+05 +1.71343186e+05j  -1.56779748e+04 +1.51258101e+05j
  -2.08639913e+05 +6.07372799e+04j  -2.90623668e+05 -2.79550838e+05j
  -1.68112214e+05 +4.47877871e+04j  -1.21289916e+03 +1.18397979e+05j
  -1.55779104e+05 +5.06852464e+04j   1.95309737e+05 +1.93876325e+04j
  -2.80400414e+05 +6.90079265e+04j   1.25892113e+04 -1.39293422e+05j
   3.10709174e+04 -1.35248953e+05j   1.31003438e+05 +1.90799303e+05j...

Bu sayıların tam olarak neyi temsil ettiğini ve bu sayıları 32 çubuğun her biri için bir yükseklik yüzdesine nasıl dönüştüreceğimi merak ediyorum. Ayrıca, 2 kanalın ortalamasını birlikte almalı mıyım?

Yanıtlar:


209

Gösterdiğiniz dizi, ses sinyalinin Fourier Dönüşümü katsayılarıdır. Bu katsayılar, sesin frekans içeriğini elde etmek için kullanılabilir. FFT, karmaşık değerli girdi fonksiyonları için tanımlanmıştır, bu nedenle çıkardığınız katsayılar, girdinizin tamamı gerçek değerler olsa bile, sanal sayılar olacaktır. Her frekanstaki güç miktarını elde etmek için, her frekansın FFT katsayısının büyüklüğünü hesaplamanız gerekir. Bu katsayının yalnızca gerçek bileşeni değildir , gerçek ve sanal bileşenlerinin karelerinin toplamının karekökünü hesaplamanız gerekir. Yani, katsayınız a + b * j ise, büyüklüğü sqrt (a ^ 2 + b ^ 2) olur.

Her bir FFT katsayısının büyüklüğünü hesapladıktan sonra, her bir FFT katsayısının hangi ses frekansına ait olduğunu bulmanız gerekir. Bir N noktalı FFT, sıfırdan başlayarak N eşit aralıklı frekansta sinyalinizin frekans içeriğini verecektir. Çünkü örnekleme frekansınız 44100 örnek / sn'dir. ve FFT'nizdeki nokta sayısı 256, frekans aralığınız 44100/256 = 172 Hz (yaklaşık olarak)

Dizinizdeki ilk katsayı, 0 frekans katsayısı olacaktır. Bu, temelde tüm frekanslar için ortalama güç seviyesidir. Katsayılarınızın geri kalanı, siz 128'e ulaşana kadar 0'dan 172 Hz'nin katları olarak sayılacaktır. Bir FFT'de, yalnızca örnek noktalarınızın yarısına kadar olan frekansları ölçebilirsiniz. Eğer cezalandırılacak bir obursanız ve nedenini bilmeniz gerekiyorsa , Nyquist Frekansı ve Nyquist-Shannon Örnekleme Teoremi üzerindeki bu bağlantıları okuyun , ancak temel sonuç, düşük frekanslarınızın daha yüksek frekans kovalarında kopyalanacağı veya takma olacağıdır. Böylece frekanslar 0'dan başlayacak, her katsayı için N / 2 katsayısına kadar 172 Hz artacak, ardından N - 1 katsayısına kadar 172 Hz azalacaktır.

Bu, başlamanız için yeterli bilgi olmalıdır. FFT'lere Wikipedia'da verilenden çok daha ulaşılabilir bir giriş istiyorsanız, Understanding Digital Signal Processing: 2nd Ed'i deneyebilirsiniz . . Benim için çok yardımcı oldu.

Demek bu sayılar bunu temsil ediyor. Bir yüksekliğin yüzdesine dönüştürme, her frekans bileşeni büyüklüğünü tüm bileşen büyüklüklerinin toplamına göre ölçeklendirerek yapılabilir. Yine de, bu size sadece göreceli frekans dağılımının bir temsilini verir, her frekans için gerçek gücü vermez. Bir frekans bileşeni için mümkün olan maksimum büyüklükte ölçeklendirmeyi deneyebilirsiniz, ancak bunun çok iyi görüneceğinden emin değilim. Uygulanabilir bir ölçeklendirme faktörü bulmanın en hızlı yolu, doğru ayarı bulmak için yüksek ve yumuşak ses sinyallerini denemek olacaktır.

Son olarak, tüm ses sinyalinin frekans içeriğini bir bütün olarak göstermek istiyorsanız, iki kanalın ortalamasını almalısınız. Stereo sesi mono sese karıştırıyorsunuz ve birleşik frekansları gösteriyorsunuz. Sağ ve sol frekanslar için iki ayrı ekran istiyorsanız, her kanalda ayrı ayrı Fourier Dönüşümü yapmanız gerekecektir.


1
Çoğunlukla çevrimiçi olarak FFT'nin aşırı karmaşık açıklamalarını bulabiliyorum, bu, örneklenen noktaların sayısının FFT'nin sonuçlarını nasıl etkilediğine dair harika ve basit bir açıklamaydı. Bunun için teşekkür ederim!
echolocation

26

Bu konu çok eski olmasına rağmen çok faydalı buldum. Ben sadece bunu bulan ve benzer bir şey yaratmaya çalışan herkese fikir vermek istedim.

Çubuklara bölmeye gelince, bu, antti'nin önerdiği gibi verileri çubuk sayısına göre eşit olarak bölerek yapılmamalıdır. En kullanışlı olanı, veriyi oktav kısımlara bölmek olacaktır, her oktav bir öncekinin frekansının iki katıdır. (yani 100 hz, 50 hz'nin üzerinde bir oktavdır, bu da 25 hz'nin üzerinde bir oktavdır).

Kaç bar istediğinize bağlı olarak, tüm aralığı 1 / X oktav aralığına bölersiniz. Çubuktaki belirli bir A merkez frekansına bağlı olarak, çubuğun üst ve alt sınırlarını şunlardan elde edersiniz:

upper limit = A * 2 ^ ( 1 / 2X )
lower limit = A / 2 ^ ( 1 / 2X )

Sonraki bitişik merkez frekansını hesaplamak için benzer bir hesaplama kullanırsınız:

next lower =  A / 2 ^ ( 1 / X )
next higher = A * 2 ^ ( 1 / X )

Daha sonra, her çubuğun genliğini elde etmek için bu aralıklara uyan verilerin ortalamasını alırsınız.

Örneğin: 1/3 oktav aralıklarına bölmek istiyoruz ve 1khz'lik bir merkez frekansı ile başlıyoruz.

Upper limit = 1000 * 2 ^ ( 1 / ( 2 * 3 ) ) = 1122.5
Lower limit = 1000 / 2 ^ ( 1 / ( 2 * 3 ) ) =  890.9

44100 hz ve 1024 örnek (her veri noktası arasında 43 hz) verildiğinde, 21'den 26'ya kadar olan değerlerin ortalamasını almalıyız. (890.9 / 43 = 20.72 ~ 21 ve 1122.5 / 43 = 26.10 ~ 26)

(1/3 oktav çubukları ~ 40hz ile ~ 20khz arasında yaklaşık 30 bar sağlar). Şimdiye kadar anlayabileceğiniz gibi, yükseldikçe daha geniş bir sayı aralığının ortalamasını alacağız. Düşük çubuklar tipik olarak yalnızca 1 veya az sayıda veri noktası içerir. Daha yüksek çubuklar ortalama yüzlerce puan olabilir. 86hz'nin 43hz'nin üzerinde bir oktav olmasının nedeni ... 10086hz neredeyse 10043hz ile aynı ses çıkarıyor.


10

sahip olduğunuz şey, süresi 256/44100 = 0.00580499 saniye olan bir örnektir. Bu, frekans çözünürlüğünüzün 1 / 0,00580499 = 172 Hz olduğu anlamına gelir. Python'dan çıkardığınız 256 değer temelde 86 Hz'den 255 * 172 + 86 Hz = 43946 Hz'ye kadar olan frekanslara karşılık gelir. Çıkardığınız sayılar karmaşık sayılardır (bu nedenle her ikinci sayının sonundaki "j").

DÜZENLENDİ: SABİT YANLIŞ BİLGİ

Karmaşık sayıları, i ve j'nin gerçek ve hayali parçalar olduğu sqrt'yi (i 2 + j 2 ) hesaplayarak genliğe dönüştürmeniz gerekir , resp.

32 bara sahip olmak istiyorsanız, anladığım kadarıyla dört ardışık genliğin ortalamasını almalısınız, istediğiniz kadar 256/4 = 32 bar elde etmelisiniz.


4
Lütfen, c karmaşık bir sayı ise, sqrt (c.real 2 + c.imag 2) == abs (c)
tzot

0

FFT, .pdf'i hesaplayabileceğiniz N karmaşık değerleri döndürür module=sqrt(real_part^2+imaginary_part^2). Her bandın değerini elde etmek için, bandın içindeki tüm harmoniklerle ilgili modülleri toplamanız gerekir. Aşağıda 10 barlık bir spektrum analizörü ile ilgili bir örnek görebilirsiniz. Bir pyd python modülü elde etmek için c kodunun sarılması gerekir.

float *samples_vett;
float *out_filters_vett;
int Nsamples;
float band_power = 0.0;
float harmonic_amplitude=0.0;
int i, out_index;

out_index=0;


for (i = 0; i < Nsamples / 2 + 1; i++)       
        {
            if (i == 1 || i == 2 || i == 4 || i == 8 || i == 17 || i == 33 || i == 66 || i == 132 || i == 264 || i == 511)
            {
                out_filters_vett[out_index] = band_power; 
                band_power = 0; 
                out_index++;  
            }

            harmonic_amplitude = sqrt(pow(ttfr_out_vett[i].r, 2) + pow(ttfr_out_vett[i].i, 2));
            band_power += harmonic_amplitude;

        }

Python tarafından 10 ledli çubuk spektrum analizörü tasarladım ve yaptım. Nunmpy kitaplığını kullanmak yerine (sadece FFT'yi elde etmek için çok büyük ve faydasız) FFT'yi almak ve tüm ses spektrumunu bantlara bölmek için bir python pyd modülü (sadece 27KB) oluşturuldu.

Ek olarak, çıkış sesini okumak için bir geridöngü WASapi portaudio pyd modülü oluşturuldu. Projeyi (blok diyagramı) 10BarsSpectrumAnalyzerWithWASapi.jpg görüntüsünde görebilirsiniz.

YouTube kanalıma bir öğretici video ekledim: çok akıllı bir Python Spectrum Analyzer 10 Led Bar nasıl tasarlanır ve yapılır

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.