Compare commits

...

2 commits

Author SHA1 Message Date
amy
c2253055a0
fix: allow playing long songs
This is actually an unsigned short, not a short.
See https://noteblock.world/song/b91BWfUTnH for an example
2026-03-29 16:03:31 -03:00
amy
84df266240
feat: support trumpets in nbs v5 format 2026-03-29 15:39:30 -03:00
2 changed files with 35 additions and 22 deletions

View file

@ -30,24 +30,20 @@ public class Instrument {
public final int offset; public final int offset;
public final String sound; public final String sound;
private Instrument (final int id, final String name, final int offset, final String sound) { protected Instrument (final int id, final String name, final int offset, final String sound) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.offset = offset; this.offset = offset;
this.sound = sound; this.sound = sound;
} }
private Instrument (final int id, final String name, final int offset) { protected Instrument (final int id, final String name, final int offset) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.offset = offset; this.offset = offset;
this.sound = "block.note_block." + name; this.sound = "block.note_block." + name;
} }
public static Instrument of (final String sound) {
return new Instrument(-1, null, 0, sound);
}
public static Instrument fromId (final int id) { public static Instrument fromId (final int id) {
return VALUES[id]; return VALUES[id];
} }

View file

@ -4,6 +4,9 @@ import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import net.minecraft.resources.Identifier;
import net.minecraft.util.Util;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class NBSConverter { public class NBSConverter {
@ -75,9 +78,9 @@ public class NBSConverter {
} }
final ArrayList<NBSNote> nbsNotes = new ArrayList<>(); final ArrayList<NBSNote> nbsNotes = new ArrayList<>();
short tick = -1; int tick = -1;
while (true) { while (true) {
final int tickJumps = buffer.getShort(); final int tickJumps = Short.toUnsignedInt(buffer.getShort());
if (tickJumps == 0) break; if (tickJumps == 0) break;
tick += tickJumps; tick += tickJumps;
@ -120,12 +123,12 @@ public class NBSConverter {
if (buffer.hasRemaining()) { if (buffer.hasRemaining()) {
final byte customInstrumentCount = buffer.get(); final byte customInstrumentCount = buffer.get();
for (int i = 0; i < customInstrumentCount; i++) { for (int i = 0; i < customInstrumentCount; i++) {
final NBSCustomInstrument customInstrument = new NBSCustomInstrument(); final String name = getString(buffer, bytes.length);
customInstrument.name = getString(buffer, bytes.length); final String file = getString(buffer, bytes.length);
customInstrument.file = getString(buffer, bytes.length); final byte pitch = buffer.get();
customInstrument.pitch = buffer.get(); final boolean visible = buffer.get() != 0; // "Press piano key"
customInstrument.key = buffer.get() != 0;
customInstruments.add(customInstrument); customInstruments.add(new NBSCustomInstrument(name, file, pitch, visible));
} }
} }
@ -145,12 +148,10 @@ public class NBSConverter {
key = (double) ((note.key * 100) + note.pitch) / 100; key = (double) ((note.key * 100) + note.pitch) / 100;
} else { } else {
final int index = note.instrument - vanillaInstrumentCount; final int index = note.instrument - vanillaInstrumentCount;
if (index >= customInstruments.size()) continue; if (index >= customInstruments.size()) continue;
final NBSCustomInstrument customInstrument = customInstruments.get(index); final NBSCustomInstrument customInstrument = customInstruments.get(index);
instrument = Instrument.of(customInstrument.name);
instrument = customInstrument;
key = (note.key) + (customInstrument.pitch + (double) note.pitch / 100); key = (note.key) + (customInstrument.pitch + (double) note.pitch / 100);
} }
@ -220,10 +221,26 @@ public class NBSConverter {
public byte stereo = 100; public byte stereo = 100;
} }
private static class NBSCustomInstrument { private static class NBSCustomInstrument extends Instrument {
public String name; public final byte pitch;
public String file; public final boolean visible;
public byte pitch = 0;
public boolean key = false; public NBSCustomInstrument(final String name, final String optionalPath, final byte pitch, final boolean visible) {
// some implementations straight up just don't save the path, so we perform conversions on name on those
final String path = optionalPath.isBlank() ? name : optionalPath;
String sound = Util.sanitizeName(path, Identifier::isAllowedInIdentifier)
.replaceFirst("\\.ogg$", "")
.replaceFirst("^minecraft/", "")
.replaceFirst("\\d+$", "")
.replace('/', '.');
// if there's no path, it's probably a note block sound
// this happens when you have trumpets saved in a v5 format NBS for example
if (!sound.contains(".")) sound = "block.note_block." + sound;
super(-1, name, 0, sound);
this.pitch = pitch;
this.visible = visible;
}
} }
} }