Estoy trabajando en un juego de plataformas que incluye música con detección de ritmo. Actualmente estoy detectando ritmos comprobando cuándo la amplitud actual excede una muestra histórica. Esto no funciona bien con géneros musicales, como el rock, que tienen una amplitud bastante estable.
Así que busqué más y encontré algoritmos que dividen el sonido en múltiples bandas usando FFT ... luego encontré el algoritmo Cooley-Tukey FFt
El único problema que tengo es que soy bastante nuevo en audio y no tengo idea de cómo usarlo para dividir la señal en varias señales.
Entonces mi pregunta es:
¿Cómo se usa una FFT para dividir una señal en múltiples bandas?
También para los chicos interesados, este es mi algoritmo en 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++;
}
}
Respuestas:
Bueno, si su señal de entrada es real (como en cada muestra es un número real), el espectro será simétrico y complejo. Explotando la simetría, generalmente los algoritmos FFT empaquetan el resultado al devolverle solo la mitad positiva del espectro. La parte real de cada banda está en las muestras pares y la parte imaginaria en las muestras impares. O, a veces, las partes reales se agrupan en la primera mitad de la respuesta y las partes imaginarias en la segunda mitad.
En las fórmulas, si X [k] = FFT (x [n]), le da un vector i [n] = x [n], y obtiene una salida o [m], entonces
(aunque a veces obtienes X [k] = o [k] + j · o [k + K / 2], donde K es la longitud de tu ventana, 1024 en tu ejemplo). Por cierto, j es la unidad imaginaria, sqrt (-1).
La magnitud de una banda se calcula como la raíz del producto de esta banda con su complejo conjugado:
Y la energía se define como el cuadrado de la magnitud.
Si llamamos a = o [2k] yb = o [2k + 1], obtenemos
por lo tanto
Desenrollando todo, si obtuviste o [m] como salida del algoritmo FFT, la energía en la banda k es:
(Nota: utilicé el símbolo · para indicar la multiplicación en lugar de la habitual * para evitar confusiones con el operador de conjugación)
La frecuencia de la banda k, suponiendo una frecuencia de muestreo de 44.1Khz y una ventana de 1024 muestras, es
Entonces, por ejemplo, su primera banda k = 0 representa 0 Hz, k = 1 es 43 Hz, y la última k = 511 es 22KHz (la frecuencia de Nyquist).
Espero que esto responda a tu pregunta sobre cómo obtienes la energía de la señal por banda usando el FFT.
Anexo : Responda su pregunta en el comentario y suponga que está utilizando el código del enlace que publicó en la pregunta (El algoritmo de Cooley-Tukey en C): Digamos que tiene sus datos de entrada como un vector de entradas cortas:
Mi C está un poco oxidado (estoy codificando principalmente en C ++ hoy en día), pero espero no haber cometido ningún gran error con este código. Por supuesto, si estuviera interesado en la energía de otras bandas, no tiene sentido transformar la ventana completa para cada una de ellas, eso sería una pérdida de tiempo de CPU. En ese caso, realice la transformación una vez y obtenga todos los valores que necesita de xout.
fuente
Aquí hay una gran lectura sobre la detección de latidos en los juegos.
http://www.badlogicgames.com/wordpress/?p=99
Es parte de una serie de blogs de 8 partes sobre el tema.
http://www.badlogicgames.com/wordpress/?category_name=onset-detection-tutorial
fuente
No he hecho esto o leí mucho sobre eso, pero mi primer disparo es algo como esto:
En primer lugar, deberá aplicar una función de ventana para obtener un espectro dependiente del tiempo con la FFT. El ritmo generalmente se encuentra en las frecuencias más bajas, así que aplique otra FFT con una ventana de tiempo más grande en las intensidades de algunas de estas frecuencias (para simplificar, comience con solo 1 a, por ejemplo, 100 Hz y vea si eso es lo suficientemente confiable). Encuentre el pico en este espectro y esa frecuencia es una conjetura para el ritmo.
fuente