Generating colors of noise in Java

4.3k views Asked by At

I would like to create a colored noise generator using Java that will be able to generate all of the colors defined in this article: http://en.wikipedia.org/wiki/Colors_of_noise

  1. Starting with the simplest one, White Noise, how would I generate the noise so that it can play indefinitely?
  2. From there, how would I modify my generator to generate any of the colors?

I am both confused about how to generate the noise itself, and confused about how once generated I can have it be output through the speakers.

Any links or tips would be very appreciated!

I've also looked at another question: Java generating sound

But I don't fully understand what is going on in the code given in one of the comments. It also doesn't tell me what noise would be generated with that code, and so I wouldn't know how to modify it so that it would generate white noise.

3

There are 3 answers

1
Michael Goldstein On

I'm actually currently working on a project for taking white noise and sampling it to produce random numbers. What you need is the reverse!

Sound is pressure vs time. Basically start with 0 pressure and add a random amount of pressure from -(max amplitude) to (max amplitude). The amplitude of white noise is random and normally distributed so you can use Random.nextGaussian() to generate random z-scores. Multiply the z-scores by the standard deviation (you may have to do some testing to find a standard deviation in the amplitude you like) and then let that be the amplitude for each sample in the audio file.

As far as generating the sound file itself, if you haven't already, you should look into Java Sound API. It features a lot of nice methods for both creating sound files as well as playback.

The next part of your question, the non-white noise, I'm afraid I'm not sure on what the waveforms look like. It probably follows the similar generate random z-scores and multiply them by some amplitude standard deviation (or more likely by some amplitude function that changes with time).

2
izilotti On

Here is a program to generate white noise in pure Java. It can be easily changed to generate other colors of noise.

import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.ByteBuffer;
import java.util.Random;

public class WhiteNoise extends JFrame {

    private GeneratorThread generatorThread;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    WhiteNoise frame = new WhiteNoise();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public WhiteNoise() {
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                generatorThread.exit();
                System.exit(0);
            }
        });

        setTitle("White Noise Generator");
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 200, 50);
        setLocationRelativeTo(null);
        getContentPane().setLayout(new BorderLayout(0, 0));
        generatorThread = new GeneratorThread();
        generatorThread.start();
    }

    class GeneratorThread extends Thread {

        final static public int SAMPLE_SIZE = 2;
        final static public int PACKET_SIZE = 5000;

        SourceDataLine line;
        public boolean exitExecution = false;

        public void run() {

            try {
                AudioFormat format = new AudioFormat(44100, 16, 1, true, true);
                DataLine.Info info = new DataLine.Info(SourceDataLine.class, format, PACKET_SIZE * 2);

                if (!AudioSystem.isLineSupported(info)) {
                    throw new LineUnavailableException();
                }

                line = (SourceDataLine)AudioSystem.getLine(info);
                line.open(format);
                line.start();
            } catch (LineUnavailableException e) {
                e.printStackTrace();
                System.exit(-1);
            }

            ByteBuffer buffer = ByteBuffer.allocate(PACKET_SIZE);

            Random random = new Random();
            while (exitExecution == false) {
                buffer.clear();
                for (int i=0; i < PACKET_SIZE /SAMPLE_SIZE; i++) {
                    buffer.putShort((short) (random.nextGaussian() * Short.MAX_VALUE));
                }
                line.write(buffer.array(), 0, buffer.position());
            }

            line.drain();
            line.close();
        }

        public void exit() {
            exitExecution =true;
        }
    }
}
0
ggorlen On

I'm not an audio engineer, so I can't vouch that all of the code below makes sense, is accurate from an acoustical perspective or is efficient, only that it sounds reasonable to my ears. I'm simply gluing together others' code taken at face value, possible warts and all, so assume this isn't production-ready. I welcome feedback and fixes!


For white noise, here's a simplified version of the code in this answer elsewhere in this thread that does away with the unnecessary GUI stuff:

