Skip to content

Commit 1fcfa0a

Browse files
committed
Moved out audio mixing code into own library
1 parent a918596 commit 1fcfa0a

19 files changed

+336
-908
lines changed

build.gradle

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
base {
7-
java.toolchain.languageVersion = JavaLanguageVersion.of(8)
7+
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
88
compileJava.options.encoding = compileTestJava.options.encoding = javadoc.options.encoding = "UTF-8"
99

1010
group = project.maven_group ?: rootProject.maven_group
@@ -21,10 +21,15 @@ configurations {
2121

2222
repositories {
2323
mavenCentral()
24+
maven {
25+
name = "Lenni0451"
26+
url = "https://maven.lenni0451.net/everything"
27+
}
2428
}
2529

2630
dependencies {
2731
include "net.raphimc:NoteBlockLib:2.1.3"
32+
include "net.raphimc:audio-mixer:1.0.0"
2833
include "com.formdev:flatlaf:3.5.1"
2934
include "net.lenni0451.commons:swing:1.6.0"
3035
include "org.lwjgl:lwjgl:3.3.4"

src/main/java/net/raphimc/noteblocktool/audio/export/AudioBuffer.java

Lines changed: 0 additions & 80 deletions
This file was deleted.

src/main/java/net/raphimc/noteblocktool/audio/export/AudioExporter.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
*/
1818
package net.raphimc.noteblocktool.audio.export;
1919

20+
import net.raphimc.audiomixer.util.GrowableArray;
2021
import net.raphimc.noteblocklib.format.nbs.model.NbsCustomInstrument;
2122
import net.raphimc.noteblocklib.model.Note;
2223
import net.raphimc.noteblocklib.model.SongView;
2324
import net.raphimc.noteblocklib.player.FullNoteConsumer;
2425
import net.raphimc.noteblocklib.util.Instrument;
2526
import net.raphimc.noteblocklib.util.SongUtil;
2627
import net.raphimc.noteblocktool.audio.SoundMap;
27-
import net.raphimc.noteblocktool.util.SampleOutputStream;
2828

2929
import javax.sound.sampled.AudioFormat;
3030
import java.io.File;
@@ -37,7 +37,7 @@ public abstract class AudioExporter implements FullNoteConsumer {
3737
protected final AudioFormat format;
3838
private final float masterVolume;
3939
private final Consumer<Float> progressConsumer;
40-
protected SampleOutputStream sampleOutputStream;
40+
protected GrowableArray samples;
4141
private final long noteCount;
4242
protected final int samplesPerTick;
4343
private int processedNotes;
@@ -47,10 +47,10 @@ public AudioExporter(final SongView<?> songView, final AudioFormat format, final
4747
this.format = format;
4848
this.progressConsumer = progressConsumer;
4949
this.masterVolume = masterVolume;
50-
this.sampleOutputStream = new SampleOutputStream(format);
5150

5251
this.noteCount = SongUtil.getNoteCount(songView);
5352
this.samplesPerTick = (int) (format.getSampleRate() / songView.getSpeed());
53+
this.samples = new GrowableArray(this.samplesPerTick * format.getChannels() * (songView.getLength() + Math.round(this.songView.getSpeed() * 3)));
5454
}
5555

5656
public void render() throws InterruptedException {
@@ -74,8 +74,8 @@ public void render() throws InterruptedException {
7474
this.finish();
7575
}
7676

77-
public byte[] getSamples() {
78-
return this.sampleOutputStream.getBytes();
77+
public int[] getSamples() {
78+
return this.samples.getArray();
7979
}
8080

8181
@Override

src/main/java/net/raphimc/noteblocktool/audio/export/impl/JavaxAudioExporter.java renamed to src/main/java/net/raphimc/noteblocktool/audio/export/impl/AudioMixerAudioExporter.java

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,69 +17,55 @@
1717
*/
1818
package net.raphimc.noteblocktool.audio.export.impl;
1919

20+
import net.raphimc.audiomixer.AudioMixer;
21+
import net.raphimc.audiomixer.sound.source.MonoSound;
22+
import net.raphimc.audiomixer.util.AudioFormats;
23+
import net.raphimc.audiomixer.util.SoundSampleUtil;
24+
import net.raphimc.audiomixer.util.io.SoundIO;
2025
import net.raphimc.noteblocklib.model.SongView;
2126
import net.raphimc.noteblocktool.audio.SoundMap;
22-
import net.raphimc.noteblocktool.audio.export.AudioBuffer;
2327
import net.raphimc.noteblocktool.audio.export.AudioExporter;
24-
import net.raphimc.noteblocktool.util.SoundSampleUtil;
28+
import net.raphimc.noteblocktool.util.SoundFileUtil;
2529

2630
import javax.sound.sampled.AudioFormat;
2731
import java.io.ByteArrayInputStream;
2832
import java.util.HashMap;
2933
import java.util.Map;
3034
import java.util.function.Consumer;
3135

32-
public class JavaxAudioExporter extends AudioExporter {
36+
public class AudioMixerAudioExporter extends AudioExporter {
3337

3438
private final Map<String, int[]> sounds;
35-
private final AudioBuffer merger;
39+
private final AudioMixer audioMixer;
3640

37-
public JavaxAudioExporter(final SongView<?> songView, final AudioFormat format, final float masterVolume, final Consumer<Float> progressConsumer) {
41+
public AudioMixerAudioExporter(final SongView<?> songView, final AudioFormat format, final float masterVolume, final Consumer<Float> progressConsumer) {
3842
super(songView, format, masterVolume, progressConsumer);
3943
try {
4044
this.sounds = new HashMap<>();
4145
for (Map.Entry<String, byte[]> entry : SoundMap.loadSoundData(songView).entrySet()) {
42-
this.sounds.put(entry.getKey(), SoundSampleUtil.readSamples(new ByteArrayInputStream(entry.getValue()), format));
46+
this.sounds.put(entry.getKey(), SoundIO.readSamples(SoundFileUtil.readAudioFile(new ByteArrayInputStream(entry.getValue())), AudioFormats.withChannels(format, 1)));
4347
}
44-
this.merger = new AudioBuffer(this.samplesPerTick * format.getChannels() * songView.getLength());
48+
this.audioMixer = new AudioMixer(format, 8192);
4549
} catch (Throwable e) {
46-
throw new RuntimeException("Failed to initialize javax audio exporter", e);
50+
throw new RuntimeException("Failed to initialize AudioMixer audio exporter", e);
4751
}
4852
}
4953

5054
@Override
5155
protected void processSound(final String sound, final float pitch, final float volume, final float panning) {
5256
if (!this.sounds.containsKey(sound)) return;
5357

54-
this.merger.pushSamples(SoundSampleUtil.mutate(this.format, this.sounds.get(sound), pitch, volume, panning));
58+
this.audioMixer.playSound(new MonoSound(this.sounds.get(sound), pitch, volume, panning));
5559
}
5660

5761
@Override
5862
protected void postTick() {
59-
this.merger.advanceIndex(this.samplesPerTick * this.format.getChannels());
63+
this.samples.add(this.audioMixer.mix(this.samplesPerTick * this.format.getChannels()));
6064
}
6165

6266
@Override
6367
protected void finish() {
64-
switch (this.format.getSampleSizeInBits()) {
65-
case 8:
66-
for (byte b : this.merger.normalizeBytes()) {
67-
this.sampleOutputStream.writeSample(b);
68-
}
69-
break;
70-
case 16:
71-
for (short s : this.merger.normalizeShorts()) {
72-
this.sampleOutputStream.writeSample(s);
73-
}
74-
break;
75-
case 32:
76-
for (int i : this.merger.normalizeInts()) {
77-
this.sampleOutputStream.writeSample(i);
78-
}
79-
break;
80-
default:
81-
throw new UnsupportedOperationException("Unsupported sample size: " + this.format.getSampleSizeInBits());
82-
}
68+
SoundSampleUtil.normalize(this.samples.getArrayDirect(), (int) Math.pow(2, this.format.getSampleSizeInBits() - 1) - 1);
8369
}
8470

8571
}

src/main/java/net/raphimc/noteblocktool/audio/export/impl/OpenALAudioExporter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ protected void preTick() {
4646

4747
@Override
4848
protected void postTick() {
49-
this.soundSystem.renderSamples(this.sampleOutputStream, this.samplesPerTick);
49+
this.soundSystem.renderSamples(this.samples, this.samplesPerTick);
5050
this.soundSystem.postTick();
5151
}
5252

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* This file is part of NoteBlockTool - https://github.com/RaphiMC/NoteBlockTool
3+
* Copyright (C) 2022-2024 RK_01/RaphiMC and contributors
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package net.raphimc.noteblocktool.audio.soundsystem.impl;
19+
20+
import net.raphimc.audiomixer.SourceDataLineAudioMixer;
21+
import net.raphimc.audiomixer.sound.source.MonoSound;
22+
import net.raphimc.audiomixer.util.AudioFormats;
23+
import net.raphimc.audiomixer.util.io.SoundIO;
24+
import net.raphimc.noteblocktool.audio.soundsystem.SoundSystem;
25+
import net.raphimc.noteblocktool.util.SoundFileUtil;
26+
27+
import javax.sound.sampled.AudioFormat;
28+
import javax.sound.sampled.AudioSystem;
29+
import javax.sound.sampled.SourceDataLine;
30+
import java.io.ByteArrayInputStream;
31+
import java.util.HashMap;
32+
import java.util.Map;
33+
34+
public class AudioMixerSoundSystem extends SoundSystem {
35+
36+
private static final AudioFormat FORMAT = new AudioFormat(48000, 16, 2, true, false);
37+
38+
protected final Map<String, int[]> sounds;
39+
protected final SourceDataLineAudioMixer audioMixer;
40+
41+
public AudioMixerSoundSystem(final Map<String, byte[]> soundData, final int maxSounds, final float playbackSpeed) {
42+
super(maxSounds);
43+
44+
try {
45+
this.sounds = new HashMap<>();
46+
for (Map.Entry<String, byte[]> entry : soundData.entrySet()) {
47+
this.sounds.put(entry.getKey(), SoundIO.readSamples(SoundFileUtil.readAudioFile(new ByteArrayInputStream(entry.getValue())), AudioFormats.withChannels(FORMAT, 1)));
48+
}
49+
this.audioMixer = new SourceDataLineAudioMixer(AudioSystem.getSourceDataLine(FORMAT), maxSounds, (int) (FORMAT.getSampleRate() / playbackSpeed) * FORMAT.getChannels());
50+
} catch (Throwable e) {
51+
throw new RuntimeException("Failed to initialize AudioMixer sound system", e);
52+
}
53+
}
54+
55+
@Override
56+
public synchronized void playSound(final String sound, final float pitch, final float volume, final float panning) {
57+
if (!this.sounds.containsKey(sound)) return;
58+
59+
this.audioMixer.playSound(new MonoSound(this.sounds.get(sound), pitch, volume, panning));
60+
}
61+
62+
@Override
63+
public synchronized void postTick() {
64+
this.audioMixer.mixSlice();
65+
}
66+
67+
@Override
68+
public synchronized void stopSounds() {
69+
final SourceDataLine sourceDataLine = this.audioMixer.getSourceDataLine();
70+
sourceDataLine.stop();
71+
sourceDataLine.flush();
72+
sourceDataLine.start();
73+
this.audioMixer.stopAllSounds();
74+
}
75+
76+
@Override
77+
public synchronized void close() {
78+
this.audioMixer.close();
79+
}
80+
81+
@Override
82+
public synchronized String getStatusLine() {
83+
return "Sounds: " + this.audioMixer.getMixedSounds() + " / " + this.maxSounds;
84+
}
85+
86+
@Override
87+
public synchronized void setMasterVolume(final float volume) {
88+
this.audioMixer.setMasterVolume(volume);
89+
}
90+
91+
}

src/main/java/net/raphimc/noteblocktool/audio/soundsystem/impl/BassSoundSystem.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import net.raphimc.noteblocktool.audio.soundsystem.BassLibrary;
2222
import net.raphimc.noteblocktool.audio.soundsystem.SoundSystem;
2323
import net.raphimc.noteblocktool.util.IOUtil;
24-
import net.raphimc.noteblocktool.util.SoundSampleUtil;
24+
import net.raphimc.noteblocktool.util.SoundFileUtil;
2525

2626
import javax.sound.sampled.AudioFormat;
2727
import javax.sound.sampled.AudioInputStream;
@@ -170,7 +170,7 @@ public synchronized void setMasterVolume(final float volume) {
170170

171171
private int loadAudioFile(final byte[] data) {
172172
try {
173-
AudioInputStream audioInputStream = SoundSampleUtil.readAudioFile(new ByteArrayInputStream(data));
173+
AudioInputStream audioInputStream = SoundFileUtil.readAudioFile(new ByteArrayInputStream(data));
174174
final AudioFormat audioFormat = audioInputStream.getFormat();
175175
final AudioFormat targetFormat = new AudioFormat(audioFormat.getSampleRate(), 16, audioFormat.getChannels(), true, false);
176176
if (!audioFormat.matches(targetFormat)) audioInputStream = AudioSystem.getAudioInputStream(targetFormat, audioInputStream);

0 commit comments

Comments
 (0)