Java beep sound: Produce sound of some specific frequencies
Asked Answered
O

2

18

I was experimenting with producing beep using Java. I found this answer on SO.

I am using the code from that answer to produce beeps. The code is:

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); 
    }
}

In the main method, I use Sound.tone(15000,1000); to produce a sound of frequency 15000 Hz to play for 1000 ms

But, I can hear the sound if I change it to :

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

Scientifically, this is not possible.

  • In the first case, the sound should be infrasonic, and I should not be able to perceive it.
  • In the second case, I should still not be able to hear the sound, because as we age, the hearing ability tends to decrease, and a person of about my age should only be able to hear a sound of approximately 16000 Hz.

Furthermore, I cannot hear:

  • Sound.tone(0,1000); (somewhat as expected)
  • Sound.tone(20000,1000);

So, How can I produce sounds of some specific frequencies?
I searched on the internet, but could not find anything regarding it.

The answers given before this edit explain why it occurs, but do not give the answer I want.

Obvolute answered 5/1, 2016 at 11:54 Comment(18)
You can still listen the sound but you can't differentiate the frequency with human eye when they are very minute :)Lille
But I certainly can't hear infrasonic sound, which I apparently can according to this program.Obvolute
There's also the chance that computers simply aren't manufactured to produce infra-/ultrasoundHibbard
@SureshAtta I suggest using an ear instead of an eye.Exurbanite
@Hibbard I guess you misunderstood my question. If computers aren't manufactured to produce infra-/ultrasound, then how am I able to hear it using this program. I mean to say that I can hear a sound if I write Sound.tone(10,1000);, which should have been infrasonic.Obvolute
Yes, so since computer aren't meant to produce such a frequency maybe they just don't. I was suggesting that maybe the frequency isn't always accurate. It just uses the nearest valid frequency.Hibbard
It'd be helpful to know what device you are using to play the sound (and the rated frequency). Also you cannot really take a decrease in your hearing for granted if it's only based on your age, there are vast differences between people of the same age when it comes to the variety of frequencies they're able to perceive. Also, take a look at this, maybe it helps: electronics.stackexchange.com/questions/156197/…Ephraim
It is possible that your hardware can't produce frequencies so low or high and doesn't throw any exception, instead it try to play as much closer as possible to the values you are passing to your hardware. Those values could be human udible.Lamellate
@Exurbanite lol that's ear. :PLille
@DavideLorenzoMARINO I can hear Sound.tone(1,1000);, and Sound.tone(19999,1000);. I cannot hear Sound.tone(0,1000);(as expected) and also Sound.tone(20000,1000);Obvolute
So I imagine that your hardware is not fine enough and that the result tones are not right. Sound.tone(0, 1000) is reasonable that is not playing nothing and that you can hear something for Sound.tone(1, 1000), instead what is strange is that you can hear Sound.tone(19999, 1000) and not Sound.tone(20000, 1000).Lamellate
@DavideLorenzoMARINO, actually I shouldn't be able to hear sounds of frequency below 20 Hz. So even Sound.tone(1, 1000) is abnormal.Obvolute
Has anybody tried it on his computer?Obvolute
How about using a microphone and doing a frequency analysis on the recorded sound. If you have a smartphone at hand, there should be an app available in your favorite appstore.Bracteole
@PriydarshiSingh Most audio devices have 20Hz set as their minimum frequency, so I'd guess that the computer accepts the lowest frequency possible if it'd be impossible otherwhise.Ephraim
I'm wondering if you are getting some weirdness due to integer division. The first three terms are all integers and are processed before the doubles. Also, you should just process the invariants one time, outside of the loop, instead of recalculating each iteration. Make a constant = hz / (samplerate * 2 * PI) and multiply that against i. The comment about Nyquist and sampling rate tell why you can't expect to generate sounds above half the sample rate. If you can hear something with a frequency setting below 20, the code is definitely buggy.Interventionist
@PriydarshiSingh why did you unaccept?Duplicity
Mainly because that did not actually solve my problem.Obvolute
T
11

What you are experiencing is a phenomenon known as aliasing. You may have witnessed examples of this in video where a spinning wheel appears to rotate slowly or even rotate in reverse. This is because the wheel has rotated only slightly more or less than a multiple of a complete number of turns per video frame. Likewise if the wheel rotates an exact number of turns per frame it will appear stationary. The reason this is called aliasing is that there is no way to know how many turns it actually rotated.

Audio sampling has the same artifact and is referred to as the Nyquist sampling theorem which basically indicates that you can only represent frequencies up to half of the sampling rate (nyquist frequency). Going beyond this frequency the tones begin to fold back down (e.g. turn backwards).

At your 8kHz sample rate, frequencies from 0Hz to 4kHz will play back fine (a 4 kHz sine wave will have 2 samples per period). Beyond 4kHz the frequencies will begin folding back such that 4001Hz is heard as 3999, 5000Hz as 3000Hz and ultimately 8000Hz as 0Hz (hence the silence). Beyond 8kHz it will once again begin folding up so that 8001Hz is 1Hz and so on.

The key take away is that you need to choose a sample rate that is at least twice the highest frequency you plan to playback.

Tangled answered 5/1, 2016 at 14:32 Comment(4)
If I stay any longer in this thread, I might become a sound engineer!Contemptible
But making the sample rate 40000 still doesn't solve the problem. I can still hear below 20 Hz, and upto 19999 Hz.Obvolute
Your sine generation is a bit suspect since 8-bit audio is typically unsigned (0-255). You should listen to a tone at 1kHz and compare with youtube.com/watch?v=3yQcXlvccCI. If yours does not sound the same then you need to change this bit (byte)(Math.sin(angle) * 127.0 * vol); to (byte)((Math.sin(angle) * 127.0 + 127) * vol);Tangled
@Tangled changing it to (byte)((Math.sin(angle) * 127.0 + 127) * vol); doesn't work.Obvolute
D
7

Just rechecked with a guitar tuner in front of my speaker - the method you provided is accurate for low frequencies.

However, if you get too low you might hear overtones or "stuttering" sound due to the speakers not working too great in infrasound (at least I got that)

On high frequencies, your code does not compute too good - with a sample rate of 8000 samples per second you'll quickly create "invalid" tones for higher frequencies - the sine wave oscillation will just accidentally align to your sample rate. So you'll get some high frequencies you can hear (basically because your samples always hit some non zero value) or not hear (all your samples occur at times when the wave goes through null.

Check this loop (with your speaker on):

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

You'll hear low-high-low-high at the end for the reasons just described.

Another test:

  Sound.tone(8000,500);   

is absolutely silent, and

  Sound.tone(8001,500);

produces a very funky sound.

Duplicity answered 5/1, 2016 at 12:21 Comment(2)
That depends on what you want to do with this. For alerting users the range 20-3000 Hz is probably sufficient. For higher sample rates though the angle calculation seems invalid as well. would have to think this trough a bit more.Duplicity
How does one get overtones off of a sin wave, Jan? I don't see anything in the calculation that should cause the playback to start/stop (stutter). Even with the integer division, the progression of the angle looks like it will be smooth.Interventionist

© 2022 - 2024 — McMap. All rights reserved.