2

I am trying to extract the tempo of a melody from the first track of a midi file and apply it to the rest of the tracks containing the note events.

Bascically I've been trying to replace the Thread.sleep() method after the noteOn() message which is playing a note for a fixed time interval everytime. Hence I'm losing the tempo of the entire track.

I was successful in extracting the tempo information from the first track from a previously asked question How does Midi TEMPO message apply to other tracks? but am unable to apply it to the rest of the tracks.

Is there a particular function that does that? I tried searching but couldn't find one.

Here is my code for reference.

      int trackNumber = 0;

     for (Track track :  sequence.getTracks()) {

        trackNumber++;           
        System.out.println();
        for (int i=0; i < track.size(); i++) { 

            MidiEvent event = track.get(i);
            MidiMessage message = event.getMessage();

            if (message instanceof MetaMessage) {

             MetaMessage mm = (MetaMessage) message;
             if(mm.getType()==SET_TEMPO){ 

               byte[] data = mm.getData();
               int tempo = (data[0] & 0xff) << 16 | (data[1] & 0xff) << 8 | (data[2] & 0xff);
               int bpm = 60000000 / tempo;
             }
            }

           if (message instanceof ShortMessage) {
                ShortMessage sm = (ShortMessage) message;
                if (sm.getCommand() == NOTE_ON) {

                    int key = sm.getData1();
                    int velocity = sm.getData2();                    
                    channels[0].noteOn(key,velocity);
                    Thread.sleep(280);//Pays all the note for fixed duration    
                } 
                else if (sm.getCommand() == NOTE_OFF) {

                    int key = sm.getData1();
                    int velocity = sm.getData2();
                    channels[0].noteOff(key);

                } 
            }
        }

        System.out.println();
    }
 }
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
BitWriter
  • 35
  • 7
  • Just clone SetTempo events from the first track and [add](https://docs.oracle.com/javase/7/docs/api/javax/sound/midi/Track.html#add(javax.sound.midi.MidiEvent)) cloned instances to other tracks with the same timestamps. – Maxim May 31 '17 at 07:14
  • Did you revise your method ? – Stephane Jun 25 '19 at 14:06

1 Answers1

1

Events in multiple tracks happen concurrently, so you cannot handle the tracks separately.

You have to either

  • put all events into a single list, and sort them by their timestamp (but ensure that events with the same time stamp keep their order, so use a stable sorting algorithm); or
  • have a current position for each track, and when determining which event is the next, search through all the tracks for a yet-unused event with the smallest timestamp.

Please note that the tempo can change during playback, so a single bpm value does not suffice. See How to correctly convert MIDI ticks to milliseconds? and How can I parse a tempo of midi using Java?.

CL.
  • 173,858
  • 17
  • 217
  • 259
  • From what I have understood, after sorting the events according to their timestamp values I should be passing the delta time value evaluated from it as a parameter to the `Thread.sleep(millisec)` function. Let me know if this is what you have been trying to explain. Thanks. – BitWriter May 31 '17 at 12:10
  • Ticks are not milliseconds, and the delta times are between events in a single track. After sorting, you have to compute the timer interval between adjacent events again. – CL. May 31 '17 at 12:52
  • What do you mean by adjacent events ? You'd have the formula to compute that interval ? – Stephane Jun 25 '19 at 14:05
  • @Stephane See the linked questions. – CL. Jun 25 '19 at 17:24