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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With