2009-11-05 13 views
12

Ho un file audio e sto iterando attraverso il file e prendendo 512 campioni ad ogni passaggio e quindi passandoli attraverso un FFT.Conversione di un FFT in uno spettogramma

Ho i dati fuori come un blocco 514 fluttua a lungo (utilizzando IPP ippsFFTFwd_RToCCS_32f_I) con componenti reali e immaginari interfogliati.

Il mio problema è che cosa faccio con questi numeri complessi una volta che li ho? Al momento sto facendo per ogni valore

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

Questo dà qualcosa di leggermente utilizzabile, ma io preferirei qualche modo di ottenere i valori nel range da 0 a 1. Il problema con lui è che sopra le cime finiscono per tornare come circa 9 o più. Ciò significa che le cose si saturano brutalmente e poi ci sono altre parti dello spettrogramma che si mostrano a malapena nonostante il fatto che sembrano essere piuttosto forti quando eseguo l'audio attraverso lo spettrogramma dell'audizione. Ammetto pienamente di non essere sicuro al 100% dei dati restituiti dalla FFT (oltre a rappresentare i valori di frequenza del blocco lungo 512 di esempio che sto passando). Soprattutto mi manca la comprensione di ciò che rappresenta esattamente il numero del compex.

Qualsiasi consiglio e aiuto sarebbe molto apprezzato!

Modifica: solo per chiarire. Il mio grosso problema è che i valori FFT restituiti non hanno senso senza una qualche idea di cosa sia la scala. Qualcuno può indicarmi di elaborare quella scala?

Edit2: ottengo veramente bello che cercano risultati facendo quanto segue:

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

Al mio occhio questo sembra ancora meglio rispetto alla maggior parte delle implementazioni spettrogramma ho guardato.

C'è qualcosa di MAIORNO che non va in quello che sto facendo?

+1

Stai facendo la cosa giusta per ottenere la grandezza del numero complesso. Hai solo bisogno di scoprire la scala di questi numeri (complessi) (0-1, 0-255, ..?), Vedi i documenti della tua funzione FFT per quello.Se l'intervallo è troppo grande per i tuoi gusti, prendere un log() della magnitudine dovrebbe aiutare, come suggerito di seguito. – Wim

+0

Probabilmente non è importante per il tuo utilizzo, ma potresti anche normalizzare i valori del dominio della frequenza (cioè i valori che ottieni dalla FFT) dividendoli per la larghezza FFT. (cioè più è ampia la FFT, maggiori saranno i valori nei vari intervalli di frequenza) –

risposta

11

La solita cosa da fare per ottenere tutto di un FFT visibile è il logaritmo della grandezza

Quindi, la posizione del buffer di uscita indica quale frequenza è stata rilevata.La magnitudine (norma L2) del numero complesso indica che w forte è stata la frequenza rilevata e la fase (arcotangente) fornisce informazioni molto più importanti nello spazio dell'immagine rispetto allo spazio audio. Poiché la FFT è discreta, le frequenze vanno da 0 alla frequenza di nyquist. Nelle immagini, il primo termine (DC) è solitamente il più grande, e quindi un buon candidato per l'uso nella normalizzazione, se questo è il tuo scopo.Non so se questo vale anche per l'audio (ne dubito)

+0

