В настоящее время я работаю над приложением 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
Как добиться более стабильного и точного результата?
Для стабильности: вы можете посмотреть интерполяцию максимального значения, так как пик может быть не совсем внутри корзины. http://www.dspguru.com/dsp/howtos/how-to-interpolate-fft-peak – notthetup
Спасибо, это исправило стабильность, а также точность. – vcapra1
О! Это хорошо. Позвольте мне сделать ответ, чтобы люди могли его использовать. – notthetup