2012-08-07 13 views
6

Tengo un archivo de sonido (.3gp) y es aproximadamente ~ 1 min. Me gustaría obtener la frecuencia de este archivo de sonido cada 1/4 segundos. Mi idea es recibir muestras cada 1/4 segundos del archivo de audio y usar FFT I podría obtener los valores de frecuencia. ¿Hay alguna manera de hacer esto?Obtenga la frecuencia de un archivo de audio cada 1/4 segundos en android

En realidad, dividiría el archivo de sonido en archivos de sonido de samples de 1/4sec (siempre sobrescribiendo el anterior), luego usaría el algoritmo FFT y detectaría la frecuencia donde la magintude es la más grande. Pero podría haber soluciones más fáciles, pero tampoco tengo ni idea de cómo hacer esto.

*** ACTUALIZACIÓN 2 - nuevo código

utilizo este código hasta ahora:

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

utilizo este: http://www.wikijava.org/wiki/The_Fast_Fourier_Transform_in_Java_%28part_1%29

cuerdas de la guitarra parecen correctas, pero mi propio sonido no es buena debido a esto:

enter image description here

La magnitud de los dos picos cambia la mayor parte del tiempo y siempre encuentro el más grande para obtener la frecuencia fundamental.

+0

Hola, tengo el mismo problema, necesito grabar la voz en tiempo real y calcular la frecuencia en cada 4ms, ¿cómo lo lograste? ¿Algún código de muestra con usted? –

+0

Hola, no tuve éxito para superar el problema; sin embargo, mis sonidos de guitarra fueron apropiados 9 de 10, pero mi voz fue tal vez 7 de 10 .. –

Respuesta

7

seguimiento del tono con la FFT se le pide con tanta frecuencia desbordamiento de pila que escribió un blog entry with sample code. El código está en C, pero con la explicación y los enlaces, deberías poder hacer lo que quieras.

En cuanto a dividirlo en incrementos de 1/4 de segundo, simplemente podría tomar FFT de segmentos de 1/4 de segundo como sugirió, en lugar del valor predeterminado (que creo que es de aproximadamente 1 segundo). Si esto no le da la resolución de frecuencia que desea, puede que tenga que usar un método de reconocimiento de tono diferente. Otra cosa que podría hacer es usar segmentos superpuestos que tengan más de 1/4 de segundo, pero que comiencen a intervalos de 1/4 de segundo. Este método se alude a la entrada del blog, pero puede no cumplir con su especificación de diseño.

+0

Gracias por la respuesta, actualicé mi pregunta con mi código. Resolví 1/4 de segundo con el inicio de un contador de muestras y cuando alcanza un valor determinado, comienza de nuevo. Pero la detección de tono no es tan buena en frecuencias más altas.Si hago un sonido alto y alto, los armónicos superiores lo hacen todo mal y en vez de eso, obtengo 13khz si 3khz. Sin embargo, por ejemplo, obtengo 600 hz en lugar de 1 kz así que no sé cuál es el problema. –

+0

El problema es que si tiene un sonido que tiene armónicos (es decir, cualquier instrumento musical o cualquier ruido que no sea una onda sinusoidal pura), simplemente encontrar el pico de la FFT no le indicará el tono. La frecuencia correspondiente al tono puede ser una amplitud menor que los armónicos. Debe leer en [Algoritmos de estimación de tono] (http://en.wikipedia.org/wiki/Definición_de_alcanal_de_pérdida) –

+0

Es cierto lo que dijo el_andrill, pero está claro que tiene otros problemas porque las frecuencias que recibe no son múltiplos y por lo tanto, no son armónicos. Si tengo la oportunidad más adelante, examinaré más de cerca tu código, pero al principio parece que estás cometiendo algunos errores: 1. mirando toda la información transformada, en lugar de la mitad inferior, 2. no haciendo ventanas tu información. Todo esto y más está cubierto en mi tutorial de entrada de blog. –

1

Trate 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(); 
       } 
     } 
    } 
} 

Call esto en su MainActivity por,

frequencyButtonListener.setOnClickListener(new OnClickListener() { 

     @Override 
     public void onClick(View v) { 

     new GetFrequency.execute(params); 

     } 
    }); 
+0

Hola, gracias por la respuesta. Tengo un error que no puedo solucionar. onPostExecute, onPreExecute y onProgress update me da errores de sintaxis. –

+0

¡Si no los quiere, simplemente elimínelos! –

+0

Los eliminé. Francamente, no entiendo cómo debería funcionar. Tengo un archivo .3gp en /sdcard/music.3gp y me gustaría analizar eso. Así que hice un botón con el nuevo GetFrequency.execute (params); pero me da un error GetFrequency.execute no se puede resolver a un tipo. –

Cuestiones relacionadas