绘制声音的音高(频率)

时间:2011-01-16 22:46:09

标签: java audio fft frequency pitch

我想将声音的音高绘制成图形。

目前我可以绘制振幅。下图是由getUnscaledAmplitude()

返回的数据创建的

alt text

AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream(file)));
byte[] bytes = new byte[(int) (audioInputStream.getFrameLength()) * (audioInputStream.getFormat().getFrameSize())];
audioInputStream.read(bytes);

// Get amplitude values for each audio channel in an array.
graphData = type.getUnscaledAmplitude(bytes, 1);


public int[][] getUnscaledAmplitude(byte[] eightBitByteArray, int nbChannels)
{
    int[][] toReturn = new int[nbChannels][eightBitByteArray.length / (2 * nbChannels)];
    int index = 0;

    for (int audioByte = 0; audioByte < eightBitByteArray.length;)
    {
        for (int channel = 0; channel < nbChannels; channel++)
        {
            // Do the byte to sample conversion.
            int low = (int) eightBitByteArray[audioByte];
            audioByte++;
            int high = (int) eightBitByteArray[audioByte];
            audioByte++;
            int sample = (high << 8) + (low & 0x00ff);

            toReturn[channel][index] = sample;
        }
        index++;
    }

    return toReturn;
}

但我需要显示音频的音高,而不是幅度。 Fast Fourier transform似乎得到了音调,但它需要知道比我原始字节更多的变量,而且非常复杂和数学。

我有办法做到这一点吗?

3 个答案:

答案 0 :(得分:48)

频率(客观指标)与 pitch (主观数量)不同。一般来说,音高检测是一个非常棘手的问题。

假设您现在只想绘制频率响应的图表,除了使用FFT之外别无选择,因为 方法可以获得时域数据的频率响应。 (好吧,还有其他一些方法,比如离散余弦变换,但它们实现起来也很棘手,解释起来比较棘手)。

如果你正在努力实现FFT,请注意它实际上只是一种计算离散傅里叶变换(DFT)的有效算法;见http://en.wikipedia.org/wiki/Discrete_Fourier_transform。基本的DFT算法更容易(只有两个嵌套循环),但运行 lot 更慢(O(N ^ 2)而不是O(N log N))。

如果你想做一些比简单绘制频率内容更复杂的事情(比如音调检测或开窗(正如其他人建议的那样)),我担心你会学习数学的含义。

答案 1 :(得分:23)

快速傅里叶变换不需要知道您拥有的输入字节。不要被维基百科的文章吓到。 FFT算法将采用您的输入信号(使用常见的FFT算法,样本数量需要为2的幂,例如256,512,1024),并返回具有相同大小的复数向量。因为您的输入是真实的,而不是复杂的(假想部分设置为零),返回的矢量将是对称的。其中只有一半将包含数据。由于您不关心阶段,您可以简单地取复数的大小,即sqrt(a ^ 2 + b ^ 2)。只取一个复数的absoulte值也可能有效,在某些语言中,这相当于前面的表达式。

可以使用FFT的Java实现,例如:http://www.cs.princeton.edu/introcs/97data/FFT.java.html

伪代码看起来像:

Complex in[1024];
Complex out[1024];
Copy your signal into in
FFT(in, out)
for every member of out compute sqrt(a^2+b^2)
To find frequency with highest power scan for the maximum value in the first 512 points in out

输出将包含采样频率的零到一半之间的频率。

由于FFT假定重复信号,您可能需要在输入信号中应用window。但一开始不要担心这个。

您可以在网络上找到更多信息,例如:FFT for beginners

同样正如Oli所说,当存在多个频率时,感知音高更复杂phenomenon

答案 2 :(得分:2)

关于此问题several other questions stackoverflow。也许这些会有所帮助。

相反,您可以尝试找到Craig Lindley的Digital Audio with Java副本。我不认为它已经打印出来了,但是我桌上的副本有一个关于FFT的部分,还有一个吉他调谐器的示例应用。