Skip to content

Commit 6885bf9

Browse files
committed
add support for .ogg and .mp3 sound
1 parent 5288dd6 commit 6885bf9

File tree

11 files changed

+178
-88
lines changed

11 files changed

+178
-88
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ time. [Feel free to help us out by contributing to the project!](#contributing-t
3030
| Scene System | [Scene-based Rendering & Scene Switching](examples/java/tech/fastj/examples/scene/Main.java) ||
3131
| Scriptable Behaviors | [Control GameObject state](examples/java/tech/fastj/examples/behaviors/Main.java) ||
3232
| Desktop Support | Full Compatibility on Windows, Linux, and macOS ||
33-
| Audio Engine | [Loading, Playing, and Controlling .wav, .au, .aiff files](examples/java/tech/fastj/examples/audio/Main.java) | |
33+
| Audio Engine | [Loading, Playing, and Controlling .wav, .ogg, .mp3 files](examples/java/tech/fastj/examples/audio/Main.java) | |
3434
| Image Support | Image rendering, Spritesheet Creation ||
3535
| Animation Engine | Interpolation, Sprite Animation, Tweening Framework ||
3636
| UI System | Flexible UI System to replace Swing UI ||

build.gradle

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,17 @@ wrapper.gradleVersion = '7.5.1'
5656
wrapper.distributionType = Wrapper.DistributionType.ALL
5757

5858
repositories.mavenCentral()
59+
60+
dependencies.api('org.slf4j:slf4j-api:2.0.0-beta1')
61+
dependencies.api('com.googlecode.soundlibs:mp3spi:1.9.5.4')
62+
dependencies.api('com.googlecode.soundlibs:jorbis:0.0.17.4')
63+
dependencies.api('com.googlecode.soundlibs:tritonus-share:0.3.7.4')
64+
dependencies.api('com.googlecode.soundlibs:vorbisspi:1.0.3.3')
65+
66+
dependencies.testImplementation('org.slf4j:slf4j-simple:2.0.0-alpha7')
5967
dependencies.testImplementation(dependencies.platform('org.junit:junit-bom:5.8.2'))
6068
dependencies.testImplementation('org.junit.jupiter:junit-jupiter:5.8.2')
6169
dependencies.testRuntimeOnly("org.junit.platform:junit-platform-launcher")
62-
dependencies.api('org.slf4j:slf4j-api:2.0.0-beta1')
63-
dependencies.testImplementation('org.slf4j:slf4j-simple:2.0.0-alpha7')
6470

6571
/* ********************* *
6672
* Unit Testing *
@@ -88,10 +94,6 @@ tasks.withType(Test) {
8894
def dashes = '-' * repeatLength
8995

9096
logger.info(String.format('%n%n%s%n%s%s%s%n%s%n%n', dashes, startItem, results, endItem, dashes))
91-
92-
if (result.resultType != TestResult.ResultType.SUCCESS) {
93-
System.exit(0)
94-
}
9597
}
9698
}
9799
}

src/main/java/tech/fastj/systems/audio/AudioManager.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,8 @@ static Clip newClip() {
326326
*/
327327
static AudioInputStream newAudioStream(Path audioPath) {
328328
try {
329-
return AudioSystem.getAudioInputStream(audioPath.toFile());
329+
AudioInputStream inputStream = AudioSystem.getAudioInputStream(audioPath.toFile());
330+
return AudioSystem.getAudioInputStream(convertAudioFormat(inputStream.getFormat()), inputStream);
330331
} catch (IOException exception) {
331332
throw new IllegalStateException(exception.getMessage(), exception);
332333
} catch (UnsupportedAudioFileException exception) {
@@ -344,7 +345,8 @@ static AudioInputStream newAudioStream(Path audioPath) {
344345
*/
345346
static AudioInputStream newAudioStream(URL audioPath) {
346347
try {
347-
return AudioSystem.getAudioInputStream(audioPath);
348+
AudioInputStream inputStream = AudioSystem.getAudioInputStream(audioPath);
349+
return AudioSystem.getAudioInputStream(convertAudioFormat(inputStream.getFormat()), inputStream);
348350
} catch (IOException exception) {
349351
throw new IllegalStateException(exception.getMessage(), exception);
350352
} catch (UnsupportedAudioFileException exception) {
@@ -375,6 +377,32 @@ static SourceDataLine newSourceDataLine(AudioFormat audioFormat) {
375377
}
376378
}
377379

380+
/**
381+
* {@return the audio format, converted to a usable format for javax.sound and the SPIs}
382+
* <p>
383+
* This aids in ensuring all audio from MP3 and OGG formats can be played, in conjunction with the dependencies on jorbis, mp3spi, and
384+
* vorbisspi, and tritonus-share.
385+
* <p>
386+
* Code source primarily determined from <a
387+
* href="https://github.com/gurkenlabs/litiengine/blob/a770fcadf05e1b30074a77bfeb6e37f33ca8cee2/core/src/main/java/de/gurkenlabs/litiengine/sound/Sound.java#L99">litiengine</a>.
388+
*
389+
* @param audioFileFormat The input audio file's format.
390+
*/
391+
private static AudioFormat convertAudioFormat(final AudioFormat audioFileFormat) {
392+
int channels = audioFileFormat.getChannels();
393+
float bitrate = audioFileFormat.getSampleRate();
394+
395+
return new AudioFormat(
396+
AudioFormat.Encoding.PCM_SIGNED,
397+
bitrate,
398+
16,
399+
channels,
400+
channels * 2,
401+
bitrate,
402+
false
403+
);
404+
}
405+
378406
@Override
379407
public List<Audio> getTaggableEntities() {
380408
List<Audio> result = new ArrayList<>();

src/test/java/unittest/testcases/systems/audio/AudioManagerTests.java

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@
2626
import static org.junit.jupiter.api.Assumptions.assumeTrue;
2727

2828
class AudioManagerTests {
29-
30-
private static final Path TestAudioPath = Path.of("src/test/resources/test_audio.wav");
31-
private static final URL TestAudioURL = MemoryAudioTests.class.getClassLoader().getResource("test_audio.wav");
3229
private static final AudioManager GeneralAudioManager = FastJEngine.getAudioManager();
3330

3431
@BeforeAll
@@ -47,29 +44,49 @@ public static void resetAudioManager() {
4744
}
4845

4946
@Test
50-
void checkMemoryAudioLoading_withPath_withWAVFormatAudio() {
51-
MemoryAudio memoryAudio = GeneralAudioManager.loadMemoryAudio(TestAudioPath);
47+
void checkMemoryAudioLoading_ofAllTypes_onSinglePaths() {
48+
MemoryAudio memoryAudio = GeneralAudioManager.loadMemoryAudio(AudioTypes.Wav.path());
49+
assertNotNull(GeneralAudioManager.getMemoryAudio(memoryAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
50+
51+
memoryAudio = GeneralAudioManager.loadMemoryAudio(AudioTypes.Mp3.path());
52+
assertNotNull(GeneralAudioManager.getMemoryAudio(memoryAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
53+
54+
memoryAudio = GeneralAudioManager.loadMemoryAudio(AudioTypes.Ogg.path());
5255
assertNotNull(GeneralAudioManager.getMemoryAudio(memoryAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
5356
}
5457

5558
@Test
56-
void checkMemoryAudioLoading_withPaths_withWAVFormatAudio_andMultiplePaths() {
57-
MemoryAudio[] memoryAudios = GeneralAudioManager.loadMemoryAudio(TestAudioPath, TestAudioPath, TestAudioPath);
59+
void checkMemoryAudioLoading_ofAllTypes_withMultiplePaths() {
60+
MemoryAudio[] memoryAudios = GeneralAudioManager.loadMemoryAudio(
61+
AudioTypes.Wav.path(),
62+
AudioTypes.Mp3.path(),
63+
AudioTypes.Ogg.path()
64+
);
5865

5966
for (MemoryAudio memoryAudio : memoryAudios) {
6067
assertNotNull(GeneralAudioManager.getMemoryAudio(memoryAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
6168
}
6269
}
6370

6471
@Test
65-
void checkMemoryAudioLoading_withURL_withWAVFormatAudio() {
66-
MemoryAudio memoryAudio = GeneralAudioManager.loadMemoryAudio(TestAudioURL);
72+
void checkMemoryAudioLoading_ofAllTypes_onSingleURLs() {
73+
MemoryAudio memoryAudio = GeneralAudioManager.loadMemoryAudio(AudioTypes.Wav.url());
74+
assertNotNull(GeneralAudioManager.getMemoryAudio(memoryAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
75+
76+
memoryAudio = GeneralAudioManager.loadMemoryAudio(AudioTypes.Mp3.url());
77+
assertNotNull(GeneralAudioManager.getMemoryAudio(memoryAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
78+
79+
memoryAudio = GeneralAudioManager.loadMemoryAudio(AudioTypes.Ogg.url());
6780
assertNotNull(GeneralAudioManager.getMemoryAudio(memoryAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
6881
}
6982

7083
@Test
71-
void checkMemoryAudioLoading_withURLs_withWAVFormatAudio_andMultiplePaths() {
72-
MemoryAudio[] memoryAudios = GeneralAudioManager.loadMemoryAudio(TestAudioURL, TestAudioURL, TestAudioURL);
84+
void checkMemoryAudioLoading_ofAllTypes_withMultipleURLs() {
85+
MemoryAudio[] memoryAudios = GeneralAudioManager.loadMemoryAudio(
86+
AudioTypes.Wav.url(),
87+
AudioTypes.Mp3.url(),
88+
AudioTypes.Ogg.url()
89+
);
7390

7491
for (MemoryAudio memoryAudio : memoryAudios) {
7592
assertNotNull(GeneralAudioManager.getMemoryAudio(memoryAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
@@ -93,12 +110,12 @@ void tryMemoryAudioLoading_withInvalidFileURL() throws MalformedURLException {
93110
}
94111

95112
@Test
96-
void tryMemoryAudioLoading_withPath_withUnsupportedAudioFormat() {
97-
Path testAudioPath = Path.of("src/test/resources/test_audio.flac");
113+
void tryMemoryAudioLoading_ofUnsupportedAudioFormat() {
114+
Path testAudioPath = AudioTypes.Flac.path();
98115

99116
Throwable exception = assertThrows(IllegalArgumentException.class, () -> GeneralAudioManager.loadMemoryAudio(testAudioPath));
100117
assertTrue(exception.getMessage()
101-
.endsWith("test_audio.flac is of an unsupported file format \"flac\"."), "Upon reading an unsupported audio file format, an error should be thrown.");
118+
.endsWith(".flac is of an unsupported file format \"flac\"."), "Upon reading an unsupported audio file format, an error should be thrown.");
102119
Throwable underlyingException = exception.getCause();
103120
assertEquals(UnsupportedAudioFileException.class, underlyingException.getClass(), "The underlying exception's class should match the expected exception's class.");
104121
}
@@ -115,32 +132,52 @@ void tryMemoryAudioLoading_withURL_withUnsupportedAudioFormat() {
115132
}
116133

117134
@Test
118-
void checkStreamedAudioLoading_withPath_withWAVFormatAudio() {
119-
StreamedAudio streamedAudio = GeneralAudioManager.loadStreamedAudio(TestAudioPath);
120-
assertNotNull(GeneralAudioManager.getStreamedAudio(streamedAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio player.");
135+
void checkStreamedAudioLoading_ofAllTypes_onSinglePaths() {
136+
StreamedAudio streamedAudio = GeneralAudioManager.loadStreamedAudio(AudioTypes.Wav.path());
137+
assertNotNull(GeneralAudioManager.getStreamedAudio(streamedAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
138+
139+
streamedAudio = GeneralAudioManager.loadStreamedAudio(AudioTypes.Mp3.path());
140+
assertNotNull(GeneralAudioManager.getStreamedAudio(streamedAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
141+
142+
streamedAudio = GeneralAudioManager.loadStreamedAudio(AudioTypes.Ogg.path());
143+
assertNotNull(GeneralAudioManager.getStreamedAudio(streamedAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
121144
}
122145

123146
@Test
124-
void checkStreamedAudioLoading_withPaths_withWAVFormatAudio_andMultiplePaths() {
125-
StreamedAudio[] memoryAudios = GeneralAudioManager.loadStreamedAudio(TestAudioPath, TestAudioPath, TestAudioPath);
147+
void checkStreamedAudioLoading_ofAllTypes_withMultiplePaths() {
148+
StreamedAudio[] streamedAudios = GeneralAudioManager.loadStreamedAudio(
149+
AudioTypes.Wav.path(),
150+
AudioTypes.Mp3.path(),
151+
AudioTypes.Ogg.path()
152+
);
126153

127-
for (StreamedAudio memoryAudio : memoryAudios) {
128-
assertNotNull(GeneralAudioManager.getStreamedAudio(memoryAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
154+
for (StreamedAudio streamedAudio : streamedAudios) {
155+
assertNotNull(GeneralAudioManager.getStreamedAudio(streamedAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
129156
}
130157
}
131158

132159
@Test
133-
void checkStreamedAudioLoading_withURL_withWAVFormatAudio() {
134-
StreamedAudio streamedAudio = GeneralAudioManager.loadStreamedAudio(TestAudioURL);
135-
assertNotNull(GeneralAudioManager.getStreamedAudio(streamedAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio player.");
160+
void checkStreamedAudioLoading_ofAllTypes_onSingleURLs() {
161+
StreamedAudio streamedAudio = GeneralAudioManager.loadStreamedAudio(AudioTypes.Wav.url());
162+
assertNotNull(GeneralAudioManager.getStreamedAudio(streamedAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
163+
164+
streamedAudio = GeneralAudioManager.loadStreamedAudio(AudioTypes.Mp3.url());
165+
assertNotNull(GeneralAudioManager.getStreamedAudio(streamedAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
166+
167+
streamedAudio = GeneralAudioManager.loadStreamedAudio(AudioTypes.Ogg.url());
168+
assertNotNull(GeneralAudioManager.getStreamedAudio(streamedAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
136169
}
137170

138171
@Test
139-
void checkStreamedAudioLoading_withURLs_withWAVFormatAudio_andMultiplePaths() {
140-
StreamedAudio[] memoryAudios = GeneralAudioManager.loadStreamedAudio(TestAudioURL, TestAudioURL, TestAudioURL);
172+
void checkStreamedAudioLoading_ofAllTypes_withMultipleURLs() {
173+
StreamedAudio[] streamedAudios = GeneralAudioManager.loadStreamedAudio(
174+
AudioTypes.Wav.url(),
175+
AudioTypes.Mp3.url(),
176+
AudioTypes.Ogg.url()
177+
);
141178

142-
for (StreamedAudio memoryAudio : memoryAudios) {
143-
assertNotNull(GeneralAudioManager.getStreamedAudio(memoryAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
179+
for (StreamedAudio streamedAudio : streamedAudios) {
180+
assertNotNull(GeneralAudioManager.getStreamedAudio(streamedAudio.getID()), "Loading the audio file into memory should cause it to be stored in the audio manager.");
144181
}
145182
}
146183

@@ -183,12 +220,16 @@ void tryStreamedAudioLoading_withURL_withUnsupportedAudioFormat() {
183220
}
184221

185222
@Test
186-
void checkPlaySound_withPath() {
187-
assertDoesNotThrow(() -> AudioManager.playSound(TestAudioPath));
223+
void checkPlaySound_ofAllTypes_withPath() {
224+
assertDoesNotThrow(() -> AudioManager.playSound(AudioTypes.Wav.path()));
225+
assertDoesNotThrow(() -> AudioManager.playSound(AudioTypes.Mp3.path()));
226+
assertDoesNotThrow(() -> AudioManager.playSound(AudioTypes.Ogg.path()));
188227
}
189228

190229
@Test
191-
void checkPlaySound_withURL() {
192-
assertDoesNotThrow(() -> AudioManager.playSound(TestAudioURL));
230+
void checkPlaySound_ofAllTypes_withURL() {
231+
assertDoesNotThrow(() -> AudioManager.playSound(AudioTypes.Wav.url()));
232+
assertDoesNotThrow(() -> AudioManager.playSound(AudioTypes.Mp3.url()));
233+
assertDoesNotThrow(() -> AudioManager.playSound(AudioTypes.Ogg.url()));
193234
}
194235
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package unittest.testcases.systems.audio;
2+
3+
import java.net.URL;
4+
import java.nio.file.Path;
5+
6+
enum AudioTypes {
7+
Wav(".wav"),
8+
Mp3(".mp3"),
9+
Ogg(".ogg"),
10+
Flac(".flac");
11+
12+
final String extension;
13+
14+
AudioTypes(String extension) {
15+
this.extension = extension;
16+
}
17+
18+
Path path() {
19+
return Path.of("src/test/resources/Test_Audio_Piano" + extension);
20+
}
21+
22+
URL url() {
23+
return AudioTypes.class.getClassLoader().getResource("Test_Audio_Piano" + extension);
24+
}
25+
}

0 commit comments

Comments
 (0)