예제 #1
0
  public void buildTrackAndStart() {
    int[] trackList = null;

    sequence.deleteTrack(track);
    track = sequence.createTrack();

    for (int i = 0; i < 16; i++) {
      trackList = new int[16];

      int key = instruments[i];

      for (int j = 0; j < 16; j++) {
        JCheckBox jc = (JCheckBox) checkboxList.get(j + (16 * i));
        if (jc.isSelected()) {
          trackList[j] = key;
        } else {
          trackList[j] = 0;
        }
      } // close inner loop

      makeTracks(trackList);
      track.add(makeEvent(176, 1, 127, 0, 16));
    } // close outer

    track.add(makeEvent(192, 9, 1, 0, 15));
    try {
      sequencer.setSequence(sequence);
      sequencer.setLoopCount(sequencer.LOOP_CONTINUOUSLY);
      sequencer.start();
      sequencer.setTempoInBPM(120);
    } catch (Exception e) {
      e.printStackTrace();
    }
  } // close buildTrackAndStart method
예제 #2
0
  public void los() {
    guiErstellen();

    try {
      Sequencer sequencer = MidiSystem.getSequencer();
      sequencer.open();
      sequencer.addControllerEventListener(ml, new int[] {127});
      Sequence seq = new Sequence(Sequence.PPQ, 4);
      Track track = seq.createTrack();

      int r = 0;

      for (int i = 0; i < 60; i += 4) {

        r = (int) ((Math.random() * 50) + 1);

        track.add(eventErzeugen(144, 1, r, 100, i));

        track.add(eventErzeugen(176, 1, 127, 0, i));

        track.add(eventErzeugen(128, 1, r, 100, i + 2));
      }

      sequencer.setSequence(seq);
      sequencer.setTempoInBPM(120);
      sequencer.start();
      Thread.sleep(5000);
      sequencer.close();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
예제 #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;
 }
  /**
   * 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;
  }
예제 #5
0
  public void go() {
    setUpGui();

    try {

      Sequencer sequencer = MidiSystem.getSequencer();
      sequencer.open();
      // make a sequencer and open
      sequencer.addControllerEventListener(m1, new int[] {127});
      Sequence seq = new Sequence(Sequence.PPQ, 4);
      Track track = seq.createTrack();

      int r = 0;
      for (int i = 0; i < 300; i += 4) {

        r = (int) ((Math.random() * 50) + 1);
        track.add(makeEvent(144, 1, r, 100, i));
        track.add(makeEvent(176, 1, 127, 0, i));
        track.add(makeEvent(128, 1, r, 100, i + 2));
      } // end loop

      sequencer.setSequence(seq);
      sequencer.start();
      sequencer.setTempoInBPM(120);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  } // close method
예제 #6
0
  public void play(int instrument, int note) {
    try {

      Sequencer player = MidiSystem.getSequencer();
      player.open();
      Sequence seq = new Sequence(Sequence.PPQ, 4);
      Track track = seq.createTrack();

      MidiEvent event = null;
      ShortMessage first = new ShortMessage();
      first.setMessage(192, 1, instrument, 0);
      MidiEvent changeInstrument = new MidiEvent(first, 1);
      track.add(changeInstrument);
      ShortMessage a = new ShortMessage();
      a.setMessage(144, 1, note, 100);
      MidiEvent noteOn = new MidiEvent(a, 1);
      track.add(noteOn);
      ShortMessage b = new ShortMessage();
      b.setMessage(128, 1, note, 100);
      MidiEvent noteOff = new MidiEvent(b, 16);
      track.add(noteOff);
      player.setSequence(seq);
      player.start();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  } // close play
  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;
  }
예제 #8
0
  public void makeTracks(int[] list) {

    for (int i = 0; i < 16; i++) {
      int key = list[i];

      if (key != 0) {
        track.add(makeEvent(144, 9, key, 100, i));
        track.add(makeEvent(128, 9, key, 100, i + 1));
      }
    }
  }
 /**
  * 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;
 }
예제 #10
0
  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()))
  }
예제 #11
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;
  }
예제 #12
0
파일: MSound.java 프로젝트: hoop21/metr
 public void go() {
   try {
     sq.open();
     Sequence seq = new Sequence(Sequence.PPQ, 4);
     Track track = seq.createTrack();
     track.add(addNote(144, 9, 56, 100, 1));
     track.add(addNote(128, 9, 56, 100, 4));
     sq.setSequence(seq);
     sq.setTempoInBPM(tempo);
     sq.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
     sq.start();
   } catch (Exception ex) {
     ex.printStackTrace();
   }
 }
예제 #13
0
 public boolean createSequence() {
   try {
     // Create a sequence with microsecond timing resolution
     // (Although this doesn't seem to have any effect on tick resolution - ticks in MidiEvents
     // always microsecond
     fSequence = new Sequence(Sequence.SMPTE_25, 40000);
     // Create track and add program change to bell sound
     Track track = fSequence.createTrack();
     // track.add(createBankChange(1, 0));
     track.add(createProgramChange(14, 0));
   } catch (InvalidMidiDataException e) {
     System.out.println("Error creating sequence: " + e);
     return false;
   }
   return true;
 }
 private void readTrack(DataInputStream dataInputStream, Track track)
     throws InvalidMidiDataException, IOException {
   // search for a "MTrk" chunk
   while (true) {
     int nMagic = dataInputStream.readInt();
     if (nMagic == MidiConstants.TRACK_MAGIC) {
       break;
     }
     int nChunkLength = dataInputStream.readInt();
     if (nChunkLength % 2 != 0) {
       nChunkLength++;
     }
     dataInputStream.skip(nChunkLength);
   }
   int nTrackChunkLength = dataInputStream.readInt();
   long lTicks = 0;
   long[] alRemainingBytes = new long[1];
   alRemainingBytes[0] = nTrackChunkLength;
   int[] anRunningStatusByte = new int[1];
   // indicates no running status in effect
   anRunningStatusByte[0] = -1;
   while (alRemainingBytes[0] > 0) {
     long lDeltaTicks = readVariableLengthQuantity(dataInputStream, alRemainingBytes);
     // TDebug.out("delta ticks: " + lDeltaTicks);
     lTicks += lDeltaTicks;
     MidiEvent event = readEvent(dataInputStream, alRemainingBytes, anRunningStatusByte, lTicks);
     track.add(event);
   }
 }
  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;
  }
예제 #16
0
 public boolean createEvent(int type, int num, long tick) {
   ShortMessage message = new ShortMessage();
   try {
     message.setMessage(type + cc.num, num, cc.velocity);
     MidiEvent event = new MidiEvent(message, tick);
     track.add(event);
     return true;
   } catch (Exception ex) {
     ex.printStackTrace();
     return false;
   }
 }
 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());
   }
 }
예제 #18
0
  public void parseSequence(boolean[][] grid) {
    ShortMessage on;
    this.grid = grid;
    ShortMessage off;
    int tickLength = 16;
    try {
      ShortMessage sm = new ShortMessage();
      sm.setMessage(ShortMessage.PROGRAM_CHANGE, 0, instrument, 0);
      track.add(new MidiEvent(sm, 0));
      for (boolean[] aGrid : grid) {
        for (int col = 0; col < aGrid.length; col++) {
          if (aGrid[col]) {
            if (other == 1) {
              other = 0;
            } else {
              other = 1;
            }
            off = new ShortMessage();
            off.setMessage(
                ShortMessage.NOTE_OFF,
                0,
                scale[(grid[0].length - col - 1 + other) % scale.length],
                velocity);
            on = new ShortMessage();
            on.setMessage(
                ShortMessage.NOTE_ON,
                0,
                scale[(grid[0].length - col - 1 + other) % scale.length],
                velocity);
            track.add(new MidiEvent(on, ticks));
            track.add(new MidiEvent(off, ticks + tickLength));
          }
        }
        ticks += tickLength;
      }

    } catch (InvalidMidiDataException e) {
      e.printStackTrace();
    }
  }
예제 #19
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;
 }
예제 #20
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 offTime the time at which to end this Note
   * @param ch the channel on which to put this Note
   * @param transposition amount by which to transpose this note in semitones
   * @throws javax.sound.midi.InvalidMidiDataException
   */
  public void render(Sequence seq, Track track, long time, long offTime, int ch, int transposition)
      throws InvalidMidiDataException {

    // To trace rendering info:
    //  Trace.log(2, "\nchannel = " + ch
    //             + " track = " + track
    //             + " beat = " + time/480.0
    //             + " pitch = " + pitch
    //             + " rhythmValue = " + rhythmValue);

    if (pitch == REST) {
      // if this Note is a rest, do nothing

      return;
    }

    int actualPitch = pitch + transposition;

    // Prevent exceptions in synth

    if (actualPitch > MAX_PITCH) {
      actualPitch = MAX_PITCH;
    } else if (actualPitch < MIN_PITCH) {
      actualPitch = MIN_PITCH;
    }

    // create a note on event at the current time
    MidiEvent evt = MidiSynth.createNoteOnEvent(ch, actualPitch, volume, time);
    track.add(evt);
    // Trace.log(0, " channel = " + ch + " time = " + time + " note on " + " pitch = " + actualPitch
    // + " velocity = " + volume);

    // advance the time and call the note off event

    evt = MidiSynth.createNoteOffEvent(ch, actualPitch, volume, offTime);
    track.add(evt);
    // Trace.log(0, " channel = " + ch + " offTime = " + time + " note off " + " pitch = " +
    // actualPitch + " velocity = " + volume);
  }
    public void actionPerformed(ActionEvent ev) {

      try {

        // make (and open) a sequencer, make a sequence and track

        Sequencer sequencer = MidiSystem.getSequencer();
        sequencer.open();

        sequencer.addControllerEventListener(myPanel, new int[] {127});
        Sequence seq = new Sequence(Sequence.PPQ, 4);
        Track track = seq.createTrack();

        // now make two midi events (containing a midi message)

        for (int i = 0; i < 100; i += 4) {

          int rNum = (int) ((Math.random() * 50) + 1);
          if (rNum < 38) { // so now only do it if num <38 (75% of the time)

            track.add(makeEvent(144, 1, rNum, 100, i));

            track.add(makeEvent(176, 1, 127, 0, i));

            track.add(makeEvent(128, 1, rNum, 100, i + 2));
          }
        } // end loop

        // add the events to the track
        // add the sequence to the sequencer, set timing, and start

        sequencer.setSequence(seq);

        sequencer.start();
        sequencer.setTempoInBPM(220);
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    } // close actionperformed
예제 #22
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);
       }
     }
   }
 }
