/**
   * 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;
  }
Beispiel #3
0
 /**
  * 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()))
  }
Beispiel #6
0
  /**
   * 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());
   }
 }
Beispiel #9
0
 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);
       }
     }
   }
 }
Beispiel #11
0
  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);
        }
      }
    }
  }