2014-12-31 3 views
-1

В настоящее время я работаю над приложением OpenGL tuner. В настоящее время у меня есть график FFT на экране, и я бы хотел рассчитать самую громкую частоту. Вот то, что мой код выглядит следующим образом:Неправильная частота в приложении тюнера

MainActivity

@Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    // ... 

    transformer = new RealDoubleFFT(samples); 
    started = true; 
    recordAudio = new RecordAudio(); 
    recordAudio.execute(); 
    } 

    // ... 

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

    @Override 
    protected Void doInBackground(Void... arg0) { 
     try { 
     int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat); 

     AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, 
      channelConfig, audioFormat, bufferSize); 

     short[] buffer = new short[samples]; 
     double[] fft = new double[samples]; 

     audioRecord.startRecording(); 

     while (started) { 
      int bufferReadResult = audioRecord.read(buffer, 0, samples); 

      for (int i = 0; i < samples && i < bufferReadResult; i++) { 
      fft[i] = (double) buffer[i]/32768.0; 
      } 
      transformer.ft(fft); 
      publishProgress(fft); 
     } 

     audioRecord.stop(); 
     } catch (Throwable t) { 
     t.printStackTrace(); 
     Log.e("MainActivity.AudioRecord", "The audio recording has encountered an error."); 
     } 
     return null; 
    } 

    @Override 
    protected void onProgressUpdate(double[]... fft) { 
     int peak = (int) ((frequency/((float) sampleRate/(float) samples)) * 2f); 
     int offset = 0; 
     for (int i = 0; i < samples; i++) { 
     fftVertices[offset++] = (((float) i/(float) samples) * 2f) - 1f; 
     fftVertices[offset++] = -1f; 

     float color = 0; 
     if (i < peak) { 
      color = (float) (peak - i)/(float) peak; 
     } else if (i > peak) { 
      color = (float) (i - peak)/(float) ((int) ((float) samples/2f) - peak); 
     } else { 
      color = 0f; 
     } 
     color = 1f - color; 

     fftVertices[offset++] = color; 
     fftVertices[offset++] = color; 
     fftVertices[offset++] = color; 

     fftVertices[offset++] = (((float) i/(float) samples) * 2f) - 1f; 
     fftVertices[offset++] = ((float) fft[0][i]/2f) - 1f; 

     fftVertices[offset++] = color; 
     fftVertices[offset++] = color; 
     fftVertices[offset++] = color; 

     if (i % 2 == 0) { 
      magnitude[(int) ((float) i/2f)] = (float) Math.sqrt(fft[0][i] * fft[0][i] + fft[0][i + 1] * fft[0][i + 1]); 
     } 
     } 
     updateFrequency(magnitude); 
    } 

    } 

    private static void updateFrequency(float[] mag) { 
    int peak = 0; 
    for (int i = 0; i < (int) ((float) samples/2f); i++) { 
     if (mag[i] >= mag[peak]) { 
     peak = i; 
     } 
    } 
    frequency = peak * sampleRate/samples; 
    Log.d("MainActivity", "Frequency: " + frequency + " Hz"); 
    } 

    private static float[] getVertices() { 
    return fftVertices; 
    } 

    private static class Renderer implements GLSurfaceView.Renderer { 

    // ... 

    private float[] vertices = new float[samples * 2 * (POSITION_COMPONENT_COUNT + COLOR_COMPONENT_COUNT)]; 

    public Renderer(Context context) { 

     // ... 

     int offset = 0; 
     for (int i = 0; i < samples; i++) { 
     vertices[offset++] = (((float) i/(float) samples) * 2f) - 1f; 
     vertices[offset++] = -1f; 

     vertices[offset++] = 1f; 
     vertices[offset++] = 1f; 
     vertices[offset++] = 1f; 

     vertices[offset++] = (((float) i/(float) samples) * 2f) - 1f; 
     vertices[offset++] = ((float) Math.random() * 2f) - 1f; 

     vertices[offset++] = 1f; 
     vertices[offset++] = 1f; 
     vertices[offset++] = 1f; 
     } 

     // ... 
    } 

    @Override 
    public void onSurfaceCreated(GL10 gl, EGLConfig config) { 
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 

     // ... 
    } 

    @Override 
    public void onSurfaceChanged(GL10 gl, int width, int height) { 
     glViewport(0, 0, width, height); 
    } 

    @Override 
    public void onDrawFrame(GL10 gl) { 
     glClear(GL_COLOR_BUFFER_BIT); 

     // ... 
    } 

    } 

