Example #1
0
  public static void convertMidi2RealTime(Sequence seqIn) {
    seq = seqIn;
    double currentTempo = 500000;
    int tickOfTempoChange = 0;
    double msb4 = 0;
    double division = seq.getResolution();
    int lastTick = 0;
    int count = 0;
    for (int track = 0; track < seq.getTracks().length; track++) nextMessageOf.add(0);
    System.out.println();

    MidiEvent nextEvent;
    while ((nextEvent = getNextEvent()) != null) {
      int tick = (int) nextEvent.getTick();
      if (noteIsOff(nextEvent)) {
        double time =
            (msb4 + (((currentTempo / seq.getResolution()) / 1000) * (tick - tickOfTempoChange)));
        System.out.println(
            "track="
                + currentTrack
                + " tick="
                + tick
                + " time="
                + (int) (time + 0.5)
                + "ms "
                + " note "
                + ((int) nextEvent.getMessage().getMessage()[1] & 0xFF)
                + " off");
      } else if (noteIsOn(nextEvent)) {
        double time =
            (msb4 + (((currentTempo / seq.getResolution()) / 1000) * (tick - tickOfTempoChange)));
        System.out.println(
            "track="
                + currentTrack
                + " tick="
                + tick
                + " time="
                + (int) (time + 0.5)
                + "ms "
                + " note "
                + ((int) nextEvent.getMessage().getMessage()[1] & 0xFF)
                + " on");
      } else if (changeTemp(nextEvent)) {
        String a = (Integer.toHexString((int) nextEvent.getMessage().getMessage()[3] & 0xFF));
        String b = (Integer.toHexString((int) nextEvent.getMessage().getMessage()[4] & 0xFF));
        String c = (Integer.toHexString((int) nextEvent.getMessage().getMessage()[5] & 0xFF));
        if (a.length() == 1) a = ("0" + a);
        if (b.length() == 1) b = ("0" + b);
        if (c.length() == 1) c = ("0" + c);
        String whole = a + b + c;
        int newTempo = Integer.parseInt(whole, 16);
        double newTime = (currentTempo / seq.getResolution()) * (tick - tickOfTempoChange);
        msb4 += (newTime / 1000);
        tickOfTempoChange = tick;
        currentTempo = newTempo;
      }
    }
  }
  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;
  }
Example #3
0
  /**
   * Given a microsecond time, convert to tick. returns tempo at the given time in
   * cache.getCurrTempoMPQ
   */
  public static long microsecond2tick(Sequence seq, long micros, TempoCache cache) {
    if (seq.getDivisionType() != Sequence.PPQ) {
      double dTick =
          (((double) micros) * ((double) seq.getDivisionType()) * ((double) seq.getResolution()))
              / ((double) 1000000);
      long tick = (long) dTick;
      if (cache != null) {
        cache.currTempo = (int) cache.getTempoMPQAt(tick);
      }
      return tick;
    }

    if (cache == null) {
      cache = new TempoCache(seq);
    }
    long[] ticks = cache.ticks;
    int[] tempos = cache.tempos; // in MPQ
    int cacheCount = tempos.length;

    int resolution = seq.getResolution();

    long us = 0;
    long tick = 0;
    int newReadPos = 0;
    int i = 1;

    // walk through all tempo changes and add time for the respective blocks
    // to find the right tick
    if (micros > 0 && cacheCount > 0) {
      // this loop requires that the first tempo Event is at time 0
      while (i < cacheCount) {
        long nextTime = us + ticks2microsec(ticks[i] - ticks[i - 1], tempos[i - 1], resolution);
        if (nextTime > micros) {
          break;
        }
        us = nextTime;
        i++;
      }
      tick = ticks[i - 1] + microsec2ticks(micros - us, tempos[i - 1], resolution);
      if (Printer.debug)
        Printer.debug("microsecond2tick(" + (micros / 1000) + ") = " + tick + " ticks.");
      // if (Printer.debug) Printer.debug("   -> convert back = " + (tick2microsecond(seq, tick,
      // null) / 1000)+" microseconds");
    }
    cache.currTempo = tempos[i - 1];
    return tick;
  }
Example #4
0
  /**
   * Given a tick, convert to microsecond
   *
   * @param cache tempo info and current tempo
   */
  public static long tick2microsecond(Sequence seq, long tick, TempoCache cache) {
    if (seq.getDivisionType() != Sequence.PPQ) {
      double seconds = ((double) tick / (double) (seq.getDivisionType() * seq.getResolution()));
      return (long) (1000000 * seconds);
    }

    if (cache == null) {
      cache = new TempoCache(seq);
    }

    int resolution = seq.getResolution();

    long[] ticks = cache.ticks;
    int[] tempos = cache.tempos; // in MPQ
    int cacheCount = tempos.length;

    // optimization to not always go through entire list of tempo events
    int snapshotIndex = cache.snapshotIndex;
    int snapshotMicro = cache.snapshotMicro;

    // walk through all tempo changes and add time for the respective blocks
    long us = 0; // microsecond

    if (snapshotIndex <= 0 || snapshotIndex >= cacheCount || ticks[snapshotIndex] > tick) {
      snapshotMicro = 0;
      snapshotIndex = 0;
    }
    if (cacheCount > 0) {
      // this implementation needs a tempo event at tick 0!
      int i = snapshotIndex + 1;
      while (i < cacheCount && ticks[i] <= tick) {
        snapshotMicro += ticks2microsec(ticks[i] - ticks[i - 1], tempos[i - 1], resolution);
        snapshotIndex = i;
        i++;
      }
      us =
          snapshotMicro
              + ticks2microsec(tick - ticks[snapshotIndex], tempos[snapshotIndex], resolution);
    }
    cache.snapshotIndex = snapshotIndex;
    cache.snapshotMicro = snapshotMicro;
    return us;
  }
