我想在Android应用程序中找到人类语音的基本频率。我正在使用this FFT class和此Complex class计算此项。
我计算FFT的代码如下:
public double calculateFFT(byte[] signal)
{
final int mNumberOfFFTPoints =1024;
double mMaxFFTSample;
double temp;
Complex[] y;
Complex[] complexSignal = new Complex[mNumberOfFFTPoints];
double[] absSignal = new double[mNumberOfFFTPoints/2];
for(int i = 0; i < mNumberOfFFTPoints; i++){
temp = (double)((signal[2*i] & 0xFF) | (signal[2*i+1] << 8)) / 32768.0F;
complexSignal[i] = new Complex(temp,0.0);
}
y = FFT.fft(complexSignal);
mMaxFFTSample = 0.0;
int mPeakPos = 0;
for(int i = 0; i < (mNumberOfFFTPoints/2); i++)
{
absSignal[i] = Math.sqrt(Math.pow(y[i].re(), 2) + Math.pow(y[i].im(), 2));
if(absSignal[i] > mMaxFFTSample)
{
mMaxFFTSample = absSignal[i];
mPeakPos = i;
}
}
return ((1.0 * sampleRate) / (1.0 * mNumberOfFFTPoints)) * mPeakPos;
}
和我有相同的价值观 How do I obtain the frequencies of each value in an FFT?
是否有可能从这些值中找到基频?有人能帮助我吗?
提前致谢。
答案 0 :(得分:3)
人类语音的基频检测是一个活跃的研究领域,如下面的参考文献所示。您的方法必须精心设计,并且必须取决于数据的性质。
例如,如果您的来源是一个人唱一个音符,录音中没有音乐或其他背景声音,修改后的峰值探测器可能会给出合理的结果。
如果您的来源是一般化的人类语音,除了演讲中的各个共振峰之外,您将无法获得唯一的基本频率。
下图说明了一个简单的检测问题。它显示了持有B-flat-3(Bb3)音符的女性女高音的频谱。 Bb3的基频是233赫兹,但女高音实际上是在唱一个236赫兹的基波(最左边和最高峰)。在这种情况下,一个简单的峰值探测器产生正确的基频。
下图说明了基频检测的挑战之一,即使对于单独演唱的音符,更不用说广义的人类语音。它显示了持有F4音符的女高音的频谱。 F4的基频是349赫兹,但女高音实际上是在演唱一个360赫兹的基音(最左边的峰值)。
然而,在这种情况下,最高峰不是基波,而是714 Hz处的一次谐波。您修改后的峰值检测器必须与这些情况相抗衡。
在广义的人类语音中,基频的概念并不真正适用于比语音中的每个单个共振峰更长持续时间的任何子集。这是因为广义人类语音的频谱是高度时变的。
请参阅以下参考资料:
的FFT,图表和音频数据答案 1 :(得分:2)
听起来你已经为你的问题选择了一个解决方案(FFT)。我不是DSP专家,但我冒昧地说你不会以这种方式获得非常好的结果。请在此处查看更详细的讨论:How do you analyse the fundamental frequency of a PCM or WAV sample?
如果您做选择坚持使用此方法:
如果您需要较低频率的准确度,请考虑使用超过1024个点 - 记住(口语)human voice is surprisingly low。
明智地选择采样频率 - 如果可以,请应用low-pass filter。电话的带宽只有~3KHz,其余的并不是真正听到人声的必要条件。
然后,检查输出值的前半部分,然后选择最低的一个:这是困难的部分 - 可能有几个(进一步的峰值应出现在这也是谐波(固定倍数),但这很难检查,因为你的水桶在这里不是一个有用的尺寸)。这是真正的基本希望存在的频率范围。
再说一次,也许值得考虑解决这个问题的其他方法,因为FFT可能会让你在现实世界中失望。
答案 2 :(得分:0)
我的自相关代码:
public double calculateFFT(double[] signal)
{
final int mNumberOfFFTPoints =1024;
double[] magnitude = new double[mNumberOfFFTPoints/2];
DoubleFFT_1D fft = new DoubleFFT_1D(mNumberOfFFTPoints);
double[] fftData = new double[mNumberOfFFTPoints*2];
double max_index=-1;
double max_magnitude=-1;
final float sampleRate=44100;
double frequency;
for (int i=0;i<mNumberOfFFTPoints;i++){
//fftData[2 * i] = buffer[i+firstSample];
fftData[2 * i] = signal[i]; //da controllare
fftData[2 * i + 1] = 0;
fft.complexForward(fftData);
}
for(int i = 0; i < mNumberOfFFTPoints/2; i++){
magnitude[i]=Math.sqrt((fftData[2*i] * fftData[2*i]) + (fftData[2*i + 1] * fftData[2*i + 1]));
if (max_magnitude<magnitude[i]){
max_magnitude=magnitude[i];
max_index=i;
}
}
return frequency=sampleRate*(double)max_index/(double)mNumberOfFFTPoints;
}
&#34;返回&#34;的值是我的基本频率?
答案 3 :(得分:0)
FFT最大值返回峰值区间频率,其可能不是基频,而是FFT结果区间最接近泛音或基频的谐波。使用更多数据的较长FFT将为您提供更紧密间隔的FFT结果区间,因此可能更接近峰值频率的区间。如果峰值位于箱之间,您也可以插入峰值。但是,如果您正在处理具有强谐波含量的信号,例如语音或音乐,则可能需要使用音调检测/估计算法而不是FFT峰值算法。