Виявлення та FFT


13

Я працюю над ігровою платформою, яка включає музику з виявленням ритму. На даний момент я виявляю удари, перевіряючи, коли поточна амплітуда перевищує історичну вибірку. Це не дуже добре працює з такими жанрами музики, як рок, які мають досить стійку амплітуду.

Тому я подивився далі і знайшов алгоритми, що розбивають звук на кілька діапазонів за допомогою FFT ... тоді я знайшов алгоритм Cooley-Tukey FFt

Єдина проблема, яка у мене виникає, полягає в тому, що я абсолютно новачок у аудіо, і я не маю уявлення, як це використовувати для розділення сигналу на кілька сигналів.

Отже, моє питання:

Як ви використовуєте FFT, щоб розділити сигнал на кілька діапазонів?

Також для хлопців, які цікавляться, це мій алгоритм на c #:

// 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++;
        }
    }

Я думаю, що хорошим початковим пунктом є записи FFT та DSP у wikipedia . Запис про виявлення ритму рідкий, але посилається на статтю на gamedev.net
Тобіас Кіенцлер

Відповіді:


14

Ну, якщо ваш вхідний сигнал справжній (як у кожному зразку - це дійсне число), спектр буде симетричним і складним. Використовуючи симетрію, зазвичай алгоритми FFT пакують результат, повертаючи лише позитивну половину спектру. Справжня частина кожної смуги знаходиться в парних зразках, а уявна частина - у непарних зразках. Або іноді реальні частини збираються разом у першій половині відповіді, а уявні частини - у другій половині.

У формулах, якщо X [k] = FFT (x [n]), ви даєте йому вектор i [n] = x [n], і отримуєте вихід o [m], тоді

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

(хоча іноді ви отримуєте X [k] = o [k] + j · o [k + K / 2], де K - довжина вашого вікна, 1024 у вашому прикладі). До речі, j - уявна одиниця, sqrt (-1).

Величина смуги обчислюється як корінь добутку цієї смуги з її складним сполученим:

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

А енергія визначається як квадрат величини.

Якщо назвати a = o [2k] і b = o [2k + 1], отримаємо

X[k] = a + j·b

тому

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

Розгортаючи все, якщо ви отримали o [m] як вихід з алгоритму FFT, енергія в діапазоні k становить:

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

(Примітка. Я використовував символ · для позначення множення замість звичайного *, щоб уникнути плутанини з оператором кон'югації)

Частота діапазону k, припускаючи частоту вибірки 44,1 КГц і вікно з 1024 зразків, становить

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

Так, наприклад, ваш перший діапазон k = 0 являє собою 0 Гц, k = 1 - 43 Гц, а останній k = 511 - 22 кГц (частота Найквіста).

Я сподіваюся, що це відповідає на ваше запитання про те, як ви отримуєте енергію сигналу на діапазон за допомогою FFT.

Додаток : Відповідаючи на запитання у коментарі та припускаючи, що ви використовуєте код із посилання, яке ви розмістили у запитанні (Алгоритм Кулі-Тукі в С): Скажімо, у вас є вхідні дані як вектор коротких вкладень:

// 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;
}

Мій C трохи іржавий (зараз я здебільшого кодую в C ++), але сподіваюся, що я не зробив жодної великої помилки з цим кодом. Звичайно, якщо вас зацікавила енергія інших гуртів, немає сенсу перетворювати ціле вікно для кожного з них, це було б марною витратою часу на процесор. У такому випадку зробіть перетворення один раз і отримайте всі необхідні значення від xout.


О, я щойно переглянув код, який ви зв'язали, він вже дає результати в "складній" формі і навіть надає вам функцію для обчислення величини складного числа. Тоді вам доведеться лише обчислити квадрат такої величини для кожного елемента вихідного вектора, не потрібно турбуватися про сортування результатів.
CeeJay

Як приклад, якщо у мене є всі 1024 зразки з вікна 0-1024 і я отримав їх як реальні значення, тому немає складної частини. і я хочу обчислити енергію в смузі частот 43 Гц. Як би я це потім інтегрував? (Мені потрібна лише реальна частина назад, позитивна частина) Якщо ви зможете це зробити в якомусь псевдокоді, я буду в глибині вас назавжди, і тоді я можу насправді зрозуміти концепцію :)
Квінці

Код, який я написав, використовує пов'язану з вами бібліотеку C, яка вже містить "складну" структуру. Це робить розгортання, яке я описав у своєму запитанні, непотрібним (і код це відображає)
CeeJay


0

Я цього не робив і не багато читав про це сам, але мій перший кадр є приблизно таким:

Перш за все, вам потрібно застосувати функцію вікна, щоб отримати часовий спектр з FFT. Збивання зазвичай лежить на нижчих частотах, тому застосуйте інший FFT з більшим часовим вікном щодо інтенсивності деяких з цих частот (для простоти почніть лише з 1, наприклад 100 Гц, і подивіться, чи це досить надійно). Знайдіть пік у цьому спектрі, і ця частота є здогадом для ритму.


Це насправді не виявлення ритму, з яким я маю проблеми, але розумію, як працює FFT. Я дійсно новачок в обробці сигналів і такі речі, як "застосувати функцію вікна, щоб отримати часовий спектр з FFT", не мають для мене ніякого сенсу. Все одно дякую :)
Квінсі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.