4

I'm trying to write a very simple sound synth in Java. I'm using the javax.sound.sampled package. The code below works, but the sine wave is very noisy and sounds like there's some kind of quiet warm noise played alongside the wave.

try {
    double sampleRate = 44100;
    //8 bits per sample, so a byte.
    AudioFormat audioFormat = new AudioFormat((float) sampleRate, 8, 1, true, false);
    SourceDataLine line = AudioSystem.getSourceDataLine(audioFormat);

    line.open(audioFormat);
    line.start();

    //A4
    double freq = 440.0;

    byte[] buf = new byte[1];

    //the formula for a sample is amplitude * sin(2.0 * PI * freq * time)
    for (int i = 0; i < sampleRate; i++) {
        double t = (i / (sampleRate - 1));
        double sample = 0.1 * Math.sin(2.0 * Math.PI * freq * t);

        //scaling the sound from -1, 1 to -127, 127
        buf[0] = (byte) (sample * (double) Byte.MAX_VALUE);

        line.write(buf, 0, 1);
    }

    line.drain();
    line.stop();
    line.close();
} catch (Exception e) {
    throw new RuntimeException(e);
}

I put the generated sound into an EQ, to verify that the sound was actually noisy, and sure enough:

this is what I got

The dominant frequency is 440 hz, but there are some other frequencies which shouldn't be present. Why is this happening? How do I fix it?

Samuel Philipp
  • 10,631
  • 12
  • 36
  • 56
abababab
  • 53
  • 5
  • Interesting. I've worked with sound output a bit in Java. Can you print to console the result of the calculation going into the sample variable? I'd be willing to guess this is a floating point precision problem, as your result will be an approximation. – Scuba Steve Mar 06 '19 at 20:46
  • Looking at your code, could the sample size be the problem? You're losing precision by putting that sample into 8 bits. – Scuba Steve Mar 06 '19 at 20:49
  • Is there a reason you're multiplying by `0.1`? What happens if you get rid of the multiplication by `0.1` and then feed that signal into your EQ? – Luke Woodward Mar 06 '19 at 20:53
  • Here are the first 100 samples https://pastebin.com/tMCK5MUH – abababab Mar 06 '19 at 20:54
  • I'm multiplying by 0.1 so that the amplitude isn't too high. If I don't multiply the same thing happens, the signal just gets louder. – abababab Mar 06 '19 at 20:56
  • First off, I agree with Luke that you're adjusting amplitude in a weird way. You're multiplying the Sin function by 0.1, then multiplying it again by Byte.MaxValue. Second of all, you're storing a 64 bit floating point number in 8 bits, so that's going to cause you to lose a ton of precision if not do even weirder stuff (I dont recall what happens when you typecast a double to a byte in Java). – Scuba Steve Mar 06 '19 at 20:59
  • Make your sample size 64 bits, and try this: http://bethecoder.com/applications/articles/java/basics/how-to-convert-double-to-byte-array.html You'll also need to modify how you write to the source data line as well. – Scuba Steve Mar 06 '19 at 21:05

1 Answers1

5

Here's your sine wave:

Jagged sine wave

It's very jagged because you're using a low bit depth combined with a low amplitude. You only have 25 different sample values to choose from.

Here's instead your sine wave if you set your amplitude to 1.0, using the full range of your 8bit sample:

Smooth sine wave

And here's keeping the amplitude at 0.1, but using 16bit samples instead:

Equally smooth sine wave

Both of these options will clearly be less noisy.

that other guy
  • 116,971
  • 11
  • 170
  • 194