2012-08-07 4 views
6

У меня есть звуковой файл (.3gp) и его около ~ 1 мин. Я хотел бы получить частоту этого звукового файла каждые 1/4 секунды. Моя идея состоит в том, чтобы получать образцы в каждые 1/4 секунды из аудиофайла, а с помощью FFT я могу получить значения частоты. Есть какой-либо способ сделать это?Получите частоту аудиофайла через каждые 1/4 секунды в android

Фактически я бы разбил звуковой файл на 1/4sec образцы звуковых файлов (alwyas переписывая превью), затем используя алгоритм FFT и обнаружив частоту, где magintude является самым большим. Но могут быть более простые решения, однако я не знаю, как это сделать.

*** UPDATE 2 - новый код

Я использую этот код до сих пор:

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

Я использую это: http://www.wikijava.org/wiki/The_Fast_Fourier_Transform_in_Java_%28part_1%29

струны гитары кажется правильным, но мой собственный звук не хорошо из-за этого:

enter image description here

Величина двух пиков меняет большую часть времени, и я всегда нахожу самое большое, чтобы получить основную частоту.

+0

Привет, У меня такая же проблема, мне нужно записывать голос в реальном времени и вычислять частоту в каждые 4 мс, как вы это достигли? Любой образец кода с вами? –

+0

Привет, мне не удалось преодолеть проблему, однако мои гитарные звуки были подходящими 9 из 10, но мой голос был, возможно, 7 из 10 .. –

ответ

7

Pitch отслеживание с помощью FFT задают так часто на переполнение стека я написал blog entry with sample code. Код находится на C, но с объяснениями и ссылками вы сможете делать то, что хотите.

Что касается деления его на 1/2-секундные приращения, вы можете просто взять БПФ из 1/4 вторых сегментов, как вы предполагали, вместо значения по умолчанию (которое, по моему мнению, составляет около 1 секунды). Если это не дает вам частотного разрешения, вам, возможно, придется использовать другой метод распознавания тона. Еще одна вещь, которую вы можете сделать, это использовать перекрывающиеся сегменты длиной более 1/4 секунды, но начинать с интервалов, разделенных на 1/4 секунды. Этот метод упоминается в блоге, но он может не соответствовать вашей спецификации дизайна.

+0

Спасибо за ответ, обновил мой квест с моим кодом. Я решил 1/4 секунды с запуском счетчика выборки, и когда он достигнет заданного значения, он снова начнется. Но обнаружение высоты тона не так хорошо на высоких частотах.Если я сделаю громкий высокий звук, верхние гармоники сделают все это неправильным, и я обернусь вокруг 13khz вместо 3khz. Однако, например, я получаю 600hz insted 1kz, поэтому я не знаю, в чем проблема. –

+0

Проблема в том, что если у вас есть звук, который имеет гармоники (т. Е. Любой музыкальный инструмент или любой шум, который не является чистой синусоидальной волной), то просто найти пик БПФ не скажет вам шаг. Частота, соответствующая шагу, может быть более низкой, чем гармоники. Вам нужно прочитать [алгоритмы оценки Pitch] (http://en.wikipedia.org/wiki/Pitch_detection_algorithm) –

+0

Это правда, что сказал the_mandrill, но ясно, что у вас есть другие проблемы, потому что частоты, которые вы получаете, не являются кратными и поэтому не являются гармониками. Если у меня будет шанс позже, я буду более внимательно смотреть на ваш код, но при первом просмотре похоже, что вы делаете несколько ошибок: 1. глядя на все преобразованные данные, а не на нижнюю половину, 2. не на окна ваши данные. Все это и многое другое описано в моем учебнике по записи в блоге. –

1

Попробуйте 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(); 
       } 
     } 
    } 
} 

Вызывайте эту MainActivity по,

frequencyButtonListener.setOnClickListener(new OnClickListener() { 

     @Override 
     public void onClick(View v) { 

     new GetFrequency.execute(params); 

     } 
    }); 
+0

Привет, Спасибо за ответ. Я получил ошибку, которую не могу исправить. onPostExecute, onPreExecute и onProgress update дают мне ошибки sytax. –

+0

Если вы не хотите их, то просто удалите их! –

+0

Я удалил их. Честно говоря, я не понимаю, как это должно работать. У меня есть файл .3gp в /sdcard/music.3gp и хотел бы проанализировать это. Поэтому я сделал кнопку с новым GetFrequency.execute (params); но это дает мне ошибку GetFrequency.execute не может быть разрешен для типа. –