2009-11-05 9 views
12

Tengo un archivo de audio y estoy iterando a través del archivo y tomando 512 muestras en cada paso y luego pasándolas a través de una FFT.Convirtiendo una FFT a un spectogram

Tengo los datos como un bloque 514 flota de largo (usando ippsFFTFwd_RToCCS_32f_I de IPP) con componentes reales e imaginarios intercalados.

Mi problema es ¿qué hago con estos números complejos una vez que los tengo? En el momento que estoy haciendo para cada valor

const float realValue = buffer[(y * 2) + 0]; 
const float imagValue = buffer[(y * 2) + 1]; 
const float value  = sqrt((realValue * realValue) + (imagValue * imagValue)); 

Esto da algo poco útil pero prefiero alguna manera de conseguir los valores en el rango de 0 a 1. El problema con él es que por encima de los picos terminan regresando como alrededor de 9 o más. Esto significa que las cosas se saturan brutalmente y luego hay otras partes del espectrograma que apenas aparecen a pesar del hecho de que parecen ser bastante fuertes cuando ejecuto el audio a través del espectrograma de la audición. Admito plenamente que no estoy 100% seguro de cuáles son los datos devueltos por la FFT (aparte de que representa los valores de frecuencia del bloque largo de 512 muestras que estoy pasando). Especialmente, mi entendimiento carece de lo que representa exactamente el número compex.

¡Cualquier consejo y ayuda sería muy apreciada!

Editar: Solo para aclarar. Mi gran problema es que los valores de FFT devueltos no tienen sentido sin una idea de lo que es la escala. ¿Alguien puede indicarme que trabaje en esa escala?

Edit2: Tengo muy buenos resultados en busca de la siguiente manera:

size_t count2 = 0; 
size_t max2  = kFFTSize + 2; 
while(count2 < max2) 
{ 
    const float realValue = buffer[(count2) + 0]; 
    const float imagValue = buffer[(count2) + 1]; 
    const float value = (log10f(sqrtf((realValue * realValue) + (imagValue * imagValue)) * rcpVerticalZoom) + 1.0f) * 0.5f; 
    buffer[count2 >> 1] = value; 
    count2 += 2; 
} 

Para mi ojo esto incluso se ve mejor que la mayoría de las otras implementaciones del espectrograma que he mirado.

¿Hay algo MAYORMENTE mal con lo que estoy haciendo?

+1

Está haciendo lo correcto para obtener la magnitud del número complejo. Solo necesita averiguar la escala de estos números (complejos) (0-1, 0-255, ...), consulte los documentos de su función FFT para eso.Si el rango es demasiado grande para su gusto, tomar un registro() de la magnitud debería ayudar, como se sugiere a continuación. – Wim

+0

Probablemente no sea importante para su uso, pero también podría normalizar los valores de dominio de frecuencia (es decir, los valores que obtiene de la FFT) dividiéndolos por el ancho de FFT. (es decir, cuanto más amplia sea su FFT, mayores serán los valores en los diferentes intervalos de frecuencia) –

Respuesta

11

Lo que se suele hacer para obtener una FFT visible es tomar el logaritmo de la magnitud

Por lo tanto, la posición del búfer de salida le dice qué frecuencia se detectó. La magnitud (norma L2) del número complejo le dice ho w strong la frecuencia detectada fue, y la fase (arcotangente) le brinda información que es mucho más importante en el espacio de la imagen que el espacio de audio. Debido a que la FFT es discreta, las frecuencias van de 0 a la frecuencia nyquist. En las imágenes, el primer término (DC) suele ser el más grande, por lo que es un buen candidato para su uso en la normalización, si ese es su objetivo.No sé si eso también es cierto para el audio (lo dudo)

+0

Interesante respuesta. Solo tenga en cuenta que en el audio, normalmente no hay un valor de CC (destruiría sus parlantes si lo deja pasar a través de su amplificador), es puramente AC. – Wim

+0

De todos modos, buscar el valor máximo es una operación bastante corta (en comparación con la FFT). –

+0

ídem en el uso de la escala de registro (y encontrar el máximo) – peterchen

1

Si obtiene resultados extraños, entonces una cosa para comprobar es la documentación de la biblioteca FFT para ver cómo se empaqueta la salida. Algunas rutinas usan un formato empaquetado donde los valores reales/imaginarios se entrelazan, o pueden comenzar en el elemento N/2 y ajustarse.

