Sound generators and happy little accidents

Writing generators of all kinds is fun. Even if the generator is not randomized, you still have many parameters to tweak — and many unexpected outcomes.

Once I needed to generate WAV files with dummy content and certain bitrate and duration.
The StackOverflow example by Joshua Beckford turned out to be the closest to what I needed.

I played around with the frequencies and accidentally created a bunch of something resembling obscure electronic music. Here are six hand-picked “tracks”, named with the help of colleagues. Listen at your own risk.

01 – Intro

02 – Afraid To Dial A Number

03 – Tickling A Space Hamster

04 – Malfunctioning Zipper

05 – Enraged Chihuahua

06 – Rainy Vacuuming

For the actual usage we went with “Malfunctioning Zipper”. It doesn’t differ much from the original version, mostly deals with one frequency instead of two, but anyway — here’s its source, as a single executable class. If you have IntelliJ IDEA open, but don’t want to mess with new project, simply paste it into a Java scratch file (Ctrl-Alt-Shift-Insert) and run.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

// sound generation magic taken from https://stackoverflow.com/a/28934588/2807168
class WavGenerator {

    public static void main(String[] args) throws IOException {
        byte[] bytes = generate();
        // places generated file in your project root
        Files.write(Paths.get("./generated.wav"), bytes);
    }

    public static byte[] generate() throws IOException {
        double seconds = 5;

        // try tweaking these three values
        double sampleRate = 44100.0;
        double frequency = 3;
        double scaleFactor = 32767.0;

        double piF = Math.PI * frequency;

        float[] buffer = new float[(int) (seconds * sampleRate)];
        for (int sample = 0; sample < buffer.length; sample++) {
            double time = sample / sampleRate;
            buffer[sample] = (float) (Math.cos(piF * time));
        }

        final byte[] byteBuffer = new byte[buffer.length * 2];
        int bufferIndex = 0;
        for (int i = 0; i < byteBuffer.length; i++) {
            final int x = (int) (buffer[bufferIndex++] * scaleFactor);
            byteBuffer[i] = (byte) x;
            i++;
            byteBuffer[i] = (byte) (x >>> 8);
        }

        AudioFormat format = getAudioFormat((float) sampleRate);
        ByteArrayInputStream bais = new ByteArrayInputStream(byteBuffer);
        AudioInputStream ais = new AudioInputStream(bais, format, buffer.length);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        AudioSystem.write(ais, AudioFileFormat.Type.WAVE, baos);

        ais.close();

        return baos.toByteArray();
    }

    private static AudioFormat getAudioFormat(float sampleRate) {
        int bits = 8;
        int channels = 1;
        boolean signed = true;
        boolean bigEndian = false;
        return new AudioFormat(sampleRate, bits, channels, signed, bigEndian);
    }
}