HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Playing pure sinusoidal tones

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
playingpuresinusoidaltones

Problem

I wrote a simple sound player to use as part of my MorseString class. I am new to using sounds (in fact, I was helped a lot by Google) and I don't know if this is the correct way to play a sound.

The method takes a specific frequency and a duration as parameters, and plays the frequency for the specified duration.

private static final int SAMPLING_FREQUENCY_IN_HZ = 8000;
private static final int NUMBER_OF_FADE_SAMPLES = 80;

private static void sound(int hz, int msecs) throws LineUnavailableException {
    byte[] buf = new byte[msecs * Byte.SIZE];
    for (int i = 0; i < buf.length; i++) {
        double angle = i / (SAMPLING_FREQUENCY_IN_HZ / hz) * 2 * Math.PI;
        buf[i] = (byte) (Math.sin(angle) * Byte.MAX_VALUE);
    }
    for (int i = 0; i < NUMBER_OF_FADE_SAMPLES && i < buf.length / 2; i++) {
        buf[i] = (byte) (buf[i] * i / NUMBER_OF_FADE_SAMPLES);
        buf[buf.length - 1 - i] = (byte) (buf[buf.length - 1 - i] * i / NUMBER_OF_FADE_SAMPLES);
    }
    AudioFormat af = new AudioFormat(SAMPLING_FREQUENCY_IN_HZ, Byte.SIZE, 1, true, false);
    SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
    sdl.open(af);
    sdl.start();
    sdl.write(buf, 0, buf.length);
    sdl.drain();
    sdl.close();
}


  • The sound sounds like it is low-quality. Is there a way to improve that?



  • Did I do my math wrong?



Please explain in as much detail as possible, as I'm new and might not understand.

Solution

In a sense, your math is wrong. In

double angle = i / (SAMPLING_FREQUENCY_IN_HZ / hz) * 2 * Math.PI;


… both of the divisions are done using integer arithmetic. As a result, I get nothing more than a clicking noise. To get the intended signal, you have to cast i to a double. Better yet, write it instead as

double angle = 2 * Math.PI * i * hz / SAMPLING_FREQUENCY_IN_HZ;


By the Nyquist criterion, a sample rate of 8000 Hz means that you can play a frequency no higher than 4000 Hz. Therefore, for robustness, you should do one of three things:

  • Throw an exception if hz >= SAMPLING_FREQUENCY_IN_HZ / 2.



  • Let the sampling frequency vary, by setting it to 16 * hz, for example.



  • Set SAMPLING_FREQUENCY_IN_HZ high enough so that it covers the range of human hearing (44.1 kHz for CD audio, 48 kHz for DVD video soundtracks).



It would also be wise to define the fade-in / fade-out period as a multiple of the sampling rate.

Code Snippets

double angle = i / (SAMPLING_FREQUENCY_IN_HZ / hz) * 2 * Math.PI;
double angle = 2 * Math.PI * i * hz / SAMPLING_FREQUENCY_IN_HZ;

Context

StackExchange Code Review Q#75438, answer score: 5

Revisions (0)

No revisions yet.