Para una verificación de cordura sugiero crear datos de muestra con características conocidas, por ejemplo, Fs/2, Fs/4 (Fs = frecuencia de muestreo) y comparar la salida de la rutina FFT con lo que cabría esperar. Intenta crear un seno y un coseno en la misma frecuencia, ya que deben tener la misma magnitud en el espectro, pero tienen diferentes fases (es decir, el valor real/valorValor diferirá, pero la suma de cuadrados debe ser la misma.

Si tiene la intención de usar la FFT, entonces realmente necesita saber cómo funciona matemáticamente, de lo contrario es probable que encuentre otros problemas extraños como aliasing.

+0

Bueno, he comprobado el perfil. Mi problema es que los números que obtengo de la FFT no tienen sentido sin una idea de lo que representa la escala. Actualizaré mi pregunta original. – Goz

7

Para cada ventana de 512 muestras, calcula la magnitud de la FFT como lo hizo. Cada valor representa la magnitud de la frecuencia correspondiente presente en la señal.

mag 
/\ 
| 
|  !   ! 
|  ! ! ! 
+--!---!----!----!---!--> freq 
0   Fs/2  Fs 

Ahora tenemos que averiguar las frecuencias.

Dado que la señal de entrada es de valores reales, la FFT es simétrica alrededor del medio (componente Nyquist) con el primer término como componente de CC. Conociendo la frecuencia de muestreo de la señal Fs, la frecuencia de Nyquist es Fs/2. Y por lo tanto, para el índice k, la frecuencia correspondiente es k*Fs/512

Entonces, para cada ventana de longitud 512, obtenemos las magnitudes a la frecuencia especificada. El grupo de aquellos en ventanas consecutivas forma el espectrograma.

5

Hay algunas cosas que creo que le resultarán útiles.

El FT hacia adelante tenderá a dar números más grandes en la salida que en la entrada. Puede pensar que se muestra toda la intensidad en una frecuencia determinada en un lugar en lugar de distribuirse a través del conjunto de datos. ¿Esto importa? Probablemente no porque siempre puede escalar los datos para satisfacer sus necesidades. Una vez escribí un par de FFT/IFFT basado en enteros y cada paso requería un reajuste para evitar el desbordamiento de enteros.

Los datos reales que son su entrada se convierten en algo que es casi complejo. Como resulta, el buffer [0] y el buffer [n/2] son ​​reales e independientes. Hay una buena discusión sobre el here.

Los datos de entrada son valores de intensidad de sonido tomados a lo largo del tiempo, igualmente espaciados. Se dice que son, apropiadamente, en el dominio del tiempo. Se dice que la salida del FT está en el dominio de la frecuencia porque el eje horizontal es la frecuencia. La escala vertical sigue siendo intensidad. Aunque no es obvio a partir de los datos de entrada, también hay información de fase en la entrada. Aunque todo el sonido es sinusoidal, no hay nada que solucione las fases de las ondas sinusoidales. Esta información de fase aparece en el dominio de la frecuencia como las fases de los números complejos individuales, pero a menudo no nos importa (¡y a menudo también lo hacemos!). Solo depende de lo que estás haciendo. El cálculo

const float value = sqrt((realValue * realValue) + (imagValue * imagValue)); 

recupera la información de intensidad pero descarta la información de fase. Tomando el logaritmo esencialmente solo amortigua los grandes picos.

Espero que esto sea útil.

+0

bien, entonces ¿cómo lo usaría sin descartar la información de fase? ¿Y cómo se aplica la fase a un espectrograma? – Goz

6

Solo para que la gente sepa que he trabajado mucho en todo este problema. Lo principal que descubrí es que la FFT requiere normalización después de hacerlo.

Para hacerlo, promedie todos los valores de su vector de ventana para obtener un valor algo menor que 1 (o 1 si está utilizando una ventana rectangular). A continuación, divida ese número por el número de intervalos de frecuencia en los que ha publicado la transformación FFT.

Finalmente se divide el número real devuelto por la FFT por el número de normalización. Sus valores de amplitud ahora deberían estar en el rango -Inf to 1. Registre, etc., como lo desee. Seguirás trabajando con un rango conocido.