0

I'm working on a simple MIDI parser that converts MIDI files to a different format, and one issue I'm facing is that occasionally I'll get some tracks in the MIDI file that don't have any assigned instrument to them (no program change events)

Here's the truncated section of the code that looks for program change events specifically. Forgive me for the messy formatting, I'm new to this and had some trouble getting code pasted, but hopefully the general idea is still clear.

    private static final int PROGRAM_CHANGE = 0xC0;
    ArrayList<Note> noteSequence = new ArrayList<Note>();
    int trackNumber = 0;
    for (Track track : sequence.getTracks()) {
        trackNumber++;
        for (int i = 0; i < track.size(); i++) {
            MidiEvent event = track.get(i);
            MidiMessage message = event.getMessage();
            currentTick = event.getTick();
            ShortMessage sm = (ShortMessage) message;
            if (sm.getCommand() == PROGRAM_CHANGE) {
                noteSequence.add(new Note(currentTick+1,sm.getData1(),0,trackNumber,0,"PROGRAM_CHANGE",0,0,0, 2));
                }}

One of the main issues I might be facing is just grabbing program change events the wrong way. They don't seem to have a tick associated with them so I've just been giving them the timing event of the previous tick + 1. I'm not sure if this is the proper way to do it, so this might be causing some issues.

For example, one MIDI file I ran through it had 23 tracks. Every track except for track #8 had an assigned instrument, and I'm at a loss at why #8 would not have an instrument assigned to it. I presume it's meant to inherit it's instrument based on another track, but I'm not too knowledgable on how that would work.

Other events such as NOTE_ON events are being captured correctly from these tracks however. I know this is a bit of a niche issue, but does anyone have any insights into this?

Meric
  • 1

2 Answers2

0

one issue I'm facing is that occasionally I'll get some tracks in the MIDI file that don't have any assigned instrument to them (no program change events)

Yes, this can happen. The Midi specification does not enforce the use of Program Changes. It really depends for what purpose the Midi files were designed. For maximum portability it's normally better to add GM Program Changes for each used Midi channel. Note that for non-GM Midi devices you usually specify an instrument using Bank Select and Program Changes messages.

Also, you might find Midi files on the web designed for a specified synth, or VST, which uses synth-specific SysEx messages to select the instrument.

They don't seem to have a tick associated with them

Yes they do. Though tick is often 0 because Program Changes are usually at the start of the song. But it happens that some songs change the instrument in the middle of the song, so with a tick position > 0.

If you want an example of a complete open-source Midi file parser: https://github.com/jjazzboss/JJazzLab-X/tree/master/Midi/src/org/jjazz/midi/api/parser

jjazzboss
  • 1,261
  • 8
  • 14
0

They don't seem to have a tick associated with them

They do we just need some maths to add these events at the right time & place using the logic below

How To convert Time(In Microseconds) to ticks and vice versa?

This depends on 2 factors

  1. The format of the sequence [PPQ or SMTP]

  2. The tempo of the track[BPM]

Here is the code for these conversions

 //convert tick's to Microseconds
 static long toMicroSeconds(float ticksPerSecond,long tick){return (long)((1E6/ticksPerSecond)*tick);}

 /*
   get resolution of the track 

   ->if format is PPQ[Pulses per quater note] then this value depends on the tempo of the Sequencer

   ->Temp scale is nothing but tempoInBPM() & tempoScale() see Sequencer documentation for more details
 */
 static float ticksPerSecond(Sequence sequence,float tempoScale)
 {
  float divisionType=sequence.getDivisionType();
  int resolution=sequence.getResolution();
  
  if(divisionType==Sequence.PPQ){return resolution*(tempoScale/60.0f);}
  else
  {
   float framesPerSecond;
   
   if(divisionType==Sequence.SMPTE_24){framesPerSecond=24;}
   else if(divisionType==Sequence.SMPTE_25){framesPerSecond=25;}
   else if(divisionType==Sequence.SMPTE_30){framesPerSecond=30;}
   else{framesPerSecond=29.97f;}
   
   return resolution * framesPerSecond;
  }
 }

 //Convert time[micro second] to ticks
 static long toTicks(float ticksPerSecond,long microSeconds){return (long)((ticksPerSecond/1E6)*microSeconds);} 

The math's for these conversions can be found here

Using the above methods you should be able to capture any event including program change at the right time

One of the main issues I might be facing is just grabbing program change events the wrong way

Program change events are actually composed of 2 event's

  1. An Controller event which changes the bank of the sequencer

  2. The actual program ID

Both this information can be obtained from the patch of the instrument

Think of each instrument located in an grid where it's X coordinate is the program id and it's Y coordinate is it's bank ID

Program ID ranges from 0 to 128 hence it can be registered with an program change event but bank ranges from 0 16385[some what close] which is 2 bytes but an ShortMessage can hold only 1 byte hence 2 control change events are required to change the bank

This utility function should do it

void registerInstrument(Track track,Instrument instrument,int channel,int tick)
{
 Patch patch=instrument.getPatch();

 int
 programID=patch.getProgram(),
 bank=patch.getBank()         

 //bank is usually 0 for the first 128 instruments after which an controller event is required
 if(bank>0)
 {
   MidiEvent c1=new MidiEvent(new ShortMessage(ShortMessage.CONTROL_CHANGE,channel,0,bank>>7),tick);
   track.add(c1);
 
   MidiEvent c2=new MidiEvent(new ShortMessage(ShortMessage.CONTROL_CHANGE,channel,32,bank&0x7f),tick);
   track.add(c2);
 }
    
 track.add(new MidiEvent(new ShortMessage(ShortMessage.PROGRAM_CHANGE,channel,progrmID,0),tick));
}

one issue I'm facing is that occasionally I'll get some tracks in the MIDI file that don't have any assigned instrument to them (no program change events)

Some track's contain only Meta data of the song such as copy right information , or text/tempo change event's . Program change events usually are usually followed by notes in the same track so it's not mandatory for each track to contain these events

Sync it
  • 1,180
  • 2
  • 11
  • 29