Пример #1
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 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;
  }
Пример #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;
  }
  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;
  }
Пример #5
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;
  }
 /* 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 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);
        }
      }
    }
  }