【问题标题】:Graphing the pitch (frequency) of a sound绘制声音的音高(频率)
【发布时间】:2011-06-10 03:58:54
【问题描述】:

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

目前我可以绘制幅度。下图是由getUnscaledAmplitude()返回的数据创建的:

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 似乎得到了音调,但它需要知道比我拥有的原始字节更多的变量,并且非常复杂和数学。

有什么办法可以做到吗?

【问题讨论】:

  • 你想获取频域信息,但不想使用THE方法获取?
  • @Coronatus:对不起,这并不是要咄咄逼人,只是很感兴趣。您似乎拒绝将 FFT 作为一种方法,但没有真正详细说明您认为它的缺点......
  • 是的,它非常复杂且具有数学意义,但它正是您所需要的,如果您阅读 Wikipedia,您就会明白它为何如此重要。你的工作是让你的数据使用它。故事结束。
  • 如果有任何帮助,FFT 实际上只是实现 DFT (en.wikipedia.org/wiki/Discrete_Fourier_transform) 的一种有效方式,它更容易编写(但运行在 O(N^2) 而不是比 O(N log N))。
  • 您需要显示音高还是频谱?前者非常棘手,其准确性很大程度上取决于输入提供的内容。后者已经做了无数次,FFT 是要走的路。查看dspdimension.com/admin/dft-a-pied。不是关于 FFT,而是用简单的语言教你基础知识

标签: java audio fft frequency pitch


【解决方案1】:

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

有可用的 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

【讨论】:

  • 请注意,frequency(客观指标)与 pitch(主观数量)不同。一般来说,音高检测是一个非常棘手的问题。
  • 没错,但无论如何,获得功率谱可能是第一步。感知到的音高可能不是功率最大的频率,需要做更多的工作。
  • 为什么输出包含采样频率在零到一半之间的条目,而不是整个采样频率?
  • @Zen 这与奈奎斯特限制(en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem)有关。对于给定的采样率,您只能区分高达采样率一半的频率。通常,您希望通过在采样之前对其进行预过滤来确保正在处理的信号不包含更高的频率。一个极端的例子是,采样频率为正弦波的信号看起来像直流信号,因为您将在同一相位对其进行采样。这就是混叠,较高的频率会在直流频率 (0Hz) 处产生混叠。
【解决方案2】:

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

假设您现在只想绘制频率响应图,您别无选择,只能使用 FFT,因为它是 THE 方法来获得时域数据的频率响应。 (嗯,还有其他方法,例如离散余弦变换,但它们同样难以实现,而且更难以解释)。

如果您在实现 FFT 时遇到困难,请注意它实际上只是一种计算离散傅里叶变换 (DFT) 的有效算法;见http://en.wikipedia.org/wiki/Discrete_Fourier_transform。基本的 DFT 算法要​​简单得多(只有两个嵌套循环),但运行速度要慢 很多(O(N^2) 而不是 O(N log N))。

如果您想做比简单地绘制频率内容更复杂的事情(例如音高检测或开窗(如其他人建议的那样)),恐怕您将了解数学的含义。

【讨论】:

  • 我已经超出了我的每日投票限制,但如果允许,我会在 1 小时内投票。谢谢!
【解决方案3】:

stackoverflow上有severalotherquestions关于这个问题。也许这些会有所帮助。

相反,您可以尝试查找 Craig Lindley 的 Digital Audio with Java 副本。我认为它不再印刷了,但我桌上的副本有一个关于 FFT 的部分,还有一个吉他调音器的示例应用程序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-30
    • 2015-08-15
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 2023-03-14
    相关资源
    最近更新 更多