From 488895ea535db0859096e88776bbe8da7194c71f Mon Sep 17 00:00:00 2001 From: Shaun Bennett Date: Sun, 6 Dec 2015 21:12:10 -0500 Subject: [PATCH] Note block player --- .../mineplex/core/noteblock/NBSReader.java | 131 ++++++++++++++++++ .../src/mineplex/core/noteblock/Note.java | 26 ++++ .../mineplex/core/noteblock/NoteLayer.java | 50 +++++++ .../mineplex/core/noteblock/NotePlayer.java | 88 ++++++++++++ .../src/mineplex/core/noteblock/NoteSong.java | 57 ++++++++ .../src/mineplex/core/noteblock/UtilNote.java | 42 ++++++ .../src/mineplex/hub/HubManager.java | 19 ++- 7 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/noteblock/NBSReader.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/noteblock/Note.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/noteblock/NoteLayer.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/noteblock/NotePlayer.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/noteblock/NoteSong.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/noteblock/UtilNote.java diff --git a/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NBSReader.java b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NBSReader.java new file mode 100644 index 000000000..1ca887ede --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NBSReader.java @@ -0,0 +1,131 @@ +package mineplex.core.noteblock; + +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashMap; + +/** + * Information about the NBS Format was taken from + * http://www.stuffbydavid.com/mcnbs/format + */ +public class NBSReader +{ + public static NoteSong loadSong(String fileName) throws FileNotFoundException + { + return loadSong(new DataInputStream(new FileInputStream(new File(fileName)))); + } + + public static NoteSong loadSong(DataInputStream stream) + { + try + { + // Header Information + short length = readShort(stream); + short height = readShort(stream); + String name = readString(stream); + String author = readString(stream); + String originalAuthor = readString(stream); + String description = readString(stream); + short tempo = readShort(stream); // Tempo multiplied by 1000 + boolean autosave = stream.readBoolean(); + byte autosaveDuration = stream.readByte(); + byte timeSignature = stream.readByte(); + int minutesSpent = readInt(stream); + int leftClicks = readInt(stream); + int rightClicks = readInt(stream); + int blocksAdded = readInt(stream); + int blocksRemoved = readInt(stream); + String midiFileName = readString(stream); + + HashMap layerMap = new HashMap(); + + // Note Block Information + int tick = -1; + int jumps = 0; + while (true) + { + jumps = readShort(stream); + if (jumps == 0) + break; + tick += jumps; + int layer = -1; + while (true) + { + jumps = readShort(stream); + if (jumps == 0) + break; + layer += jumps; + byte instrument = stream.readByte(); + byte key = stream.readByte(); + + Note note = new Note(instrument, key); + NoteLayer noteLayer = layerMap.get(layer); + if (noteLayer == null) + { + noteLayer = new NoteLayer(); + layerMap.put(layer, noteLayer); + } + noteLayer.setNote(tick, note); + } + } + + // Layer Information + for (int i = 0; i < height; i++) + { + NoteLayer layer = layerMap.get(i); + if (layer != null) + { + layer.setName(readString(stream)); + layer.setVolume(stream.readByte()); + } + } + + System.out.println("[NBSReader] Successfully loaded song " + name + ""); + System.out.println("Tempo: " + tempo); + return new NoteSong(length, height, name, tempo, timeSignature, layerMap); + } + catch (IOException e) + { + e.printStackTrace(); + } + + return null; + } + + private static int readInt(DataInputStream stream) throws IOException + { + // For some reason the bytes are in reverse order from stream.readInt() + int ch1 = stream.read(); + int ch2 = stream.read(); + int ch3 = stream.read(); + int ch4 = stream.read(); + if ((ch1 | ch2 | ch3 | ch4) < 0) + throw new EOFException(); + return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1); + } + + private static short readShort(DataInputStream stream) throws IOException + { + // For some reason the bytes are in reverse order from stream.readShort() + int ch1 = stream.read(); + int ch2 = stream.read(); + if ((ch1 | ch2) < 0) + throw new EOFException(); + return (short)((ch2 << 8) + ch1); + } + + private static String readString(DataInputStream stream) throws IOException + { + int length = readInt(stream); + char[] string = new char[length]; + for (int i = 0; i < length; i++) + { + string[i] = (char) stream.readByte(); + } + return new String(string); + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/noteblock/Note.java b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/Note.java new file mode 100644 index 000000000..223d56011 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/Note.java @@ -0,0 +1,26 @@ +package mineplex.core.noteblock; + +/** + * Represents a single note to be played + */ +public class Note +{ + private byte _instrument; + private byte _note; + + public Note(byte instrument, byte note) + { + _instrument = instrument; + _note = note; + } + + public byte getInstrument() + { + return _instrument; + } + + public byte getNote() + { + return _note; + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NoteLayer.java b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NoteLayer.java new file mode 100644 index 000000000..6c83bbab1 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NoteLayer.java @@ -0,0 +1,50 @@ +package mineplex.core.noteblock; + +import java.util.HashMap; + +/** + * Represents a layer of notes in Note Block Studio + */ +public class NoteLayer +{ + private HashMap _noteMap; // Notes indexed by ticks + private int _volume; // Volume as a percentage 1-100 + private String _name; + + public NoteLayer() + { + _noteMap = new HashMap(); + _volume = 100; + _name = ""; + } + + public int getVolume() + { + return _volume; + } + + public void setVolume(int volume) + { + _volume = volume; + } + + public String getName() + { + return _name; + } + + public void setName(String name) + { + _name = name; + } + + public void setNote(int ticks, Note note) + { + _noteMap.put(ticks, note); + } + + public Note getNote(int ticks) + { + return _noteMap.get(ticks); + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NotePlayer.java b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NotePlayer.java new file mode 100644 index 000000000..19e48f16d --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NotePlayer.java @@ -0,0 +1,88 @@ +package mineplex.core.noteblock; + +import org.bukkit.entity.Player; + +import mineplex.core.common.util.UtilServer; + +public class NotePlayer +{ + private final NoteSong _song; + private final long _sleepMs; + private volatile boolean _loop; + private volatile int _tick; + private volatile boolean _finished; + + public NotePlayer(NoteSong song, boolean loop) + { + _song = song; + _sleepMs = (long) (1000 / (song.getTempo() / 100D)); + _loop = loop; + _tick = 0; + _finished = false; + + startThread(); + } + + private void startThread() + { + Thread thread = new Thread(new Runnable() + { + @Override + public void run() + { + long startTime = System.currentTimeMillis(); + while (!_finished) + { + _tick++; + if (_tick > _song.getLength()) + { + if (_loop) + { + _tick = 1; + } + else + { + _finished = true; + return; + } + } + + playTick(_tick); + + try + { + Thread.sleep(_sleepMs); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + }); + + thread.start(); + } + + private void playTick(int tick) + { + for (NoteLayer layer : _song.getLayers()) + { + Note note = layer.getNote(tick); + if (note != null) + { + float volume = layer.getVolume() / 100F; + for (Player player : UtilServer.getPlayers()) + { + player.playSound(player.getEyeLocation(), UtilNote.getInstrumentSound(note.getInstrument()), volume, (float) UtilNote.getPitch(note.getNote() - 33)); + } + } + } + } + + public void cancel() + { + _finished = true; + } + +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NoteSong.java b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NoteSong.java new file mode 100644 index 000000000..ee892552b --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/NoteSong.java @@ -0,0 +1,57 @@ +package mineplex.core.noteblock; + +import java.util.Collection; +import java.util.HashMap; + +public class NoteSong +{ + // Song Data + private short _length; + private short _height; + private String _name; + private short _tempo; + private byte _timeSignature; + + // Layer Data + private HashMap _layerMap; + + public NoteSong(short length, short height, String name, short tempo, byte timeSignature, HashMap layerMap) + { + _length = length; + _height = height; + _name = name; + _tempo = tempo; + _timeSignature = timeSignature; + _layerMap = layerMap; + } + + public short getLength() + { + return _length; + } + + public short getHeight() + { + return _height; + } + + public String getName() + { + return _name; + } + + public short getTempo() + { + return _tempo; + } + + public byte getTimeSignature() + { + return _timeSignature; + } + + public Collection getLayers() + { + return _layerMap.values(); + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/noteblock/UtilNote.java b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/UtilNote.java new file mode 100644 index 000000000..4ac15643e --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/noteblock/UtilNote.java @@ -0,0 +1,42 @@ +package mineplex.core.noteblock; + +import org.bukkit.Sound; + +/** + * See http://minecraft.gamepedia.com/Note_Block for information about pitches + */ +public class UtilNote +{ + private static final double[] PITCH = { 0.5, 0.53, 0.56, 0.6, 0.63, 0.67, 0.7, 0.76, 0.8, 0.84, 0.9, 0.94, 1.0, + 1.06, 1.12, 1.18, 1.26, 1.34, 1.42, 1.5, 1.6, 1.68, 1.78, 1.88, 2.0 }; + + public static Sound getInstrumentSound(byte instrument) + { + switch (instrument) + { + case 0: + return Sound.NOTE_PIANO; + case 1: + return Sound.NOTE_BASS_GUITAR; + case 2: + return Sound.NOTE_BASS_DRUM; + case 3: + return Sound.NOTE_SNARE_DRUM; + case 4: + return Sound.NOTE_STICKS; + default: + return Sound.NOTE_PIANO; + } + } + + public static double getPitch(int note) + { + if (note >= 0 && note < PITCH.length) + { + return PITCH[note]; + } + + return 0.0; + } + +} diff --git a/Plugins/Mineplex.Hub/src/mineplex/hub/HubManager.java b/Plugins/Mineplex.Hub/src/mineplex/hub/HubManager.java index 4e16d3376..956cd13cc 100644 --- a/Plugins/Mineplex.Hub/src/mineplex/hub/HubManager.java +++ b/Plugins/Mineplex.Hub/src/mineplex/hub/HubManager.java @@ -1,5 +1,6 @@ package mineplex.hub; +import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.HashMap; @@ -34,6 +35,9 @@ import mineplex.core.hologram.HologramManager; import mineplex.core.inventory.InventoryManager; import mineplex.core.message.PrivateMessageEvent; import mineplex.core.mount.MountManager; +import mineplex.core.noteblock.NBSReader; +import mineplex.core.noteblock.NotePlayer; +import mineplex.core.noteblock.NoteSong; import mineplex.core.notifier.NotificationManager; import mineplex.core.npc.NpcManager; import mineplex.core.packethandler.PacketHandler; @@ -219,7 +223,6 @@ public class HubManager extends MiniClientPlugin ((CraftWorld)Bukkit.getWorlds().get(0)).getHandle().pvpMode = true; - new BonusManager(plugin, clientManager, serverStatusManager, donationManager, pollManager , npcManager, hologramManager, statsManager, _inventoryManager, petManager, giveawayManager); // _halloweenManager = new HalloweenSpookinessManager(this); @@ -229,6 +232,20 @@ public class HubManager extends MiniClientPlugin _serverName = getPlugin().getConfig().getString("serverstatus.name"); _serverName = _serverName.substring(0, Math.min(16, _serverName.length())); + +// try +// { +// NoteSong song = NBSReader.loadSong("songs/LetItGo.nbs"); +// if (song != null) +// { +// NotePlayer player = new NotePlayer(song, true); +// } +// } +// catch (FileNotFoundException e) +// { +// e.printStackTrace(); +// System.out.println("FAILED TO LOAD SONG!!"); +// } } @Override