Skip to content

Commit 0d8287d

Browse files
committed
Implement mp3/ogg/.. SoundFile decoding using JavaSound
This will break Android support to some extent, but fixes #32 by switching to the much more well-supported (and 4x faster!) mp3spi, closes #53 by adding support via vorbisspi, and also fixes #15 since 8 bit wav files are are now simply decoded through JavaSound.
1 parent 9f79c7e commit 0d8287d

File tree

4 files changed

+60
-40
lines changed

4 files changed

+60
-40
lines changed

build.xml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
<property file="build.properties"/>
55

66
<path id="classpath">
7-
<pathelement location="library/javamp3-1.0.4.jar" />
8-
<pathelement location="library/jsyn-17.1.0.jar" />
7+
<pathelement location="${lib}/jsyn-17.1.0.jar" />
98
<pathelement location="${lib}/processing-core.zip" />
109
<pathelement location="${lib}/android.jar" />
1110
</path>
@@ -52,13 +51,10 @@
5251

5352
<target name="check-bundled-deps">
5453
<available file="library/jsyn-17.1.0.jar" property="hasjsyn" />
55-
<available file="library/javamp3-1.0.4.jar" property="hasjavamp3" />
5654
</target>
5755

58-
<target name="bundled-deps" depends="check-bundled-deps" unless="hasjsyn" description="Download JSyn, JavaMP3 and (J)PortAudio">
56+
<target name="bundled-deps" depends="check-bundled-deps" unless="hasjsyn" description="Download JSyn and (J)PortAudio">
5957
<get src="https://github.com/philburk/jsyn/releases/download/v17.1.0/jsyn-17.1.0.jar" dest="library/" usetimestamp="true" />
60-
<get src="https://github.com/kevinstadler/JavaMP3/releases/download/v1.0.4/javamp3-1.0.4.jar" dest="library/" usetimestamp="true" />
61-
6258
<get src="https://www.softsynth.com/jsyn/developers/archives/jportaudio_pc_20120904.zip" dest="library/" usetimestamp="true" />
6359
<unzip src="${lib}/jportaudio_pc_20120904.zip" dest="${lib}">
6460
<patternset>
@@ -120,7 +116,7 @@
120116
<exclude name="library/hamcrest-*" />
121117
</patternset>
122118

123-
<target name="dist" depends="clean,jar,javadoc" description="Build clean Sound library zip">
119+
<target name="dist" depends="clean,maven-deps,javadoc" description="Build clean Sound library zip">
124120
<touch mkdirs="true" file="${lib}/linux-aarch64/dummy" />
125121
<touch mkdirs="true" file="${lib}/linux-amd64/dummy" />
126122
<touch mkdirs="true" file="${lib}/linux-arm/dummy" />
@@ -156,13 +152,13 @@
156152

157153
<!-- test targets -->
158154

159-
<target name="test-deps">
155+
<target name="maven-deps">
160156
<exec executable="mvn" discardOutput="true">
161157
<arg line="dependency:copy-dependencies -DoutputDirectory=${lib}" />
162158
</exec>
163159
</target>
164160

165-
<target name="compile-tests" depends="compile,test-deps">
161+
<target name="compile-tests" depends="compile,maven-deps">
166162
<javac source="1.8" target="1.8" srcdir="test" destdir="test" encoding="UTF-8" includeAntRuntime="false" nowarn="true">
167163
<classpath refid="classpath.testing" />
168164
</javac>

pom.xml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<!-- this is a dummy pom.xml that is only used for automatically downloading
66
unit testing dependencies using the following command:
7-
mvn dependency:copy-dependencies -DoutputDirectory=build-deps -->
7+
mvn dependency:copy-dependencies -DoutputDirectory=library -->
88

99
<groupId>processing</groupId>
1010
<artifactId>sound</artifactId>
@@ -17,5 +17,24 @@
1717
<version>4.13.2</version>
1818
<scope>test</scope>
1919
</dependency>
20+
<dependency><!-- depends on tritonus-share-0.3.7.4 and jlayer-1.0.1.4 -->
21+
<groupId>com.googlecode.soundlibs</groupId>
22+
<artifactId>mp3spi</artifactId>
23+
<version>1.9.5.4</version>
24+
</dependency>
25+
<!-- TODO switch to this github package repository, which requires auth setup
26+
it would up tritonus to 0.3.11, jlayer to 1.0.2, plus add two extra dependencies.
27+
https://github.com/umjammer/mp3spi/packages/1067666
28+
<dependency>
29+
<groupId>net.javazoom</groupId>
30+
<artifactId>mp3spi</artifactId>
31+
<version>1.9.13</version>
32+
</dependency>
33+
-->
34+
<dependency>
35+
<groupId>com.googlecode.soundlibs</groupId>
36+
<artifactId>vorbisspi</artifactId>
37+
<version>1.0.3.3</version>
38+
</dependency>
2039
</dependencies>
2140
</project>

