Vuruş tespiti ve FFT


13

Vuruş algılamalı müzik içeren bir platform oyunu üzerinde çalışıyorum. Şu anda mevcut genliğin tarihsel bir örneği aştığında kontrol ederek atımları tespit ediyorum. Bu, oldukça sabit bir genliğe sahip olan rock gibi müzik türleriyle iyi çalışmaz.

Bu yüzden daha ileriye baktım ve sesi FFT kullanarak birden fazla banda bölen algoritmalar buldum ... sonra Cooley-Tukey FFt ​​algoritmasını buldum

Sahip olduğum tek sorun, ses için oldukça yeni olduğum ve bunu sinyali birden fazla sinyale bölmek için nasıl kullanacağım hakkında hiçbir fikrim yok.

Benim sorum şu:

Bir sinyali birden fazla banda bölmek için FFT'yi nasıl kullanırsınız?

Ayrıca ilgilenen çocuklar için bu c # benim algoritması:

// C = threshold, N = size of history buffer / 1024
    public void PlaceBeatMarkers(float C, int N)
    {
        List<float> instantEnergyList = new List<float>();
        short[] samples = soundData.Samples;

        float timePerSample = 1 / (float)soundData.SampleRate;
        int sampleIndex = 0;
        int nextSamples = 1024;

        // Calculate instant energy for every 1024 samples.
        while (sampleIndex + nextSamples < samples.Length)
        {

            float instantEnergy = 0;

            for (int i = 0; i < nextSamples; i++)
            {
                instantEnergy += Math.Abs((float)samples[sampleIndex + i]);
            }

            instantEnergy /= nextSamples;
            instantEnergyList.Add(instantEnergy);

            if(sampleIndex + nextSamples >= samples.Length)
                nextSamples = samples.Length - sampleIndex - 1;

            sampleIndex += nextSamples;
        }


        int index = N;
        int numInBuffer = index;
        float historyBuffer = 0;

        //Fill the history buffer with n * instant energy
        for (int i = 0; i < index; i++)
        {
            historyBuffer += instantEnergyList[i];
        }

        // If instantEnergy / samples in buffer < instantEnergy for the next sample then add beatmarker.
        while (index + 1 < instantEnergyList.Count)
        {
            if(instantEnergyList[index + 1] > (historyBuffer / numInBuffer) * C)
                beatMarkers.Add((index + 1) * 1024 * timePerSample); 
            historyBuffer -= instantEnergyList[index - numInBuffer];
            historyBuffer += instantEnergyList[index + 1];
            index++;
        }
    }

İyi bir başlangıç ​​noktası, wikipedia'nın FFT ve DSP girişleri. Vuruş
Tobias Kienzler

Yanıtlar:


14

Giriş sinyaliniz gerçekse (her bir örnekte olduğu gibi), spektrum simetrik ve karmaşık olacaktır. Simetriden yararlanarak, genellikle FFT algoritmaları sonucu, spektrumun yalnızca pozitif yarısını geri vererek sonucu toplar. Her bandın gerçek kısmı çift örneklerde ve hayali kısım tek örneklerde bulunur. Ya da bazen gerçek kısımlar yanıtın ilk yarısında, hayali kısımlar ise ikinci yarısında paketlenir.

Formüllerde, X [k] = FFT (x [n]) ise, ona bir i [n] = x [n] vektörü verir ve o [m] çıktısı alırsınız, o zaman

X[k] = o[2k] + j·o[2k+1]

(bazen X [k] = o [k] + j · o [k + K / 2] elde edersiniz, burada K pencerenizin uzunluğu, örneğin örneğinizde 1024). Bu arada, j hayali birimdir, sqrt (-1).

Bir bandın büyüklüğü, karmaşık konjugatı ile bu bandın ürününün kökü olarak hesaplanır:

|X[k]| = sqrt( X[k] · X[k]* )

Ve enerji, büyüklüğün karesi olarak tanımlanır.

A = o [2k] ve b = o [2k + 1] adını verirsek,

X[k] = a + j·b

bu nedenle

E[k] = |X[k]|^2 = (a+j·b)·(a-j·b) = a·a + b·b

Her şeyi açmak, eğer o [m] yi FFT algoritmasından çıktı olarak aldıysanız, k bandındaki enerji:

E[k] = o[2k] · o[2k] + o[2k+1] · o[2k+1]

(Not: Konjugasyon operatörü ile karışıklığı önlemek için · sembolü olağan * yerine çarpımı belirtmek için kullandım)

