Я работаю над платформерной игрой, которая включает в себя музыку с обнаружением ударов. В настоящее время я обнаруживаю биения, проверяя, превышает ли текущая амплитуда историческую выборку. Это плохо работает с жанрами музыки, такими как рок, которые имеют довольно устойчивую амплитуду.
Поэтому я посмотрел дальше и нашел алгоритмы, разделяющие звук на несколько полос, используя FFT ... затем я нашел алгоритм Cooley-Tukey 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 упаковывают результат, возвращая вам только положительную половину спектра. Действительная часть каждой полосы находится в четных выборках, а мнимая часть - в нечетных. Или иногда реальные части упакованы вместе в первой половине ответа и мнимые части во второй половине.
В формулах, если X [k] = FFT (x [n]), вы задаете ему вектор i [n] = x [n] и получаете вывод o [m], тогда
(хотя иногда вы получаете X [k] = o [k] + j · o [k + K / 2], где K - длина вашего окна, 1024 в вашем примере). Кстати, j - мнимая единица, sqrt (-1).
Величина полосы вычисляется как корень произведения этой полосы с ее комплексным сопряжением:
И энергия определяется как квадрат величины.
Если мы называем a = o [2k] и b = o [2k + 1], мы получаем
следовательно
Развернув все это, если вы получили o [m] в качестве вывода из алгоритма FFT, энергия в полосе k равна:
(Примечание: я использовал символ · для обозначения умножения вместо обычного *, чтобы избежать путаницы с оператором сопряжения)
Частота полосы k, предполагающая частоту дискретизации 44,1 кГц и окно из 1024 выборок, составляет
Так, например, ваша первая полоса k = 0 представляет 0 Гц, k = 1 составляет 43 Гц, а последняя k = 511 составляет 22 кГц (частота Найквиста).
Я надеюсь, что это отвечает на ваш вопрос о том, как вы получаете энергию сигнала на полосу, используя БПФ.
Приложение : отвечая на ваш вопрос в комментарии и предполагая, что вы используете код из ссылки, которую вы разместили в вопросе (алгоритм Кули-Тьюки в C): допустим, у вас есть входные данные в виде вектора коротких целых:
Мой C немного заржавел (в настоящее время я в основном пишу на C ++), но я надеюсь, что не допустил большой ошибки с этим кодом. Конечно, если бы вы были заинтересованы в энергии других полос, нет смысла преобразовывать целое окно для каждой из них, это было бы пустой тратой процессорного времени. В этом случае выполните преобразование один раз и получите все необходимые значения от xout.
источник
Вот отличная статья об обнаружении ударов в играх.
http://www.badlogicgames.com/wordpress/?p=99
Это часть серии из 8 блогов по этому вопросу.
http://www.badlogicgames.com/wordpress/?category_name=onset-detection-tutorial
источник
Я сам этого не делал и не читал об этом, но мой первый снимок выглядит примерно так:
Прежде всего, вам нужно применить оконную функцию, чтобы получить зависящий от времени спектр с БПФ. Удар обычно лежит на более низких частотах, поэтому примените другое БПФ с большим временным окном к интенсивностям некоторых из этих частот (для простоты начните только с 1 при, например, 100 Гц и посмотрите, достаточно ли это надежно). Найдите пик в этом спектре, и эта частота является предположением для ритма.
источник