import javax.sound.sampled.*; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.FileOutputStream; import java.io.FileInputStream; import java.io.Serializable; class MatInf1100Sound implements Serializable { public static final int SAMPLE_SOUND_LENGTH = 88200; private static SourceDataLine defaultLine; public short [] samples; public double sampleRate; MatInf1100Sound() {} MatInf1100Sound(short [] samples, double sampleRate) { this.samples = samples; this.sampleRate = sampleRate; } public void play() throws Exception { play(Integer.MAX_VALUE/2); } public void play(int samplesToPlay) throws Exception { if(defaultLine == null) makeDefaultLine(); // play(defaultLine, Math.min(samplesToPlay, (int)Math.round(samples.length * 44100 / sampleRate))); play(defaultLine, samplesToPlay); } // for now we assume that the sound has the same sampleRate as the line // this (private) play() assumes the line is mono and 44100 samples per second private void play(SourceDataLine line, int samplesToPlay) throws Exception { byte [] bytesToWrite = toByteArray(toNormalizedSamples()); int numBytesToWrite = Math.min(bytesToWrite.length, samplesToPlay * 2); // System.err.println("Playing..."); if(numBytesToWrite != line.write(bytesToWrite, 0, numBytesToWrite)) throw new Exception("Not all bytes written, line probably closed"); // System.err.println("Sound played successfully"); } public void finish() { int i; for (i=0; i<100000000; i++) ; System.exit(0); } private short [] toNormalizedSamples() { if(sampleRate == 44100.0) return samples; else { int normalSamplesLength = (int)(44100.0 * (samples.length - 1) / sampleRate); short [] result = new short[normalSamplesLength]; for(int i = 0; i < normalSamplesLength; i++) { int correspondingSample = (int)(i * sampleRate / 44100.0); double overrun = i * sampleRate / 44100.0 - correspondingSample; double underrun = 1 - overrun; result[i] = (short)(underrun * samples[correspondingSample] + overrun * samples[correspondingSample + 1]); } return result; } } public MatInf1100Sound toNormalizedSound() { return new MatInf1100Sound(toNormalizedSamples(), 44100.0); } public static byte [] toByteArray(short [] array) // this is a noop in c { if(array == null) return null; int len = array.length; byte [] result = new byte[2 * len]; for(int i = 0; i < len; i++) { result[2 * i] = (byte)(array[i] >> 8); result[2 * i + 1] = (byte)array[i]; } // System.err.println("Sound converted to bytes"); return result; } public static SourceDataLine makeDefaultLine() throws Exception { // System.err.println("Making default line..."); AudioFormat format = new AudioFormat(44100.0f,16,1,true,true); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); SourceDataLine line; line = (SourceDataLine) AudioSystem.getLine(info); line.open(format, 1 << 14); line.start(); // System.err.println("line created and opened"); return defaultLine = line; } public static void close() { defaultLine.close(); defaultLine = null; } public static MatInf1100Sound getSampleSound() { java.util.Random rand = new java.util.Random(2003L); short [] samples = new short[SAMPLE_SOUND_LENGTH]; for(int i = 0; i < 901; i++) samples[i] = (short)rand.nextInt(); for(int i = 901; i < SAMPLE_SOUND_LENGTH; i++) samples[i] = (short)((samples[i - 901] + samples[i - 900]) / 2); // System.err.println("Sample sound produced"); // return new MatInf1100Sound(samples, 44100.0); return new MatInf1100Sound(samples, 44100.0); } public static MatInf1100Sound getSoundFromFile(String str) throws Exception { return getSoundFromFile(new java.io.File(str)); } public static MatInf1100Sound getSoundFromFile(java.io.File f) throws Exception { /* First, if we have a .obj file, read the object */ if(f.getName().endsWith(".obj")) { Object obj = readObjectFromFile(f.getAbsolutePath()); if(obj instanceof MatInf1100Sound) return (MatInf1100Sound)obj; else if(obj instanceof AbstractCompressedSound) return ((AbstractCompressedSound)obj).decompress(); else throw new Exception(".obj file is not an object file containg a MatInf1100 Sound"); } else { AudioInputStream soundIn = AudioSystem.getAudioInputStream(f); AudioFormat inFormat = soundIn.getFormat(); float sampleRate = inFormat.getFrameRate(); AudioFormat monoFormat = new AudioFormat(sampleRate, 16, 1, true, true); AudioFormat stereoFormat = new AudioFormat(sampleRate, 16, 2, true, true); short samples[]; if(inFormat.matches(monoFormat) || AudioSystem.isConversionSupported(monoFormat, inFormat)) { System.err.println("Mono stream, hurray!"); int avail = soundIn.available(); AudioInputStream convertedIn = AudioSystem.getAudioInputStream(monoFormat, soundIn); // this test should not be necessary, but is for .au files. if(inFormat.getFrameSize() == 1) avail *= 2; else avail = convertedIn.available(); System.out.println(avail + " bytes available; " + (avail/(sampleRate*2)) + " seconds."); byte [] clipBytes = new byte[avail]; int bytesRead = convertedIn.read(clipBytes); if(bytesRead != avail) throw new Exception("Could not read available() bytes; " + bytesRead + " bytes read."); if((avail & 1) != 0) throw new Exception("byte size not a multiple of 2"); samples = new short[avail / 2]; for(int i = 0; i < samples.length; i++) { samples[i] = (short)((clipBytes[i * 2] << 8) + clipBytes[i * 2 + 1]); } } else if(inFormat.matches(stereoFormat) || AudioSystem.isConversionSupported(stereoFormat, inFormat)) { System.err.println("Stereo stream, reducing to mono..."); AudioInputStream convertedIn = AudioSystem.getAudioInputStream(stereoFormat, soundIn); int avail = convertedIn.available() & 0xfffffffc; System.out.println(avail + " bytes available; " + (avail/(sampleRate*4)) + " seconds."); byte [] clipBytes = new byte[avail]; int bytesRead = convertedIn.read(clipBytes); if(bytesRead != avail) throw new Exception("Could not read available() bytes; " + bytesRead + " bytes read."); if((avail & 3) != 0) throw new Exception("byte size not a multiple of 4"); samples = new short[avail / 4]; for(int i = 0; i < samples.length; i++) { short left = (short)((clipBytes[i * 4] << 8) + clipBytes[i * 4 + 1]); short right = (short)((clipBytes[i * 4 + 2] << 8) + clipBytes[i * 4 + 3]); samples[i] = (short)((right + left) / 2); } } else throw new Exception("No conversion from\n" + soundIn.getFormat() + "\nto\n" + monoFormat + "\nor\n" + stereoFormat); /* getAudioInputStream(AudioFormat.Encoding targetEncoding, AudioInputStream sourceStream) getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream) AudioFormat(float sampleRate, int sampleSizeInBits, int channels, boolean signed, boolean bigEndian) */ soundIn.close(); return new MatInf1100Sound(samples, sampleRate); } } public static void writeObjectToFile(Serializable obj, String filename) throws Exception { FileOutputStream fileOut = new FileOutputStream(filename); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(obj); out.close(); fileOut.close(); } public static Object readObjectFromFile(String filename) throws Exception { FileInputStream fileIn = new FileInputStream(filename); ObjectInputStream in = new ObjectInputStream(fileIn); Object result = in.readObject(); in.close(); fileIn.close(); return result; } public Object clone() { return new MatInf1100Sound((short [])samples.clone(), sampleRate); } public boolean equals(Object o) { if(!(o instanceof MatInf1100Sound)) return false; MatInf1100Sound other = (MatInf1100Sound)o; return other.sampleRate == sampleRate && java.util.Arrays.equals(other.samples, samples); } public String diffString(MatInf1100Sound other) { String result = (other.samples.length != samples.length) ? "lengths differ by " + Math.abs(other.samples.length - samples.length) : "same length"; long sumDiff = 0, sumSquareDiff = 0; int len = Math.min(other.samples.length, samples.length); double dLen = len; for(int i = 0; i < len; i++) { int diff = Math.abs(other.samples[i] - samples[i]); sumDiff += diff; sumSquareDiff += diff * ((long)diff); } return result + ", average diff = " + sumDiff / dLen + ", standard deviation = " + Math.sqrt(sumSquareDiff / dLen); } public static void main(String [] args) { MatInf1100Sound snd; try { if(args.length == 0) { snd = getSampleSound(); snd.play(); snd.sampleRate=26000; snd.play(); writeObjectToFile(snd, "snd.obj"); } else { for(int i = 0; i < args.length; i++) { snd = (MatInf1100Sound) readObjectFromFile(args[i]); snd.play(); } } } catch(Exception e) { System.err.println("Exception: " + e); e.printStackTrace(); } System.exit(0); } }