44.1 Khz örnekleme frekansı ve 1024 numunelik bir pencere olduğu varsayılarak k bandının frekansı,

freq(k) = k / 1024 * 44100 [Hz]

Örneğin, ilk k = 0 bandınız 0 Hz, k = 1 43 Hz'dir ve sonuncusu k = 511 22KHz'dir (Nyquist frekansı).

Umarım bu, FFT'yi kullanarak bant başına sinyalin enerjisini nasıl elde edeceğinizle ilgili sorunuza cevap verir.

Ek : Sorunuz yorumda yanıtlanıyor ve soruyu yayınladığınız bağlantıdaki kodu kullandığınızı varsayarak (C'deki Cooley-Tukey algoritması): Diyelim ki kısa bir vektör olarak giriş verileriniz var:

// len is 1024 in this example.  It MUST be a power of 2
// centerFreq is given in Hz, for example 43.0
double EnergyForBand( short *input, int len, double centerFreq)
{
  int i;
  int band;
  complex *xin;
  complex *xout;
  double magnitude;
  double samplingFreq = 44100.0; 

  // 1. Get the input as a vector of complex samples
  xin = (complex *)malloc(sizeof(struct complex_t) * len);

  for (i=0;i<len;i++) {
    xin[i].re = (double)input[i];
    xin[i].im = 0;
  }

  // 2. Transform the signal
  xout = FFT_simple(xin, len);

  // 3. Find the band ( Note: floor(x+0.5) = round(x) )
  band = (int) floor(centerFreq * len / samplingFreq + 0.5); 

  // 4. Get the magnitude
  magnitude = complex_magnitude( xout[band] );

  // 5. Don't leak memory
  free( xin );
  free( xout );

  // 6. Return energy
  return magnitude * magnitude;
}

Benim C biraz paslı (ben çoğunlukla C ++ günümüzde kodlama), ama umarım bu kod ile büyük bir hata yapmadım. Tabii ki diğer grupların enerjisiyle ilgileniyorsanız, her bir pencere için tüm pencereyi dönüştürmek mantıklı değildir, bu CPU zamanının boşa gitmesi olacaktır. Bu durumda dönüşümü bir kez yapın ve xout'tan ihtiyacınız olan tüm değerleri alın.


Oh, sadece bağladığınız koda bir göz attım, zaten sonuçları "karmaşık" formda veriyor ve hatta size karmaşık bir sayının büyüklüğünü hesaplamak için bir işlev sunuyor. O zaman, çıktı vektörünün her elemanı için sadece o büyüklüğün karesini hesaplamanız gerekir, sonuçları sıralamak konusunda endişelenmenize gerek yoktur.
CeeJay

Örnek olarak 0-1024 penceresinden 1024 örneğimin tamamı varsa ve bunları gerçek değerler olarak aldım, bu yüzden karmaşık bir parça yok. ve orada enerjiyi 43Hz frekans bandında hesaplamak istiyorum. O zaman onu nasıl entegre ederim? (Sadece gerçek parçaya, pozitif parçaya ihtiyacım var) Eğer bazı sahte kodlarda yapabilirsen sonsuza kadar derinlemesine olacağım ve o zaman konsepti biraz kavrayabilirim :)
Quincy

Yazdığım kod, zaten bir "karmaşık" yapı içeren, bağladığınız C kütüphanesini kullanıyor. Bu,
sorumda tarif ettiğim paketin açılmasını


0

Bunu yapmadım ya da kendim hakkında çok fazla şey okumadım, ancak ilk çekimim şöyle bir şey:

Her şeyden önce, FFT ile zamana bağlı bir spektrum elde etmek için bir pencere fonksiyonu uygulamanız gerekir. Vuruş genellikle daha düşük frekanslardadır, bu nedenle bu frekansların bazılarının yoğunluğuna daha büyük bir zaman penceresi olan başka bir FFT uygulayın (basitlik için örneğin 100 Hz'de sadece 1 ile başlayın ve bunun yeterince güvenilir olup olmadığına bakın). Bu spektrumda zirveyi bulun ve bu frekans, vuruş için bir tahmindir.


Aslında sorunla karşılaştığım vuruş tespiti değil, FFT'nin nasıl çalıştığını anlıyorum. Ben süreç işleme sinyal yeni ve "FFT ile zamana bağlı bir spektrum elde etmek için bir pencere fonksiyonu uygulamak" bana bir anlam ifade etmiyor gibi şeyler. Neyse teşekkürler :)
Quincy
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.