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 = new GuiTextField(this.fontRenderer, this.width / 2 - 98, this.height / 6 + 24, 195, 20);
this.iteminfo.setFocused(true); this.iteminfo.setFocused(true);
this.iteminfo.setText("383"); this.iteminfo.setText("383");
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 6 + 124, "Give")); 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 + 100, "Toggle Notebot")); 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 + 76, "Toggle Legit mode")); 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 + 52, "Toggle Flight")); this.buttonList.add(new GuiButton(4, this.width / 2 - 100, this.height / 6 + 124, "Toggle Flight"));
} }
public void onGuiClosed() { public void onGuiClosed() {
@ -57,7 +57,7 @@ public class GuiScreenModules extends GuiScreen {
NoteblockPlayer.thr = new Thread(new Runnable() { NoteblockPlayer.thr = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
EaglerAdapter.openFileChooser("nbs", "application/nbs"); EaglerAdapter.openFileChooser("nbs", ".nbs,.mid,.midi");
try { try {
Thread.sleep(1000); Thread.sleep(1000);
while(!EaglerAdapter.isFocused()){ while(!EaglerAdapter.isFocused()){
@ -70,7 +70,7 @@ public class GuiScreenModules extends GuiScreen {
String name = EaglerAdapter.getFileChooserResultName(); String name = EaglerAdapter.getFileChooserResultName();
NoteblockPlayer.songdata = b; NoteblockPlayer.songdata = b;
mc.thePlayer.sendChatToPlayer("Playing \""+name+"\" on notebot!"); 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; mc.thePlayer.rotationPitch=pitch%360.0F;
} }
public static void play(){ public static void play(String[] songLines){
playingSong=false; playingSong=false;
playing=true; playing=true;
String[] songLines=loadSong().split("\n");
HashMap<Integer, HashMap<Integer, Vec3>> songBlocks=songLinesToBlocks(songLines); HashMap<Integer, HashMap<Integer, Vec3>> songBlocks=songLinesToBlocks(songLines);
if(playing)placeAndTuneNoteblocks(songBlocks); if(playing)placeAndTuneNoteblocks(songBlocks);
if(playing) { if(playing) {

View File

@ -15,7 +15,14 @@ public enum Instrument {
FLUTE (6), FLUTE (6),
BELL (7), BELL (7),
CHIME (8), 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 final int ID;
private Instrument(int ID) { private Instrument(int ID) {
@ -32,23 +39,29 @@ public enum Instrument {
/** /**
* Determines the instrument from its NBS file 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. * @return The corresponding instrument.
* @throws IllegalArgumentException * @throws IllegalArgumentException
*/ */
public static Instrument fromID(int ID) throws IllegalArgumentException { public static Instrument fromID(int ID) throws IllegalArgumentException {
switch (ID) { switch (ID) {
case 0: return HARP; case 0: return HARP;
case 1: return BASS; case 1: return BASS;
case 2: return DRUM; case 2: return DRUM;
case 3: return SNARE; case 3: return SNARE;
case 4: return CLICK; case 4: return CLICK;
case 5: return GUITAR; case 5: return GUITAR;
case 6: return FLUTE; case 6: return FLUTE;
case 7: return BELL; case 7: return BELL;
case 8: return CHIME; case 8: return CHIME;
case 9: return XYLOPHONE; case 9: return XYLOPHONE;
default: throw new IllegalArgumentException("ID must be from 1 to 9."); 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 { public class Note {
private Instrument instrument; private Instrument instrument;
private byte pitch; private byte pitch;
private byte velocity;
private int panning;
private short precisePitch;
/** /**
* A note in a song. * A note in a song.
* @param instrument The instrument of the note. * @param instrument The instrument of the note.
* @param pitch The pitch of the note (0-87). * @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 * @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 (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); setInstrument(instrument);
setPitch(pitch); setPitch(pitch);
setVelocity(velocity);
setPanning(panning);
setPrecisePitch(precisePitch);
} }
public Instrument getInstrument() { 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."); if (pitch < 0 || pitch > 87) throw new IllegalArgumentException("Pitch must be from 0 to 87.");
this.pitch = pitch; 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; package me.ayunami2000.ayuncraft.nbsapi;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.*; import java.util.*;
import java.io.*; import java.io.*;
@ -26,6 +28,7 @@ public class Song {
private int blocksRemoved; private int blocksRemoved;
private String MidiSchematicFile; private String MidiSchematicFile;
private List<Layer> songBoard; private List<Layer> songBoard;
private boolean isONBS=false;
private ByteArrayInputStream instream; private ByteArrayInputStream instream;
private DataInputStream in; private DataInputStream in;
@ -49,6 +52,7 @@ public class Song {
* @param blocksAdded * @param blocksAdded
* @param blocksRemoved * @param blocksRemoved
* @param MidiSchematicFile * @param MidiSchematicFile
* @param isONBS
* @param songBoard * @param songBoard
* @throws IllegalArgumentException * @throws IllegalArgumentException
*/ */
@ -68,6 +72,7 @@ public class Song {
int blocksAdded, int blocksAdded,
int blocksRemoved, int blocksRemoved,
String MidiSchematicFile, String MidiSchematicFile,
boolean isONBS,
List<Layer> songBoard) throws IllegalArgumentException { List<Layer> songBoard) throws IllegalArgumentException {
setLength(length); setLength(length);
setName(name); setName(name);
@ -84,17 +89,24 @@ public class Song {
setBlocksRemoved(blocksRemoved); setBlocksRemoved(blocksRemoved);
setMidiSchematicFile(MidiSchematicFile); setMidiSchematicFile(MidiSchematicFile);
changeSongBoardTo(songBoard); changeSongBoardTo(songBoard);
setIsONBS(isONBS);
} }
/** /**
* Reads a song from a byte array. * Reads a song from a file.
* @param fromBytes The byte array that should be read. * @param fromBytes The file that should be read.
* @throws IOException * @throws IOException
*/ */
public Song(byte[] fromBytes) throws IOException { public Song(byte[] fromBytes) throws IOException {
instream = new ByteArrayInputStream(fromBytes); instream = new ByteArrayInputStream(fromBytes);
in = new DataInputStream(instream); in = new DataInputStream(instream);
setLength(readShort()); setLength(readShort());
setIsONBS(length==0);
if(isONBS){
byte onbsVersion=in.readByte();
byte vanillaInstrumentCount=in.readByte();
setLength(readShort());
}
setHeight(readShort()); setHeight(readShort());
setName(readString()); setName(readString());
setAuthor(readString()); setAuthor(readString());
@ -110,6 +122,11 @@ public class Song {
setBlocksAdded(readInt()); setBlocksAdded(readInt());
setBlocksRemoved(readInt()); setBlocksRemoved(readInt());
setMidiSchematicFile(readString()); setMidiSchematicFile(readString());
if(isONBS){
byte loop=in.readByte();
byte maxLoopCount=in.readByte();
short loopStartTick=readShort();
}
songBoard = new ArrayList<Layer>(); songBoard = new ArrayList<Layer>();
for (int i = 0; i < height; i++) songBoard.add(new Layer("",(byte) 100)); for (int i = 0; i < height; i++) songBoard.add(new Layer("",(byte) 100));
@ -126,12 +143,18 @@ public class Song {
while (songBoard.size() < layer+1) { while (songBoard.size() < layer+1) {
songBoard.add(new Layer("",(byte) 100)); 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++) { for (int i = 0; i < getHeight(); i++) {
songBoard.get(i).setName(readString()); 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(); in.close();
instream.close(); instream.close();
@ -244,7 +267,8 @@ public class Song {
} }
public void setTempo(short tempo) throws IllegalArgumentException { public void setTempo(short tempo) throws IllegalArgumentException {
if (tempo < 25) throw new IllegalArgumentException("Tempo is too small!"); 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; this.tempo = tempo;
} }
public boolean isAutoSaveEnabled() { public boolean isAutoSaveEnabled() {
@ -308,6 +332,12 @@ public class Song {
public void setMidiSchematicFile(String midiSchematicFile) { public void setMidiSchematicFile(String midiSchematicFile) {
MidiSchematicFile = midiSchematicFile; MidiSchematicFile = midiSchematicFile;
} }
public void setIsONBS(boolean bool){
this.isONBS=bool;
}
public boolean getIsONBS(){
return isONBS;
}
public List<Layer> getSongBoard() { public List<Layer> getSongBoard() {
return songBoard; return songBoard;
@ -321,30 +351,30 @@ public class Song {
private short readShort() throws IOException { private short readShort() throws IOException {
int byte1 = in.readUnsignedByte(); int byte1 = in.readUnsignedByte();
int byte2 = in.readUnsignedByte(); int byte2 = in.readUnsignedByte();
return (short) (byte1 + (byte2 << 8)); return (short) (byte1 + (byte2 << 8));
} }
private int readInt() throws IOException { private int readInt() throws IOException {
int byte1 = in.readUnsignedByte(); int byte1 = in.readUnsignedByte();
int byte2 = in.readUnsignedByte(); int byte2 = in.readUnsignedByte();
int byte3 = in.readUnsignedByte(); int byte3 = in.readUnsignedByte();
int byte4 = in.readUnsignedByte(); int byte4 = in.readUnsignedByte();
return (byte1 + (byte2 << 8) + (byte3 << 16) + (byte4 << 24)); return (byte1 + (byte2 << 8) + (byte3 << 16) + (byte4 << 24));
} }
private String readString() throws IOException { private String readString() throws IOException {
int length = readInt(); int length = readInt();
StringBuilder sb = new StringBuilder(length); StringBuilder sb = new StringBuilder(length);
for (; length > 0; --length) { for (; length > 0; --length) {
char c = (char) in.readByte(); char c = (char) in.readByte();
if (c == (char) 0x0D) { if (c == (char) 0x0D) {
c = ' '; c = ' ';
} }
sb.append(c); sb.append(c);
} }
return sb.toString(); return sb.toString();
} }
// End of inported code. // End of inported code.

View File

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