diff --git a/build.xml b/build.xml index 922f401..dbbd81b 100644 --- a/build.xml +++ b/build.xml @@ -4,8 +4,7 @@ - - + @@ -52,13 +51,10 @@ - - + - - @@ -120,7 +116,7 @@ - + @@ -156,13 +152,13 @@ - + - + diff --git a/pom.xml b/pom.xml index cb51402..d711ec0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ + mvn dependency:copy-dependencies -DoutputDirectory=library --> processing sound @@ -17,5 +17,24 @@ 4.13.2 test + + com.googlecode.soundlibs + mp3spi + 1.9.5.4 + + + + com.googlecode.soundlibs + vorbisspi + 1.0.3.3 + diff --git a/src/processing/sound/SoundFile.java b/src/processing/sound/SoundFile.java index aa61803..3579898 100644 --- a/src/processing/sound/SoundFile.java +++ b/src/processing/sound/SoundFile.java @@ -6,10 +6,14 @@ import java.util.HashMap; import java.util.Map; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.UnsupportedAudioFileException; + import com.jsyn.data.FloatSample; import com.jsyn.util.SampleLoader; -import fr.delthas.javamp3.Sound; import processing.core.PApplet; // calls to amp(), pan() etc affect both the LAST initiated and still running sample, AND all subsequently started ones @@ -63,40 +67,41 @@ public SoundFile(PApplet parent, String path, boolean cache) { try { // load WAV or AIF using JSyn this.sample = SampleLoader.loadFloatSample(fin); - - // switching to JavaSound decoders is supposed to support 8bit - // unsigned WAV files as well, but doesn't actually seem to be - // the case - //SampleLoader.setJavaSoundPreferred(true); - } catch (IOException e) { - // try parsing as mp3 + // not wav/aiff -- try converting via JavaSound... try { - // stream as to be re-created, since it was modified in SampleLoader.loadFloatSample() - fin = parent.createInput(path); - Sound mp3 = new Sound(fin); - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - // TODO make decoding asynchronous with a FutureTask - // this call is expensive - mp3.decodeFullyInto(os); - float data[] = new float[os.size() / 2]; - SampleLoader.decodeLittleI16ToF32(os.toByteArray(), 0, os.size(), data, 0); - this.sample = new FloatSample(data, mp3.isStereo() ? 2 : 1); - this.sample.setFrameRate(mp3.getSamplingFrequency()); - } catch (IOException ee) { - throw ee; - } catch (NullPointerException ee) { - throw new IOException(); - } catch (ArrayIndexOutOfBoundsException ee) { - throw new IOException(); - } finally { - mp3.close(); + // stream was modified by first read attempt, so re-create it + AudioInputStream in = AudioSystem.getAudioInputStream(parent.createInput(path)); + // https://docs.oracle.com/javase%2Ftutorial%2F/sound/converters.html + // https://stackoverflow.com/questions/41784397/convert-mp3-to-wav-in-java + AudioFormat targetFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, + in.getFormat().getSampleRate(), 16, // in.getFormat().getSampleSizeInBits(), + in.getFormat().getChannels(), in.getFormat().getChannels() * 2, + in.getFormat().getSampleRate(), false); + // if AudioSystem.isConversionSupported(targetFormat, in.getFormat()) + // returns false, then this will raise an Exception: + AudioInputStream converted = AudioSystem.getAudioInputStream(targetFormat, in); + // decoded mpeg streams don't know their exact output framelength, so + // no other way than to just decode the whole thing, then allocate the + // array for it... + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] buf = new byte[65536]; + while ((nRead = converted.read(buf, 0, buf.length)) != -1) { + buffer.write(buf, 0, nRead); } + buffer.flush(); + float data[] = new float[buffer.size() / 2]; + SampleLoader.decodeLittleI16ToF32(buffer.toByteArray(), 0, buffer.size(), data, 0); + this.sample = new FloatSample(data, converted.getFormat().getChannels()); + this.sample.setFrameRate(converted.getFormat().getSampleRate()); + fin.close(); } catch (IOException ee) { Engine.printError("unable to decode sound file " + path); // return dysfunctional SoundFile object return; + } catch (UnsupportedAudioFileException ee) { + throw new RuntimeException(ee); } } if (cache) {