Risposta interessante. Basta notare che nell'audio, normalmente non c'è un valore DC (distruggerebbe i diffusori se li si lascia passare attraverso l'amplificatore), è puramente AC. – Wim

+0

Ad ogni modo, cercare il valore massimo è un'operazione piuttosto breve (rispetto alla FFT). –

+0

idem sull'uso della scala di log (e trovando il massimo) – peterchen

1

Se si ottengono risultati strani, una cosa da controllare è la documentazione per la libreria FFT per vedere come viene prodotto l'output. Alcune routine utilizzano un formato compresso in cui i valori reali/immaginari sono interlacciati, oppure possono iniziare dall'elemento N/2 e avvolgere.

Per un controllo di integrità suggerisco di creare dati di esempio con caratteristiche note, ad esempio Fs/2, Fs/4 (Fs = frequenza di campionamento) e confrontare l'output della routine FFT con quello che ci si aspetterebbe. Prova a creare sia un seno che un coseno alla stessa frequenza, poiché dovrebbero avere la stessa ampiezza nello spettro, ma avere fasi diverse (cioè il valore reale/valore dell'imagine sarà diverso, ma la somma dei quadrati dovrebbe essere la stessa

Se hai intenzione di utilizzare la FFT, allora hai davvero bisogno di sapere come funziona matematicamente, altrimenti potresti incontrare altri strani problemi come l'aliasing

+0

Bene, ho controllato il profilo. Il mio problema è che i numeri che torno dalla FFT sono privi di significato senza alcuna idea di ciò che rappresenta la scala. Aggiornerò la mia domanda originale – Goz

7

Per ogni finestra di 512 campioni, si calcola la grandezza della FFT come hai fatto tu. Ogni valore rappresenta la grandezza della frequenza corrispondente presente nel segnale.

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

Ora abbiamo bisogno di capire le frequenze.

Poiché il segnale di ingresso è di valori reali, la FFT è simmetrica intorno al centro (componente Nyquist) con il primo termine che è il componente CC. Conoscendo la frequenza di campionamento del segnale Fs, la frequenza di Nyquist è Fs/2. E quindi per l'indice k, la frequenza corrispondente è k*Fs/512

Quindi per ogni finestra di lunghezza 512, otteniamo le grandezze alla frequenza specificata. Il gruppo di quelle finestre consecutive consecutive forma lo spettrogramma.

5

Ci sono alcune cose che penso che troverete utili.

Il FT avanti tenderà a fornire numeri più grandi nell'output che nell'input. Puoi pensarlo come tutta l'intensità di una determinata frequenza visualizzata in un posto piuttosto che essere distribuita attraverso il set di dati. Questo importa? Probabilmente no, perché puoi sempre ridimensionare i dati in base alle tue esigenze. Una volta ho scritto una coppia FFT/IFFT basata su interi e ogni passaggio richiedeva il ridimensionamento per evitare l'overflow dei numeri interi.

I dati reali che vengono immessi vengono convertiti in qualcosa che è quasi complesso. Come risulta che il buffer [0] e il buffer [n/2] sono reali e indipendenti. C'è una buona discussione su di esso here.

I dati di input sono valori di intensità del suono rilevati nel tempo, equidistanti. Si dice che siano abbastanza nel dominio del tempo. Si dice che l'uscita dell'FT sia nel dominio della frequenza perché l'asse orizzontale è la frequenza. La scala verticale rimane intensità. Sebbene non sia ovvio dai dati di input, ci sono anche informazioni di fase nell'input. Sebbene tutto il suono sia sinusoidale, non c'è nulla che corregge le fasi delle onde sinusoidali. Questa informazione di fase appare nel dominio della frequenza come le fasi dei singoli numeri complessi, ma spesso non ci interessa (e spesso lo facciamo anche noi!). Dipende solo da cosa stai facendo. Il calcolo

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

recupera l'informazione di intensità ma elimina le informazioni di fase. Prendendo il logaritmo essenzialmente si attenuano le grandi vette.

Spero che questo sia utile.

+0

bene, quindi come lo userei senza scartare le informazioni sulla fase? E come si applica la fase a uno spettrogramma? – Goz

6

Solo così la gente sa che ho lavorato MOLTO su questo intero problema. La cosa principale che ho scoperto è che FFT richiede la normalizzazione dopo averlo fatto.

Per fare ciò, media tutti i valori del vettore della finestra insieme per ottenere un valore leggermente inferiore a 1 (o 1 se si utilizza una finestra rettangolare). Quindi dividi quel numero per il numero di contenitori di frequenza che hai inserito la trasformazione FFT.

Infine, si divide il numero effettivo restituito dalla FFT per il numero di normalizzazione. I valori di ampiezza dovrebbero ora essere nell'intervallo da -Inf a 1. Entra, ecc. Come ti pare. Lavorerai ancora con un raggio noto.