teaching machines

CS 145 Lecture 26 – Sound

May 9, 2012 by . Filed under cs145, lectures, spring 2012.

Agenda

Code

WavIO.java

import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class WavIO {
  /**
   * The sampling frequency.
   */
  public static final int SAMPLE_RATE = 22050;

  /**
   * Write the buffer containing audio samples to the specified file in WAV
   * format.
   *
   * @param buffer Audio samples.
   * @param path Path to destination WAV file.
   */
  public static void write(short[] buffer, String path) {
    try {
      LEDataOutputStream out = new LEDataOutputStream(path);
      out.writeBytes("RIFF"); // 0-3
      out.writeInt(36 + buffer.length * 2); // 4-7
      out.writeBytes("WAVE"); // 8-11
      out.writeBytes("fmt "); // 12-15
      out.writeInt(16); // chunk size, 16-19
      out.writeShort((short) 1); // pcm, 20-21
      out.writeShort((short) 1); // nchannels, 22-23
      out.writeInt(SAMPLE_RATE); // 24-27
      out.writeInt(SAMPLE_RATE * 2); // 28-31
      out.writeShort((short) 2); // 32-33
      out.writeShort((short) 16); // 34-35
      out.writeBytes("data"); // 36-39
      out.writeInt(buffer.length * 1 * 16 / 8); // 40-43
      out.write(buffer);
      out.close();
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }
}

/**
 * A class for writing primitives to a file in little endian format.
 */
class LEDataOutputStream {

  /** The underlying big endian file writer. */
  private DataOutputStream out;

  /** A collection of bytes used for an endian switcharoo. */
  private byte[] bytes;

  /** An endian switcher. */
  private ByteBuffer buffer;

  /**
   * Create a new little endian file writer for the specified file.
   *
   * @param file
   * Name of file to write.
   *
   * @throws FileNotFoundException
   */
  public LEDataOutputStream(String file) throws FileNotFoundException {
    out = new DataOutputStream(new FileOutputStream(file));
    bytes = new byte[4];
    buffer = ByteBuffer.wrap(bytes);
    buffer.order(ByteOrder.LITTLE_ENDIAN);
  }

  /**
   * Write the specified String as a series of bytes.
   *
   * @param s
   * The String to write.
   *
   * @throws IOException
   */
  public void writeBytes(String s) throws IOException {
    out.writeBytes(s);
  }

  /**
   * Write the specified int.
   *
   * @param value
   * Value to write.
   *
   * @throws IOException
   */
  public void writeInt(int value) throws IOException {
    buffer.putInt(0, value);
    out.write(bytes, 0, 4);
  }

  /**
   * Writer the specified short.
   *
   * @param value
   * Value to write.
   *
   * @throws IOException
   */
  public void writeShort(short value) throws IOException {
    buffer.putShort(0, value);
    out.write(bytes, 0, 2);
  }

  /**
   * Write the sequence of shorts. All values in the array are written in the
   * order in which they appear in the array.
   *
   * @param shorts
   * The sequence of values to write.
   *
   * @throws IOException
   */
  public void write(short[] shorts) throws IOException {
    for (int i = 0; i < shorts.length; ++i) {
      writeShort(shorts[i]);
    }
  }

  /**
   * Close the file.
   *
   * @throws IOException
   */
  public void close() throws IOException {
    out.close();
  }
}

Moosic.java

package prefinal;

import java.util.Random;

public class Moosic {
  public static void main(String[] args) {
    Random generator = new Random();

    short[] a = getNote(440.0, 2);
    short[] c = getNote(523.25, 2);
    
    short[] song = merge(a, c);
    song = merge(song, song);
    
    WavIO.write(song, "/home/user/Desktop/song.wav");
  }
  
  private static short[] merge(short[] a,
                               short[] b) {
    short[] merged = new short[a.length + b.length];
    System.arraycopy(a, 0, merged, 0, a.length);
    System.arraycopy(b, 0, merged, a.length, b.length);
    return merged;
  }
  
  private static short[] getNote(double frequency,
                                 double nseconds) {
    int nsamples = (int) (nseconds * WavIO.SAMPLE_RATE);
    
    short[] samples = new short[nsamples];
    
    for (int i = 0; i < samples.length; ++i) {
      double t = i / (double) WavIO.SAMPLE_RATE;
      samples[i] = (short) (Short.MAX_VALUE * Math.cos(frequency * t * 2 * Math.PI));
    }
    
    return samples;
  }
}

Haiku

At first, plain beeps please.
But folks start to expect more.
You’ll have to beep songs.