Example #5
0
  /**
   * Adds this Note at the specified time on the specified Track and channel in the specified
   * Sequence, then returns the time that a sequential Note should be added.
   *
   * @param seq the Sequence to which to add this Note
   * @param track the Track in the Sequence to which to add this Note
   * @param time the time at which to start this Note
   * @param ch the channel on which to put this Note
   * @param transposition amount by which to transpose this note in semitones
   * @param sendBankSelect
   * @return
   * @throws javax.sound.midi.InvalidMidiDataException
   */
  public long render(
      Sequence seq, Track track, long time, int ch, int transposition, boolean sendBankSelect)
      throws InvalidMidiDataException {
    if (sendBankSelect) {}
    int dur = getRhythmValue();
    long offTime = time + dur * seq.getResolution() / BEAT;
    render(seq, track, time, offTime, ch, transposition);

    return offTime;
  }
Example #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;
  }
 /* Write a sequence to an output stream in standard midi format.
  * @see javax.sound.midi.spi.MidiFileWriter#write(javax.sound.midi.Sequence, int, java.io.OutputStream)
  */
 public int write(Sequence in, int fileType, OutputStream out) throws IOException {
   MidiDataOutputStream dos = new MidiDataOutputStream(out);
   Track[] tracks = in.getTracks();
   dos.writeInt(0x4d546864); // MThd
   dos.writeInt(6);
   dos.writeShort(fileType);
   dos.writeShort(tracks.length);
   float divisionType = in.getDivisionType();
   int resolution = in.getResolution();
   // FIXME: division computation is incomplete.
   int division = 0;
   if (divisionType == Sequence.PPQ) division = resolution & 0x7fff;
   dos.writeShort(division);
   int length = 14;
   for (Track track : tracks) length += writeTrack(track, dos);
   return length;
 }
  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 void process() throws InvalidMidiDataException, IOException, JAXBException {

    Sequence orchestraSequence = MidiSystem.getSequence(inputFile);

    Sequence masterSequence = new Sequence(Sequence.PPQ, resolution);

    //  the orchestra tracks
    for (int i = 0; i < orchestraSequence.getTracks().length; i++) {
      masterSequence =
          TrackMerger.process(
              masterSequence, orchestraSequence, new int[] {i}, -1, null, loggingHandler); //
    }

    // This is a hack....to make the track 0 as long as the whole sequence
    double rawSeqLen = masterSequence.getTickLength();
    double quarterLen = masterSequence.getResolution();
    double barLen = 4 * quarterLen;
    long fullSeqLen = (long) (barLen * (Math.ceil((rawSeqLen + quarterLen) / barLen)));
    masterSequence.getTracks()[0].add(newEndOfTrackMessage(fullSeqLen));

    // add track 5; the metronome track
    masterSequence = MetronomeCreator.process(masterSequence, 0, loggingHandler);

    System.out.println("** Track 0 length " + masterSequence.getTracks()[0].ticks());
    System.out.println("** Sequence length " + masterSequence.getTickLength());

    ChannelCleaner sequenceImporter = new ChannelCleaner(masterSequence, loggingHandler);
    masterSequence = sequenceImporter.getResult();

    // Write the file to disk
    MidiSystem.write(masterSequence, 1, outputMidiFile);
    System.out.println("############ Midi file is: " + outputMidiFile.getCanonicalPath());

    // ----------------------------------------------------------------------------------------
    // create the appropriate song object
    Song songObject = new Song();
    songObject.setName(description);

    MasterTrack mastertrack = songObject.createMastertrack();
    mastertrack.setSequencefile(outputMidiFile.getName());
    mastertrack.setName(sequenceImporter.getTrackName(0));
    mastertrack.setMidiTrackIndex(0);
    mastertrack.setMidiChannel(sequenceImporter.getChannel(0));

    // create a super track that will collect all orchestra tracs
    MidiSynthesizerTrack orchestraSuperTrack = new MidiSynthesizerTrack();
    orchestraSuperTrack.setName("Orchester");
    mastertrack.addSubtrack(orchestraSuperTrack);
    BuiltinSynthesizer OrchestraSynt = new BuiltinSynthesizer();
    OrchestraSynt.setSoundbankfile("../Schickardt.sf2");
    orchestraSuperTrack.setSynthesizer(OrchestraSynt);

    // create a super track that will collect the voices tracs
    MidiSynthesizerTrack voicesSuperTrack = new MidiSynthesizerTrack();
    voicesSuperTrack.setName("Voices");
    mastertrack.addSubtrack(voicesSuperTrack);
    BuiltinSynthesizer voicesSynt = new BuiltinSynthesizer();
    voicesSynt.setSoundbankfile("../StringPiano.sf2");
    voicesSuperTrack.setSynthesizer(voicesSynt);

    // link all the orchestra tracks
    int orchestraBase = 1; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    int orchestraEnd = 2; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    for (int i = orchestraBase; i <= orchestraEnd; i++) {
      MidiTrack songTrack = new MidiTrack();
      songTrack.setName(sequenceImporter.getTrackName(i));
      songTrack.setMidiTrackIndex(i);
      songTrack.setMidiChannel(sequenceImporter.getChannel(i));
      songTrack.setInstrumentDescription(sequenceImporter.getInstrumentDescription(i));
      orchestraSuperTrack.addSubtrack(songTrack);
    }

    MidiTrack newSongTrack;
    int voiceBase = 3;
    // -- Diskant
    newSongTrack = new MidiTrack();
    newSongTrack.setName("Flute 1");
    newSongTrack.setMidiTrackIndex(voiceBase);
    newSongTrack.setMidiChannel(0);
    newSongTrack.setInstrumentDescription("Piano");
    newSongTrack.setMute(true);
    voicesSuperTrack.addSubtrack(newSongTrack);
    // -- Bass
    voiceBase++; // 4
    newSongTrack = new MidiTrack();
    newSongTrack.setName("Flute 2");
    newSongTrack.setMidiTrackIndex(voiceBase);
    newSongTrack.setMidiChannel(1);
    newSongTrack.setInstrumentDescription("Piano");
    newSongTrack.setMute(true);
    voicesSuperTrack.addSubtrack(newSongTrack);

    // -- Metronome
    voiceBase++; // 5
    newSongTrack = new MidiTrack();
    newSongTrack.setName("Metronome");
    newSongTrack.setMidiTrackIndex(voiceBase);
    newSongTrack.setMidiChannel(9);
    newSongTrack.setInstrumentDescription("Metronome");
    newSongTrack.setMute(true);
    voicesSuperTrack.addSubtrack(newSongTrack);

    songObject.marshal(new FileOutputStream(outputSongFile));
    System.out.println("############ Song file is: " + outputSongFile.getCanonicalPath());
  }
