由于某种原因,频率发生了偏移
391 hz => 1162
440 hz => 2196
493 hz => 2454
我正在使用这个值
final int audioFrames= 1024;
final float sampleRate= 44100.0f;
final int bitsPerRecord= 16;
final int channels= 1;
final boolean bigEndian = true;
final boolean signed= true;
byteData= new byte[audioFrames * 2]; //two bytes per audio frame, 16 bits
dData= new double[audioFrames * 2]; // real & imaginary
这就是我准备数据并将其转换为 double 据的方式:
format = new AudioFormat(sampleRate, bitsPerRecord, channels, signed, bigEndian);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
microphone = (TargetDataLine) AudioSystem.getLine(info);
microphone.open(format);
microphone.start();
int numBytesRead = microphone.read(byteData, 0, byteData.length);
一旦数据被读取,从 16 位转换,大端,签名为 double
public void byteToDouble(){
ByteBuffer buf= ByteBuffer.wrap(byteData);
buf.order(ByteOrder.BIG_ENDIAN);
int i=0;
while(buf.remaining()>1){
short s = buf.getShort();
dData[ 2 * i ] = (double) s / 32768.0; //real
dData[ 2 * i + 1] = 0.0; // imag
++i;
}
}
最后,运行 FFT 并找到频率:
public void findFrequency(){
double frequency;
DoubleFFT_1D fft= new DoubleFFT_1D(audioFrames);
/* edu/emory/mathcs/jtransforms/fft/DoubleFFT_1D.java */
fft.complexForward(dData); // do the magic so we can find peak
for(int i = 0; i < audioFrames; i++){
re[i] = dData[i*2];
im[i] = dData[(i*2)+1];
mag[i] = Math.sqrt((re[i] * re[i]) + (im[i]*im[i]));
}
double peak = -1.0;
int peakIn=-1;
for(int i = 0; i < audioFrames; i++){
if(peak < mag[i]){
peakIn=i;
peak= mag[i];
}
}
frequency = (sampleRate * (double)peakIn) / (double)audioFrames;
System.out.print("Peak: "+peakIn+", Frequency: "+frequency+"\n");
}
最佳答案
您可以在 FFT 结果 bin 之间进行插值(抛物线或 Sinc 插值)以获得更准确的频率估计。但是您可能有一个更大的问题:您的频率源可能正在产生(或被削波产生)一些非常强的奇次谐波或泛音,这些谐波或泛音掩盖了 FFT 结果幅度中的任何基波正弦波。因此,您应该尝试使用音调检测/估计算法,而不是仅仅尝试寻找(可能丢失的)FFT 峰值。
关于java - 如何从 java 中的 PCM 数据中获取频率 - fft,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18375656/