예제 #23
0
  public void los() {
    guiErstellen();

    try {

      // einen Sequencer erzeugen (und öffnen),
      // eine Sequence und einen Track erzeugen

      Sequencer sequencer = MidiSystem.getSequencer();
      sequencer.open();
      sequencer.addControllerEventListener(ml, new int[] {127});
      Sequence seq = new Sequence(Sequence.PPQ, 4);
      Track track = seq.createTrack();

      // jetzt werden MidiEvents (die eine
      // MidiMessage enthalten) erzeugt

      int r = 0;
      for (int i = 0; i < 60; i += 4) {
        r = (int) ((Math.random() * 50) + 1);
        track.add(eventErzeugen(144, 1, r, 100, i));
        track.add(eventErzeugen(176, 1, 127, 0, i));
        track.add(eventErzeugen(128, 1, r, 100, i + 2));
      } // Ende der for-Schleife

      // Hinzufügen der Events zum Track und der Sequence
      // zum Sequencer, Setzen der Zeiten und Starten

      sequencer.setSequence(seq);
      sequencer.start();
      sequencer.setTempoInBPM(120);

    } catch (Exception ex) {
      ex.printStackTrace();
    }
  } // Methode los schließen
예제 #24
0
  /**
   * Converts the given tune to a midi sequence.
   *
   * @param tune The tune to be converted.
   * @return The midi sequence of the tune.
   */
  public Sequence toMidiSequence(Tune tune) {
    Sequence sequence = null;
    try {
      if (instrument == null) {
        Synthesizer synth = MidiSystem.getSynthesizer();
        synth.open();
        try {
          setInstrument(synth.getAvailableInstruments()[0]);
        } finally {
          synth.close();
        }
      }
      // Sequence in ticks per quarter note : PPQ = Pulse Per Quarter Note
      // Resolution is expressed in ticks per beat.
      // Last parameter "1" is the number of tracks.
      sequence = new Sequence(Sequence.PPQ, SEQUENCE_RESOLUTION, 1);
      // Set the instrument on channel 0
      ShortMessage sm = new ShortMessage();
      sm.setMessage(ShortMessage.PROGRAM_CHANGE, 0, instrument.getPatch().getProgram(), 0);

      Track track = sequence.createTrack();
      track.add(new MidiEvent(sm, 0));
      // long trackLengthInTicks = track.ticks();
      int lastRepeatOpen = -1;
      int repeatNumber = 1;
      boolean inWrongEnding = false;
      KeySignature tuneKey = null;
      KeySignature currentKey = null;
      Hashtable partsKey = new Hashtable();

      long elapsedTime = 0;
      NoteAbstract[] graceNotes = null;
      Music staff = tune.getMusicForAudioRendition();
      Iterator it = staff.getVoices().iterator();
      while (it.hasNext()) {
        Voice voice = (Voice) it.next();
        int i = 0; // StaffItem iterator
        while (i < voice.size()) {
          if (!inWrongEnding) {
            // ==================================================================== TEMPO
            if (voice.elementAt(i) instanceof abc.notation.Tempo) {
              addTempoEventsFor(
                  track,
                  elapsedTime,
                  getMidiMessagesFor((Tempo) voice.elementAt(i))); // , trackLengthInTicks));
            } else
            /*if (voice.elementAt(i) instanceof abc.notation.PartLabel) {
            	//Imagine... part A in Gmaj, B in Amin
            	//in tune you have K:G, P:A, ... P:B, K:Am
            	//if you have part order ABA, when you return to A
            	//you stay in Amin. This stores the tuneKey when a
            	//new part appear, and restitute it when part is played again
            	abc.notation.PartLabel pl = (abc.notation.PartLabel) voice.elementAt(i);
            	if (partsKey.get(pl.getLabel()+"") == null) {
            		partsKey.put(pl.getLabel()+"", tuneKey);
            	} else {
            		tuneKey = (KeySignature) partsKey.get(pl.getLabel()+"");
            	}
            }
            else*/
            // ==================================================================== KEY SIGNATURE
            if (voice.elementAt(i) instanceof abc.notation.KeySignature) {
              tuneKey = (KeySignature) (voice.elementAt(i));
              currentKey = new KeySignature(tuneKey.getAccidentals());
            } else
            // ==================================================================== NOTE
            // Notes ending ties should be ignored. Already taken into
            // account in getNoteLengthInTicks(Note)
            if (voice.elementAt(i) instanceof abc.notation.Note
                && !((abc.notation.Note) voice.elementAt(i)).isEndingTie()) {

              Note note = (Note) voice.elementAt(i);
              long noteDuration;
              boolean fermata = false;
              Vector decorationNotes = new Vector();
              if (note.hasGeneralGracing() || note.hasDecorations()) {
                Decoration[] d = note.getDecorations();
                for (int j = 0; j < d.length; j++) {
                  switch (d[j].getType()) {
                    case Decoration.FERMATA:
                    case Decoration.FERMATA_INVERTED:
                      fermata = true;
                      break;
                    case Decoration.LOWERMORDENT:
                    case Decoration.UPPERMORDENT:
                    case Decoration.DOUBLE_LOWER_MORDANT:
                    case Decoration.DOUBLE_UPPER_MORDANT:
                    case Decoration.TRILL:
                    case Decoration.TURN: // GRUPETTO_UP
                    case Decoration.TURN_INVERTED: // GRUPETTO_DOWN
                    case Decoration.TURNX:
                    case Decoration.TURNX_INVERTED:
                      Note n = new Note(note.getHeight());
                      n.setAccidental(note.getAccidental(currentKey));
                      Note o =
                          new Interval(Interval.SECOND, Interval.MAJOR, Interval.UPWARD)
                              .calculateSecondNote(n);
                      Note m =
                          new Interval(Interval.SECOND, Interval.MAJOR, Interval.DOWNWARD)
                              .calculateSecondNote(n);
                      // TODO ornament templates: regular, musette, balkan...
                      // n.setStrictDuration(Note.SIXTEENTH);
                      // o.setDuration((short)(Note.EIGHTH+Note.SIXTEENTH));
                      o.setAccidental(Accidental.NONE);
                      m.setAccidental(Accidental.NONE);
                      n.setStrictDuration(Note.THIRTY_SECOND);
                      m.setStrictDuration(Note.THIRTY_SECOND);
                      o.setStrictDuration(Note.THIRTY_SECOND);
                      switch (d[j].getType()) {
                        case Decoration.DOUBLE_LOWER_MORDANT:
                          decorationNotes.add(n);
                          decorationNotes.add(m);
                        case Decoration.LOWERMORDENT:
                          decorationNotes.add(n);
                          decorationNotes.add(m);
                          break;
                        case Decoration.DOUBLE_UPPER_MORDANT:
                        case Decoration.TRILL:
                          decorationNotes.add(n);
                          decorationNotes.add(o);
                        case Decoration.UPPERMORDENT:
                          decorationNotes.add(n);
                          decorationNotes.add(o);
                          break;
                        case Decoration.TURNX_INVERTED:
                        case Decoration.TURN:
                          decorationNotes.add(o);
                          decorationNotes.add(n);
                          decorationNotes.add(m);
                          break;
                        case Decoration.TURNX:
                        case Decoration.TURN_INVERTED:
                          decorationNotes.add(m);
                          decorationNotes.add(n);
                          decorationNotes.add(o);
                      }
                      break;
                  }
                }
                // currently not used
                // future use: playing rolls, slides, etc.
              }
              long graceNotesDuration = 0;
              if (note.hasGracingNotes() || (decorationNotes.size() > 0)) {
                graceNotes = note.getGracingNotes();
                // gracing are eighth note for graphical rendition
                // and because that's it in the parser
                // adapt duration to note length
                int divisor = 1;
                if (note.getStrictDuration() >= Note.HALF) divisor = 1; // grace is an eighth
                else if (note.getStrictDuration() >= Note.QUARTER) divisor = 2; // 16th
                else if (note.getStrictDuration() >= Note.EIGHTH) divisor = 4; // 32nd
                else divisor = 8; // 64th
                if (note.hasGracingNotes()) {
                  for (int j = 0; j < graceNotes.length; j++) {
                    noteDuration = getNoteLengthInTicks(graceNotes[j], staff) / divisor;
                    graceNotesDuration += noteDuration;
                    if (graceNotes[j] instanceof Note)
                      playNote(
                          (Note) graceNotes[j], i, currentKey, elapsedTime, noteDuration, track);
                    else
                      playMultiNote(
                          (MultiNote) graceNotes[j],
                          i,
                          currentKey, /*elapsedTime,*/
                          noteDuration,
                          track,
                          staff);
                    elapsedTime += noteDuration;
                  }
                }
                for (int j = 0; j < decorationNotes.size(); j++) {
                  noteDuration = getNoteLengthInTicks((Note) decorationNotes.elementAt(j), staff);
                  graceNotesDuration += noteDuration;
                  playNote(
                      (Note) decorationNotes.elementAt(j),
                      i,
                      currentKey,
                      elapsedTime,
                      noteDuration,
                      track);
                  elapsedTime += noteDuration;
                }
              }
              // The note duration if the note isn't part of a tuplet.
              noteDuration = getNoteLengthInTicks(note, staff) - graceNotesDuration;
              if (noteDuration <= 0) // in case of too much grace notes
              noteDuration = getNoteLengthInTicks(note, staff);
              if (fermata) noteDuration *= 2;
              playNote(note, i, currentKey, elapsedTime, noteDuration, track);
              elapsedTime += noteDuration;
            } else
            // ==================================================================== MULTI NOTE
            if ((voice.elementAt(i) instanceof abc.notation.MultiNote)) {
              MultiNote multiNote = (MultiNote) voice.elementAt(i);
              playMultiNote(multiNote, i, currentKey, elapsedTime, track, staff);
              elapsedTime += getNoteLengthInTicks(multiNote, staff);
            }
          } // endif (!inWrongEnding)
          // ====================================================================== REPEAT BAR LINE
          if (voice.elementAt(i) instanceof abc.notation.RepeatBarLine) {
            RepeatBarLine bar = (RepeatBarLine) voice.elementAt(i);
            if (repeatNumber < bar.getRepeatNumbers()[0] && lastRepeatOpen != -1) {
              repeatNumber++;
              i = lastRepeatOpen;
            } else if (repeatNumber > bar.getRepeatNumbers()[0]) inWrongEnding = true;
            else inWrongEnding = false;
          } else
          // ====================================================================== BAR LINE OPEN /
          // CLOSE
          if (voice.elementAt(i) instanceof abc.notation.BarLine) {
            // currentKey = new KeySignature(tuneKey.getAccidentals());
            switch (((BarLine) (voice.elementAt(i))).getType()) {
              case BarLine.SIMPLE:
                break;
              case BarLine.REPEAT_OPEN:
                lastRepeatOpen = i;
                repeatNumber = 1;
                break;
              case BarLine.REPEAT_CLOSE:
                if (repeatNumber < 2 && lastRepeatOpen != -1) {
                  repeatNumber++;
                  i = lastRepeatOpen;
                } else {
                  repeatNumber = 1;
                  lastRepeatOpen = -1;
                }
                break;
                // TODO case BarLine.BEGIN_AND_END_REPEAT
            }
          }
          // Whatever kind of bar line it is
          if (voice.elementAt(i) instanceof abc.notation.BarLine) {
            currentKey = new KeySignature(tuneKey.getAccidentals());
          }
          i++;
        } // end while each element in voice
      } // end while each voice in music
    } catch (InvalidMidiDataException e) {
      e.printStackTrace();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return sequence;
  }
예제 #25
0
 protected static long addEventsToTrack(Track track, MidiEvent event) {
   track.add(event);
   return track.ticks();
 }
예제 #26
0
 /** @return The length of the track in ticks, once events have been added to it. */
 protected static long addEventsToTrack(Track track, MidiEvent[] events) {
   if (events != null) for (int i = 0; i < events.length; i++) track.add(events[i]);
   return track.ticks();
 }
예제 #27
0
  private static Sequence convert(
      List<FileAndData> filesData,
      boolean useLotroInstruments,
      Map<Integer, LotroInstrument> instrumentOverrideMap,
      AbcInfo abcInfo,
      final boolean enableLotroErrors,
      final boolean stereo)
      throws ParseException {
    if (abcInfo == null) abcInfo = new AbcInfo();
    else abcInfo.reset();

    TuneInfo info = new TuneInfo();
    Sequence seq = null;
    Track track = null;

    int channel = 0;
    int trackNumber = 0;
    int noteDivisorChangeLine = 0;

    long chordStartTick = 0;
    long chordEndTick = 0;
    long PPQN = 0;
    Map<Integer, Integer> tiedNotes =
        new HashMap<Integer, Integer>(); // noteId => (line << 16) | column
    Map<Integer, Integer> accidentals = new HashMap<Integer, Integer>(); // noteId => deltaNoteId

    List<MidiEvent> noteOffEvents = new ArrayList<MidiEvent>();
    for (FileAndData fileAndData : filesData) {
      String fileName = fileAndData.file.getName();
      int lineNumber = 0;
      int partStartLine = 0;
      for (String line : fileAndData.lines) {
        lineNumber++;

        // Handle extended info
        Matcher xInfoMatcher = XINFO_PATTERN.matcher(line);
        if (xInfoMatcher.matches()) {
          AbcField field =
              AbcField.fromString(
                  xInfoMatcher.group(XINFO_FIELD) + xInfoMatcher.group(XINFO_COLON));

          if (field == AbcField.TEMPO) {
            try {
              info.addTempoEvent(chordStartTick, xInfoMatcher.group(XINFO_VALUE).trim());
            } catch (IllegalArgumentException e) {
              // Apparently that wasn't actually a tempo change
            }
          } else if (field != null) {
            String value = xInfoMatcher.group(XINFO_VALUE).trim();

            abcInfo.setExtendedMetadata(field, value);

            if (field == AbcField.PART_NAME) {
              info.setTitle(value, true);
              abcInfo.setPartName(trackNumber, value, true);
            }
          }

          continue;
        }

        int comment = line.indexOf('%');
        if (comment >= 0) line = line.substring(0, comment);
        if (line.trim().length() == 0) continue;

        int chordSize = 0;

        Matcher infoMatcher = INFO_PATTERN.matcher(line);
        if (infoMatcher.matches()) {
          char type = Character.toUpperCase(infoMatcher.group(INFO_TYPE).charAt(0));
          String value = infoMatcher.group(INFO_VALUE).trim();

          abcInfo.setMetadata(type, value);

          try {
            switch (type) {
              case 'X':
                for (int lineAndColumn : tiedNotes.values()) {
                  throw new ParseException(
                      "Tied note does not connect to another note",
                      fileName,
                      lineAndColumn >>> 16,
                      lineAndColumn & 0xFFFF);
                }

                accidentals.clear();
                noteOffEvents.clear();

                info.newPart(Integer.parseInt(value));
                trackNumber++;
                partStartLine = lineNumber;
                chordStartTick = 0;
                abcInfo.setPartNumber(trackNumber, info.getPartNumber());
                track = null; // Will create a new track after the header is done
                if (instrumentOverrideMap != null
                    && instrumentOverrideMap.containsKey(trackNumber)) {
                  info.setInstrument(instrumentOverrideMap.get(trackNumber));
                }
                break;
              case 'T':
                if (track != null)
                  throw new ParseException(
                      "Can't specify the title in the middle of a part", fileName, lineNumber, 0);

                info.setTitle(value, false);
                abcInfo.setPartName(trackNumber, value, false);
                if (instrumentOverrideMap == null
                    || !instrumentOverrideMap.containsKey(trackNumber)) {
                  info.setInstrument(TuneInfo.findInstrumentName(value, info.getInstrument()));
                }
                break;
              case 'K':
                info.setKey(value);
                break;
              case 'L':
                info.setNoteDivisor(value);
                noteDivisorChangeLine = lineNumber;
                break;
              case 'M':
                info.setMeter(value);
                noteDivisorChangeLine = lineNumber;
                break;
              case 'Q':
                {
                  int tempo = info.getPrimaryTempoBPM();
                  info.setPrimaryTempoBPM(value);
                  if (seq != null && (info.getPrimaryTempoBPM() != tempo)) {
                    throw new ParseException(
                        "The tempo must be the same for all parts of the song",
                        fileName,
                        lineNumber);
                  }
                  break;
                }
            }
          } catch (IllegalArgumentException e) {
            throw new ParseException(
                e.getMessage(), fileName, lineNumber, infoMatcher.start(INFO_VALUE));
          }
        } else {
          // The line contains notes

          if (trackNumber == 0) {
            // This ABC file doesn't have an "X:" line before notes. Tsk tsk.
            trackNumber = 1;
            if (instrumentOverrideMap != null && instrumentOverrideMap.containsKey(trackNumber)) {
              info.setInstrument(instrumentOverrideMap.get(trackNumber));
            }
          }

          if (seq == null) {
            try {
              PPQN = info.getPpqn();
              seq = new Sequence(Sequence.PPQ, (int) PPQN);

              abcInfo.setPrimaryTempoBPM(info.getPrimaryTempoBPM());

              // Create track 0, which will later be filled with the
              // tempo events and song metadata (title, etc.)
              seq.createTrack();

              abcInfo.setPartNumber(0, 0);
              abcInfo.setPartName(0, info.getTitle(), false);

              track = null;
            } catch (InvalidMidiDataException mde) {
              throw new ParseException("Midi Error: " + mde.getMessage(), fileName);
            }
          }

          if (track == null) {
            channel = getTrackChannel(seq.getTracks().length);
            if (channel > MidiConstants.CHANNEL_COUNT - 1)
              throw new ParseException(
                  "Too many parts (max = " + (MidiConstants.CHANNEL_COUNT - 1) + ")",
                  fileName,
                  partStartLine);
            track = seq.createTrack();
            track.add(
                MidiFactory.createProgramChangeEvent(
                    info.getInstrument().midiProgramId, channel, 0));
            if (useLotroInstruments)
              track.add(MidiFactory.createChannelVolumeEvent(MidiConstants.MAX_VOLUME, channel, 1));
            abcInfo.setPartInstrument(trackNumber, info.getInstrument());
          }

          Matcher m = NOTE_PATTERN.matcher(line);
          int i = 0;
          boolean inChord = false;
          Tuplet tuplet = null;
          int brokenRhythmNumerator = 1; // The numerator of the note after the broken rhythm sign
          int brokenRhythmDenominator =
              1; // The denominator of the note after the broken rhythm sign
          while (true) {
            boolean found = m.find(i);
            int parseEnd = found ? m.start() : line.length();
            // Parse anything that's not a note
            for (; i < parseEnd; i++) {
              char ch = line.charAt(i);
              if (Character.isWhitespace(ch)) {
                if (inChord)
                  throw new ParseException(
                      "Unexpected whitespace inside a chord", fileName, lineNumber, i);
                continue;
              }

              switch (ch) {
                case '[': // Chord start
                  if (inChord) {
                    throw new ParseException(
                        "Unexpected '" + ch + "' inside a chord", fileName, lineNumber, i);
                  }

                  if (brokenRhythmDenominator != 1 || brokenRhythmNumerator != 1) {
                    throw new ParseException(
                        "Can't have broken rhythm (< or >) within a chord",
                        fileName,
                        lineNumber,
                        i);
                  }

                  chordSize = 0;
                  inChord = true;
                  break;

                case ']': // Chord end
                  if (!inChord) {
                    throw new ParseException("Unexpected '" + ch + "'", fileName, lineNumber, i);
                  }
                  inChord = false;
                  chordStartTick = chordEndTick;
                  break;

                case '|': // Bar line
                  if (inChord) {
                    throw new ParseException(
                        "Unexpected '" + ch + "' inside a chord", fileName, lineNumber, i);
                  }

                  if (trackNumber == 1) abcInfo.addBar(chordStartTick);

                  accidentals.clear();
                  if (i + 1 < line.length() && line.charAt(i + 1) == ']') {
                    i++; // Skip |]
                  } else if (trackNumber == 1) {
                    abcInfo.addBar(chordStartTick);
                  }
                  break;

                case '+':
                  {
                    int j = line.indexOf('+', i + 1);
                    if (j < 0) {
                      throw new ParseException("There is no matching '+'", fileName, lineNumber, i);
                    }
                    try {
                      info.setDynamics(line.substring(i + 1, j));
                    } catch (IllegalArgumentException iae) {
                      throw new ParseException("Unsupported +decoration+", fileName, lineNumber, i);
                    }

                    if (enableLotroErrors && inChord) {
                      throw new LotroParseException(
                          "Can't include a +decoration+ inside a chord", fileName, lineNumber, i);
                    }

                    i = j;
                    break;
                  }

                case '(':
                  // Tuplet or slur start
                  if (i + 1 < line.length() && Character.isDigit(line.charAt(i + 1))) {
                    // If it has a digit following it, it's a tuplet
                    if (tuplet != null)
                      throw new ParseException(
                          "Unexpected '" + ch + "' before end of tuplet", fileName, lineNumber, i);

                    try {
                      for (int j = i + 1; j < line.length(); j++) {
                        if (line.charAt(i) != ':' && !Character.isDigit(line.charAt(i))) {
                          tuplet = new Tuplet(line.substring(i + 1, j + 1), info.isCompoundMeter());
                          i = j;
                          break;
                        }
                      }
                    } catch (IllegalArgumentException e) {
                      throw new ParseException("Invalid tuplet", fileName, lineNumber, i);
                    }
                  } else {
                    // Otherwise it's a slur, which LotRO conveniently ignores
                    if (inChord) {
                      throw new ParseException(
                          "Unexpected '" + ch + "' inside a chord", fileName, lineNumber, i);
                    }
                  }
                  break;

                case ')':
                  // End of a slur, ignore
                  if (inChord) {
                    throw new ParseException(
                        "Unexpected '" + ch + "' inside a chord", fileName, lineNumber, i);
                  }
                  break;

                case '\\':
                  // Ignore backslashes
                  break;

                default:
                  throw new ParseException(
                      "Unknown/unexpected character '" + ch + "'", fileName, lineNumber, i);
              }
            }

            if (i >= line.length()) break;

            // The matcher might find +f+, +ff+, or +fff+ and think it's a note
            if (i > m.start()) continue;

            if (inChord) chordSize++;

            if (enableLotroErrors && inChord && chordSize > AbcConstants.MAX_CHORD_NOTES) {
              throw new LotroParseException(
                  "Too many notes in a chord", fileName, lineNumber, m.start());
            }

            // Parse the note
            int numerator;
            int denominator;

            numerator =
                (m.group(NOTE_LEN_NUMER) == null) ? 1 : Integer.parseInt(m.group(NOTE_LEN_NUMER));
            String denom = m.group(NOTE_LEN_DENOM);
            if (denom == null) denominator = 1;
            else if (denom.equals("/")) denominator = 2;
            else if (denom.equals("//")) denominator = 4;
            else denominator = Integer.parseInt(denom.substring(1));

            String brokenRhythm = m.group(NOTE_BROKEN_RHYTHM);
            if (brokenRhythm != null) {
              if (brokenRhythmDenominator != 1 || brokenRhythmNumerator != 1) {
                throw new ParseException(
                    "Invalid broken rhythm: " + brokenRhythm,
                    fileName,
                    lineNumber,
                    m.start(NOTE_BROKEN_RHYTHM));
              }
              if (inChord) {
                throw new ParseException(
                    "Can't have broken rhythm (< or >) within a chord",
                    fileName,
                    lineNumber,
                    m.start(NOTE_BROKEN_RHYTHM));
              }
              if (m.group(NOTE_TIE) != null) {
                throw new ParseException(
                    "Tied notes can't have broken rhythms (< or >)",
                    fileName,
                    lineNumber,
                    m.start(NOTE_BROKEN_RHYTHM));
              }

              int factor = 1 << brokenRhythm.length();

              if (brokenRhythm.charAt(0) == '>') {
                numerator *= 2 * factor - 1;
                denominator *= factor;
                brokenRhythmDenominator = factor;
              } else {
                brokenRhythmNumerator = 2 * factor - 1;
                brokenRhythmDenominator = factor;
                denominator *= factor;
              }
            } else {
              numerator *= brokenRhythmNumerator;
              denominator *= brokenRhythmDenominator;
              brokenRhythmNumerator = 1;
              brokenRhythmDenominator = 1;
            }

            if (tuplet != null) {
              if (!inChord || chordSize == 1) tuplet.r--;
              numerator *= tuplet.q;
              denominator *= tuplet.p;
              if (tuplet.r == 0) tuplet = null;
            }

            // Convert back to the original tempo
            int curTempoBPM = info.getCurrentTempoBPM(chordStartTick);
            int primaryTempoBPM = info.getPrimaryTempoBPM();
            numerator *= curTempoBPM;
            denominator *= primaryTempoBPM;

            // Try to guess if this note is using triplet timing
            if ((denominator % 3 == 0) && (numerator % 3 != 0)) {
              abcInfo.setHasTriplets(true);
            }

            long noteEndTick = chordStartTick + DEFAULT_NOTE_TICKS * numerator / denominator;

            // A chord is as long as its shortest note
            if (chordEndTick == chordStartTick || noteEndTick < chordEndTick)
              chordEndTick = noteEndTick;

            char noteLetter = m.group(NOTE_LETTER).charAt(0);
            String octaveStr = m.group(NOTE_OCTAVE);
            if (octaveStr == null) octaveStr = "";
            if (noteLetter == 'z' || noteLetter == 'x') {
              if (m.group(NOTE_ACCIDENTAL) != null && m.group(NOTE_ACCIDENTAL).length() > 0) {
                throw new ParseException(
                    "Unexpected accidental on a rest",
                    fileName,
                    lineNumber,
                    m.start(NOTE_ACCIDENTAL));
              }
              if (octaveStr.length() > 0) {
                throw new ParseException(
                    "Unexpected octave indicator on a rest",
                    fileName,
                    lineNumber,
                    m.start(NOTE_OCTAVE));
              }
            } else {
              int octave = Character.isUpperCase(noteLetter) ? 3 : 4;
              if (octaveStr.indexOf('\'') >= 0) octave += octaveStr.length();
              else if (octaveStr.indexOf(',') >= 0) octave -= octaveStr.length();

              int noteId;
              int lotroNoteId;

              lotroNoteId =
                  noteId =
                      (octave + 1) * 12 + CHR_NOTE_DELTA[Character.toLowerCase(noteLetter) - 'a'];
              if (!useLotroInstruments) noteId += 12 * info.getInstrument().octaveDelta;

              if (m.group(NOTE_ACCIDENTAL) != null) {
                if (m.group(NOTE_ACCIDENTAL).startsWith("_"))
                  accidentals.put(noteId, -m.group(NOTE_ACCIDENTAL).length());
                else if (m.group(NOTE_ACCIDENTAL).startsWith("^"))
                  accidentals.put(noteId, m.group(NOTE_ACCIDENTAL).length());
                else if (m.group(NOTE_ACCIDENTAL).equals("=")) accidentals.put(noteId, 0);
              }

              int noteDelta;
              if (accidentals.containsKey(noteId)) {
                noteDelta = accidentals.get(noteId);
              } else {
                // Use the key signature to determine the accidental
                noteDelta = info.getKey().getDefaultAccidental(noteId).deltaNoteId;
              }
              lotroNoteId += noteDelta;
              noteId += noteDelta;

              if (enableLotroErrors && lotroNoteId < Note.MIN_PLAYABLE.id)
                throw new LotroParseException("Note is too low", fileName, lineNumber, m.start());
              else if (enableLotroErrors && lotroNoteId > Note.MAX_PLAYABLE.id)
                throw new LotroParseException("Note is too high", fileName, lineNumber, m.start());

              if (info.getInstrument() == LotroInstrument.COWBELL
                  || info.getInstrument() == LotroInstrument.MOOR_COWBELL) {
                if (useLotroInstruments) {
                  // Randomize the noteId unless it's part of a note tie
                  if (m.group(NOTE_TIE) == null && !tiedNotes.containsKey(noteId)) {
                    int min = info.getInstrument().lowestPlayable.id;
                    int max = info.getInstrument().highestPlayable.id;
                    lotroNoteId = noteId = min + (int) (Math.random() * (max - min));
                  }
                } else {
                  noteId = (info.getInstrument() == LotroInstrument.COWBELL) ? 76 : 71;
                  lotroNoteId = 71;
                }
              }

              // Check for overlapping notes, and remove extra note off events
              Iterator<MidiEvent> noteOffIter = noteOffEvents.iterator();
              while (noteOffIter.hasNext()) {
                MidiEvent evt = noteOffIter.next();
                if (evt.getTick() <= chordStartTick) {
                  noteOffIter.remove();
                  continue;
                }

                int noteOffId = ((ShortMessage) evt.getMessage()).getData1();
                if (noteOffId == noteId) {
                  track.remove(evt);
                  evt.setTick(chordStartTick);
                  track.add(evt);
                  noteOffIter.remove();
                  break;
                }
              }

              if (!tiedNotes.containsKey(noteId)) {
                if (info.getPpqn() != PPQN) {
                  throw new ParseException(
                      "The default note length must be the same for all parts of the song",
                      fileName,
                      noteDivisorChangeLine);
                }
                track.add(
                    MidiFactory.createNoteOnEventEx(
                        noteId,
                        channel,
                        info.getDynamics().getVol(useLotroInstruments),
                        chordStartTick));
              }

              if (m.group(NOTE_TIE) != null) {
                int lineAndColumn = (lineNumber << 16) | m.start();
                tiedNotes.put(noteId, lineAndColumn);
              } else {
                double MPQN = MidiUtils.convertTempo(curTempoBPM);
                double lengthMicros = (noteEndTick - chordStartTick) * MPQN / PPQN;

                if (enableLotroErrors && lengthMicros < AbcConstants.SHORTEST_NOTE_MICROS) {
                  throw new LotroParseException(
                      "Note's duration is too short", fileName, lineNumber, m.start());
                } else if (enableLotroErrors && lengthMicros > AbcConstants.LONGEST_NOTE_MICROS) {
                  throw new LotroParseException(
                      "Note's duration is too long", fileName, lineNumber, m.start());
                }

                // Stringed instruments, drums, and woodwind breath sounds always play the
                // sound sample in its entirety. Since Gervill doesn't support the SoundFont
                // extension that specifies this, we have to increase the note length.
                // One second should do the trick.
                long noteEndTickTmp = noteEndTick;
                if (useLotroInstruments && !info.getInstrument().isSustainable(lotroNoteId)) {
                  noteEndTickTmp =
                      Math.max(
                          noteEndTick,
                          chordStartTick
                              + Math.round(AbcConstants.ONE_SECOND_MICROS * PPQN / MPQN));
                }
                MidiEvent noteOff =
                    MidiFactory.createNoteOffEventEx(
                        noteId,
                        channel,
                        info.getDynamics().getVol(useLotroInstruments),
                        noteEndTickTmp);
                track.add(noteOff);
                noteOffEvents.add(noteOff);

                tiedNotes.remove(noteId);
              }
            }

            if (!inChord) chordStartTick = noteEndTick;
            i = m.end();
          }

          if (tuplet != null)
            throw new ParseException("Tuplet not finished by end of line", fileName, lineNumber, i);

          if (inChord)
            throw new ParseException("Chord not closed at end of line", fileName, lineNumber, i);

          if (brokenRhythmDenominator != 1 || brokenRhythmNumerator != 1)
            throw new ParseException(
                "Broken rhythm unfinished at end of line", fileName, lineNumber, i);
        }
      }

      if (seq == null) throw new ParseException("The file contains no notes", fileName, lineNumber);

      for (int lineAndColumn : tiedNotes.values()) {
        throw new ParseException(
            "Tied note does not connect to another note",
            fileName,
            lineAndColumn >>> 16,
            lineAndColumn & 0xFFFF);
      }
    }

    PanGenerator pan = null;
    if (stereo && trackNumber > 1) pan = new PanGenerator();

    Track[] tracks = seq.getTracks();

    // Add tempo events
    for (Map.Entry<Long, Integer> tempoEvent : info.getAllPartsTempoMap().entrySet()) {
      long tick = tempoEvent.getKey();
      int mpq = (int) MidiUtils.convertTempo(tempoEvent.getValue());
      tracks[0].add(MidiFactory.createTempoEvent(mpq, tick));
    }

    // Add name and pan events
    tracks[0].add(MidiFactory.createTrackNameEvent(abcInfo.getTitle()));
    for (int i = 1; i <= trackNumber; i++) {
      tracks[i].add(MidiFactory.createTrackNameEvent(abcInfo.getPartName(i)));

      int panAmount = PanGenerator.CENTER;
      if (pan != null) panAmount = pan.get(abcInfo.getPartInstrument(i), abcInfo.getPartName(i));
      tracks[i].add(MidiFactory.createPanEvent(panAmount, getTrackChannel(i)));
    }

    return seq;
  }
예제 #28
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");
    }
  }
예제 #29
0
 /**
  * Create a note and add it to the track.
  *
  * @param note The MIDI value for the note to be played
  * @param startTime When this note should be played
  * @param duration How long the note should last
  */
 private void createNote(Note note, int startTime, int duration, Track track) {
   int endTime = startTime + duration;
   int midiNumber = note.getMidiNumber();
   track.add(createNoteEvent(ShortMessage.NOTE_ON, midiNumber, startTime, VELOCITY));
   track.add(createNoteEvent(ShortMessage.NOTE_OFF, midiNumber, endTime, 0));
 }
 /** We assume that we are holding the "this" monitor */
 private void addStopEvent(int note) throws InvalidMidiDataException {
   ShortMessage message = new ShortMessage();
   message.setMessage(ShortMessage.NOTE_OFF, 0, note, 0);
   track.add(new MidiEvent(message, pos));
 }