/** * Write a track to an output stream. * * @param track the track to write * @param dos a MidiDataOutputStream to write to * @return the number of bytes written */ private int writeTrack(Track track, MidiDataOutputStream dos) throws IOException { int i = 0, elength = track.size(), trackLength; MidiEvent pme = null; dos.writeInt(0x4d54726b); // "MTrk" trackLength = computeTrackLength(track, dos); dos.writeInt(trackLength); while (i < elength) { MidiEvent me = track.get(i); int dtime = 0; if (pme != null) dtime = (int) (me.getTick() - pme.getTick()); dos.writeVariableLengthInt(dtime); // FIXME: use running status byte byte msg[] = me.getMessage().getMessage(); dos.write(msg); pme = me; i++; } // We're done if the last event was an End of Track meta message. if (pme != null && (pme.getMessage() instanceof MetaMessage)) { MetaMessage mm = (MetaMessage) pme.getMessage(); if (mm.getType() == 0x2f) // End of Track message return trackLength + 8; } // Write End of Track meta message dos.writeVariableLengthInt(0); // Delta time of 0 dos.writeByte(0xff); // Meta Message dos.writeByte(0x2f); // End of Track message dos.writeVariableLengthInt(0); // Length of 0 return trackLength + 8 + 4; }
public static Sequence process( final Sequence inputSequence, int track, long startTick, long endTick, int noteOverlap, int minimumRest, Handler loggingHandler) throws InvalidMidiDataException { Sequence outputSequence = new Sequence(inputSequence.getDivisionType(), inputSequence.getResolution()); Track[] inputTracks = inputSequence.getTracks(); SlurBinderA.noteOverlap = noteOverlap; SlurBinderA.minimumRest = minimumRest; slurEndNotes = new HashSet<>(); for (int trackI = 0; trackI < inputTracks.length; trackI++) { Track inputTrack = inputTracks[trackI]; Track outputTrack = outputSequence.createTrack(); if (trackI == track) { handleTrack(outputTrack, inputTrack, startTick, endTick); } else { for (int eventI = 0; eventI < inputTrack.size(); eventI++) { MidiEvent midEvent = inputTrack.get(eventI); outputTrack.add(midEvent); } } } return outputSequence; }
/** * Binary search for the event indexes of the track * * @param tick tick number of index to be found in array * @return index in track which is on or after "tick". if no entries are found that follow after * tick, track.size() is returned */ public static int tick2index(Track track, long tick) { int ret = 0; if (tick > 0) { int low = 0; int high = track.size() - 1; while (low < high) { // take the middle event as estimate ret = (low + high) >> 1; // tick of estimate long t = track.get(ret).getTick(); if (t == tick) { break; } else if (t < tick) { // estimate too low if (low == high - 1) { // "or after tick" ret++; break; } low = ret; } else { // if (t>tick) // estimate too high high = ret; } } } return ret; }
/** * Compute the length of a track as it will be written to the output stream. * * @param track the track to measure * @param dos a MidiDataOutputStream used for helper method * @return the length of the track */ private int computeTrackLength(Track track, MidiDataOutputStream dos) { int count = 0, length = 0, i = 0, eventCount = track.size(); long ptick = 0; while (i < eventCount) { MidiEvent me = track.get(i); long tick = me.getTick(); length += dos.variableLengthIntLength((int) (tick - ptick)); ptick = tick; length += me.getMessage().getLength(); i++; } return length; }
private void loadNotes() { int program = 0; HashMap<Integer, Float> lastTimeNote = new HashMap<Integer, Float>(); HashMap<Float, Integer> secondsNotes = new HashMap<Float, Integer>(); this.notas = new ArrayList<MIDINote>(); for (Track track : sequencia.getTracks()) { for (int c = 0; c < track.size(); ++c) { MidiEvent event = track.get(c); MidiMessage msg = event.getMessage(); if (msg instanceof ShortMessage) { ShortMessage shortmsg = (ShortMessage) msg; if (shortmsg.getCommand() == ShortMessage.PROGRAM_CHANGE) { program = shortmsg.getData1(); } else { // }else if(program>=25 && program <= 40){ // else if(program== 30){ if (shortmsg.getCommand() == ShortMessage.NOTE_ON) { MIDINote midiNote = new MIDINote(event, sequencia, tempoProcessor, program); // tocador.start(); int noteChord = midiNote.getChord(); float noteSecond = midiNote.getSecond(); if (!lastTimeNote.containsKey(noteChord)) { lastTimeNote.put(noteChord, 0.0f); } if (noteSecond - lastTimeNote.get(noteChord).floatValue() <= this.interval) { continue; } lastTimeNote.put(noteChord, noteSecond); // System.out.println("Play chord "+noteChord+" in "+noteSecond+" seconds"); notas.add(midiNote); if (!secondsNotes.containsKey(noteSecond)) { secondsNotes.put(noteSecond, 1); } secondsNotes.put(noteSecond, secondsNotes.get(noteSecond).intValue() + 1); } } } } } // System.out.println("tamanho da pista "+notas.size()+" e track "+maxNote); for (float second : secondsNotes.keySet()) { int repeated = secondsNotes.get(second).intValue(); if (repeated > maxNote) { this.maxNote = repeated; } } this.notesLength = secondsNotes.size(); // GameEngine.getInstance().setFramesPerSecond((int)(((tocador.getMicrosecondLength()/1000000)/(notas.size()*1.0))*4000)); // System.out.println("(int)(("+sequencia.getMicrosecondLength()+"/1000000)/"+notas.size()+"="+(int)((sequencia.getMicrosecondLength()/1000000)/notas.size())) }
/** * Send entry MIDI Sequence into Receiver using time stamps. * * @return The total length of the sequence. */ private double send(final Sequence seq, final Receiver recv) { assert seq.getDivisionType() == Sequence.PPQ; final float divtype = seq.getDivisionType(); final Track[] tracks = seq.getTracks(); tune(recv); final int[] trackspos = new int[tracks.length]; int mpq = 500000; final int seqres = seq.getResolution(); long lasttick = 0; long curtime = 0; while (true) { MidiEvent selevent = null; int seltrack = -1; for (int i = 0; i < tracks.length; i++) { final int trackpos = trackspos[i]; final Track track = tracks[i]; if (trackpos < track.size()) { final MidiEvent event = track.get(trackpos); if (selevent == null || event.getTick() < selevent.getTick()) { selevent = event; seltrack = i; } } } if (seltrack == -1) { break; } trackspos[seltrack]++; final long tick = selevent.getTick(); if (divtype == Sequence.PPQ) { curtime += (tick - lasttick) * mpq / seqres; } else { curtime = (long) (tick * 1000000.0 * divtype / seqres); } lasttick = tick; final MidiMessage msg = selevent.getMessage(); if (msg instanceof MetaMessage) { if (divtype == Sequence.PPQ && ((MetaMessage) msg).getType() == 0x51) { final byte[] data = ((MetaMessage) msg).getData(); mpq = (data[0] & 0xff) << 16 | (data[1] & 0xff) << 8 | data[2] & 0xff; } } else if (recv != null) { recv.send(msg, curtime); } } return curtime / 1000000.0; }
public static double send(Sequence seq, Receiver recv) { float divtype = seq.getDivisionType(); assert (seq.getDivisionType() == Sequence.PPQ); Track[] tracks = seq.getTracks(); int[] trackspos = new int[tracks.length]; int mpq = 60000000 / 100; int seqres = seq.getResolution(); long lasttick = 0; long curtime = 0; while (true) { MidiEvent selevent = null; int seltrack = -1; for (int i = 0; i < tracks.length; i++) { int trackpos = trackspos[i]; Track track = tracks[i]; if (trackpos < track.size()) { MidiEvent event = track.get(trackpos); if (selevent == null || event.getTick() < selevent.getTick()) { selevent = event; seltrack = i; } } } if (seltrack == -1) break; trackspos[seltrack]++; long tick = selevent.getTick(); if (divtype == Sequence.PPQ) curtime += ((tick - lasttick) * mpq) / seqres; else curtime = (long) ((tick * 1000000.0 * divtype) / seqres); lasttick = tick; MidiMessage msg = selevent.getMessage(); if (msg instanceof MetaMessage) { if (divtype == Sequence.PPQ) if (((MetaMessage) msg).getType() == 0x51) { byte[] data = ((MetaMessage) msg).getData(); mpq = ((data[0] & 0xff) << 16) | ((data[1] & 0xff) << 8) | (data[2] & 0xff); } } else { if (recv != null) recv.send(msg, curtime); } } return curtime / 1000000.0; }
private static void handleTrack( Track outputTrack, Track inputTrack, long startTick, long endTick) { // handle all other events for (int eventI = 0; eventI < inputTrack.size(); eventI++) { MidiEvent midEvent = inputTrack.get(eventI); if (!Note.isNoteOnEvent(midEvent)) { if (!Note.isNoteOffEvent(midEvent)) { outputTrack.add(midEvent); } } } // handle all note events NoteTrack noteTrack = new NoteTrack(inputTrack); for (int i = 0; i < noteTrack.size(); i++) { Note thisNote = noteTrack.get(i); List<Note> followingNotes = findFollowingNotes(noteTrack, i); Note newNote = handleNote(thisNote, followingNotes, startTick, endTick); outputTrack.add(newNote.getNoteOnEvent()); outputTrack.add(newNote.getNoteOffEvent()); } }
public synchronized void refresh(Sequence seq) { ArrayList<MidiEvent> list = new ArrayList<>(); Track[] tracks = seq.getTracks(); if (tracks.length > 0) { // tempo events only occur in track 0 Track track = tracks[0]; int c = track.size(); for (int i = 0; i < c; i++) { MidiEvent ev = track.get(i); MidiMessage msg = ev.getMessage(); if (isMetaTempo(msg)) { // found a tempo event. Add it to the list list.add(ev); } } } int size = list.size() + 1; firstTempoIsFake = true; if ((size > 1) && (list.get(0).getTick() == 0)) { // do not need to add an initial tempo event at the beginning size--; firstTempoIsFake = false; } ticks = new long[size]; tempos = new int[size]; int e = 0; if (firstTempoIsFake) { // add tempo 120 at beginning ticks[0] = 0; tempos[0] = DEFAULT_TEMPO_MPQ; e++; } for (int i = 0; i < list.size(); i++, e++) { MidiEvent evt = list.get(i); ticks[e] = evt.getTick(); tempos[e] = getTempoMPQ(evt.getMessage()); } snapshotIndex = 0; snapshotMicro = 0; }
public static final void addNotesToTrack(Track From, Track To) throws InvalidMidiDataException { for (int i = 0; i < From.size(); i++) { MidiEvent Me = From.get(i); MidiMessage Mm = Me.getMessage(); if (Mm instanceof ShortMessage) { ShortMessage Sm = (ShortMessage) Mm; int Command = Sm.getCommand(); int Com = -1; if (Command == ShortMessage.NOTE_ON) { Com = MetaEventOffset; } else if (Command == ShortMessage.NOTE_OFF) { Com = MetaEventOffset + 1; } if (Com > 0) { byte[] b = Sm.getMessage(); int l = (b == null ? 0 : b.length); MetaMessage MetaMessage = new MetaMessage(Com, b, l); MidiEvent Me2 = new MidiEvent(MetaMessage, Me.getTick()); To.add(Me2); } } } }
public void _testGPWithPlayer(MidiSongDefinition sd, int usq) throws Exception { MidiSongDefinition testFile = SongArchive.testFileSongDefinition(); try { GPInputStream gpis = new GPInputStream(sd.getGpFileName()); GPSong gpsong = (GPSong) gpis.readObject(); gpis.close(); int tempoGPSong = gpsong.getTempo(); // OLD assertEquals((int)(60*1000*1000/usq),tempoGPSong); assertEquals((60 * 1000 * 1000 / usq), tempoGPSong); Song song = GPAdaptor.makeSong(gpsong); Tempo tempoSong = song.getTempo(); assertEquals(usq, (int) tempoSong.getUSQ()); MasterPlayer player = new MasterPlayer(); player.setSoundPlayer(new MidiFiler(testFile.getMidiFileName())); Performance performance = player.arrange(song, null); Tempo tempoPerformance = performance.getTempo(); assertEquals(usq, (int) tempoPerformance.getUSQ()); // a performance is really a sequence. So make sure there // is a tempo event (meta 0x51) on track 0. // make sure as well there is exactly ONE tempo event at timestamp 0 Sequence sequence = (Sequence) performance; Track[] midiTracks = sequence.getTracks(); Track tempoMap = midiTracks[0]; Tempo tempoInMIDI = new Tempo(); for (int i = 0; i < tempoMap.size(); i++) { MidiEvent me = tempoMap.get(i); long tick = me.getTick(); if (tick > 0) break; MidiMessage mm = me.getMessage(); if (mm.getStatus() == MetaMessage.META) { MetaMessage meta = (MetaMessage) mm; if (meta.getType() == 0x51) { byte[] data = meta.getData(); tempoInMIDI.setUSQ( ((data[0] & 0x00FF) << 16) | ((data[1] & 0x00FF) << 8) | ((data[2] & 0x00FF))); break; } } } assertEquals(usq, (int) tempoInMIDI.getUSQ()); MidiOutputStream mos = new MidiOutputStream(new FileOutputStream(testFile.getMidiFileName())); mos.write(performance); mos.close(); compareMIDIFiles( sd.getMidiFileName(), testFile.getMidiFileName(), sd.getChannels(), sd.getEventRemap()); } catch (FileNotFoundException e) { fail("file not found exception"); } catch (GPFormatException e) { fail("gp format exception"); } catch (IOException e) { fail("ioexception"); } catch (CodecFormatException e) { fail("codec format exception"); } catch (InvalidMidiDataException e) { fail("invalid midi data exception"); } }
public static void main(String[] args) { /* * We check that there is exactly 3 command-line * argument. If not, we display the usage message and * exit. */ if (args.length != 3) { System.out.println("DumpSequence: usage:"); System.out.println( "\tjava DumpSequence <midifile> <\"guitar\" | \"bass\" | \"drums\" | \"vocals\"> <\"easy\" | \"medium\" | \"hard\" | \"expert\">"); System.exit(1); } /* * Now, that we're sure there is an argument, we take it as * the filename of the soundfile we want to play. */ String strFilename = args[0]; String selectInstru = args[1]; if (!selectInstru.equals("guitar") && !selectInstru.equals("bass") && !selectInstru.equals("drums") && !selectInstru.equals("vocals")) { System.out.println("invalid instrument"); System.exit(1); } String level = args[2]; int lvl = 0; if (level.equals("easy")) { lvl = 4; } else if (level.equals("medium")) { lvl = 5; } else if (level.equals("hard")) { lvl = 6; } else if (level.equals("expert")) { lvl = 7; } else { System.out.println("invalid level"); System.exit(1); } File midiFile = new File(strFilename); /* * We try to get a Sequence object, which the content * of the MIDI file. */ Sequence sequence = null; try { sequence = MidiSystem.getSequence(midiFile); } catch (Exception e) { e.printStackTrace(); System.exit(1); } // catch (InvalidMidiDataException e) // { // e.printStackTrace(); // System.exit(1); // } // catch (IOException e) // { // e.printStackTrace(); // System.exit(1); // } /* * And now, we output the data. */ if (sequence == null) { System.out.println("Cannot retrieve Sequence."); } else { System.out.println("File: " + strFilename); System.out.println("Instrument: " + selectInstru); System.out.println("Level: " + level); long dur = sequence.getMicrosecondLength(); String strResolutionType = null; if (sequence.getDivisionType() == Sequence.PPQ) { strResolutionType = " ticks per beat"; } else { strResolutionType = " ticks per frame"; } long ticks_per_beat = sequence.getResolution(); // System.out.println(ticks_per_beat); Track[] tracks = sequence.getTracks(); // we want only track with track name "midi_export", "EVENTS" and "PART DRUMS" // create an arrayList with only the index of the tracks we want String timeSig = ""; // , timeSigOther = "", tempoBPM = ""; int useTrack = 0; ArrayList<String[]> events = new ArrayList<String[]>(); for (int nTrack = 0; nTrack < tracks.length; nTrack++) { Track track = tracks[nTrack]; MidiEvent event = track.get(0); MidiMessage m = event.getMessage(); if (m instanceof MetaMessage) { MetaMessage meta = (MetaMessage) m; String trackName = DumpReceiver.myDecodeMessage(meta); trackName = trackName.toLowerCase(); if (trackName.contains(selectInstru)) // get indexes of the tracks which contain the songs sections, and drum notes { useTrack = nTrack; } else if (trackName.contains("midi_export")) // get information about the song // time signature and tempo are entries 2 and 3, where tick ==0 { for (int nEvent = 1; nEvent < track.size(); nEvent++) { event = track.get(nEvent); m = event.getMessage(); long lTicks = event.getTick(); if (lTicks == 0) { String line = DumpReceiver.mySend(m, lTicks).toLowerCase(); // System.out.println(line); if (line.contains("time signature")) { timeSig = line.substring( line.indexOf("time signature: ") + ("time signature: ").length()); timeSig = timeSig.substring(0, timeSig.indexOf(',')); } } } } else if (trackName.contains("events")) // store the song sections, and the tick values where they start { for (int nEvent = 1; nEvent < track.size(); nEvent++) { String[] tick_and_event = new String[2]; event = track.get(nEvent); m = event.getMessage(); long lTicks = event.getTick(); String line = DumpReceiver.mySend(m, lTicks).toLowerCase(); if (line.contains("text event: [section")) { tick_and_event[0] = "" + lTicks; line = line.substring( line.indexOf("text event: [section") + "text event: [section".length(), line.length() - 1); tick_and_event[1] = line; events.add(tick_and_event); } } } } } if (timeSig.equals("")) { // no time signature found. Assume 4/4 timeSig = "4/4"; } // create an ArrayList of all tick indexes we want in our tab ArrayList<Long> allTicks = new ArrayList<Long>(); Track track = tracks[useTrack]; long lastTick = 0; for (int nEvent = 0; nEvent < track.size(); nEvent++) { String line = ""; MidiEvent event = track.get(nEvent); MidiMessage message = event.getMessage(); long lTicks = event.getTick(); if (message instanceof ShortMessage) { line = DumpReceiver.myDecodeMessage((ShortMessage) message).toLowerCase(); if (line.contains("note on") && line.endsWith("" + lvl) && !allTicks.contains(lTicks)) { allTicks.add(lTicks); } } } // allTicks are now all the unique time indexes of notes // create a 2d array, containging the timeTick and all notes played for that timeTick, for the // whole song String[][] masterList = new String[allTicks.size() + 1][0]; // plus one, to take into account for the drum part masterList[0] = new String[] {"0", "B |", "FT|", "T2|", "S |", "HH|", "C |"}; for (int i = 0; i < allTicks.size(); i++) // loop through all saved tick times { String[] oneTick = new String[] {"", "", "", "", "", "", ""}; oneTick[0] = "" + allTicks.get(i); for (int nEvent = 0; nEvent < track.size(); nEvent++) // loop through all events in track { String line = ""; MidiEvent event = track.get(nEvent); MidiMessage message = event.getMessage(); long lTicks = event.getTick(); if (message instanceof ShortMessage && lTicks == allTicks.get(i)) // if it's a short message, and is the tick we're looking for { line = DumpReceiver.myDecodeMessage((ShortMessage) message).toLowerCase(); if (line.contains("note on") && line.endsWith("" + lvl)) { insert(oneTick, line); } } else if (lTicks > allTicks.get(i)) // we've gone past that point in the song { nEvent += track.size(); } } // if there are any notes that are not played, use "-" for (int j = 0; j < oneTick.length; j++) { if (oneTick[j].equals("")) { oneTick[j] = "-"; } } masterList[i + 1] = oneTick; // i+1, since [0] is the names of the drums } // work with time sig long note_amount = Long.valueOf(timeSig.substring(0, timeSig.indexOf("/"))); System.out.println( "timeSig " + timeSig + " ticks_per_beat/note_amount " + ticks_per_beat / note_amount); long note_type = Long.valueOf(timeSig.substring(timeSig.indexOf("/") + 1)); // GENERATE FINAL CONTENT TO BE PRINTED // the amount of --- should be printed in reverse, ie 1---3, is determined by 3. // if time 1 is 0, 3 is ticks_per_beat, and the ticks per beat is 480, --- is printed, then 3 // if time 1 is 0, 3 is ticks_per_beat/2, "" , - is printed, then 3 // if time 1 is 0, 3 is ticks_per_beat/4, "" , nothing is printed, then 3 // every ticks_per_beat*note_amount there should be a bar int noteAmount = 6; // amount of notes defined. 1 is the tick time, anything more is a drum int amountOfBarsPerLine = 4; String[][] complete; ArrayList<String[]> completeList = new ArrayList<String[]>(); ArrayList<String> tickTimesUsed = new ArrayList<String>(); // TODO: fix structure of code. seperate into smaller functions. for (int j = 1; j <= noteAmount; j++) // crash at the top, bass at the bottom { int listIndex = 0; // TODO fix error where events are in margin long bar_index = 0; int barCount = 0; String[] lineArray; String[] eventLineArray; ArrayList<String> line = new ArrayList<String>(); ArrayList<String> eventLine = new ArrayList<String>(); String start = ""; for (int i = 0; i < masterList.length; i++) // loop through all saved tick times, for this drum { if (i > 1) // the symbols for the drum kit, and the very first note should be printed // without anything else in front of them { long currentNoteTick = Long.valueOf(masterList[i][0]); // the tick belonging to the current note long previousNoteTick = Long.valueOf(masterList[i - 1][0]); // the tick belonging to the previous note long diff = currentNoteTick - previousNoteTick; while (diff > (ticks_per_beat / note_amount) + 5) // to allow for some time differences { // NOTE line.add("-"); bar_index++; // update bar_index to reflect adding the "-" diff -= (ticks_per_beat / note_amount); // seems to be 17 for first bar, 16 for the rest if (j == 1) // EVENT { eventLine.add( " "); // have to add an additional gap to eventLine, to keep it the same length // as line } if (bar_index == (note_amount * note_type)) // every (note_amount*note_type)+1 character should be a bar // line { line.add("|"); if (j == 1) // EVENT { eventLine.add( " "); // have to add an additional gap to eventLine, to keep it the same // length as line } bar_index = 0; // reset bar_index, as we are now in a new bar barCount++; if (barCount == amountOfBarsPerLine) // a new line { // int num = 1; if (j == 1) // EVENT { // NOTE // we want to start new line lineArray = new String[line.size()]; lineArray = line.toArray(lineArray); // cast ArrayList to Array completeList.add(listIndex, lineArray); listIndex++; line = new ArrayList<String>(); line.add(start); // always have which drum it is, at the start of the line barCount = 0; // reset barCount for the current line // we want to start new line eventLineArray = new String[eventLine.size()]; eventLineArray = eventLine.toArray(eventLineArray); // cast ArrayList to Array completeList.add(listIndex, eventLineArray); listIndex++; eventLine = new ArrayList<String>(); eventLine.add(" "); // 2 gaps } else { // NOTE // we want to start new line lineArray = new String[line.size()]; lineArray = line.toArray(lineArray); // cast ArrayList to Array completeList.add(listIndex, lineArray); listIndex += j + 1; // + num; //this orders the notes line = new ArrayList<String>(); line.add(start); // always have which drum it is, at the start of the line barCount = 0; // reset barCount for the current line } } } } if (j == 1) // && !tickTimesUsed.contains(""+currentNoteTick)) //EVENT { String s = getEventAtTick(events, "" + currentNoteTick); eventLine.add(s); tickTimesUsed.add("" + currentNoteTick); } } else if (i == 1) // check to see where abouts in the bar the first note should be { long currentNoteTick = Long.valueOf(masterList[i][0]); long gapBeforeFirst = currentNoteTick % (ticks_per_beat * note_amount); while (gapBeforeFirst > 0) { if (j == 1) // && !tickTimesUsed.contains(""+currentNoteTick)) //EVENT { String s = getEventAtTick(events, "" + currentNoteTick); eventLine.add(s); tickTimesUsed.add("" + currentNoteTick); } // NOTE line.add("-"); bar_index++; // update bar_index to reflect adding the "-" gapBeforeFirst -= (ticks_per_beat / note_amount); } } else if (i == 0) // the very first index of an array for a note, ie "B |", "HH|", etc { start += masterList[i][j]; // "B |", "HH|", etc bar_index--; // printing out the first "|" will make bar_index = 1, when we want it to // be 0 } long currentNoteTick = Long.valueOf(masterList[i][0]); // the tick belonging to the current note if (j == 1 && !tickTimesUsed.contains("" + currentNoteTick)) // EVENT { String s = getEventAtTick(events, "" + currentNoteTick); eventLine.add(s); tickTimesUsed.add("" + currentNoteTick); } // NOTE line.add(masterList[i][j]); bar_index++; // update bar_index to reflect adding the note // if adding the note has ended the bar if (bar_index == (note_amount * note_type)) // every (note_amount*note_type)+1 character should be a bar line { line.add("|"); if (j == 1) // EVENT { eventLine.add( " "); // have to add an additional gap to eventLine, to keep it the same length as // line } bar_index = 0; // reset bar_index, as we are now in a new bar barCount++; if (barCount == amountOfBarsPerLine) // a new line { // int num = 1; if (j == 1) // EVENT { // NOTE // we want to start new line lineArray = new String[line.size()]; lineArray = line.toArray(lineArray); // cast ArrayList to Array completeList.add(listIndex, lineArray); listIndex++; line = new ArrayList<String>(); line.add(start); // always have which drum it is, at the start of the line barCount = 0; // reset barCount for the current line // we want to start new line eventLineArray = new String[eventLine.size()]; eventLineArray = eventLine.toArray(eventLineArray); // cast ArrayList to Array completeList.add(listIndex, eventLineArray); listIndex++; eventLine = new ArrayList<String>(); eventLine.add(" "); // 2 gaps } else { // NOTE // we want to start new line lineArray = new String[line.size()]; lineArray = line.toArray(lineArray); // cast ArrayList to Array completeList.add(listIndex, lineArray); listIndex += j + 1; // + num; //this orders the notes line = new ArrayList<String>(); line.add(start); // always have which drum it is, at the start of the line barCount = 0; // reset barCount for the current line } } } if (i == masterList.length - 1) // the very last index of an array for a note. Could be a note, or a "-" { // we want to add this bar to the arrayList, because it is the end, regardless if it's a // full bar lineArray = new String[line.size()]; lineArray = line.toArray(lineArray); // cast ArrayList to Array completeList.add(listIndex, lineArray); listIndex += j; // this orders the notes line = new ArrayList<String>(); line.add(start); // always have which drum it is, at the start of the line barCount = 0; // reset barCount for the current line } } } complete = new String[completeList.size()][]; complete = completeList.toArray(complete); // cast ArrayList to Array // complete is the tab with bar lines // PRINT for (int i = 0; i < complete.length; ++i) { if (i % (noteAmount + 1) == 0) // a new section. Add a gap to make it easier to read. Plus 1, for event line { System.out.println(); } String line = ""; // reset line for (int j = 0; j < complete[i].length; j++) // create a whole line to print { line += complete[i][j]; // a single character } if (line.contains("O") || line.contains("X") || line.substring(3).matches(".*[a-z]+.*")) // the line contains a note // substring, so the [a-z] isn't the drum part, of a blank line { System.out.println(line); } } } }