Mercurial > jmeCapture
view src/com/aurellem/capture/StdAudio.java @ 4:edaa7e7806e4
migrated IsoTimer
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 12:03:01 -0700 |
parents | a92de00f0414 |
children |
line wrap: on
line source
1 package com.aurellem.capture;4 /*************************************************************************5 * Compilation: javac StdAudio.java6 * Execution: java StdAudio7 *8 * Simple library for reading, writing, and manipulating .wav files.10 *11 * Limitations12 * -----------13 * - Does not seem to work properly when reading .wav files from a .jar file.14 * - Assumes the audio is monaural, with sampling rate of 44,100.15 *16 *************************************************************************/18 import java.applet.Applet;19 import java.applet.AudioClip;20 import java.io.ByteArrayInputStream;21 import java.io.File;22 import java.net.MalformedURLException;23 import java.net.URL;25 import javax.sound.sampled.AudioFileFormat;26 import javax.sound.sampled.AudioFormat;27 import javax.sound.sampled.AudioInputStream;28 import javax.sound.sampled.AudioSystem;29 import javax.sound.sampled.SourceDataLine;31 /**32 * <i>Standard audio</i>. This class provides a basic capability for33 * creating, reading, and saving audio.34 * <p>35 * The audio format uses a sampling rate of 44,100 (CD quality audio), 16-bit, monaural.36 *37 * <p>38 * For additional documentation, see <a href="http://introcs.cs.princeton.edu/15inout">Section 1.5</a> of39 * <i>Introduction to Programming in Java: An Interdisciplinary Approach</i> by Robert Sedgewick and Kevin Wayne.40 */41 public final class StdAudio {43 /**44 * The sample rate - 44,100 Hz for CD quality audio.45 */46 public static final int SAMPLE_RATE = 44100;48 //private static final int BYTES_PER_SAMPLE = 2; // 16-bit audio49 //private static final int BITS_PER_SAMPLE = 16; // 16-bit audio50 private static final double MAX_16_BIT = Short.MAX_VALUE; // 32,76751 //private static final int SAMPLE_BUFFER_SIZE = 4096;54 private static SourceDataLine line; // to play the sound55 private static byte[] buffer; // our internal buffer56 private static int bufferSize = 0; // number of samples currently in internal buffer58 // not-instantiable59 private StdAudio() { }62 // static initializer63 //static { init(); }65 // open up an audio stream67 /*68 private static void init() {69 try {70 // 44,100 samples per second, 16-bit audio, mono, signed PCM, little Endian71 AudioFormat format = new AudioFormat((float) SAMPLE_RATE, BITS_PER_SAMPLE, 1, true, false);72 DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);74 line = (SourceDataLine) AudioSystem.getLine(info);76 // RLM: set to 1 and see what happens!77 line.open(format, SAMPLE_BUFFER_SIZE);78 //line.open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE);80 // the internal buffer is a fraction of the actual buffer size, this choice is arbitrary81 // it gets divided because we can't expect the buffered data to line up exactly with when82 // the sound card decides to push out its samples.83 buffer = new byte[SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE/3];84 } catch (Exception e) {85 System.out.println(e.getMessage());86 System.exit(1);87 }89 // no sound gets made before this call90 line.start();91 }92 */95 /**96 * Close standard audio.97 */98 public static void close() {99 line.drain();100 line.stop();101 }103 /**104 * Write one sample (between -1.0 and +1.0) to standard audio. If the sample105 * is outside the range, it will be clipped.106 */107 public static void play(double in) {109 // clip if outside [-1, +1]110 if (in < -1.0) in = -1.0;111 if (in > +1.0) in = +1.0;113 // convert to bytes114 short s = (short) (MAX_16_BIT * in);115 buffer[bufferSize++] = (byte) s;116 buffer[bufferSize++] = (byte) (s >> 8); // little Endian118 // send to sound card if buffer is full119 if (bufferSize >= buffer.length) {120 line.write(buffer, 0, buffer.length);121 bufferSize = 0;122 }123 }125 /**126 * Write an array of samples (between -1.0 and +1.0) to standard audio. If a sample127 * is outside the range, it will be clipped.128 */129 public static void play(double[] input) {130 for (int i = 0; i < input.length; i++) {131 play(input[i]);132 }133 }135 /**136 * Read audio samples from a file (in .wav or .au format) and return them as a double array137 * with values between -1.0 and +1.0.138 */139 public static double[] read(String filename) {140 byte[] data = readByte(filename);141 int N = data.length;142 double[] d = new double[N/2];143 for (int i = 0; i < N/2; i++) {144 d[i] = ((short) (((data[2*i+1] & 0xFF) << 8) + (data[2*i] & 0xFF))) / ((double) MAX_16_BIT);145 }146 return d;147 }152 /**153 * Play a sound file (in .wav or .au format) in a background thread.154 */155 public static void play(String filename) {156 URL url = null;157 try {158 File file = new File(filename);159 if (file.canRead()) url = file.toURI().toURL();160 }161 catch (MalformedURLException e) { e.printStackTrace(); }162 // URL url = StdAudio.class.getResource(filename);163 if (url == null) throw new RuntimeException("audio " + filename + " not found");164 AudioClip clip = Applet.newAudioClip(url);165 clip.play();166 }168 /**169 * Loop a sound file (in .wav or .au format) in a background thread.170 */171 public static void loop(String filename) {172 URL url = null;173 try {174 File file = new File(filename);175 if (file.canRead()) url = file.toURI().toURL();176 }177 catch (MalformedURLException e) { e.printStackTrace(); }178 // URL url = StdAudio.class.getResource(filename);179 if (url == null) throw new RuntimeException("audio " + filename + " not found");180 AudioClip clip = Applet.newAudioClip(url);181 clip.loop();182 }185 // return data as a byte array186 private static byte[] readByte(String filename) {187 byte[] data = null;188 AudioInputStream ais = null;189 try {190 URL url = StdAudio.class.getResource(filename);191 ais = AudioSystem.getAudioInputStream(url);192 data = new byte[ais.available()];193 ais.read(data);194 }195 catch (Exception e) {196 System.out.println(e.getMessage());197 throw new RuntimeException("Could not read " + filename);198 }200 return data;201 }205 /**206 * Save the double array as a sound file (using .wav or .au format).207 */208 public static void save(String filename, double[] input) {210 // assumes 44,100 samples per second211 // use 16-bit audio, mono, signed PCM, little Endian212 AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, 1, true, false);213 byte[] data = new byte[2 * input.length];214 for (int i = 0; i < input.length; i++) {215 int temp = (short) (input[i] * MAX_16_BIT);216 data[2*i + 0] = (byte) temp;217 data[2*i + 1] = (byte) (temp >> 8);218 }220 // now save the file221 try {222 ByteArrayInputStream bais = new ByteArrayInputStream(data);223 AudioInputStream ais = new AudioInputStream(bais, format, input.length);224 if (filename.endsWith(".wav") || filename.endsWith(".WAV")) {225 AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File(filename));226 }227 else if (filename.endsWith(".au") || filename.endsWith(".AU")) {228 AudioSystem.write(ais, AudioFileFormat.Type.AU, new File(filename));229 }230 else {231 throw new RuntimeException("File format not supported: " + filename);232 }233 }234 catch (Exception e) {235 System.out.println(e);236 System.exit(1);237 }238 }240 public static void save(String filename, byte[] data){241 // now save the file242 AudioFormat format = new AudioFormat(SAMPLE_RATE, 32, 1, true, false);244 try {245 ByteArrayInputStream bais = new ByteArrayInputStream(data);246 AudioInputStream ais = new AudioInputStream(bais, format, data.length/2);247 if (filename.endsWith(".wav") || filename.endsWith(".WAV")) {248 AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File(filename));249 }250 else if (filename.endsWith(".au") || filename.endsWith(".AU")) {251 AudioSystem.write(ais, AudioFileFormat.Type.AU, new File(filename));252 }253 else {254 throw new RuntimeException("File format not supported: " + filename);255 }256 }257 catch (Exception e) {258 System.out.println(e);259 System.exit(1);260 }261 }263 /*264 public static void save(String filename, Byte[] data){265 // now save the file266 save(filename, ArrayUtils.toPrimitive(data));268 }269 */271 /***********************************************************************272 * sample test client273 ***********************************************************************/275 // create a note (sine wave) of the given frequency (Hz), for the given276 // duration (seconds) scaled to the given volume (amplitude)277 private static double[] note(double hz, double duration, double amplitude) {278 int N = (int) (StdAudio.SAMPLE_RATE * duration);279 double[] a = new double[N+1];280 for (int i = 0; i <= N; i++)281 a[i] = amplitude * Math.sin(2 * Math.PI * i * hz / StdAudio.SAMPLE_RATE);282 return a;283 }285 /**286 * Test client - play an A major scale to standard audio.287 */288 public static void main(String[] args) {290 // 440 Hz for 1 sec291 double freq = 440.0;292 for (int i = 0; i <= StdAudio.SAMPLE_RATE; i++) {293 StdAudio.play(0.5 * Math.sin(2*Math.PI * freq * i / StdAudio.SAMPLE_RATE));294 }296 // scale increments297 int[] steps = { 0, 2, 4, 5, 7, 9, 11, 12 };298 for (int i = 0; i < steps.length; i++) {299 double hz = 440.0 * Math.pow(2, steps[i] / 12.0);300 StdAudio.play(note(hz, 1.0, 0.5));301 }304 // need to call this in non-interactive stuff so the program doesn't terminate305 // until all the sound leaves the speaker.306 StdAudio.close();308 // need to terminate a Java program with sound309 System.exit(0);310 }311 }