2012-08-07 16 views
6

Ho un file audio (.3gp) ed è circa ~ 1 min. Mi piacerebbe ottenere la frequenza di questo file audio ogni 1/4 secondi. La mia idea è di ricevere campioni ogni 1/4 secondi dal file audio e utilizzando FFT potrei ottenere i valori di frequenza. C'è un modo per fare questo?Ottieni la frequenza di un file audio in ogni 1/4 secondi in Android

In realtà avrei diviso il file audio in file audio da 1/4sec (alwyas sovrascrivendo quello più importante), quindi utilizzando l'algoritmo FFT e rilevando la frequenza in cui il magintude è il più grande. Ma potrebbero esserci soluzioni più semplici, ma non ho idea di come farlo.

*** AGGIORNAMENTO 2 - nuovo codice

io uso questo codice finora:

public class RecordAudio extends AsyncTask<Void, double[], Void> { 

    @Override 
    protected Void doInBackground(Void... arg0) { 

     try { 
      int bufferSize = AudioRecord.getMinBufferSize(frequency, 
      AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); 


      //int bufferSize = AudioRecord.getMinBufferSize(frequency, 
        // channelConfiguration, audioEncoding); 

      AudioRecord audioRecord = new AudioRecord( 
        MediaRecorder.AudioSource.MIC, frequency, 
        channelConfiguration, audioEncoding, bufferSize); 

      short[] buffer = new short[blockSize]; 
      //double[] toTransform = new double[blockSize]; 


      audioRecord.startRecording(); 


      // started = true; hopes this should true before calling 
      // following while loop 

      while (started) { 
       sampling++; 

       double[] re = new double[blockSize]; 
       double[] im = new double[blockSize]; 

       double[] newArray = new double[blockSize*2]; 
       double[] magns = new double[blockSize]; 

       double MaxMagn=0; 
       double pitch = 0; 

       int bufferReadResult = audioRecord.read(buffer, 0, 
         blockSize); 


       for (int i = 0; i < blockSize && i < bufferReadResult; i++) { 
        re[i] = (double) buffer[i]/32768.0; // signed 16bit 
        im[i] = 0; 
       }  

       newArray = FFTbase.fft(re, im,true); 

       for (int i = 0; i < newArray.length; i+=2) { 

        re[i/2]=newArray[i]; 
        im[i/2]=newArray[i+1]; 
        magns[i/2] = Math.sqrt(re[i/2]*re[i/2]+im[i/2]*im[i/2]); 
       } 

       // I only need the first half  

       for (int i = 0; i < (magns.length)/2; i++) { 
        if (magns[i]>MaxMagn) 
        { 
         MaxMagn = magns[i]; 
         pitch=i; 
        } 
       }           
       if (sampling > 50) { 
        Log.i("pitch and magnitude", "" + MaxMagn + " " + pitch*15.625f); 
        sampling=0; 
        MaxMagn=0;pitch=0; 
        }     


      } 

      audioRecord.stop(); 

     } catch (Throwable t) { 
      t.printStackTrace(); 
      Log.e("AudioRecord", "Recording Failed"); 
     } 
     return null; 
    } 

Io uso questo: http://www.wikijava.org/wiki/The_Fast_Fourier_Transform_in_Java_%28part_1%29

corde della chitarra sembrano corrette, ma il mio suono non è buona a causa di questo:

enter image description here

La grandezza dei due picchi cambia la maggior parte del tempo e trovo sempre il più grande per ottenere la frequenza fondamentale.

+0

Ciao, ho lo stesso problema, ho bisogno di registrare la voce in tempo reale e calcolare la frequenza in ogni 4ms, come hai ottenuto questo? Qualunque codice di esempio con te? –

+0

Ciao, non sono riuscito a superare il problema, tuttavia i miei suoni di chitarra erano appropriati 9 su 10, ma la mia voce era forse 7 su 10 .. –

risposta

7

Passo di monitoraggio con la FFT è chiesto così spesso su Stack Overflow ho scritto un blog entry with sample code. Il codice è in C, ma con la spiegazione e i collegamenti dovresti essere in grado di fare ciò che vuoi.

Come dividerlo in incrementi di 1/4 di secondo, potresti semplicemente prendere FFT di segmenti di 1/4 di secondo come suggerito, invece del valore predefinito (che credo sia di circa 1 secondo). Se ciò non ti dà la risoluzione di frequenza desiderata, potresti dover utilizzare un metodo di riconoscimento dell'intonazione diverso. Un'altra cosa che potresti fare è usare segmenti sovrapposti che sono più lunghi di 1/4 di secondo, ma iniziare ad intervalli che sono di 1/4 di secondo di distanza. Questo metodo è accennato al post di blog, ma potrebbe non soddisfare le specifiche di progettazione.

+0

Grazie per la risposta, ho aggiornato la mia versione con il mio codice. Ho risolto il 1/4 di secondo con l'avvio di un contatore di campionamento e quando raggiunge un dato valore ricomincia. Ma il rilevamento del pitch non è così buono alle frequenze più alte.Se faccio un suono forte, le armoniche superiori rendono l'intera cosa sbagliata e ottengo circa 13khz invece se 3khz. Comunque per esempio ottengo 600hz di 1kz quindi non so quale sia il problema. –

+0

Il problema è che se si ha un suono che ha armoniche (cioè qualsiasi strumento musicale, o qualsiasi rumore che non sia un'onda sinusoidale pura), allora semplicemente trovare il picco della FFT non ti dirà il tono. La frequenza corrispondente al tono può essere un'ampiezza inferiore rispetto alle armoniche. Devi leggere su [Pitch Estimation algorithms] (http://en.wikipedia.org/wiki/Pitch_detection_algorithm) –

+0

È vero quello che the_mandrill ha detto, ma è chiaro che hai altri problemi perché le frequenze che stai ricevendo non sono multipli e quindi non sono armonici. Se ne avrò una possibilità in seguito, guarderò più da vicino il tuo codice, ma a prima vista sembra che tu stia commettendo alcuni errori: 1. guardando i dati trasformati interi, piuttosto che la metà inferiore, 2. non i finestrini i tuoi dati. Tutto questo e altro è coperto nel mio tutorial sul blog. –

1

Prova AsyncTask:

class GetFrequency extends AsyncTask<String, Void, Void> { 
    public Void doInBackground(String... params) { 
      while (true) { 

      // Apply Logic Here 

      try { 
       Thread.sleep(250); 
       } catch (Exception ie) { 
        // TODO Auto-generated catch block 
       e.printStackTrace(); 
       } 
     } 
    } 
} 

chiamata questo nella vostra MainActivity da,

frequencyButtonListener.setOnClickListener(new OnClickListener() { 

     @Override 
     public void onClick(View v) { 

     new GetFrequency.execute(params); 

     } 
    }); 
+0

Ciao, grazie per la risposta. Ho qualche errore che non riesco a risolvere. onPostExecute, onPreExecute e onProgress update mi danno errori di sytax. –

+0

Se non li vuoi, allora rimuovili !! –

+0

Li ho rimossi. Francamente, non capisco come dovrebbe funzionare. Ho un file .3gp in /sdcard/music.3gp e vorrei analizzarlo. Così ho creato un pulsante con il nuovo GetFrequency.execute (params); ma mi dà un errore che GetFrequency.execute non può essere risolto con un tipo. –

Problemi correlati