package dk.itu.mario.engine.sonar.sample; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; public class SampleLoader { /** * Loads a sample from an url */ public static SonarSample loadSample(String resourceName) throws UnsupportedAudioFileException, IOException { // Hack to prevent "mark/reset not supported" on some systems InputStream in=SampleLoader.class.getResourceAsStream(resourceName); byte[] d = rip(in); AudioInputStream ais = AudioSystem.getAudioInputStream(new ByteArrayInputStream(d)); return buildSample(rip(ais), ais.getFormat()); } /** * Rips the entire contents of an inputstream into a byte array */ private static byte[] rip(InputStream in) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] b = new byte[4096]; int read = 0; while ((read = in.read(b)) > 0) { bos.write(b, 0, read); } bos.close(); return bos.toByteArray(); } /** * Reorganizes audio sample data into the intenal sonar format */ private static SonarSample buildSample(byte[] b, AudioFormat af) throws UnsupportedAudioFileException { // Rip audioformat data int channels = af.getChannels(); int sampleSize = af.getSampleSizeInBits(); float rate = af.getFrameRate(); boolean signed = af.getEncoding() == AudioFormat.Encoding.PCM_SIGNED; // Sanity checking if (channels != 1) throw new UnsupportedAudioFileException("Only mono samples are supported"); if (!(sampleSize == 8 || sampleSize == 16 || sampleSize == 32)) throw new UnsupportedAudioFileException("Unsupported sample size"); if (!(af.getEncoding() == AudioFormat.Encoding.PCM_UNSIGNED || af.getEncoding() == AudioFormat.Encoding.PCM_SIGNED)) throw new UnsupportedAudioFileException("Unsupported encoding"); // Wrap the data into a bytebuffer, and set up the byte order ByteBuffer bb = ByteBuffer.wrap(b); bb.order(af.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); int s = b.length / (sampleSize / 8); float[] buf = new float[s]; // Six different cases for reordering the data. Can this be improved without slowing it down? if (sampleSize == 8) { if (signed) { for (int i = 0; i < s; i++) buf[i] = bb.get() / (float)0x80; } else { for (int i = 0; i < s; i++) buf[i] = ((bb.get()&0xFF)-0x80) / (float)0x80; } } else if (sampleSize == 16) { if (signed) { for (int i = 0; i < s; i++) buf[i] = bb.getShort() / (float)0x8000; } else { for (int i = 0; i < s; i++) buf[i] = ((bb.getShort()&0xFFFF)-0x8000) / (float)0x8000; } } else if (sampleSize == 32) { if (signed) { for (int i = 0; i < s; i++) buf[i] = bb.getInt() / (float)0x80000000; } else { // Nasty.. check this. for (int i = 0; i < s; i++) buf[i] = ((bb.getInt()&0xFFFFFFFFl)-0x80000000l) / (float)0x80000000; } } // Return the completed sample return new SonarSample(buf, rate); } }