import java.nio.ByteBuffer;
import java.util.Random;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

public class WhiteNoise {
    public static void main(String[] args) {
        final int SAMPLE_RATE = 44100;
        final int BITS = 16;
        final int CHANNELS = 1;
        final int SAMPLE_SIZE = 2;
        final int PACKET_SIZE = 5000;
        AudioFormat format = new AudioFormat(
            SAMPLE_RATE,
            BITS,
            CHANNELS,
            true, // signed
            true  // big endian
        );
        DataLine.Info info = new DataLine.Info(
            SourceDataLine.class,
            format,
            PACKET_SIZE * 2
        );
        SourceDataLine line;

        try {
            line = (SourceDataLine)AudioSystem.getLine(info);
            line.open(format);
        }
        catch (LineUnavailableException e) {
            e.printStackTrace();
            return;
        }

        line.start();
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            //line.drain(); // seems to hang my Windows machine
            line.close();
        }));
        ByteBuffer buffer = ByteBuffer.allocate(PACKET_SIZE);
        Random random = new Random();

        for (;;) {
            buffer.clear();

            for (int i = 0; i < PACKET_SIZE / SAMPLE_SIZE; i++) {
                buffer.putShort((short)(random.nextGaussian() * Short.MAX_VALUE));
            }

            line.write(buffer.array(), 0, buffer.position());
        }
    }
}

Now, we can change the color of the noise using a variety of techniques, such as adapting the JavaScript code from How to Generate Noise with the Web Audio API to Java. All of the boilerplate code above is the same; this just changes the code around the for (;;) {...} block.

Pink:

// ...
        double b0 = 0.0;
        double b1 = 0.0;
        double b2 = 0.0;
        double b3 = 0.0;
        double b4 = 0.0;
        double b5 = 0.0;
        double b6 = 0.0;        

        for (;;) {
            buffer.clear();

            for (int i = 0; i < PACKET_SIZE / SAMPLE_SIZE; i++) {
                double white = random.nextGaussian();
                b0 = 0.99886 * b0 + white * 0.0555179;
                b1 = 0.99332 * b1 + white * 0.0750759;
                b2 = 0.96900 * b2 + white * 0.1538520;
                b3 = 0.86650 * b3 + white * 0.3104856;
                b4 = 0.55000 * b4 + white * 0.5329522;
                b5 = -0.7616 * b5 - white * 0.0168980;
                double output = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
                output *= 0.05; // (roughly) compensate for gain
                b6 = white * 0.115926;
                buffer.putShort((short)(output * Short.MAX_VALUE));
            }

            line.write(buffer.array(), 0, buffer.position());
        }
// ...

Brownian:

// ...
        double lastOut = 0.0;

        for (;;) {
            buffer.clear();

            for (int i = 0; i < PACKET_SIZE / SAMPLE_SIZE; i++) {
                double white = random.nextGaussian();
                double output = (lastOut + (0.02 * white)) / 1.02;
                lastOut = output;
                output *= 1.5; // (roughly) compensate for gain
                buffer.putShort((short)(output * Short.MAX_VALUE));
            }

            line.write(buffer.array(), 0, buffer.position());
        }
// ...

Elsewhere in the thread, Mars shared PinkNoise.java, so I might as well put it in the answer as an alternative approach for posterity. One suggestion among many they offer is swapping random.nextGaussian() for random.nextDouble() - 0.5 to improve performance.

Another possible optimization at the expense of randomness and "acoustical correctness" is pre-generating a bunch of random buffers, then randomly picking from them or cycling through them. This might be sufficiently accurate-sounding for many use cases.

Lastly, the while loop is probably doing more work in the above examples than it needs to do. Generating an Audio Sine Wave with Java shows code that uses Thread.sleep to throttle based on line buffer availability. Naively adding a Thread.sleep(20) into the loop dropped CPU usage for the process massively without any noticable audio dropout, but I'll leave it out of the main code for now.