MIDI note duration

Hi,

I try to deal with MIDI music. I load a midi file with javax.sound.midi.* classes and I want to convert them in my own format. Basicly I want the time when the note begin and the duration of the note. The code below works for some midi but for most of them, duration of note are wrong (seems like playing for ever). I don’t understand what I have miss ???.


public static Score load(InputStream is) throws InvalidMidiDataException, IOException
  {
    Score score = new Score();

    Sequence sequence = MidiSystem.getSequence(is);
    long totalTick = sequence.getTickLength();
    long totalTime = sequence.getMicrosecondLength();

    float fDivisionType = sequence.getDivisionType();

    if (fDivisionType == Sequence.PPQ)
    {
      System.out.println("PPQ");
    }
    else if (fDivisionType == Sequence.SMPTE_24)
    {
      System.out.println("SMPTE, 24 frames per second");
    }
    else if (fDivisionType == Sequence.SMPTE_25)
    {
      System.out.println("SMPTE, 25 frames per second");
    }
    else if (fDivisionType == Sequence.SMPTE_30DROP)
    {
      System.out.println("SMPTE, 29.97 frames per second");
    }
    else if (fDivisionType == Sequence.SMPTE_30)
    {
      System.out.println("SMPTE, 30 frames per second");
    }

    if (sequence.getDivisionType() == Sequence.PPQ)
    {
      System.out.println(" ticks per beat");
    }
    else
    {
      System.out.println(" ticks per frame");
    }

    Track[] tracks = sequence.getTracks();
    for (int nTrack = 0; nTrack < tracks.length; nTrack++)
    {
      Staff staff = score.createStaff();
      Track	track = tracks[nTrack];

      for (int nEvent = 0; nEvent < track.size(); nEvent++)
      {
	MidiEvent event = track.get(nEvent);
        long time = event.getTick();
        MidiMessage message = event.getMessage();

        if (message instanceof ShortMessage)
	{
	  ShortMessage message2 = (ShortMessage) message;

          if(message2.getCommand() == ShortMessage.PROGRAM_CHANGE)
          {
            staff.setInstrument(message2.getData1());
          }
          else if(message2.getCommand() == ShortMessage.NOTE_ON)
          {
            int midi = message2.getData1();
            int velocity = message2.getData2();

            if(message2.getChannel() == Staff.CHANNEL_DRUMS) { staff.setInstrument(Staff.DRUMS);}

            int i = staff.getNotesCount()-1;
            while((i>=0)&&(staff.getNote(i).getPitch() != midi)) { i--; }

            if(i>=0)
            {
              Note n = staff.getNote(i);
              if(n.getDuration() == -1)
              {
                n.setDuration((int)(time-n.getTime()));
              }
            }

            Note note = staff.createNote();
            note.setPitch(midi);
            //note.setVelocity(velocity);
            note.setTime((int)time);
            note.setDuration(-1);

            staff.validate();
          }
          else if(message2.getCommand() == ShortMessage.NOTE_OFF)
          {
            int midi = message2.getData1();
            int velocity = message2.getData2();

            int i = staff.getNotesCount()-1;
            while((i>=0)&&(staff.getNote(i).getPitch() != midi)) { i--; }

            if(i>=0)
            {
              Note n = staff.getNote(i);
              if(n.getDuration() == -1)
              {
                n.setDuration((int)(time-n.getTime()));
              }
            }
            
            staff.validate();
          }
          else if(message2.getCommand() == ShortMessage.PITCH_BEND)
          {
            // Pitch bend : skip
          }
          else if(message2.getCommand() == ShortMessage.CONTROL_CHANGE)
          {
            int control = message2.getData1();

            if(control == 0x06)
            {
              // Data Entry (coarse) : skip ?
            }
            else if(control == 0x07)
            {
              // Volume (coarse) : skip
            }
            else if(control == 0x0A)
            {
              // Pan (coarse) : skip
            }
            else if(control == 0x64)
            {
              // Registered Param LSB : skip ?
            }
            else if(control == 0x65)
            {
              // Registered Param MSB : skip ?
            }
            else
            {
              System.out.println("Control : "+String.format("%02x",control));
            }
          }
          else
          {
            System.out.println("ShortMessage : "+String.format("%02x",message2.getCommand()));
          }
        }
        else if(message instanceof MetaMessage)
        {
          MetaMessage message2 = (MetaMessage)message;

          if(message2.getType() == 0x2F)
          {
            for(int i=0;i<staff.getNotesCount();i++)
            {
              Note n = staff.getNote(i);
              if(n.getDuration() == -1)
              {
                n.setDuration((int)(time-n.getTime()));
              }
            }
          }
          else if(message2.getType() == 0x51)
          {
            byte[]	abData = message2.getData();

            int	nTempo = ((abData[0] & 0xFF) << 16) | ((abData[1] & 0xFF) << 8) | (abData[2] & 0xFF);
	    score.setBPM( (int)convertTempo(nTempo));
          }
          else if(message2.getType() == 0x03)
          {
            String str = new String(message2.getData());

            System.out.println("Name : "+str);
          }
          else if(message2.getType() == 0x7F)
          {
            // Proprietary Event : skip
          }
          else if(message2.getType() == 0x59)
          {
            // Key Signature : skip
          }
          else if(message2.getType() == 0x20)
          {
            // MIDI Channel : ?
          }
          else if(message2.getType() == 0x21)
          {
            // MIDI Port : skip
          }
          else if(message2.getType() == 0x01)
          {
            // Text : skip
          }
          else if(message2.getType() == 0x58)
          {
            // Time Signature : skip
          }
          else
          {
            System.out.println("MetaMessage : "+String.format("%02x", message2.getType()));
          }
        }
      }

      long baseTime = (60l*1000000l)/score.getBPM()/Note.QUARTER;

      for(int i=0;i<staff.getNotesCount();i++)
      {
        Note n = staff.getNote(i);

        // Dirty hack -_-

        long time = n.getTime();
        long duration = n.getDuration();

        long t = (time*totalTime)/totalTick;
        time = (int)(t / baseTime);
        
        t = (duration*totalTime)/totalTick;
        duration = (int)(t / baseTime);

        n.setTime((int)time);
        n.setDuration((int)duration);
      }
    }

    // Converte DRUMs channels

    ArrayList<Staff> drums = new ArrayList<Staff>();

    for(int i=0;i<score.getStaffsCount();i++)
    {
      Staff st = score.getStaff(i);

      if(st.getInstrument() == Staff.DRUMS)
      {
        score.removeStaff(st);
        drums.add(st);
      }
    }

    for(int i=0;i<drums.size();i++)
    {
      Staff st = drums.get(i);

      for(int j = 0;j<st.getNotesCount();j++)
      {
        Note n = st.getNote(j);

        Staff sd = score.getStaffWithInstrumen(Staff.DRUMS+n.getPitch());

        if(sd == null)
        {
          sd = score.createStaff();
          sd.setInstrument(Staff.DRUMS+n.getPitch());
        }

        Note nd = sd.createNote();
        nd.setPitch(  DRUM_NOTE[n.getPitch()] );
        nd.setDuration( n.getDuration() );
        nd.setTime( n.getTime() );
      }
    }

    score.validate();
    return score;
  }

… why I allways find out the problem - after several days of search - right after I post in this forum… ;D

OK. I just find out that a NoteOn event with a velocity of 0 is in fact a NoteOff. Of course now it sounds far better :wink:

I was burned by this myself a few times. It was done for bandwidth optimisation reasons as you could send a series of NoteOn’s without having to send the message type byte for each one.