绘制声音的音高(频率)图
我想将声音的音调绘制成图表。
目前我可以绘制振幅。下图是由 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;
}
但我需要显示音频的音调,而不是幅度。 快速傅立叶变换似乎可以得到音调,但它需要知道比我拥有的原始字节更多的变量,并且非常复杂且数学化。
我有办法做到这一点吗?
I want to plot the pitch of a sound into a graph.
Currently I can plot the amplitude. The graph below is created by the data returned by 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;
}
But I need to show the audio's pitch, not amplitude. Fast Fourier transform appears to get the pitch, but it needs to know more variables than the raw bytes I have, and is very complex and mathematical.
Is there a way I can do this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
频率(客观指标)与音调(主观量)不同。一般来说,基音检测是一个非常棘手的问题。
假设您现在只想绘制频率响应图表,那么您别无选择,只能使用 FFT,因为它是获取时域数据频率响应的方法。 (嗯,还有其他方法,例如离散余弦变换,但它们实施起来同样棘手,解释起来也更棘手)。
如果您在 FFT 的实现上遇到困难,请注意,它实际上只是计算离散傅立叶变换 (DFT) 的有效算法;请参阅http://en.wikipedia.org/wiki/Discrete_Fourier_transform。基本的 DFT 算法要简单得多(只需两个嵌套循环),但运行速度要慢很多(O(N^2) 而不是 O(N log N))。
如果您希望做比简单地绘制频率内容更复杂的事情(例如音调检测或加窗(如其他人所建议的)),恐怕您将了解数学的含义。
Frequency (an objective metric) is not the same as pitch (a subjective quantity). In general, pitch detection is a very tricky problem.
Assuming you just want to graph the frequency response for now, you have little choice but to use the FFT, as it is THE method to obtain the frequency response of time-domain data. (Well, there are other methods, such as the discrete cosine transform, but they're just as tricky to implement, and more tricky to interpret).
If you're struggling with the implementation of the FFT, note that it's really just an efficient algorithm for calculating the discrete Fourier transform (DFT); see http://en.wikipedia.org/wiki/Discrete_Fourier_transform. The basic DFT algorithm is much easier (just two nested loops), but runs a lot slower (O(N^2) rather than O(N log N)).
If you wish to do anything more complex than simply plotting frequency content (like pitch detection, or windowing (as others have suggested)), I'm afraid you are going to have learn what the maths means.
快速傅里叶变换不需要知道更多的输入字节。不要被维基百科的文章吓跑。 FFT 算法将获取您的输入信号(使用常见的 FFT 算法,样本数需要为 2 的幂,例如 256、512、1024)并返回具有相同大小的复数向量。因为您的输入是实数,而不是复杂的,(虚部设置为零),返回的向量将是对称的。其中只有一半包含数据。由于您不关心相位,因此您可以简单地获取复数的大小,即 sqrt(a^2+b^2)。仅取复数的绝对值也可能有效,在某些语言中,这相当于前面的表达式。
有可用的 FFT Java 实现,例如: http://www.cs .princeton.edu/introcs/97data/FFT.java.html
伪代码将类似于:
输出将包含零到采样频率一半之间的频率的整体。
由于 FFT 假定重复信号,您可能需要对输入信号应用窗口。但一开始不用担心这个。
您可以在网上找到更多信息,例如:FFT 初学者
也正如 Oli 指出的那样存在多个频率时,感知的音高是一个更复杂的现象。
Fast Fourier Transform doesn't need to know more then the input bytes you have. Don't be scared off by the Wikipedia article. An FFT algorithm will take your input signal (with the common FFT algorithms the number of samples is required to be a power of 2, e.g. 256, 512, 1024) and return a vector of complex numbers with the same size. Because your input is real, not complex, (imaginary portion set to zero) the returned vector will be symmetric. Only half of it will contain data. Since you do not care about the phase you can simply take the magnitude of the complex numbers, which is sqrt(a^2+b^2). Just taking the absoulte value of a complex number may also work, in some languages this is equivalent to the previous expression.
There are Java implementations of FFT available, e.g.: http://www.cs.princeton.edu/introcs/97data/FFT.java.html
Pseudo code will look something like:
The output will contain entires for frequencies between zero and half your sampling frequency.
Since FFT assumes a repeating signal you may want to apply a window to your input signal. But don't worry about this at first.
You can find more information on the web, e.g.: FFT for beginners
Also as Oli notes when multiple frequencies are present the perceived pitch is a more complex phenomenon.
有几个 其他 问题< /a> 在 stackoverflow 上了解此问题。也许这些会有所帮助。
相反,您可以尝试查找 Craig 的 Digital Audio with Java 的副本林德利.我认为它不再印刷了,但我桌上的副本有一个关于 FFT 的部分,还有一个吉他调音器的示例应用程序。
There are several other questions on stackoverflow about this problem. Maybe these will help.
Instead, you could try to find a copy of Digital Audio with Java by Craig Lindley. I don't think it's in print anymore, but the copy on my desk has a section on the FFT and also a sample application of a guitar tuner.