我目前正在开发一个OpenGL调谐器应用程序。我目前在屏幕上有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]);
这里,在循环遍历FFT的for循环内,索引为偶数的每次迭代,都会向数组添加一个幅度。然后在最后,计算频率:
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
如何才能获得更稳定和准确的结果?
答案 0 :(得分:2)
DFT的一个问题是,如果你的峰值很宽并且位于两个(或更多)的箱子上并且它会稍微移动(由于多普勒或其他原因),你会得到能量波动。两个箱子。
一种方法是增加FFT的区间数(FFT的点数)。您也可以考虑Zero-padding。
如果您不想弄乱FFT的点数,另一种方法是从FFT数据中插入峰值(和频率)。 This article gives这种技术的良好方法。
我不确定准确性,但这应该有助于我认为结果的稳定性(减少波动)。