我使用this FFTBasedSpectrumAnalyzer来分析麦克风收集的声音。但是,FFTBasedSpectrumAnalyzer创建了一个图形,而我想要一个单独的频率,我可以将其放置在一个标签中,所以我试图通过以下公式获得峰值的频率:mFreq = (((1.0 * frequency)/(1.0 * blockSize)) * mPeakPos)/2
。我也通过这个公式得到幅度(以及因此的峰值和峰值频率):Android:通过fft获取更精确的频率
int mPeakPos = 0;
double mMaxFFTSample = 150.0;
for (int i = 0; i < progress[0].length; i++) {
int x = i;
int downy = (int) (150 - (progress[0][i] * 10));
int upy = 150;
//Log.i("SETTT", "X: " + i + " downy: " + downy + " upy: " + upy);
if(downy < mMaxFFTSample)
{
mMaxFFTSample = downy;
//mMag = mMaxFFTSample;
mPeakPos = i;
}
}
但是,我有两个问题。首先,最大频率偏离10-40赫兹,即使我演奏的是稳定的音调也会变化。其次,我只能分析高达4000赫兹的音频。有没有办法让这个更准确和/或分析高达22 kHz的音频?也许通过编辑块大小为非256或8000以外的频率(即使当我尝试这样做时,mFreq会降至0,并且mMaxFFTSample通常会变为-2)。 谢谢。
下面是完整的代码:
public class FrequencyListener extends AppCompatActivity {
private double mFreq;
private double mMag;
private boolean mDidHitTargetFreq;
private View mBackgroundView;
int frequency = 8000;
int channelConfiguration = AudioFormat.CHANNEL_IN_MONO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
AudioRecord audioRecord;
private RealDoubleFFT transformer;
int blockSize;
boolean started = false;
boolean CANCELLED_FLAG = false;
RecordAudio recordTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
blockSize = 256;
transformer = new RealDoubleFFT(blockSize);
started = true;
CANCELLED_FLAG = false;
recordTask = new RecordAudio();
recordTask.execute();
}
private class RecordAudio extends AsyncTask<Void, double[], Boolean> {
@Override
protected Boolean doInBackground(Void... params) {
int bufferSize = AudioRecord.getMinBufferSize(frequency,
channelConfiguration, audioEncoding);
audioRecord = new AudioRecord(
MediaRecorder.AudioSource.DEFAULT, frequency,
channelConfiguration, audioEncoding, bufferSize);
int bufferReadResult;
short[] buffer = new short[blockSize];
double[] toTransform = new double[blockSize];
try {
audioRecord.startRecording();
} catch (IllegalStateException e) {
Log.e("Recording failed", e.toString());
}
while (started) {
if (isCancelled() || (CANCELLED_FLAG == true)) {
started = false;
//publishProgress(cancelledResult);
Log.d("doInBackground", "Cancelling the RecordTask");
break;
} else {
bufferReadResult = audioRecord.read(buffer, 0, blockSize);
for (int i = 0; i < blockSize && i < bufferReadResult; i++) {
toTransform[i] = (double) buffer[i]/32768.0; // signed 16 bit
}
transformer.ft(toTransform);
publishProgress(toTransform);
}
}
return true;
}
@Override
protected void onProgressUpdate(double[]...progress) {
int mPeakPos = 0;
double mMaxFFTSample = 150.0;
for (int i = 0; i < progress[0].length; i++) {
int x = i;
int downy = (int) (150 - (progress[0][i] * 10));
int upy = 150;
//Log.i("SETTT", "X: " + i + " downy: " + downy + " upy: " + upy);
if(downy < mMaxFFTSample)
{
mMaxFFTSample = downy;
//mMag = mMaxFFTSample;
mPeakPos = i;
}
}
mFreq = (((1.0 * frequency)/(1.0 * blockSize)) * mPeakPos)/2;
Log.i("SETTT", "FREQ: " + mFreq + " MAG: " + mMaxFFTSample);
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
try{
audioRecord.stop();
}
catch(IllegalStateException e){
Log.e("Stop failed", e.toString());
}
}
}
@Override
protected void onPause() {
super.onPause();
started = false;
}
@Override
protected void onResume() {
super.onResume();
started = true;
}
}
因此,如果我要将频率从8,000增加到,40,000我将能够检测到更高的频率,但他们会更不准确 我试图改变频率从8,000到4,000,但有一个错误:'无效au dio buffer size'在这部分代码中: 'int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration,audioEncoding); audioRecord =新的AudioRecord( MediaRecorder.AudioSource。默认,频率, channelConfiguration,audioEncoding,bufferSize);' – Jameson
是的,这是正确的。但你总是可以增加FFT大小来补偿。关于这个错误,我猜想40000是不支持的速率,而'getMinBufferSize'返回一个你没有检查的错误。尝试44100或48000. – jaket
在iOS上我是如何设计相同的东西的,但它能够检测高达21kHz的频率,同时保持高水平的准确度?通过增加垃圾箱数量和采样率?出于某种原因无法运作 - 频率我得到的都是0或接近0。 如果我不得不满足于出大的采样率和很高的精确度,是有办法,我可以只具有高精确度低于2 kHz?这只是减少采样率,并以某种方式避免我刚刚收到的错误? – Jameson