import javax.sound.sampled.*; import java.util.*; public class SoundTool { // Play the given sampled sound at the given rate. Good numbers to use for // samplesPerSecond are 400 (low quality, but easy to compute with) and // 16000 (high-quality, but long computations). public static void playSoundBytes (byte[] samples, int samplesPerSecond) { // Modified from code at www.jsresources.org // Make an AudioFormat instance that specifies: // sampleRate, #bits/sample=8, #channels=1 (mono), signed-PCM. AudioFormat audioFormat = new AudioFormat (samplesPerSecond, 8, 1, true, true); SourceDataLine line = null; DataLine.Info info = new DataLine.Info (SourceDataLine.class, audioFormat); try { line = (SourceDataLine) AudioSystem.getLine (info); line.open (audioFormat); } catch (LineUnavailableException e) { e.printStackTrace (); System.exit (1); } catch (Exception e) { e.printStackTrace(); System.exit (1); } line.start(); line.write (samples, 0, samples.length); line.drain(); line.close(); } // Create sampled sound at a desired frequency, such as 256Hz. // For example, use freq=440.0 to get the sound for middle-A // on the piano, the standard by which all notes are created // on musical instruments. public static byte[] makeSampledSound (double freq, int samplesPerSecond, int playTimeSeconds) { // Total number of samples: int numSamples = samplesPerSecond * playTimeSeconds; // Build the array of bytes (one byte per sample). byte[] samples = new byte [numSamples]; // The samples/second determines the #samples per cycle. int samplesPerCycle = (int) Math.floor (samplesPerSecond / freq); // We'll advance the angle by #samples/cycle: double delTheta = 2*Math.PI / samplesPerCycle; // Start at angle=0. double theta = 0; // Now build all the samples. for (int i=0; i 0) { b = (byte) Math.floor (y); } if (y < 0) { b = (byte) Math.ceil (y); } samples[i] = b; theta += delTheta; } return samples; } // Here, lowNotes and highNotes need to be strings like "CCEE" // (two C notes, followed by two E notes). The two strings must be the same length. // The method returns sampled sound bytes at the given # samples per note. public static void playMusic (String notes) { if (! areValid (notes)) { System.out.println ("SoundTool ERROR: invalid notes in " + notes); return; } byte[] samples = makeMusic (notes, notes.toUpperCase(), 4000); playSoundBytes (samples, 4000); } static boolean areValid (String notes) { for (int i=0; i lowNoteFrequencies, highNoteFrequencies; static double getFrequency (char note, boolean isLow) { if ((lowNoteFrequencies == null) || (highNoteFrequencies == null)) { lowNoteFrequencies = new HashMap (); lowNoteFrequencies.put ('C', 130.81); lowNoteFrequencies.put ('D', 146.83); lowNoteFrequencies.put ('E', 164.81); lowNoteFrequencies.put ('F', 174.61); lowNoteFrequencies.put ('G', 196.00); lowNoteFrequencies.put ('A', 220.00); lowNoteFrequencies.put ('B', 246.94); highNoteFrequencies = new HashMap (); highNoteFrequencies.put ('C', 261.63); highNoteFrequencies.put ('D', 293.66); highNoteFrequencies.put ('E', 329.63); highNoteFrequencies.put ('F', 349.23); highNoteFrequencies.put ('G', 392.00); highNoteFrequencies.put ('A', 440.00); highNoteFrequencies.put ('B', 493.88); } if (isLow) { return lowNoteFrequencies.get (note); } return highNoteFrequencies.get (note); } public static byte[] addSounds (byte[] signal1, byte[] signal2) { if (signal1.length != signal2.length) { System.out.println ("ERROR: SignalProcUtil.addSounds(): input arrays of different length"); return null; } double [] combinedInt = new double [signal1.length]; double max = Double.MIN_VALUE, min = Double.MAX_VALUE; for (int i=0; i max) { max = combinedInt[i]; } } // Get the larger of the two absolute values. max = Math.abs (max); min = Math.abs (min); if (min > max) { max = min; } // Now scale byte[] combined = new byte [combinedInt.length]; for (int i=0; i 126) { b = 126; } combined[i] = b; } return combined; } }