From a964354a180cbf36297f24d8bc197c175eeca2ff Mon Sep 17 00:00:00 2001
From: Lenni0451 <20379977+Lenni0451@users.noreply.github.com>
Date: Tue, 7 May 2024 19:55:37 +0200
Subject: [PATCH] Started adding audio exporter
---
.../raphimc/noteblocktool/audio/SoundMap.java | 48 +++++++
.../audio/export/AudioExporter.java | 117 ++++++++++++++++++
.../audio/export/AudioMerger.java | 79 ++++++++++++
.../{ => export}/SampleOutputStream.java | 21 ++--
.../audio/export/impl/JavaxAudioExporter.java | 116 +++++++++++++++++
.../export/impl/OpenALAudioExporter.java | 48 +++++++
.../{ => soundsystem}/JavaxSoundSystem.java | 35 ++----
.../{ => soundsystem}/OpenALSoundSystem.java | 26 ++--
.../noteblocktool/frames/SongPlayerFrame.java | 4 +-
.../noteblocktool/util/SoundSampleUtil.java | 32 +++++
10 files changed, 468 insertions(+), 58 deletions(-)
create mode 100644 src/main/java/net/raphimc/noteblocktool/audio/SoundMap.java
create mode 100644 src/main/java/net/raphimc/noteblocktool/audio/export/AudioExporter.java
create mode 100644 src/main/java/net/raphimc/noteblocktool/audio/export/AudioMerger.java
rename src/main/java/net/raphimc/noteblocktool/audio/{ => export}/SampleOutputStream.java (82%)
create mode 100644 src/main/java/net/raphimc/noteblocktool/audio/export/impl/JavaxAudioExporter.java
create mode 100644 src/main/java/net/raphimc/noteblocktool/audio/export/impl/OpenALAudioExporter.java
rename src/main/java/net/raphimc/noteblocktool/audio/{ => soundsystem}/JavaxSoundSystem.java (68%)
rename src/main/java/net/raphimc/noteblocktool/audio/{ => soundsystem}/OpenALSoundSystem.java (84%)
create mode 100644 src/main/java/net/raphimc/noteblocktool/util/SoundSampleUtil.java
diff --git a/src/main/java/net/raphimc/noteblocktool/audio/SoundMap.java b/src/main/java/net/raphimc/noteblocktool/audio/SoundMap.java
new file mode 100644
index 0000000..7eeb1cd
--- /dev/null
+++ b/src/main/java/net/raphimc/noteblocktool/audio/SoundMap.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of NoteBlockTool - https://github.com/RaphiMC/NoteBlockTool
+ * Copyright (C) 2022-2024 RK_01/RaphiMC and contributors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package net.raphimc.noteblocktool.audio;
+
+import net.raphimc.noteblocklib.util.Instrument;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class SoundMap {
+
+ public static final Map SOUNDS = new HashMap<>();
+
+ static {
+ SOUNDS.put(Instrument.HARP, "/noteblock_sounds/harp.wav");
+ SOUNDS.put(Instrument.BASS, "/noteblock_sounds/bass.wav");
+ SOUNDS.put(Instrument.BASS_DRUM, "/noteblock_sounds/bd.wav");
+ SOUNDS.put(Instrument.SNARE, "/noteblock_sounds/snare.wav");
+ SOUNDS.put(Instrument.HAT, "/noteblock_sounds/hat.wav");
+ SOUNDS.put(Instrument.GUITAR, "/noteblock_sounds/guitar.wav");
+ SOUNDS.put(Instrument.FLUTE, "/noteblock_sounds/flute.wav");
+ SOUNDS.put(Instrument.BELL, "/noteblock_sounds/bell.wav");
+ SOUNDS.put(Instrument.CHIME, "/noteblock_sounds/icechime.wav");
+ SOUNDS.put(Instrument.XYLOPHONE, "/noteblock_sounds/xylobone.wav");
+ SOUNDS.put(Instrument.IRON_XYLOPHONE, "/noteblock_sounds/iron_xylophone.wav");
+ SOUNDS.put(Instrument.COW_BELL, "/noteblock_sounds/cow_bell.wav");
+ SOUNDS.put(Instrument.DIDGERIDOO, "/noteblock_sounds/didgeridoo.wav");
+ SOUNDS.put(Instrument.BIT, "/noteblock_sounds/bit.wav");
+ SOUNDS.put(Instrument.BANJO, "/noteblock_sounds/banjo.wav");
+ SOUNDS.put(Instrument.PLING, "/noteblock_sounds/pling.wav");
+ }
+
+}
diff --git a/src/main/java/net/raphimc/noteblocktool/audio/export/AudioExporter.java b/src/main/java/net/raphimc/noteblocktool/audio/export/AudioExporter.java
new file mode 100644
index 0000000..3a0c39f
--- /dev/null
+++ b/src/main/java/net/raphimc/noteblocktool/audio/export/AudioExporter.java
@@ -0,0 +1,117 @@
+/*
+ * This file is part of NoteBlockTool - https://github.com/RaphiMC/NoteBlockTool
+ * Copyright (C) 2022-2024 RK_01/RaphiMC and contributors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package net.raphimc.noteblocktool.audio.export;
+
+import net.raphimc.noteblocklib.format.nbs.NbsDefinitions;
+import net.raphimc.noteblocklib.format.nbs.model.NbsNote;
+import net.raphimc.noteblocklib.model.Note;
+import net.raphimc.noteblocklib.model.NoteWithPanning;
+import net.raphimc.noteblocklib.model.NoteWithVolume;
+import net.raphimc.noteblocklib.model.SongView;
+import net.raphimc.noteblocklib.util.Instrument;
+import net.raphimc.noteblocklib.util.MinecraftDefinitions;
+
+import javax.sound.sampled.AudioFileFormat;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.function.Consumer;
+
+public abstract class AudioExporter {
+
+ private final SongView> songView;
+ protected final AudioFormat format;
+ private final Consumer progressConsumer;
+ protected SampleOutputStream sampleOutputStream;
+ private final long noteCount;
+ protected final int samplesPerTick;
+ private int processedNotes;
+
+ public AudioExporter(final SongView> songView, final AudioFormat format, final Consumer progressConsumer) {
+ this.songView = songView;
+ this.format = format;
+ this.progressConsumer = progressConsumer;
+ this.sampleOutputStream = new SampleOutputStream(format);
+
+ this.noteCount = songView.getNotes().values().stream().mapToLong(List::size).sum();
+ this.samplesPerTick = (int) (format.getSampleRate() / songView.getSpeed());
+ }
+
+ public void render() {
+ for (int tick = 0; tick <= this.songView.getLength(); tick++) {
+ List extends Note> notes = this.songView.getNotesAtTick(tick);
+ this.processNotes(notes);
+ this.writeSamples();
+
+ this.progressConsumer.accept((float) this.processedNotes / this.noteCount);
+ }
+ this.finish();
+ }
+
+ public void write(final File file) throws IOException {
+ ByteArrayInputStream bais = new ByteArrayInputStream(this.sampleOutputStream.getBytes());
+ AudioInputStream audioInputStream = new AudioInputStream(bais, this.format, bais.available());
+ AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, file);
+ audioInputStream.close();
+ }
+
+ private void processNotes(final List extends Note> notes) {
+ for (Note note : notes) {
+ if (note.getInstrument() >= Instrument.values().length) continue;
+ final float volume;
+ if (note instanceof NoteWithVolume) {
+ final NoteWithVolume noteWithVolume = (NoteWithVolume) note;
+ volume = noteWithVolume.getVolume();
+ } else {
+ volume = 100F;
+ }
+ if (volume <= 0) continue;
+ final float panning;
+ if (note instanceof NoteWithPanning) {
+ final NoteWithPanning noteWithPanning = (NoteWithPanning) note;
+ panning = noteWithPanning.getPanning();
+ } else {
+ panning = 0F;
+ }
+ final float pitch;
+ if (note instanceof NbsNote) {
+ final NbsNote nbsNote = (NbsNote) note;
+ pitch = MinecraftDefinitions.nbsPitchToMcPitch(NbsDefinitions.getPitch(nbsNote));
+ } else {
+ pitch = MinecraftDefinitions.mcKeyToMcPitch(MinecraftDefinitions.nbsKeyToMcKey(note.getKey()));
+ }
+ final Instrument instrument = Instrument.fromNbsId(note.getInstrument());
+ final float playerVolume = volume / 100F;
+ final float playerPanning = panning / 100F;
+
+ this.processNote(instrument, playerVolume, pitch, playerPanning);
+ this.processedNotes++;
+ }
+ }
+
+ protected abstract void processNote(final Instrument instrument, final float volume, final float pitch, final float panning);
+
+ protected abstract void writeSamples();
+
+ protected abstract void finish();
+
+}
diff --git a/src/main/java/net/raphimc/noteblocktool/audio/export/AudioMerger.java b/src/main/java/net/raphimc/noteblocktool/audio/export/AudioMerger.java
new file mode 100644
index 0000000..078d707
--- /dev/null
+++ b/src/main/java/net/raphimc/noteblocktool/audio/export/AudioMerger.java
@@ -0,0 +1,79 @@
+/*
+ * This file is part of NoteBlockTool - https://github.com/RaphiMC/NoteBlockTool
+ * Copyright (C) 2022-2024 RK_01/RaphiMC and contributors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package net.raphimc.noteblocktool.audio.export;
+
+public class AudioMerger {
+
+ private final long[] samples;
+ private int sampleIndex;
+
+ public AudioMerger(final int sampleCount) {
+ this.samples = new long[sampleCount];
+ }
+
+ public long[] getSamples() {
+ return this.samples;
+ }
+
+ public void addSamples(final int[] samples) {
+ for (int i = 0; i < samples.length; i++) {
+ int index = this.sampleIndex + i;
+ if (index >= this.samples.length) break;
+ int sample = samples[i];
+ this.samples[index] += sample;
+ }
+ }
+
+ public void pushSamples(final int samples) {
+ this.sampleIndex += samples;
+ }
+
+ public byte[] normalizeBytes() {
+ byte[] bytes = new byte[this.samples.length];
+ long max = this.getMax();
+ for (int i = 0; i < this.samples.length; i++) {
+ bytes[i] = (byte) (this.samples[i] * Byte.MAX_VALUE / max);
+ }
+ return bytes;
+ }
+
+ public short[] normalizeShorts() {
+ short[] shorts = new short[this.samples.length];
+ long max = this.getMax();
+ for (int i = 0; i < this.samples.length; i++) {
+ shorts[i] = (short) (this.samples[i] * Short.MAX_VALUE / max);
+ }
+ return shorts;
+ }
+
+ public int[] normalizeInts() {
+ int[] ints = new int[this.samples.length];
+ long max = this.getMax();
+ for (int i = 0; i < this.samples.length; i++) {
+ ints[i] = (int) (this.samples[i] * Integer.MAX_VALUE / max);
+ }
+ return ints;
+ }
+
+ private long getMax() {
+ long max = 0;
+ for (long sample : this.samples) max = Math.max(max, Math.abs(sample));
+ return max;
+ }
+
+}
diff --git a/src/main/java/net/raphimc/noteblocktool/audio/SampleOutputStream.java b/src/main/java/net/raphimc/noteblocktool/audio/export/SampleOutputStream.java
similarity index 82%
rename from src/main/java/net/raphimc/noteblocktool/audio/SampleOutputStream.java
rename to src/main/java/net/raphimc/noteblocktool/audio/export/SampleOutputStream.java
index 5ea0fdb..b768610 100644
--- a/src/main/java/net/raphimc/noteblocktool/audio/SampleOutputStream.java
+++ b/src/main/java/net/raphimc/noteblocktool/audio/export/SampleOutputStream.java
@@ -15,31 +15,30 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package net.raphimc.noteblocktool.audio;
+package net.raphimc.noteblocktool.audio.export;
import javax.sound.sampled.AudioFormat;
-import java.io.IOException;
+import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
public class SampleOutputStream extends OutputStream {
- private final OutputStream outputStream;
+ private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
private final AudioFormat audioFormat;
- public SampleOutputStream(final OutputStream outputStream, final AudioFormat audioFormat) {
+ public SampleOutputStream(final AudioFormat audioFormat) {
if (audioFormat.getEncoding() != AudioFormat.Encoding.PCM_SIGNED && audioFormat.getEncoding() != AudioFormat.Encoding.PCM_UNSIGNED) {
throw new IllegalArgumentException("Unsupported audio format: " + audioFormat);
}
- this.outputStream = outputStream;
this.audioFormat = audioFormat;
}
@Override
- public void write(final int b) throws IOException {
+ public void write(final int b) {
this.outputStream.write(b);
}
- public void writeSample(final int sample) throws IOException {
+ public void writeSample(final int sample) {
switch (this.audioFormat.getSampleSizeInBits()) {
case 8:
this.write(sample);
@@ -55,7 +54,11 @@ public void writeSample(final int sample) throws IOException {
}
}
- private void write16Bit(final int sample) throws IOException {
+ public byte[] getBytes() {
+ return this.outputStream.toByteArray();
+ }
+
+ private void write16Bit(final int sample) {
if (this.audioFormat.isBigEndian()) {
this.write((sample >> 8) & 0xFF);
this.write(sample & 0xFF);
@@ -65,7 +68,7 @@ private void write16Bit(final int sample) throws IOException {
}
}
- private void write32Bit(final int sample) throws IOException {
+ private void write32Bit(final int sample) {
if (this.audioFormat.isBigEndian()) {
this.write((sample >> 24) & 0xFF);
this.write((sample >> 16) & 0xFF);
diff --git a/src/main/java/net/raphimc/noteblocktool/audio/export/impl/JavaxAudioExporter.java b/src/main/java/net/raphimc/noteblocktool/audio/export/impl/JavaxAudioExporter.java
new file mode 100644
index 0000000..4a7b0f9
--- /dev/null
+++ b/src/main/java/net/raphimc/noteblocktool/audio/export/impl/JavaxAudioExporter.java
@@ -0,0 +1,116 @@
+/*
+ * This file is part of NoteBlockTool - https://github.com/RaphiMC/NoteBlockTool
+ * Copyright (C) 2022-2024 RK_01/RaphiMC and contributors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package net.raphimc.noteblocktool.audio.export.impl;
+
+import com.google.common.io.ByteStreams;
+import net.raphimc.noteblocklib.model.SongView;
+import net.raphimc.noteblocklib.util.Instrument;
+import net.raphimc.noteblocktool.audio.SoundMap;
+import net.raphimc.noteblocktool.audio.export.AudioExporter;
+import net.raphimc.noteblocktool.audio.export.AudioMerger;
+import net.raphimc.noteblocktool.audio.soundsystem.JavaxSoundSystem;
+import net.raphimc.noteblocktool.util.SoundSampleUtil;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class JavaxAudioExporter extends AudioExporter {
+
+ private final Map sounds;
+ private final AudioMerger merger;
+
+ public JavaxAudioExporter(final SongView> songView, final AudioFormat format, final Consumer progressConsumer) {
+ super(songView, format, progressConsumer);
+ this.sounds = this.loadSounds(format);
+ this.merger = new AudioMerger(this.samplesPerTick * (songView.getLength() + 1));
+ }
+
+ @Override
+ protected void processNote(Instrument instrument, float volume, float pitch, float panning) {
+ this.merger.addSamples(SoundSampleUtil.mutate(this.sounds.get(instrument), volume, pitch));
+ }
+
+ @Override
+ protected void writeSamples() {
+ this.merger.pushSamples(this.samplesPerTick);
+ }
+
+ @Override
+ protected void finish() {
+ switch (this.format.getSampleSizeInBits()) {
+ case 8:
+ for (byte b : this.merger.normalizeBytes()) {
+ this.sampleOutputStream.writeSample(b);
+ }
+ break;
+ case 16:
+ for (short s : this.merger.normalizeShorts()) {
+ this.sampleOutputStream.writeSample(s);
+ }
+ break;
+ case 32:
+ for (int i : this.merger.normalizeInts()) {
+ this.sampleOutputStream.writeSample(i);
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported sample size: " + this.format.getSampleSizeInBits());
+ }
+ }
+
+ private Map loadSounds(final AudioFormat format) {
+ try {
+ Map sounds = new HashMap<>();
+ for (Map.Entry entry : SoundMap.SOUNDS.entrySet()) {
+ sounds.put(entry.getKey(), readSound(format, JavaxSoundSystem.class.getResourceAsStream(entry.getValue())));
+ }
+ return sounds;
+ } catch (Throwable e) {
+ throw new RuntimeException("Could not load audio buffer", e);
+ }
+ }
+
+ private int[] readSound(final AudioFormat format, final InputStream is) {
+ try {
+ AudioInputStream in = AudioSystem.getAudioInputStream(new BufferedInputStream(is));
+ if (!in.getFormat().matches(format)) in = AudioSystem.getAudioInputStream(format, in);
+ final byte[] audioBytes = ByteStreams.toByteArray(in);
+
+ final int sampleSize = format.getSampleSizeInBits() / 8;
+ final int[] samples = new int[audioBytes.length / sampleSize];
+ for (int i = 0; i < samples.length; i++) {
+ final byte[] sampleBytes = new byte[sampleSize];
+ System.arraycopy(audioBytes, i * sampleSize, sampleBytes, 0, sampleSize);
+ samples[i] = ByteBuffer.wrap(sampleBytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
+ }
+
+ return samples;
+ } catch (Throwable t) {
+ throw new RuntimeException("Could not read sound", t);
+ }
+ }
+
+}
diff --git a/src/main/java/net/raphimc/noteblocktool/audio/export/impl/OpenALAudioExporter.java b/src/main/java/net/raphimc/noteblocktool/audio/export/impl/OpenALAudioExporter.java
new file mode 100644
index 0000000..9c75edd
--- /dev/null
+++ b/src/main/java/net/raphimc/noteblocktool/audio/export/impl/OpenALAudioExporter.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of NoteBlockTool - https://github.com/RaphiMC/NoteBlockTool
+ * Copyright (C) 2022-2024 RK_01/RaphiMC and contributors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package net.raphimc.noteblocktool.audio.export.impl;
+
+import net.raphimc.noteblocklib.model.SongView;
+import net.raphimc.noteblocklib.util.Instrument;
+import net.raphimc.noteblocktool.audio.export.AudioExporter;
+import net.raphimc.noteblocktool.audio.soundsystem.OpenALSoundSystem;
+
+import javax.sound.sampled.AudioFormat;
+import java.util.function.Consumer;
+
+public class OpenALAudioExporter extends AudioExporter {
+
+ public OpenALAudioExporter(final SongView> songView, final AudioFormat format, final Consumer progressConsumer) {
+ super(songView, format, progressConsumer);
+ }
+
+ @Override
+ protected void processNote(Instrument instrument, float volume, float pitch, float panning) {
+ OpenALSoundSystem.playNote(instrument, volume, pitch, panning);
+ }
+
+ @Override
+ protected void writeSamples() {
+ OpenALSoundSystem.renderSamples(this.sampleOutputStream, this.samplesPerTick);
+ }
+
+ @Override
+ protected void finish() {
+ }
+
+}
diff --git a/src/main/java/net/raphimc/noteblocktool/audio/JavaxSoundSystem.java b/src/main/java/net/raphimc/noteblocktool/audio/soundsystem/JavaxSoundSystem.java
similarity index 68%
rename from src/main/java/net/raphimc/noteblocktool/audio/JavaxSoundSystem.java
rename to src/main/java/net/raphimc/noteblocktool/audio/soundsystem/JavaxSoundSystem.java
index 88e53e9..b4cf6ef 100644
--- a/src/main/java/net/raphimc/noteblocktool/audio/JavaxSoundSystem.java
+++ b/src/main/java/net/raphimc/noteblocktool/audio/soundsystem/JavaxSoundSystem.java
@@ -15,12 +15,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package net.raphimc.noteblocktool.audio;
+package net.raphimc.noteblocktool.audio.soundsystem;
import com.google.common.io.ByteStreams;
import com.google.common.io.LittleEndianDataOutputStream;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import net.raphimc.noteblocklib.util.Instrument;
+import net.raphimc.noteblocktool.audio.SoundMap;
+import net.raphimc.noteblocktool.util.SoundSampleUtil;
import javax.sound.sampled.*;
import java.io.*;
@@ -43,22 +45,9 @@ public class JavaxSoundSystem {
public static void init(final int maxSounds) {
MAX_SOUNDS = maxSounds;
try {
- SOUNDS.put(Instrument.HARP, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/harp.wav")));
- SOUNDS.put(Instrument.BASS, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/bass.wav")));
- SOUNDS.put(Instrument.BASS_DRUM, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/bd.wav")));
- SOUNDS.put(Instrument.SNARE, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/snare.wav")));
- SOUNDS.put(Instrument.HAT, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/hat.wav")));
- SOUNDS.put(Instrument.GUITAR, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/guitar.wav")));
- SOUNDS.put(Instrument.FLUTE, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/flute.wav")));
- SOUNDS.put(Instrument.BELL, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/bell.wav")));
- SOUNDS.put(Instrument.CHIME, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/icechime.wav")));
- SOUNDS.put(Instrument.XYLOPHONE, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/xylobone.wav")));
- SOUNDS.put(Instrument.IRON_XYLOPHONE, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/iron_xylophone.wav")));
- SOUNDS.put(Instrument.COW_BELL, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/cow_bell.wav")));
- SOUNDS.put(Instrument.DIDGERIDOO, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/didgeridoo.wav")));
- SOUNDS.put(Instrument.BIT, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/bit.wav")));
- SOUNDS.put(Instrument.BANJO, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/banjo.wav")));
- SOUNDS.put(Instrument.PLING, readSound(JavaxSoundSystem.class.getResourceAsStream("/noteblock_sounds/pling.wav")));
+ for (Map.Entry entry : SoundMap.SOUNDS.entrySet()) {
+ SOUNDS.put(entry.getKey(), readSound(JavaxSoundSystem.class.getResourceAsStream(entry.getValue())));
+ }
} catch (Throwable e) {
throw new RuntimeException("Could not load audio buffer", e);
}
@@ -74,7 +63,7 @@ public static void playNote(final Instrument instrument, final float volume, fin
if (PLAYING_SOUNDS.size() >= MAX_SOUNDS) return;
try {
final Sound sound = SOUNDS.get(instrument);
- final int[] samples = mutate(sound.getSamples(), volume, pitch);
+ final int[] samples = SoundSampleUtil.mutate(sound.getSamples(), volume, pitch);
final Sound newSound = new Sound(sound.getAudioFormat(), samples);
final AudioInputStream audioStream = writeSound(newSound);
final Clip clip = AudioSystem.getClip();
@@ -138,16 +127,6 @@ private static Sound readSound(final InputStream is) throws UnsupportedAudioFile
return new Sound(audioFormat, samples);
}
- private static int[] mutate(final int[] samples, final float volume, final float pitchChangeFactor) {
- final int[] newSamples = new int[(int) (samples.length / pitchChangeFactor)];
- for (int i = 0; i < newSamples.length; i++) {
- // Long to prevent clipping of the index
- final long index = (long) i * samples.length / newSamples.length;
- newSamples[i] = (int) (samples[(int) index] * volume);
- }
- return newSamples;
- }
-
private static AudioInputStream writeSound(final Sound sound) throws IOException {
final int sampleSize = sound.getAudioFormat().getSampleSizeInBits() / 8;
final ByteArrayOutputStream baos = new ByteArrayOutputStream(sound.getSamples().length * sampleSize);
diff --git a/src/main/java/net/raphimc/noteblocktool/audio/OpenALSoundSystem.java b/src/main/java/net/raphimc/noteblocktool/audio/soundsystem/OpenALSoundSystem.java
similarity index 84%
rename from src/main/java/net/raphimc/noteblocktool/audio/OpenALSoundSystem.java
rename to src/main/java/net/raphimc/noteblocktool/audio/soundsystem/OpenALSoundSystem.java
index 30e9b82..28127f6 100644
--- a/src/main/java/net/raphimc/noteblocktool/audio/OpenALSoundSystem.java
+++ b/src/main/java/net/raphimc/noteblocktool/audio/soundsystem/OpenALSoundSystem.java
@@ -15,11 +15,13 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package net.raphimc.noteblocktool.audio;
+package net.raphimc.noteblocktool.audio.soundsystem;
import com.google.common.io.ByteStreams;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import net.raphimc.noteblocklib.util.Instrument;
+import net.raphimc.noteblocktool.audio.SoundMap;
+import net.raphimc.noteblocktool.audio.export.SampleOutputStream;
import org.lwjgl.openal.*;
import org.lwjgl.system.MemoryUtil;
@@ -27,7 +29,6 @@
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.BufferedInputStream;
-import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.EnumMap;
@@ -109,22 +110,9 @@ private static void init(final int[] attributes) {
AL10.alListenerfv(AL10.AL_ORIENTATION, new float[]{0F, 0F, -1F, 0F, 1F, 0F});
checkError("Could not set listener orientation");
- INSTRUMENT_BUFFERS.put(Instrument.HARP, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/harp.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.BASS, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/bass.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.BASS_DRUM, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/bd.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.SNARE, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/snare.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.HAT, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/hat.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.GUITAR, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/guitar.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.FLUTE, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/flute.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.BELL, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/bell.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.CHIME, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/icechime.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.XYLOPHONE, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/xylobone.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.IRON_XYLOPHONE, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/iron_xylophone.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.COW_BELL, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/cow_bell.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.DIDGERIDOO, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/didgeridoo.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.BIT, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/bit.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.BANJO, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/banjo.wav")));
- INSTRUMENT_BUFFERS.put(Instrument.PLING, loadWav(OpenALSoundSystem.class.getResourceAsStream("/noteblock_sounds/pling.wav")));
+ for (Map.Entry entry : SoundMap.SOUNDS.entrySet()) {
+ INSTRUMENT_BUFFERS.put(entry.getKey(), loadWav(OpenALSoundSystem.class.getResourceAsStream(entry.getValue())));
+ }
TICK_TASK = SCHEDULER.scheduleAtFixedRate(OpenALSoundSystem::tick, 0, 100, TimeUnit.MILLISECONDS);
Runtime.getRuntime().addShutdownHook(SHUTDOWN_HOOK = new Thread(() -> {
@@ -163,7 +151,7 @@ public static void playNote(final Instrument instrument, final float volume, fin
}
}
- public static void renderSamples(final SampleOutputStream outputStream, final int sampleCount) throws IOException {
+ public static void renderSamples(final SampleOutputStream outputStream, final int sampleCount) {
final int samplesLength = sampleCount * AUDIO_FORMAT.getChannels();
if (samplesLength * AUDIO_FORMAT.getSampleSizeInBits() / 8 > CAPTURE_BUFFER.capacity()) {
throw new IllegalArgumentException("Sample count too high");
diff --git a/src/main/java/net/raphimc/noteblocktool/frames/SongPlayerFrame.java b/src/main/java/net/raphimc/noteblocktool/frames/SongPlayerFrame.java
index aa0ecaf..a0ef414 100644
--- a/src/main/java/net/raphimc/noteblocktool/frames/SongPlayerFrame.java
+++ b/src/main/java/net/raphimc/noteblocktool/frames/SongPlayerFrame.java
@@ -31,8 +31,8 @@
import net.raphimc.noteblocklib.util.Instrument;
import net.raphimc.noteblocklib.util.MinecraftDefinitions;
import net.raphimc.noteblocklib.util.SongResampler;
-import net.raphimc.noteblocktool.audio.JavaxSoundSystem;
-import net.raphimc.noteblocktool.audio.OpenALSoundSystem;
+import net.raphimc.noteblocktool.audio.soundsystem.JavaxSoundSystem;
+import net.raphimc.noteblocktool.audio.soundsystem.OpenALSoundSystem;
import net.raphimc.noteblocktool.elements.FastScrollPane;
import net.raphimc.noteblocktool.elements.NewLineLabel;
diff --git a/src/main/java/net/raphimc/noteblocktool/util/SoundSampleUtil.java b/src/main/java/net/raphimc/noteblocktool/util/SoundSampleUtil.java
new file mode 100644
index 0000000..41b2e0f
--- /dev/null
+++ b/src/main/java/net/raphimc/noteblocktool/util/SoundSampleUtil.java
@@ -0,0 +1,32 @@
+/*
+ * This file is part of NoteBlockTool - https://github.com/RaphiMC/NoteBlockTool
+ * Copyright (C) 2022-2024 RK_01/RaphiMC and contributors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package net.raphimc.noteblocktool.util;
+
+public class SoundSampleUtil {
+
+ public static int[] mutate(final int[] samples, final float volume, final float pitchChangeFactor) {
+ final int[] newSamples = new int[(int) (samples.length / pitchChangeFactor)];
+ for (int i = 0; i < newSamples.length; i++) {
+ // Long to prevent clipping of the index
+ final long index = (long) i * samples.length / newSamples.length;
+ newSamples[i] = (int) (samples[(int) index] * volume);
+ }
+ return newSamples;
+ }
+
+}