2013-02-22 9 views
9

Sto costruendo uno strumento che dovrebbe funzionare su un server e analizzare i file audio. Voglio farlo in Ruby poiché tutti i miei altri strumenti sono scritti anche in Ruby. Ma ho difficoltà a trovare un buon modo per realizzare questo.Estrai dati di trasformazione di Fourier veloce dal file

Molti degli esempi che ho trovato hanno fatto visualizzatori e materiale grafico. Ho solo bisogno dei dati FFT, niente di più. Ho bisogno sia di ottenere i dati audio, sia di FFT. Il mio obiettivo finale è quello di calcolare alcune cose come la media/mediana/modalità, il 25 ° percentile e il 75 ° percentile su tutte le frequenze (ampiezza ponderata), il BPM e forse qualche altra buona caratteristica per poter poi raggruppare insieme suoni simili .

Per prima cosa ho cercato di usare rubino audio e fftw3 ma non ho mai andare i due a lavorare davvero insieme. Anche la documentazione non era buona, quindi non sapevo davvero quali dati venissero mescolati. Poi ho provato a usare bplay/brec e limitare il mio script Ruby per usare semplicemente STDIN ed eseguire una FFT su quello (usando ancora fftw3). Ma non riuscivo a far funzionare bplay/brec dal momento che il server non ha una scheda audio e non sono riuscito a ottenere l'audio direttamente su STDOUT senza prima passare a un dispositivo audio.

Ecco la più vicina che ho ottenuto:

# extracting audio from wav with ruby-audio 
buf = RubyAudio::Buffer.float(1024) 
RubyAudio::Sound.open(fname) do |snd| 
    while snd.read(buf) != 0 
     # ??? 
    end 
end 

# performing FFT on audio 
def get_fft(input, window_size) 
    data = input.read(window_size).unpack("s*") 
    na = NArray.to_na(data) 
    fft = FFTW3.fft(na).to_a[0, window_size/2] 
    return fft 
end 

Così ora mi sono bloccato e non riesco a trovare nessun altro buoni risultati su Google. Quindi forse voi ragazzi potete aiutarmi?

Grazie!

+0

Forse questa discussione precedente potrebbe essere utile: http://stackoverflow.com/questions/2834548/ruby-play-pause-resume -aac-audio-files – fmendez

+0

Potresti approfondire il motivo per cui sei bloccato? Per favore includi messaggi di errore o lacune nella comprensione di come dovrebbero funzionare le cose. –

+0

Ho aggiunto il mio codice finora. Ho un enorme divario tra la lettura dei dati usando ruby-audio e l'estrazione della FFT usando fftw3. Vedi il commento con tre punti interrogativi. Ho i dati wav all'interno di buf ma non so cosa sia/cosa rappresenti realmente il dato. Ci sono intestazioni lì dentro? È compresso/codificato? ecc, ecc. Voglio ottenere i dati in get_fft (che è preso quasi letteralmente da un altro post SO). –

risposta

8

Ecco la soluzione finale a quello che stavo cercando di ottenere, grazie molto al consiglio utile di Randall Cook. Il codice per estrarre onda sonora e FFT di un file wav in Ruby:

require "ruby-audio" 
require "fftw3" 

fname = ARGV[0] 
window_size = 1024 
wave = Array.new 
fft = Array.new(window_size/2,[]) 

begin 
    buf = RubyAudio::Buffer.float(window_size) 
    RubyAudio::Sound.open(fname) do |snd| 
     while snd.read(buf) != 0 
      wave.concat(buf.to_a) 
      na = NArray.to_na(buf.to_a) 
      fft_slice = FFTW3.fft(na).to_a[0, window_size/2] 
      j=0 
      fft_slice.each { |x| fft[j] << x; j+=1 } 
     end 
    end 

rescue => err 
    log.error "error reading audio file: " + err 
    exit 
end 

# now I can work on analyzing the "fft" and "wave" arrays... 
+1

Sembra giusto. +1 per pubblicare il tuo codice. Sono felice che tu sia stato sbloccato e potresti creare qualcosa che funzioni. A proposito, un ottimo modo per dire grazie su Stack Overflow è di upvotare e/o accettare una risposta, se non lo hai già fatto. ;) –

+0

