Java蜂鸣声:产生某些特定频率的声音

2022-09-03 06:58:24

我正在尝试使用Java产生哔哔声。我在SO上找到了这个答案。

我正在使用该答案中的代码来产生哔哔声。代码是:

import javax.sound.sampled.*;
public class Sound
{
    public static float SAMPLE_RATE = 8000f;
    public static void tone(int hz, int msecs) 
    throws LineUnavailableException 
    {
        tone(hz, msecs, 1.0);
    }

    public static void tone(int hz, int msecs, double vol)
    throws LineUnavailableException 
    {
        byte[] buf = new byte[1];
        AudioFormat af = new AudioFormat(SAMPLE_RATE,8,1,true,false);     
        SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
        sdl.open(af);
        sdl.start();
        for (int i=0; i < msecs*8; i++) {
              double angle = i / (SAMPLE_RATE / hz) * 2.0 * Math.PI;
              buf[0] = (byte)(Math.sin(angle) * 127.0 * vol);
              sdl.write(buf,0,1);
        }
        sdl.drain();
        sdl.stop();
        sdl.close();
    }

    public static void main(String[] args) throws Exception {
        Sound.tone(15000,1000); 
    }
}

在该方法中,我用产生频率15000Hz的声音来播放1000msmainSound.tone(15000,1000);

但是,如果我将其更改为:,我可以听到声音:

  • Sound.tone(1,1000);, .
  • Sound.tone(19999,1000);

从科学上讲,这是不可能的。

  • 在第一种情况下,声音应该是次音速的,我不应该能够感知它。
  • 在第二种情况下,我仍然无法听到声音,因为随着年龄的增长,听力能力往往会下降,而我这个年龄的人应该只能听到大约16000 Hz的声音。

此外,我听不到:

  • Sound.tone(0,1000);(有点符合预期)
  • Sound.tone(20000,1000);

那么,如何产生某些特定频率的声音呢?
我在互联网上搜索,但找不到任何关于它的东西。

在此编辑之前给出的答案解释了为什么会发生,但没有给出我想要的答案。


答案 1

您正在经历的是一种称为混叠的现象。您可能已经在视频中目睹了这样的例子,其中旋转的轮子似乎旋转缓慢甚至反向旋转。这是因为滚轮旋转的次数仅略高于或小于每视频帧完整圈数的倍数。同样,如果车轮每帧旋转一个确切的转数,它将显得静止不动。这被称为混叠的原因是没有办法知道它实际旋转了多少圈。

音频采样具有相同的伪像,被称为奈奎斯特采样定理,它基本上表明您只能表示高达采样率(奈奎斯特频率)一半的频率。超过这个频率,音调开始向下折叠(例如,向后转动)。

在 8kHz 采样率下,0Hz 至 4kHz 的频率可以正常回放(4 kHz 正弦波每个周期将有 2 个样本)。超过4kHz,频率将开始折返,使得4001Hz被听到为3999,5000Hz为3000Hz,最终8000Hz为0Hz(因此沉默)。超过8kHz,它将再次开始折叠,使8001Hz成为1Hz,依此类推。

关键是您需要选择的采样率至少是计划播放的最高频率的两倍。


答案 2

刚刚在我的扬声器前用吉他调谐器重新检查了一下 - 您提供的方法对于低频是准确的。

但是,如果您太低,您可能会听到泛音或“口吃”的声音,因为扬声器在次声中工作得不太好(至少我得到了)

高频上,您的代码计算得不太好 - 采样率为每秒8000个样本,您将很快为更高频率创建“无效”音调 - 正弦波振荡将意外地与您的采样率对齐。因此,您将获得一些可以听到的高频(基本上是因为您的样本总是达到一些非零值)或听不到(所有样本都发生在波通过null时。

检查此循环(打开扬声器):

 public static void main(String[] args) throws Exception {
    for(int freq = 400; freq < 20000; freq *= 2) {
        Sound.tone(freq,500);
    }
 }

由于上述原因,您将在最后听到低-高-低-高。

另一个测试:

  Sound.tone(8000,500);   

是绝对沉默的,和

  Sound.tone(8001,500);

产生非常时髦的声音。


推荐