Example #10
0
 public double getTickRate() {
   return ((double) (sequence.getResolution() * getBPM() / 60));
 }
Example #11
0
  private void parseMidi() {
    try {
      // Allow for alternate file extensions of midi files.
      File midiFile = new File("data/out/" + name + ".midi");
      File altFile = new File("data/out/" + name + ".mid");

      if (!midiFile.exists() && altFile.exists()) {
        midiFile = altFile;
      }

      // parse midi
      Sequence sequence = MidiSystem.getSequence(midiFile);
      resolution = sequence.getResolution();

      for (int i = 0; i < staves; ++i) {
        currNotes[i] = notes[i].iterator();
      }

      MidiParser parser = new MidiParser();
      parser.addParserListener(this);
      parser.parse(sequence);

      // initialize any rests trailing at the end
      for (int i = 0; i < staves; ++i) {
        Vector<Chord> currChords = chords[i];
        Iterator<NotePanel> currNote = currNotes[i];
        long tempTime = 0;
        for (int j = currChords.size() - 1; j >= 0; --j) {
          if (!currChords.get(j).isTie()) {
            tempTime = currChords.get(j).getTime() + currChords.get(j).getDuration();
            break;
          }
        }
        while (currNote.hasNext()) {
          NotePanel notePanel = currNote.next();
          if (notePanel.getNote() != null) {
            notePanel.setTime(tempTime).setTempo(tempo);

            tempTime += notePanel.getDuration();
            Chord chord = new Chord(notePanel);
            currChords.add(chord);
          }
        }
      }

      // attach staff lines
      for (int layer = 0; layer < staves; ++layer) {
        NotePanel prevNote = null;
        for (Chord chord : chords[layer]) {
          for (NotePanel note : chord.notes) {
            double staffLine = 0.0;
            for (double line : staffLines.get(layer).get(note.page - 1)) {
              if (staffLine < 0.0001 || Math.abs(staffLine - note.y) > Math.abs(line - note.y)) {
                staffLine = line;
              }
            }
            if (prevNote != null) {
              if (Math.abs(staffLine - prevNote.staffLine) > 0.001) {
                // either reached the end of the line, or the note is really high/low
                // make a hacky guess if the note is really high/low,
                // and if so, set its staffline equal to the previous note's
                if (prevNote.x < note.x + 1.0) {
                  note.setStaffLine(prevNote.staffLine);
                  prevNote = note;
                  continue;
                }
              }
            }
            note.setStaffLine(staffLine);
            prevNote = note;
          }
        }
      }

    } catch (Exception e) {
      System.err.println("Parsing the score from Lilypond's output has failed. Error: ");
      e.printStackTrace();
    }
  }
  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);
        }
      }
    }
  }