This commit is contained in:
ayunami2000 2022-01-18 12:45:02 -05:00
parent d1dbd01df9
commit 95a818e7b0
10 changed files with 20011 additions and 19346 deletions

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -18,10 +18,10 @@ public class GuiScreenModules extends GuiScreen {
this.iteminfo = new GuiTextField(this.fontRenderer, this.width / 2 - 98, this.height / 6 + 24, 195, 20);
this.iteminfo.setFocused(true);
this.iteminfo.setText("383");
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 6 + 124, "Give"));
this.buttonList.add(new GuiButton(2, this.width / 2 - 100, this.height / 6 + 100, "Toggle Notebot"));
this.buttonList.add(new GuiButton(3, this.width / 2 - 100, this.height / 6 + 76, "Toggle Legit mode"));
this.buttonList.add(new GuiButton(4, this.width / 2 - 100, this.height / 6 + 52, "Toggle Flight"));
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 6 + 52, "Give"));
this.buttonList.add(new GuiButton(2, this.width / 2 - 100, this.height / 6 + 76, "Toggle Notebot"));
this.buttonList.add(new GuiButton(3, this.width / 2 - 100, this.height / 6 + 100, "Toggle Legit mode"));
this.buttonList.add(new GuiButton(4, this.width / 2 - 100, this.height / 6 + 124, "Toggle Flight"));
}
public void onGuiClosed() {
@ -57,7 +57,7 @@ public class GuiScreenModules extends GuiScreen {
NoteblockPlayer.thr = new Thread(new Runnable() {
@Override
public void run() {
EaglerAdapter.openFileChooser("nbs", "application/nbs");
EaglerAdapter.openFileChooser("nbs", ".nbs,.mid,.midi");
try {
Thread.sleep(1000);
while(!EaglerAdapter.isFocused()){
@ -70,7 +70,7 @@ public class GuiScreenModules extends GuiScreen {
String name = EaglerAdapter.getFileChooserResultName();
NoteblockPlayer.songdata = b;
mc.thePlayer.sendChatToPlayer("Playing \""+name+"\" on notebot!");
NoteblockPlayer.play();
NoteblockPlayer.play((name.toLowerCase().endsWith(".nbs")?NoteblockPlayer.loadSong():me.ayunami2000.ayunAudioStreamer.MidiConverter.midiToTxt()).split("\n"));
}
}
});

View File

@ -0,0 +1,228 @@
package me.ayunami2000.ayunAudioStreamer;
import me.ayunami2000.ayuncraft.NoteblockPlayer;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;
public class MidiConverter {
public static int[] instrument_offsets = new int[] {
54, //harp
0, //basedrum
0, //snare
0, //hat
30, //bass
66, //flute
78, //bell
42, //guitar
78, //chime
78, //xylophone
54, //iron xylophone
66, //cow bell
30, //didgeridoo
54, //bit
54, //banjo
54, //electric piano
};
public static String midiToTxt() {
String midiTxtSong = "";
try {
BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(NoteblockPlayer.songdata));
TreeMap<Long, ArrayList<Integer>> noteMap = MidiConverter.getMidi(bis);
for (Map.Entry<Long, ArrayList<Integer>> entry : noteMap.entrySet()) {
for (int i=0;i<entry.getValue().size();i++) {
long time = entry.getKey();
int note = entry.getValue().get(i);
int velocity = velocityMap.get(entry.getKey()).get(i);
midiTxtSong += ((int) Math.floor(20.0*time/1000.0)) + ":" + (note % 25) + ":" + ((int) Math.floor(note / 25)) + ":" + velocity + "\n";
}
}
if (midiTxtSong.endsWith("\n")) midiTxtSong = midiTxtSong.substring(0, midiTxtSong.length() - 1);
}catch(Exception e){}
return midiTxtSong;
}
public static TreeMap<Long, ArrayList<Integer>> noteMap;
public static TreeMap<Long, ArrayList<Integer>> velocityMap;
public static TreeMap<Long, ArrayList<Integer>> getMidi(BufferedInputStream downloadStream) throws Exception {
noteMap = new TreeMap<>();
velocityMap = new TreeMap<>();
Sequence sequence = MidiSystem.getSequence(downloadStream);
long tpq = sequence.getResolution();
ArrayList<MidiEvent> tempoEvents = new ArrayList<>();
for (Track track : sequence.getTracks()) {
for (int i = 0; i < track.size(); i++) {
MidiEvent event = track.get(i);
MidiMessage message = event.getMessage();
if (message instanceof MetaMessage) {
MetaMessage mm = (MetaMessage) message;
if (mm.getType() == 0x51) { //SET_TEMPO
tempoEvents.add(event);
}
}
}
}
Collections.sort(tempoEvents, new Comparator<MidiEvent>() {
@Override
public int compare(MidiEvent a, MidiEvent b) {
return (Long.valueOf(a.getTick())).compareTo(b.getTick());
}
});
for (Track track : sequence.getTracks()) {
long microTime = 0;
int[] instrumentIds = new int[16];
//int apparent_mpq = (int) (sequence.getMicrosecondLength()/sequence.getTickLength()*tpq);
int mpq = 500000;
int tempoEventIdx = 0;
long prevTick = 0;
for (int i = 0; i < track.size(); i++) {
MidiEvent event = track.get(i);
MidiMessage message = event.getMessage();
while (tempoEventIdx < tempoEvents.size() && event.getTick() > tempoEvents.get(tempoEventIdx).getTick()) {
long deltaTick = tempoEvents.get(tempoEventIdx).getTick() - prevTick;
prevTick = tempoEvents.get(tempoEventIdx).getTick();
microTime += (mpq/tpq) * deltaTick;
MetaMessage mm = (MetaMessage) tempoEvents.get(tempoEventIdx).getMessage();
byte[] data = mm.getData();
int new_mpq = (data[2]&0xFF) | ((data[1]&0xFF)<<8) | ((data[0]&0xFF)<<16);
if (new_mpq != 0) mpq = new_mpq;
tempoEventIdx++;
}
if (message instanceof ShortMessage) {
ShortMessage sm = (ShortMessage) message;
if (sm.getCommand() == ShortMessage.PROGRAM_CHANGE) {
instrumentIds[sm.getChannel()] = sm.getData1();
}
else if (sm.getCommand() == ShortMessage.NOTE_ON) {
if (sm.getData2() == 0) continue;
int key = sm.getData1();
int velocity = sm.getData2();
long deltaTick = event.getTick() - prevTick;
prevTick = event.getTick();
microTime += (mpq/tpq) * deltaTick;
if (sm.getChannel() == 9) {
processMidiNote(128, key, velocity, microTime);
}
else {
processMidiNote(instrumentIds[sm.getChannel()], key, velocity, microTime);
}
}
else {
}
}
}
}
downloadStream.close();
return noteMap;
}
public static void processMidiNote(int midiInstrument, int midiPitch, int midiVelocity, long microTime) {
int[] noteData=noteConv(midiInstrument,midiPitch);
long milliTime = microTime / 1000;
if (noteData[0] >= 0) {
int noteId = (noteData[1]-instrument_offsets[noteData[0]]) + noteData[0]*25;
if (!noteMap.containsKey(milliTime)) {
noteMap.put(milliTime, new ArrayList<Integer>());
velocityMap.put(milliTime, new ArrayList<Integer>());
}
if (!noteMap.get(milliTime).contains(noteId)) {
noteMap.get(milliTime).add(noteId);
velocityMap.get(milliTime).add(midiVelocity);
}
}
}
public static int[] noteConv(int midiInstrument, int midiPitch) {
int minecraftInstrument = -1;
if ((midiInstrument >= 0 && midiInstrument <= 7) || (midiInstrument >= 24 && midiInstrument <= 31)) { //normal
if (midiPitch >= 54 && midiPitch <= 78) {
minecraftInstrument = 0; //piano
}
else if (midiPitch >= 30 && midiPitch <= 54) {
minecraftInstrument = 4; //bass
}
else if (midiPitch >= 78 && midiPitch <= 102) {
minecraftInstrument = 6; //bells
}
}
else if (midiInstrument >= 8 && midiInstrument <= 15) { //chromatic percussion
if (midiPitch >= 54 && midiPitch <= 78) {
minecraftInstrument = 10; //iron xylophone
}
else if (midiPitch >= 78 && midiPitch <= 102) {
minecraftInstrument = 9; //xylophone
}
else if (midiPitch >= 30 && midiPitch <= 54) {
minecraftInstrument = 4; //bass
}
}
else if ((midiInstrument >= 16 && midiInstrument <= 23) || (midiInstrument >= 32 && midiInstrument <= 71) || (midiInstrument >= 80 && midiInstrument <= 111)) { //synth
if (midiPitch >= 54 && midiPitch <= 78) {
minecraftInstrument = 13; //bit
}
else if (midiPitch >= 30 && midiPitch <= 54) { //didgeridoo
minecraftInstrument = 12;
}
else if (midiPitch >= 78 && midiPitch <= 102) { //bells
minecraftInstrument = 6;
}
}
else if ((midiInstrument >= 72 && midiInstrument <= 79)) { //woodwind
if (midiPitch >= 66 && midiPitch <= 90) {
minecraftInstrument = 5; //flute
}
else if (midiPitch >= 30 && midiPitch <= 54) { //didgeridoo
minecraftInstrument = 12;
}
else if (midiPitch >= 54 && midiPitch <= 78) {
minecraftInstrument = 13; //bit
}
else if (midiPitch >= 78 && midiPitch <= 102) { //bells
minecraftInstrument = 6;
}
}
else if (midiInstrument == 128) {
if (midiPitch == 35 || midiPitch == 36 || midiPitch == 41 || midiPitch == 43 || midiPitch == 45 || midiPitch == 57) {
minecraftInstrument = 1; //bass drum
}
else if (midiPitch == 38 || midiPitch == 39 || midiPitch == 40 || midiPitch == 54 || midiPitch == 69 || midiPitch == 70 || midiPitch == 73 || midiPitch == 74 || midiPitch == 78 || midiPitch == 79) {
minecraftInstrument = 2; //snare
}
else if (midiPitch == 37 || midiPitch == 42 || midiPitch == 44 || midiPitch == 46 || midiPitch == 49 || midiPitch == 51 || midiPitch == 52 || midiPitch == 55 || midiPitch == 57 || midiPitch == 59) {
minecraftInstrument = 3; //hat
}
midiPitch = 0;
}
return new int[]{minecraftInstrument,midiPitch};
}
}

View File

@ -74,10 +74,9 @@ public class NoteblockPlayer {
mc.thePlayer.rotationPitch=pitch%360.0F;
}
public static void play(){
public static void play(String[] songLines){
playingSong=false;
playing=true;
String[] songLines=loadSong().split("\n");
HashMap<Integer, HashMap<Integer, Vec3>> songBlocks=songLinesToBlocks(songLines);
if(playing)placeAndTuneNoteblocks(songBlocks);
if(playing) {

View File

@ -15,13 +15,20 @@ public enum Instrument {
FLUTE (6),
BELL (7),
CHIME (8),
XYLOPHONE (9);
XYLOPHONE (9),
IRON_XYLOPHONE (10),
COW_BELL (11),
DIDGERIDOO (12),
BIT (13),
BANJO (14),
PLING (15),
CUSTOM (-1);
private final int ID;
private Instrument(int ID) {
this.ID = ID;
}
/**
* Returns an ID of the instrument to be written in NBS files.
* @return The ID.
@ -29,26 +36,32 @@ public enum Instrument {
public int getID() {
return ID;
}
/**
* Determines the instrument from its NBS file ID.
* @param ID The instrument ID (0-9).
* @param ID The instrument ID (0-15).
* @return The corresponding instrument.
* @throws IllegalArgumentException
*/
public static Instrument fromID(int ID) throws IllegalArgumentException {
switch (ID) {
case 0: return HARP;
case 1: return BASS;
case 2: return DRUM;
case 3: return SNARE;
case 4: return CLICK;
case 5: return GUITAR;
case 6: return FLUTE;
case 7: return BELL;
case 8: return CHIME;
case 9: return XYLOPHONE;
default: throw new IllegalArgumentException("ID must be from 1 to 9.");
case 0: return HARP;
case 1: return BASS;
case 2: return DRUM;
case 3: return SNARE;
case 4: return CLICK;
case 5: return GUITAR;
case 6: return FLUTE;
case 7: return BELL;
case 8: return CHIME;
case 9: return XYLOPHONE;
case 10: return IRON_XYLOPHONE;
case 11: return COW_BELL;
case 12: return DIDGERIDOO;
case 13: return BIT;
case 14: return BANJO;
case 15: return PLING;
default: return CUSTOM;//throw new IllegalArgumentException("ID must be from 0 to 15.");
}
}
}
}

View File

@ -8,17 +8,29 @@ package me.ayunami2000.ayuncraft.nbsapi;
public class Note {
private Instrument instrument;
private byte pitch;
private byte velocity;
private int panning;
private short precisePitch;
/**
* A note in a song.
* @param instrument The instrument of the note.
* @param pitch The pitch of the note (0-87).
* @param velocity The velocity of the note (0-100).
* @param panning The panning of the note (0-100).
* @param precisePitch The precise pitch of the note (-32767-32767).
* @throws IllegalArgumentException
*/
public Note(Instrument instrument, byte pitch) throws IllegalArgumentException {
public Note(Instrument instrument, byte pitch, byte velocity, int panning, short precisePitch) throws IllegalArgumentException {
if (pitch < 0 || pitch > 87) throw new IllegalArgumentException("Pitch must be from 0 to 87.");
if (velocity < 0 || velocity > 100) throw new IllegalArgumentException("Velocity must be from 0 to 100.");
if (panning < 0 || panning > 200) throw new IllegalArgumentException("Panning must be from 0 to 100.");
if (precisePitch < -32767 || precisePitch > 32767) throw new IllegalArgumentException("Precise pitch must be from -32767 to 32767.");
setInstrument(instrument);
setPitch(pitch);
setVelocity(velocity);
setPanning(panning);
setPrecisePitch(precisePitch);
}
public Instrument getInstrument() {
@ -37,4 +49,31 @@ public class Note {
if (pitch < 0 || pitch > 87) throw new IllegalArgumentException("Pitch must be from 0 to 87.");
this.pitch = pitch;
}
}
public byte getVelocity() {
return velocity;
}
public void setVelocity(byte velocity) throws IllegalArgumentException {
if (velocity < 0 || velocity > 100) throw new IllegalArgumentException("Velocity must be from 0 to 100.");
this.velocity = velocity;
}
public int getPanning() {
return panning;
}
public void setPanning(int panning) throws IllegalArgumentException {
if (panning < 0 || panning > 200) throw new IllegalArgumentException("Panning must be from 0 to 100.");
this.panning = panning;
}
public short getPrecisePitch() {
return precisePitch;
}
public void setPrecisePitch(short precisePitch) throws IllegalArgumentException {
if (precisePitch < -32767 || precisePitch > 32767) throw new IllegalArgumentException("Precise pitch must be from -32767 to 32767.");
this.precisePitch = precisePitch;
}
}

View File

@ -1,5 +1,7 @@
package me.ayunami2000.ayuncraft.nbsapi;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.*;
import java.io.*;
@ -26,12 +28,13 @@ public class Song {
private int blocksRemoved;
private String MidiSchematicFile;
private List<Layer> songBoard;
private boolean isONBS=false;
private ByteArrayInputStream instream;
private DataInputStream in;
private FileOutputStream outstream;
private DataOutputStream out;
/**
* Builds a new song with the given information.
* @param length
@ -49,6 +52,7 @@ public class Song {
* @param blocksAdded
* @param blocksRemoved
* @param MidiSchematicFile
* @param isONBS
* @param songBoard
* @throws IllegalArgumentException
*/
@ -68,6 +72,7 @@ public class Song {
int blocksAdded,
int blocksRemoved,
String MidiSchematicFile,
boolean isONBS,
List<Layer> songBoard) throws IllegalArgumentException {
setLength(length);
setName(name);
@ -84,17 +89,24 @@ public class Song {
setBlocksRemoved(blocksRemoved);
setMidiSchematicFile(MidiSchematicFile);
changeSongBoardTo(songBoard);
setIsONBS(isONBS);
}
/**
* Reads a song from a byte array.
* @param fromBytes The byte array that should be read.
* Reads a song from a file.
* @param fromBytes The file that should be read.
* @throws IOException
*/
public Song(byte[] fromBytes) throws IOException {
instream = new ByteArrayInputStream(fromBytes);
in = new DataInputStream(instream);
setLength(readShort());
setIsONBS(length==0);
if(isONBS){
byte onbsVersion=in.readByte();
byte vanillaInstrumentCount=in.readByte();
setLength(readShort());
}
setHeight(readShort());
setName(readString());
setAuthor(readString());
@ -110,7 +122,12 @@ public class Song {
setBlocksAdded(readInt());
setBlocksRemoved(readInt());
setMidiSchematicFile(readString());
if(isONBS){
byte loop=in.readByte();
byte maxLoopCount=in.readByte();
short loopStartTick=readShort();
}
songBoard = new ArrayList<Layer>();
for (int i = 0; i < height; i++) songBoard.add(new Layer("",(byte) 100));
int tick = -1;
@ -126,17 +143,23 @@ public class Song {
while (songBoard.size() < layer+1) {
songBoard.add(new Layer("",(byte) 100));
}
songBoard.get(layer).setNote(tick, new Note(Instrument.fromID(in.readByte()), in.readByte()));
songBoard.get(layer).setNote(tick, new Note(Instrument.fromID(in.readByte()), in.readByte(), isONBS ? in.readByte() : 100, isONBS ? (in.readByte() & 0xFF) : 100, isONBS ? ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putShort(in.readShort()).order(ByteOrder.LITTLE_ENDIAN).getShort(0) : 0));
}
}
for (int i = 0; i < getHeight(); i++) {
songBoard.get(i).setName(readString());
songBoard.get(i).setVolume(in.readByte());
if(isONBS){
byte lock=in.readByte();
songBoard.get(i).setVolume(in.readByte());
byte stereo=in.readByte();
}else{
songBoard.get(i).setVolume(in.readByte());
}
}
in.close();
instream.close();
}
/**
* Writes the song to the specific file.
* @param toFile The file to write to.
@ -153,7 +176,7 @@ public class Song {
}
setLength((short) Math.max(1, maxLength));
setHeight((short) songBoard.size());
outstream = new FileOutputStream(toFile);
out = new DataOutputStream(outstream);
writeShort(length);
@ -172,7 +195,7 @@ public class Song {
writeInt(blocksAdded);
writeInt(blocksRemoved);
writeString(MidiSchematicFile);
List<WritableNote> noteList = Utils.convertToWritable(songBoard);
int oldTick = -1;
int oldLayer = -1;
@ -190,17 +213,17 @@ public class Song {
}
writeShort((short)0);
writeShort((short)0);
for (Layer l : songBoard) {
writeString(l.getName());
out.writeByte(l.getVolume());
}
out.writeByte(0);
out.close();
outstream.close();
}
public short getLength() {
return length;
}
@ -244,7 +267,8 @@ public class Song {
}
public void setTempo(short tempo) throws IllegalArgumentException {
if (tempo < 25) throw new IllegalArgumentException("Tempo is too small!");
if (tempo%25 != 0) throw new IllegalArgumentException("Tempo must be a multiplication of 25.");
//if (tempo%25 != 0) throw new IllegalArgumentException("Tempo must be a multiplication of 25.");
if (tempo%25 != 0) tempo = (short) (25*(tempo/25));
this.tempo = tempo;
}
public boolean isAutoSaveEnabled() {
@ -308,6 +332,12 @@ public class Song {
public void setMidiSchematicFile(String midiSchematicFile) {
MidiSchematicFile = midiSchematicFile;
}
public void setIsONBS(boolean bool){
this.isONBS=bool;
}
public boolean getIsONBS(){
return isONBS;
}
public List<Layer> getSongBoard() {
return songBoard;
@ -316,52 +346,52 @@ public class Song {
public void changeSongBoardTo(List<Layer> songBoard) {
this.songBoard = songBoard;
}
// The code below is imported from xxmicloxx's NoteBlockAPI (LGPL 3.0).
private short readShort() throws IOException {
int byte1 = in.readUnsignedByte();
int byte2 = in.readUnsignedByte();
return (short) (byte1 + (byte2 << 8));
int byte2 = in.readUnsignedByte();
return (short) (byte1 + (byte2 << 8));
}
private int readInt() throws IOException {
int byte1 = in.readUnsignedByte();
int byte2 = in.readUnsignedByte();
int byte3 = in.readUnsignedByte();
int byte4 = in.readUnsignedByte();
return (byte1 + (byte2 << 8) + (byte3 << 16) + (byte4 << 24));
}
int byte1 = in.readUnsignedByte();
int byte2 = in.readUnsignedByte();
int byte3 = in.readUnsignedByte();
int byte4 = in.readUnsignedByte();
return (byte1 + (byte2 << 8) + (byte3 << 16) + (byte4 << 24));
}
private String readString() throws IOException {
int length = readInt();
StringBuilder sb = new StringBuilder(length);
for (; length > 0; --length) {
char c = (char) in.readByte();
if (c == (char) 0x0D) {
c = ' ';
}
sb.append(c);
}
return sb.toString();
}
int length = readInt();
StringBuilder sb = new StringBuilder(length);
for (; length > 0; --length) {
char c = (char) in.readByte();
if (c == (char) 0x0D) {
c = ' ';
}
sb.append(c);
}
return sb.toString();
}
// End of inported code.
private void writeShort(short num) throws IOException {
out.writeByte(num%256);
out.writeByte(num/256);
}
private void writeInt(int num) throws IOException {
out.writeByte(num%256);
out.writeByte(num%65536/256);
out.writeByte(num%16777216/65536);
out.writeByte(num/16777216);
}
private void writeString(String str) throws IOException {
writeInt(str.length());
out.writeBytes(str);
}
}
}

View File

@ -37,12 +37,7 @@ public class Utils {
thisTick.add(new WritableNote(n.getInstrument(), n.getPitch(), currentLayer, i));
}
}
Collections.sort(thisTick, new Comparator<WritableNote>() {
@Override
public int compare(WritableNote note1, WritableNote note2) {
return Integer.compare(note1.getLayer(), note2.getLayer());
}
});
Collections.sort(thisTick, Comparator.comparing(WritableNote::getLayer));
result.addAll(thisTick);
}
return result;