Ho upvoted il tuo post ma ho dovuto aspettare un po 'prima che potessi accettare la mia risposta. :) –

+0

@ ChristofferBrodd-Reijer il tuo codice funziona perfettamente con i file wav di impronte digitali, ma l'impronta digitale è troppo grande. Hai trovato una soluzione per migliorare la velocità e ridurre le impronte digitali? –

7

Penso che ci siano due problemi qui. Uno sta prendendo i campioni, l'altro sta eseguendo la FFT.

Per ottenere i campioni, ci sono due passaggi principali: decodifica e downmix. Per decodificare i file wav, devi solo analizzare l'intestazione in modo da poter sapere come interpretare i campioni. Per i file mp3, dovrai eseguire una decodifica completa. Una volta che l'audio è stato decodificato, se non si è interessati all'elaborazione dei canali stereo separatamente, potrebbe essere necessario eseguire il downmix in mono, poiché la FFT prevede un singolo canale come input. Se non ti dispiace avventurarti al di fuori di Ruby, lo sox tool rende tutto ciò facile. Ad esempio, sox song.mp3 -b 16 song.raw channels 1 dovrebbe convertire un file mp3 in un file mono di campioni PCM puri (ovvero numeri interi a 16 bit). A proposito, una ricerca rapida ha rivelato la libreria ruby/audio (forse è quella menzionata nel tuo post). Sembra piuttosto buono, soprattutto perché avvolge libsndfile.

Per eseguire la FFT, vedo tre opzioni. Uno è quello di utilizzare this snippet del codice che esegue un FFT. Non sono un esperto di Ruby, ma sembra che potrebbe essere OK. La seconda opzione è utilizzare NArray. Ha un sacco di metodi matematici, tra cui FFTW, disponibile in un modulo separato, un tarball per il quale è collegato nel mezzo della pagina NArray. La terza opzione è scrivere il tuo codice FFT. Non è un algoritmo particolarmente complicato e potrebbe darti una grande esperienza con l'elaborazione numerica in Ruby (se necessario).

Probabilmente siete a conoscenza di questo, ma la FFT si aspetta un input complesso e genera un output complesso. I segnali audio sono reali, ovviamente, quindi la componente immaginaria dell'input dovrebbe sempre essere zero (a + 0*i). Poiché l'input è reale, l'output sarà simmetrico rispetto al punto medio dell'array di output. Puoi tranquillamente ignorare la metà superiore. Se si desidera l'energia in un particolare scomparto di frequenza (sono distanziati linearmente fino a metà della frequenza di campionamento), sarà necessario calcolare l'entità del valore complesso (sqrt(real*real + imag*imag)).

Un'ultima cosa: poiché la frequenza zero (l'offset CC del segnale) e la frequenza di Nyquist (metà della frequenza di campionamento) non hanno componenti di fase, alcune implementazioni FFT le mettono insieme nello stesso scomparto complesso (una nel componente, uno nel componente immaginario, tipicamente del primo contenitore). È possibile creare alcuni segnali semplici (tutti 1 per un segnale DC e alternare +1, -1 per un segnale di Nyquist) e vedere come appare l'uscita FFT.

+0

Grazie per la lunga risposta. Questo è più o meno come stavo pensando. Ma non sono stato in grado di mettere insieme tutto questo. Ho aggiunto un po 'di codice in modo da mostrare il massimo che ho ottenuto usando ruby-audio (quello che hai collegato) e la gemma di fftw3. –

+0

Spesso, quando ho difficoltà a mettere insieme le cose, inizio molto piccolo e aggiungo solo un passo alla volta, aggiungendo un sacco di codice diagnostico (o controllando le variabili da vicino nel debugger) per assicurarmi che le cose funzionino come previsto: posso aprire il file? posso leggere i dati? è il formato dei dati che cosa mi aspetto? posso trasformare i dati? sembra ancora giusto? ecc. –

+0

Sì, ma sono bloccato: quali sono questi dati che sto guardando e come dovrei inserirli nella funzione FFT? Dovrei semplicemente dargli il contenuto del buffer (chiama a_a su buf) o devo elaborarlo prima? Non sono sicuro di ciò che rappresentano i dati che ricevo da Ruby-Audio. –

Problemi correlati