CS 145 Lecture 26 – Sound
May 9, 2012 by Chris Johnson. Filed under cs145, lectures, spring 2012.
Agenda
- digital music
- WAV format
- binary file I/O
- generate static
- generate pitches
- generate chords
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.
show