craigsapp/midifile

How to support tempo change for many times?

zhangkg5 opened this issue · 1 comments

hello, When reading a midi file, if tempo changes, the absolute time of midi event will go wrong. How can I solve this problem?

You need to provide an example of the problem, as I do not see it.

You can compare to my simple test below.

First, here is the musical content:

Screen Shot 2019-04-13 at 8 28 04 AM

The tempo starts at quarter notes = 60 bpm, then in the second measure changes to 120 bpm. So the quarter notes in the first measure are one second long and in the second measure they are 0.5 seconds long. The total duration of the music is 8 seconds (4 seconds for the first measure, 2 seconds for the second measure and 2 seconds for the last measure).

And here is the MIDI file (in ASCII format) representing the above music:

"MThd"			; MIDI header chunk marker
4'6			; bytes to follow in header chunk
2'1			; file format: Type-1 (multitrack)
2'2			; number of tracks
2'120			; ticks per quarter note

;;; TRACK 0 ----------------------------------
"MTrk"			; MIDI track chunk marker
4'27			; bytes to follow in track chunk
v0	ff 51 v3 t60	; tempo
v0	ff 58 v4 '4 '2 '24 '8	; time signature
v480	ff 51 v3 t120	; tempo
v0	ff 2f v0	; end-of-track

;;; TRACK 1 ----------------------------------
"MTrk"			; MIDI track chunk marker
4'81			; bytes to follow in track chunk
v0	90 '60 '64	; note-on C4
v120	80 '60 '64	; note-off C4
v0	90 '62 '64	; note-on D4
v120	80 '62 '64	; note-off D4
v0	90 '64 '64	; note-on E4
v120	80 '64 '64	; note-off E4
v0	90 '65 '64	; note-on F4
v120	80 '65 '64	; note-off F4
v0	90 '67 '64	; note-on G4
v120	80 '67 '64	; note-off G4
v0	90 '69 '64	; note-on A4
v120	80 '69 '64	; note-off A4
v0	90 '71 '64	; note-on B4
v120	80 '71 '64	; note-off B4
v0	90 '72 '64	; note-on C5
v120	80 '72 '64	; note-off C5
v0	90 '74 '64	; note-on D5
v480	80 '74 '64	; note-off D5
v0	ff 2f v0	; end-of-track

And here is a program to print the total time of the MIDI file, and the start-times of each note:

#include "MidiFile.h"
#include <iostream>
#include <stdlib.h>

using namespace std;
using namespace smf;

int main(int argc, char** argv) {
	if (argc != 2) {
		cerr << "Usage: " << argv[0] << " file.mid" << endl;
		exit(1);
	}
	MidiFile infile;
	infile.read(argv[1]);
	if (!infile.status()) {
		cerr << "Problem reading MIDI file " << argv[1] << endl;
	}

	infile.joinTracks();
	infile.doTimeAnalysis();
	cout << "Duration of MIDI file in ticks: " 
	     << infile.getFileDurationInTicks() << endl;
	cout << "Duration of MIDI file in seconds: " 
	     << infile.getFileDurationInSeconds() << endl;
	
	// Print start times of notes in file:
	cout << "Tick\tTime (seconds)\n";
	for (int i=0; i<infile[0].getEventCount(); i++) {
		if (infile[0][i].isNoteOn()) {
			cout << infile[0][i].tick << "\t"
			     << infile[0][i].seconds << endl;
		}
	}

	return 0;
}

Here is the output of the program using the test MIDI file as input (the ASCII form will be understood by the program):

Duration of MIDI file in ticks: 1440
Duration of MIDI file in seconds: 8
Tick	Time (seconds)
0	0
120	1
240	2
360	3
480	4
600	4.5
720	5
840	5.5
960	6

Compare the time values in the second column to the musical example:

Screen Shot 2019-04-13 at 8 28 04 AM

The numbers underneath the notes and the numbers in the second column are the same, so the tempo change is correctly handled to calculate the time in seconds throughout the example.