Skip to content

Commit

Permalink
Prepare for custom instruments support
Browse files Browse the repository at this point in the history
  • Loading branch information
RaphiMC committed May 11, 2024
1 parent 9e24d30 commit 9d00a51
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 156 deletions.
5 changes: 1 addition & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,10 @@ configurations {

repositories {
mavenCentral()
maven {
url = "https://maven.lenni0451.net/everything"
}
}

dependencies {
include "net.raphimc:NoteBlockLib:2.0.7-SNAPSHOT"
include "net.raphimc:NoteBlockLib:2.1.0"
include "com.formdev:flatlaf:3.4.1"
include "net.lenni0451.commons:swing:1.5.1"
include "org.lwjgl:lwjgl:3.3.3"
Expand Down
80 changes: 56 additions & 24 deletions src/main/java/net/raphimc/noteblocktool/audio/SoundMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,77 @@
package net.raphimc.noteblocktool.audio;

import net.raphimc.noteblocklib.util.Instrument;
import net.raphimc.noteblocktool.audio.soundsystem.impl.JavaxSoundSystem;
import net.raphimc.noteblocktool.util.SoundSampleUtil;

import javax.sound.sampled.AudioFormat;
import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;

public class SoundMap {

public static final Map<Instrument, String> SOUNDS = new EnumMap<>(Instrument.class);
public static final Map<Instrument, String> INSTRUMENT_SOUNDS = new EnumMap<>(Instrument.class);
public static final Map<String, URL> SOUND_LOCATIONS = new HashMap<>();

static {
SOUNDS.put(Instrument.HARP, "/noteblock_sounds/harp.ogg");
SOUNDS.put(Instrument.BASS, "/noteblock_sounds/bass.ogg");
SOUNDS.put(Instrument.BASS_DRUM, "/noteblock_sounds/bd.ogg");
SOUNDS.put(Instrument.SNARE, "/noteblock_sounds/snare.ogg");
SOUNDS.put(Instrument.HAT, "/noteblock_sounds/hat.ogg");
SOUNDS.put(Instrument.GUITAR, "/noteblock_sounds/guitar.ogg");
SOUNDS.put(Instrument.FLUTE, "/noteblock_sounds/flute.ogg");
SOUNDS.put(Instrument.BELL, "/noteblock_sounds/bell.ogg");
SOUNDS.put(Instrument.CHIME, "/noteblock_sounds/icechime.ogg");
SOUNDS.put(Instrument.XYLOPHONE, "/noteblock_sounds/xylobone.ogg");
SOUNDS.put(Instrument.IRON_XYLOPHONE, "/noteblock_sounds/iron_xylophone.ogg");
SOUNDS.put(Instrument.COW_BELL, "/noteblock_sounds/cow_bell.ogg");
SOUNDS.put(Instrument.DIDGERIDOO, "/noteblock_sounds/didgeridoo.ogg");
SOUNDS.put(Instrument.BIT, "/noteblock_sounds/bit.ogg");
SOUNDS.put(Instrument.BANJO, "/noteblock_sounds/banjo.ogg");
SOUNDS.put(Instrument.PLING, "/noteblock_sounds/pling.ogg");
INSTRUMENT_SOUNDS.put(Instrument.HARP, "harp.ogg");
INSTRUMENT_SOUNDS.put(Instrument.BASS, "bass.ogg");
INSTRUMENT_SOUNDS.put(Instrument.BASS_DRUM, "bd.ogg");
INSTRUMENT_SOUNDS.put(Instrument.SNARE, "snare.ogg");
INSTRUMENT_SOUNDS.put(Instrument.HAT, "hat.ogg");
INSTRUMENT_SOUNDS.put(Instrument.GUITAR, "guitar.ogg");
INSTRUMENT_SOUNDS.put(Instrument.FLUTE, "flute.ogg");
INSTRUMENT_SOUNDS.put(Instrument.BELL, "bell.ogg");
INSTRUMENT_SOUNDS.put(Instrument.CHIME, "icechime.ogg");
INSTRUMENT_SOUNDS.put(Instrument.XYLOPHONE, "xylobone.ogg");
INSTRUMENT_SOUNDS.put(Instrument.IRON_XYLOPHONE, "iron_xylophone.ogg");
INSTRUMENT_SOUNDS.put(Instrument.COW_BELL, "cow_bell.ogg");
INSTRUMENT_SOUNDS.put(Instrument.DIDGERIDOO, "didgeridoo.ogg");
INSTRUMENT_SOUNDS.put(Instrument.BIT, "bit.ogg");
INSTRUMENT_SOUNDS.put(Instrument.BANJO, "banjo.ogg");
INSTRUMENT_SOUNDS.put(Instrument.PLING, "pling.ogg");

reload(null);
}

public static void reload(final File customSoundsFolder) {
SOUND_LOCATIONS.clear();
for (Map.Entry<Instrument, String> entry : INSTRUMENT_SOUNDS.entrySet()) {
SOUND_LOCATIONS.put(entry.getValue(), SoundMap.class.getResource("/noteblock_sounds/" + entry.getValue()));
}

if (customSoundsFolder != null && customSoundsFolder.exists() && customSoundsFolder.isDirectory()) {
try {
Files.walk(customSoundsFolder.toPath()).forEach(path -> {
try {
if (Files.isDirectory(path)) return;

final String fileName = customSoundsFolder.toPath().relativize(path).toString();
if (fileName.endsWith(".ogg") || fileName.endsWith(".wav")) {
SOUND_LOCATIONS.put(fileName.replace(File.separatorChar, '/'), path.toUri().toURL());
}
} catch (Throwable e) {
throw new RuntimeException("Error while loading custom sound sample", e);
}
});
} catch (Throwable e) {
throw new RuntimeException("Could not load custom sound samples", e);
}
}
}

public static Map<Instrument, int[]> loadInstrumentSamples(final AudioFormat targetFormat) {
public static Map<String, int[]> loadInstrumentSamples(final AudioFormat targetFormat) {
try {
final Map<Instrument, int[]> instrumentSamples = new EnumMap<>(Instrument.class);
for (Map.Entry<Instrument, String> entry : SOUNDS.entrySet()) {
instrumentSamples.put(entry.getKey(), SoundSampleUtil.readSamples(JavaxSoundSystem.class.getResourceAsStream(entry.getValue()), targetFormat));
final Map<String, int[]> soundSamples = new HashMap<>();
for (Map.Entry<String, URL> entry : SOUND_LOCATIONS.entrySet()) {
soundSamples.put(entry.getKey(), SoundSampleUtil.readSamples(entry.getValue().openStream(), targetFormat));
}
return instrumentSamples;
return soundSamples;
} catch (Throwable e) {
throw new RuntimeException("Could not load instrument samples", e);
throw new RuntimeException("Could not load sound samples", e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
*/
package net.raphimc.noteblocktool.audio.export;

import net.raphimc.noteblocklib.format.nbs.model.NbsCustomInstrument;
import net.raphimc.noteblocklib.model.Note;
import net.raphimc.noteblocklib.model.SongView;
import net.raphimc.noteblocklib.player.FullNoteConsumer;
import net.raphimc.noteblocklib.util.Instrument;
import net.raphimc.noteblocklib.util.SongUtil;
import net.raphimc.noteblocktool.util.DefaultSongPlayerCallback;
import net.raphimc.noteblocktool.audio.SoundMap;
import net.raphimc.noteblocktool.util.SampleOutputStream;

import javax.sound.sampled.AudioFileFormat;
Expand All @@ -34,9 +36,8 @@
import java.util.List;
import java.util.function.Consumer;

public abstract class AudioExporter {
public abstract class AudioExporter implements FullNoteConsumer {

private final DefaultSongPlayerCallback noteConsumer = this::processNote;
private final SongView<?> songView;
protected final AudioFormat format;
private final Consumer<Float> progressConsumer;
Expand All @@ -60,7 +61,7 @@ public void render() throws InterruptedException {
final List<? extends Note> notes = this.songView.getNotesAtTick(tick);
for (Note note : notes) {
if (Thread.currentThread().isInterrupted()) throw new InterruptedException();
this.noteConsumer.playNote(note);
this.playNote(note);
}
this.processedNotes += notes.size();
this.writeSamples();
Expand All @@ -78,7 +79,17 @@ public void write(final AudioFileFormat.Type format, final File file) throws IOE
audioInputStream.close();
}

protected abstract void processNote(final Instrument instrument, final float volume, final float pitch, final float panning);
@Override
public void playNote(final Instrument instrument, final float pitch, final float volume, final float panning) {
this.processSound(SoundMap.INSTRUMENT_SOUNDS.get(instrument), pitch, volume, panning);
}

@Override
public void playCustomNote(final NbsCustomInstrument customInstrument, final float pitch, final float volume, final float panning) {
this.processSound(customInstrument.getSoundFileName().replace(File.separatorChar, '/'), pitch, volume, panning);
}

protected abstract void processSound(final String sound, final float pitch, final float volume, final float panning);

protected abstract void writeSamples();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package net.raphimc.noteblocktool.audio.export.impl;

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;
Expand All @@ -31,7 +30,7 @@

public class JavaxAudioExporter extends AudioExporter {

private final Map<Instrument, int[]> sounds;
private final Map<String, int[]> sounds;
private final Map<String, int[]> mutationCache;
private final AudioMerger merger;

Expand All @@ -43,9 +42,11 @@ public JavaxAudioExporter(final SongView<?> songView, final AudioFormat format,
}

@Override
protected void processNote(final Instrument instrument, final float volume, final float pitch, final float panning) {
String key = instrument + "\0" + volume + "\0" + pitch + "\0" + panning;
this.merger.addSamples(this.mutationCache.computeIfAbsent(key, k -> SoundSampleUtil.mutate(this.format, this.sounds.get(instrument), volume, pitch, panning)));
protected void processSound(final String sound, final float pitch, final float volume, final float panning) {
if (!this.sounds.containsKey(sound)) return;

final String key = sound + "\0" + pitch + "\0" + volume + "\0" + panning;
this.merger.addSamples(this.mutationCache.computeIfAbsent(key, k -> SoundSampleUtil.mutate(this.format, this.sounds.get(sound), pitch, volume, panning)));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
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.impl.OpenALSoundSystem;

Expand All @@ -35,8 +34,8 @@ public OpenALAudioExporter(final OpenALSoundSystem soundSystem, final SongView<?
}

@Override
protected void processNote(final Instrument instrument, final float volume, final float pitch, final float panning) {
this.soundSystem.playNote(instrument, volume, pitch, panning);
protected void processSound(final String sound, final float pitch, final float volume, final float panning) {
this.soundSystem.playSound(sound, pitch, volume, panning);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
*/
package net.raphimc.noteblocktool.audio.soundsystem;

import net.raphimc.noteblocklib.util.Instrument;

public abstract class SoundSystem implements AutoCloseable {

protected final int maxSounds;
Expand All @@ -27,7 +25,7 @@ public SoundSystem(final int maxSounds) {
this.maxSounds = maxSounds;
}

public abstract void playNote(final Instrument instrument, final float volume, final float pitch, final float panning);
public abstract void playSound(final String sound, final float pitch, final float volume, final float panning);

public void writeSamples() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/
package net.raphimc.noteblocktool.audio.soundsystem.impl;

import net.raphimc.noteblocklib.util.Instrument;
import net.raphimc.noteblocktool.audio.SoundMap;
import net.raphimc.noteblocktool.audio.soundsystem.SoundSystem;
import net.raphimc.noteblocktool.util.SoundSampleUtil;
Expand All @@ -33,7 +32,7 @@ public class JavaxSoundSystem extends SoundSystem {

private static final AudioFormat FORMAT = new AudioFormat(44100, 16, 2, true, false);

private final Map<Instrument, int[]> sounds;
private final Map<String, int[]> sounds;
private final int samplesPerTick;
private final SourceDataLine dataLine;
private final Map<String, int[]> mutationCache;
Expand All @@ -56,9 +55,11 @@ public JavaxSoundSystem(final float playbackSpeed) {
}

@Override
public void playNote(final Instrument instrument, final float volume, final float pitch, final float panning) {
final String key = instrument.ordinal() + "\0" + volume + "\0" + pitch + "\0" + panning;
final int[] samples = this.mutationCache.computeIfAbsent(key, k -> SoundSampleUtil.mutate(FORMAT, this.sounds.get(instrument), volume * this.masterVolume, pitch, panning));
public void playSound(final String sound, final float pitch, final float volume, final float panning) {
if (!this.sounds.containsKey(sound)) return;

final String key = sound + "\0" + pitch + "\0" + volume + "\0" + panning;
final int[] samples = this.mutationCache.computeIfAbsent(key, k -> SoundSampleUtil.mutate(FORMAT, this.sounds.get(sound), pitch, volume * this.masterVolume, panning));
if (this.buffer.length < samples.length) this.buffer = Arrays.copyOf(this.buffer, samples.length);
for (int i = 0; i < samples.length; i++) this.buffer[i] += samples[i];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package net.raphimc.noteblocktool.audio.soundsystem.impl;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import net.raphimc.noteblocklib.util.Instrument;
import net.raphimc.noteblocktool.audio.SoundMap;
import net.raphimc.noteblocktool.audio.soundsystem.SoundSystem;
import net.raphimc.noteblocktool.util.IOUtil;
Expand All @@ -30,8 +29,9 @@
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
Expand Down Expand Up @@ -61,7 +61,7 @@ public static OpenALSoundSystem createCapture(final int maxSounds, final AudioFo


private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setNameFormat("OpenAL Sound System").setDaemon(true).build());
private final Map<Instrument, Integer> instrumentBuffers = new EnumMap<>(Instrument.class);
private final Map<String, Integer> soundBuffers = new HashMap<>();
private final List<Integer> playingSources = new CopyOnWriteArrayList<>();
private final AudioFormat audioFormat;
private long device;
Expand Down Expand Up @@ -127,8 +127,12 @@ private OpenALSoundSystem(final int maxSounds, final AudioFormat captureAudioFor
AL10.alListenerfv(AL10.AL_ORIENTATION, new float[]{0F, 0F, -1F, 0F, 1F, 0F});
this.checkError("Could not set listener orientation");

for (Map.Entry<Instrument, String> entry : SoundMap.SOUNDS.entrySet()) {
this.instrumentBuffers.put(entry.getKey(), this.loadAudioFile(OpenALSoundSystem.class.getResourceAsStream(entry.getValue())));
try {
for (Map.Entry<String, URL> entry : SoundMap.SOUND_LOCATIONS.entrySet()) {
this.soundBuffers.put(entry.getKey(), this.loadAudioFile(entry.getValue().openStream()));
}
} catch (Throwable e) {
throw new RuntimeException("Could not load sound samples", e);
}

this.scheduler.scheduleAtFixedRate(this::tick, 0, 100, TimeUnit.MILLISECONDS);
Expand All @@ -145,7 +149,9 @@ private OpenALSoundSystem(final int maxSounds, final AudioFormat captureAudioFor
}

@Override
public void playNote(final Instrument instrument, final float volume, final float pitch, final float panning) {
public void playSound(final String sound, final float pitch, final float volume, final float panning) {
if (!this.soundBuffers.containsKey(sound)) return;

if (this.playingSources.size() >= this.maxSounds) {
AL10.alDeleteSources(this.playingSources.remove(0));
this.checkError("Could not delete audio source");
Expand All @@ -154,12 +160,12 @@ public void playNote(final Instrument instrument, final float volume, final floa
final int source = AL10.alGenSources();
this.checkError("Could not generate audio source");
if (source > 0) {
AL10.alSourcei(source, AL10.AL_BUFFER, this.instrumentBuffers.get(instrument));
AL10.alSourcei(source, AL10.AL_BUFFER, this.soundBuffers.get(sound));
this.checkError("Could not set audio source buffer");
AL10.alSourcef(source, AL10.AL_GAIN, volume);
this.checkError("Could not set audio source volume");
AL10.alSourcef(source, AL10.AL_PITCH, pitch);
this.checkError("Could not set audio source pitch");
AL10.alSourcef(source, AL10.AL_GAIN, volume);
this.checkError("Could not set audio source volume");
AL10.alSource3f(source, AL10.AL_POSITION, panning * 2F, 0F, 0F);
this.checkError("Could not set audio source position");

Expand Down Expand Up @@ -207,8 +213,8 @@ public void close() {
this.shutdownHook = null;
}
this.scheduler.shutdownNow();
this.instrumentBuffers.values().forEach(AL10::alDeleteBuffers);
this.instrumentBuffers.clear();
this.soundBuffers.values().forEach(AL10::alDeleteBuffers);
this.soundBuffers.clear();
this.playingSources.forEach(AL10::alDeleteSources);
this.playingSources.clear();
if (this.context != 0L) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/
package net.raphimc.noteblocktool.elements.drag;

import net.raphimc.noteblocklib.util.Instrument;
import net.raphimc.noteblocklib.util.MinecraftDefinitions;
import net.raphimc.noteblocklib.util.SongUtil;
import net.raphimc.noteblocktool.frames.ListFrame;
Expand Down Expand Up @@ -92,13 +91,9 @@ private CompatibilityResult isSchematicCompatible(final ListFrame.LoadedSong son
return false;
});

SongUtil.iterateAllNotes(song.getSong().getView(), note -> {
if (note.getInstrument() >= Instrument.values().length) {
result.add("The song contains notes with custom instruments");
return true;
}
return false;
});
if (!SongUtil.getUsedCustomInstruments(song.getSong().getView()).isEmpty()) {
result.add("The song contains notes with custom instruments");
}

return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,6 @@ private void writeNbsSong(final ListFrame.LoadedSong song, final File file) {
final NbsSong exportNbsSong = (NbsSong) exportSong;
final NbsHeader exportNbsHeader = exportNbsSong.getHeader();
if (song.getSong() instanceof NbsSong) {
final NbsSong nbsSong = (NbsSong) song.getSong();
final NbsHeader nbsHeader = ((NbsSong) song.getSong()).getHeader();
exportNbsHeader.setVersion((byte) Math.max(nbsHeader.getVersion(), exportNbsHeader.getVersion()));
exportNbsHeader.setAuthor(nbsHeader.getAuthor());
Expand All @@ -423,7 +422,6 @@ private void writeNbsSong(final ListFrame.LoadedSong song, final File file) {
exportNbsHeader.setLoop(nbsHeader.isLoop());
exportNbsHeader.setMaxLoopCount(nbsHeader.getMaxLoopCount());
exportNbsHeader.setLoopStartTick(nbsHeader.getLoopStartTick());
exportNbsSong.getData().setCustomInstruments(nbsSong.getData().getCustomInstruments());
} else if (song.getSong() instanceof McSpSong) {
final McSpHeader mcSpHeader = ((McSpSong) song.getSong()).getHeader();
exportNbsHeader.setAuthor(mcSpHeader.getAuthor());
Expand Down
Loading

0 comments on commit 9d00a51

Please sign in to comment.