Так MainActivity содержит RecordAudio класс. Я оставил некоторые строки, которые не были важны, например OpenGL или приложения. В основном, мое приложение читает аудиовход и отображает FFT на экране. Я создал вычисление частоты следующим образом:

я сначала сделать флоат массив величин:

magnitude[(int) ((float) i/2f)] = (float) Math.sqrt(fft[0][i] * fft[0][i] + fft[0][i + 1] * fft[0][i + 1]); 

Здесь, внутри для цикла, который перебирает БПФ, каждой итерации, где индекс является даже, величина добавляется к массиву. Затем в конце вычисляется частота:

private static void updateFrequency(float[] mag) { 
    int peak = 0; 
    for (int i = 0; i < (int) ((float) samples/2f); i++) { 
     if (mag[i] >= mag[peak]) { 
     peak = i; 
     } 
    } 
    frequency = peak * (int) ((float) sampleRate/(float) samples); 
    Log.d("MainActivity", "Frequency: " + frequency + " Hz"); 
    } 

Обнаружив положение пика, вычислив частоту.

Моя проблема: я играю синусоидальную волну 440 Гц в микрофон телефона, и на выходе это много спорадических чисел, но некоторые из них 430 Гц. Вот пример вывода:

12-31 16:34:24.992 387.59766 Hz 
12-31 16:34:25.022 430.66406 Hz 
12-31 16:34:25.042 387.59766 Hz 
12-31 16:34:25.072 430.66406 Hz 
12-31 16:34:25.122 387.59766 Hz 
12-31 16:34:25.142 430.66406 Hz 
12-31 16:34:25.162 387.59766 Hz 
12-31 16:34:25.182 430.66406 Hz 
12-31 16:34:25.182 387.59766 Hz 
12-31 16:34:25.192 430.66406 Hz 
12-31 16:34:25.222 430.66406 Hz 
12-31 16:34:25.242 387.59766 Hz 
12-31 16:34:25.262 430.66406 Hz 
12-31 16:34:25.292 430.66406 Hz 
12-31 16:34:25.312 387.59766 Hz 
12-31 16:34:25.332 387.59766 Hz 
12-31 16:34:25.372 430.66406 Hz 
12-31 16:34:25.392 387.59766 Hz 
12-31 16:34:25.422 430.66406 Hz 
12-31 16:34:25.432 1722.6563 Hz 
12-31 16:34:25.452 387.59766 Hz 
12-31 16:34:25.472 430.66406 Hz 
12-31 16:34:25.502 387.59766 Hz 
12-31 16:34:25.522 430.66406 Hz 
12-31 16:34:25.553 387.59766 Hz 
12-31 16:34:25.573 387.59766 Hz 
12-31 16:34:25.603 430.66406 Hz 
12-31 16:34:25.623 387.59766 Hz 

Как добиться более стабильного и точного результата?

+1

Для стабильности: вы можете посмотреть интерполяцию максимального значения, так как пик может быть не совсем внутри корзины. http://www.dspguru.com/dsp/howtos/how-to-interpolate-fft-peak – notthetup

+1

Спасибо, это исправило стабильность, а также точность. – vcapra1

+0

О! Это хорошо. Позвольте мне сделать ответ, чтобы люди могли его использовать. – notthetup

ответ

2

Одна из проблем ДПФ заключается в том, что если ваш пик широк и лежит над двумя (или более) бункерами, и он сдвигается так медленно (из-за доплеровских или других причин), вы получите колебания энергии между два бункера.

Один из способов - увеличить количество ячеек БПФ (количество точек вашего БПФ). Вы можете также рассмотреть Zero-padding.

Если вы не хотите возиться с количеством точек БПФ, другой подход заключается в интерполяции пикового значения (и частоты) из данных БПФ. This article gives хороший подход к такой технике.

Я не уверен в точности, но это должно помочь с стабильностью (уменьшить флуктуацию) результата, на мой взгляд.

Смежные вопросы