Виявлення та 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.