1

I have the following class which generates a buffer containing sound data:

package musicbox.example;

import javax.sound.sampled.LineUnavailableException;

import musicbox.engine.SoundPlayer;

public class CChordTest {

    private static final int SAMPLE_RATE = 1024 * 64;
    private static final double PI2 = 2 * Math.PI;

    /*
     * Note frequencies in Hz.
     */
    private static final double C4 = 261.626;
    private static final double E4 = 329.628;
    private static final double G4 = 391.995;

    /**
     * Returns buffer containing audio information representing the C chord
     * played for the specified duration.
     * 
     * @param duration The duration in milliseconds.
     * @return Array of bytes representing the audio information.
     */
    private static byte[] generateSoundBuffer(int duration) {

        double durationInSeconds = duration / 1000.0;
        int samples = (int) durationInSeconds * SAMPLE_RATE;

        byte[] out = new byte[samples];

        for (int i = 0; i < samples; i++) {
            double value = 0.0;
            double t = (i * durationInSeconds) / samples;
            value += Math.sin(t * C4 * PI2); // C note
            value += Math.sin(t * E4 * PI2); // E note
            value += Math.sin(t * G4 * PI2); // G note
            out[i] = (byte) (value * Byte.MAX_VALUE);
        }

        return out;
    }

    public static void main(String... args) throws LineUnavailableException {
        SoundPlayer player = new SoundPlayer(SAMPLE_RATE);
        player.play(generateSoundBuffer(1000));
    }

}

Perhaps I'm misunderstanding some physics or math here, but it seems like each sinusoid ought to represent the sound of each note (C, E, and G), and by summing the three sinusoids, I should hear something similar to when I play those three notes simultaneously on the keyboard. What I'm hearing, however, is not even close to that.

For what it's worth, if I comment out any two of the sinusoids and keep the third, I do hear the (correct) note corresponding to that sinusoid.

Can somebody spot what I'm doing wrong?

double-beep
  • 5,031
  • 17
  • 33
  • 41
Deomachus
  • 179
  • 1
  • 10

2 Answers2

2

To combine audio signals you need to average their samples, not sum them.

Divide the value by 3 before converting to byte.

Amit
  • 45,440
  • 9
  • 78
  • 110
1

You don't say in what way it sounds incorrect, adding three sin values like that you are going to get a signal that ranges from -3.0 to 3.0 and so is going to clip when you apply your *Byte.MAX_VALUE, this is why averaging probable worked for you, adding is correct its just you need to scale the result after to prevent clipping and dividing by the number of sine waves is the easiest way to do this. But if you start changing the number of sine waves dynamically and try to use the same strategy you wont get the result you expect, you have to scale the signal for when you signal is at its loudest. Remember real audio is not going to be at maximum amplitude so you don't have to worry about it two much if you synthesised audio isn't, also, the way we perceive sound volume is logarithmic so a signal at half amplitude is a difference of -3dB which is pretty close to the smallest change in amplitude we can hear.

Nathan Day
  • 5,981
  • 2
  • 24
  • 40