Java - 估计基频的问题

标签 java signal-processing fft wav

我正在尝试从包含 1 个单词的语音录音的 .wav 文件中估计基频。

我尝试做的是使用 audioInputStream 读取文件。格式为 PCM_SIGNED 44100.0 Hz,16 位,立体声,4 字节/帧,小端。

因此我制作了一个新的缓冲区,只包含一个 channel 。此代码实现了这一点:

double [] audioRight = new double[audioBytes.length/2]; 
for(int i = 0, k = 0; i <= audioBytes.length-1; i+=4, k+=2){
    audioRight[k]=audioBytes[i];
    audioRight[k+1]=audioBytes[i+1];
}

然后数据被移动到一个 fftBuffer,它的大小是原来的两倍,然后应用 DFT。使用的库是 JTransform。使用的函数称为 realForwardFull。

DoubleFFT_1D fftDo= new DoubleFFT_1D(audioLeft.length);
double[] fftBuffer = new double [audioLeft.length*2];

for (int i = 0; i < audioLeft.length; i++){
     fftBuffer[i] = audioLeft[i];
}
fftDo.realForwardFull(fftBuffer);

这给出了一个复数列表,我用它来计算每个复数的幅度/振幅以制作功率谱。

用于获取振幅的公式Amplitude=sqrt(IMIM+RERE).

这提供了一个振幅数组,我对其应用了谐波求和方法。谐波求和是指给出最高和的指数 + 3 次谐波是代表基频的指数。

double top_sum = 0;
double first_index = 0;
double sum = 0;
double f_0 = 0;
double FR = audioInputStream.getFormat().getSampleRate()/2/ampBuffer.length;

for (int i = 50; i <= ampBuffer.length/4-1; i++){
sum = ampBuffer[i]+ampBuffer[i*2]+ampBuffer[i*3]+ampBuffer[i*4];
     if (top_sum < sum){
 top_sum=sum;
 first_index = i;

然而,该索引需要映射回正确的频率域。 据我了解,应该通过说 (index/fttBuffer.length)*sampleRate 来完成。

这提供了对基频的估计。

然而,结果并不“正确”。我有几个不同的 .wav 文件要测试,其中大部分的结果超出预期范围。对于相同的女声,三个不同的词给出的结果分别为 40、13 和 360。所有这三个结果预计都在大约 250 到 350 的范围内。

我认为造成这种情况的一些问题是振幅缓冲区值。绘制时,该图没有显示任何代表谐波的清晰峰。

这是图表的图像:

Amplitudes

我知道这是很多信息,但我相信更多的信息可以更容易理解所做的事情。

回顾:我不确定的是振幅数据。这些值(value)观有意义吗?它们绘制正确吗?在搜索和声并找到基频之前,我是否需要对数据做些什么?

我考虑过应用某种窗口,因为我怀疑泄漏可能是绘图中确实具有的峰值彼此不谐波的原因。

如有任何帮助或建议,我们将不胜感激。 预先感谢您的帮助!

编辑: 作为对建议的尝试:

 ByteBuffer buf = ByteBuffer.wrap(audioBytes);
         buf.order(ByteOrder.LITTLE_ENDIAN);
         double[] audio = new double[audioBytes.length/2];  


         for(int i = 0; i < audioBytes.length/2; i++) {
             short s = buf.getShort();
             double mono = (double) s;
             double mono_norm = mono / 32768.0;

             audio[i]=mono_norm;


         }

现在应该在数组audio[]中保存一个 channel 的pcm数据。

最佳答案

一些一般提示:

你说你试图估计一个口语词的基本频率。一个“单词”由几个辅音和元音(或更好的 phonemes )组成。每个“元音”都有不同的基频,在大多数情况下,频率甚至会在一个元音内发生变化(这会产生我们句子的“旋律”)。 Thius 意味着您应该估计语音的一个非常短的间隔的基频/音调,并确保您正在查看元音(辅音是某种形式的噪声并且具有循环分量)。

所以第一步应该是生成你的单词的频谱图。

然后您可以计算感兴趣部分的短期 FFT,并进行调和求和。

不过,使用短期自相关函数会得到更好的结果。

其他要研究的东西:音调检测、倒谱

关于Java - 估计基频的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17746350/

相关文章:

java - 如何强制派生类调用 super 方法? (就像安卓一样)

java - 使用其他 PC 时出现 SSL 验证程序异常

java - 将由空格分隔的文件中的值放入多维数组

android - 如何解释 FFT 的输出并提取频率信息

c++ - 在 FFT 之后在频谱中定位频率

c++ - 提高 FFT 执行速度

java - 从 Java 中的一系列日期中获取所有连续间隔的列表

r - 在生态记录中查找和计数音频丢失

java - 将FFT应用于java中的录音

java - 从 wav 文件中提取频率