src/processing/sound/SoundFile.java

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@
66
import java.util.HashMap;
77
import java.util.Map;
88

9+
import javax.sound.sampled.AudioFormat;
10+
import javax.sound.sampled.AudioInputStream;
11+
import javax.sound.sampled.AudioSystem;
12+
import javax.sound.sampled.UnsupportedAudioFileException;
13+
914
import com.jsyn.data.FloatSample;
1015
import com.jsyn.util.SampleLoader;
1116

12-
import fr.delthas.javamp3.Sound;
1317
import processing.core.PApplet;
1418

1519
// 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) {
6367
try {
6468
// load WAV or AIF using JSyn
6569
this.sample = SampleLoader.loadFloatSample(fin);
66-
67-
// switching to JavaSound decoders is supposed to support 8bit
68-
// unsigned WAV files as well, but doesn't actually seem to be
69-
// the case
70-
//SampleLoader.setJavaSoundPreferred(true);
71-
7270
} catch (IOException e) {
73-
// try parsing as mp3
71+
// not wav/aiff -- try converting via JavaSound...
7472
try {
75-
// stream as to be re-created, since it was modified in SampleLoader.loadFloatSample()
76-
fin = parent.createInput(path);
77-
Sound mp3 = new Sound(fin);
78-
try {
79-
ByteArrayOutputStream os = new ByteArrayOutputStream();
80-
// TODO make decoding asynchronous with a FutureTask<FloatSample>
81-
// this call is expensive
82-
mp3.decodeFullyInto(os);
83-
float data[] = new float[os.size() / 2];
84-
SampleLoader.decodeLittleI16ToF32(os.toByteArray(), 0, os.size(), data, 0);
85-
this.sample = new FloatSample(data, mp3.isStereo() ? 2 : 1);
86-
this.sample.setFrameRate(mp3.getSamplingFrequency());
87-
} catch (IOException ee) {
88-
throw ee;
89-
} catch (NullPointerException ee) {
90-
throw new IOException();
91-
} catch (ArrayIndexOutOfBoundsException ee) {
92-
throw new IOException();
93-
} finally {
94-
mp3.close();
73+
// stream was modified by first read attempt, so re-create it
74+
AudioInputStream in = AudioSystem.getAudioInputStream(parent.createInput(path));
75+
// https://docs.oracle.com/javase%2Ftutorial%2F/sound/converters.html
76+
// https://stackoverflow.com/questions/41784397/convert-mp3-to-wav-in-java
77+
AudioFormat targetFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
78+
in.getFormat().getSampleRate(), 16, // in.getFormat().getSampleSizeInBits(),
79+
in.getFormat().getChannels(), in.getFormat().getChannels() * 2,
80+
in.getFormat().getSampleRate(), false);
81+
// if AudioSystem.isConversionSupported(targetFormat, in.getFormat())
82+
// returns false, then this will raise an Exception:
83+
AudioInputStream converted = AudioSystem.getAudioInputStream(targetFormat, in);
84+
// decoded mpeg streams don't know their exact output framelength, so
85+
// no other way than to just decode the whole thing, then allocate the
86+
// array for it...
87+
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
88+
int nRead;
89+
byte[] buf = new byte[65536];
90+
while ((nRead = converted.read(buf, 0, buf.length)) != -1) {
91+
buffer.write(buf, 0, nRead);
9592
}
93+
buffer.flush();
94+
float data[] = new float[buffer.size() / 2];
95+
SampleLoader.decodeLittleI16ToF32(buffer.toByteArray(), 0, buffer.size(), data, 0);
96+
this.sample = new FloatSample(data, converted.getFormat().getChannels());
97+
this.sample.setFrameRate(converted.getFormat().getSampleRate());
98+
fin.close();
9699
} catch (IOException ee) {
97100
Engine.printError("unable to decode sound file " + path);
98101
// return dysfunctional SoundFile object
99102
return;
103+
} catch (UnsupportedAudioFileException ee) {
104+
throw new RuntimeException(ee);
100105
}
101106
}
102107
if (cache) {

test/processing/sound/SoundTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ public class SoundTest {
1212

1313
@Test
1414
public void testList() {
15-
System.setOut(new PrintStream(outputStreamCaptor));
16-
Sound.list();
15+
// System.setOut(new PrintStream(outputStreamCaptor));
16+
// Sound.list();
1717
// assertEquals("asd", outputStreamCaptor.toString());
1818
}
1919
}

0 commit comments

Comments
 (0)