-
Notifications
You must be signed in to change notification settings - Fork 186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Duplicate tempo change export in MIDI output #3776
Comments
Without investigating any further I think it comes from the default tempo of 120 which is always set and then gets overwritten by the first tempo change. We always write this because it's set and needed for the time map calculation. |
Yes, it would not be objectionable to add q=120 to the exported MIID file even if there were no starting tempo in either When there is a tempo in For the timemap output, it would be good to always include an explicit tempo for the first entry, since this is not a standard representation such as a Standard MIDI File (which says that implicitly the starting tempo is 120). I think this is already the case (I thought I generated an example where there was no tempo entry in the timemap, but I cannot reproduce the case). For completeness, here is the URL for the official MIDI file specs that state the implicit tempo of q=120 unless otherwise indicated: https://midi.org/standard-midi-files The "STANDARD MIDI FILES SPECIFICATION On PDF page 16 (print page 14): There are currently two bugs: (1) There should only be one tempo for the system in the exported MIDI file. Currently there is one tempo for each staff, when a measure has a (2) Somewhat related and mentioned in the first section of this message, Consider one of the example expression tracks given in the initial measure: ;;; TRACK 0 ----------------------------------
"MTrk" ; MIDI track chunk marker
4'81 ; bytes to follow in track chunk
v0 ff 51 v3 t35 ; tempo
v0 ff 51 v3 t35 ; tempo
v0 ff 51 v3 t35 ; tempo
v0 ff 51 v3 t35 ; tempo
v0 ff 51 v3 t35 ; tempo
v0 ff 51 v3 t35 ; tempo
v0 ff 51 v3 t35 ; tempo
v0 ff 51 v3 t35 ; tempo
v0 ff 51 v3 t35 ; tempo
v0 ff 51 v3 t35 ; tempo
v0 ff 51 v3 t120 ; tempo
v0 ff 2f v0 ; end-of-track
Instead of altering the timemap generation or export to a MIDI file, I propose post-processing the tempo markings in the MIDI data before it is written to a file (or data stream in javascript): After the MidiFile has been filled with events, and before it is sorted by absolute tick time, add a sequence number to the track 0 events: Code snippet 1: MidiFile midifile;
for (int i=0; i<midifile[0].getEventCount(); ++i) {
midifile[0][i].seq = i;
} The When you want to preserve the original order that events were added randomly in an unsorted fashion to a MIDI file track, the the After the MidiFile events have been sorted and before converting to delta ticks (optionally) and then writing the MIDI file, you would then search through track 0 for cases where there are more than one tempo change at a given tick-time, and then delete any before the last tempo change at the given tick time: Code snippet 2: MidiFile midifile;
int lastTempoTick = -1;
for (int i=midifile[0].getEventCount() - 1; i>=0; --i) {
if (midifile[0][i].isTempo()) {
if (midifile[0][i].tick == lastTempoTick) {
midifile[0][i].clear();
}
lastTempoTick = midifile[0][i].tick;
}
} Optionally the following function could be run after the for-loop: Summary: The current Line 1740 of outputfile.sortTracks(); to: // Preserve insertion sequence of tempo-change messages:
for (int i=0; i<outputfile[0].getEventCount(); ++i) {
outputfile[0][i].seq = i;
}
outputfile.sortTracks();
// Remove unnecesary tempo-change messages:
int lastTempoTick = -1;
for (int i=outputfile[0].getEventCount() - 1; i>=0; --i) {
if (outputfile[0][i].isTempo()) {
if (outputfile[0][i].tick == lastTempoTick) {
outputfile[0][i].clear();
}
lastTempoTick = outputfile[0][i].tick;
}
} I can add this fix if you like (I would be lazy and do it in the You can implement the perservation of implicit/explicit tempo changes at the start of the MIDI File for q=120 if you want to do it (I would not object to the implicit tempo-change of q=120 always being added, so this fix is optional), and the above fix should solve the problem of having both It is also possible for different staves to have different tempos. In reasonable music, this can only happen if the durations of the notes have a mutual scaling factor that resolve to the same tempo in quarter notes. Suppose one staff has music has quarter notes at q=160 and another staff has half notes at h=80. In this case the half-note tempo easily resolves to q=160. A more exotic case is where quarter notes in one staff align with half notes in another staff. This would probably be down using These sorts of more exotic cases can be dealt with later when someone has a real musical example that does this (and would need to be resolved before inserting tempo changes into the MIDI file). |
Core of issue not fixed (PR was for preserving implicit/explicit default tempo). I can add the proposed fix which is to filter duplicate tempo-changes after they are inserted into the MidiFile object and before it is written (as opposed to preventing duplicates tempo-changes from being inserted into the MidiFile object). I will do that now. Also I will add warnings for conflicting tempos that occur at the same time. |
@craigsapp isn't that one a ticket for https://github.com/craigsapp/midifile/? |
That is possible, but if so, it would be as function called something like Also I am thinking of issuing a I suggested initially that the change needs to be done in |
Related to studying the example MusicXML in issue #3775, I notice a problem with generating tempo changes in the MIDI export from verovio.
Here is a minimal example that behaves properly:
Click to view original MusicXML data for above example
Tempo in the MusicXML file:
Click to view MEI conversion for original MusicXML.
Tempo in MEI:
The output MIDI from verovio does not has a slight problem:
There are two tempo markings at tick-time 0: (1) q=120 (which is undesired) and (2) q=35 (which is desired). Where the q=120 is coming from is uncertain, but probably related to there being two tracks in the MIDI file, and a tempo change is added for each track, and it is inserting q=120 when there is no tempo change for the track (q=120 is the default tempo for Standard MIDI Files, so the music should play at tempo q=120 even if there were no tempo change in the file). In this case the q=120 tempo marking is overriden by q=35, which occurs at the same time, but follows the q=120. The placement of the tempo marking within the measure seems to be causing the duplate tempo change (I will give a third example below where this q=120 tempo change is not added when the tempo is added in the
score/scoreDef
.Example 2: when I change
<staves>1</staves>
in the attributes element of the MusicXML file to<staves>10</staves>
, ten q=35 tempo changes are added at tick-time 0, plus one spurious q=120 tempo change, and there is still only one tempo element at the end of the measure element (as expected):MusicXML data with 10 staves
Converting to MEI does not cause any problems in terms of duplicated tempo changes
Click to view MEI data of 10-staff MusicXML data.
However, when converting this MEI data to MIDI with verovio, 10 identical q=35 tempo changes are found in the MIDI data, plus the spurious q=120 tempo change:
Here are the tempo changes in track 0:
The problem is that each staff is generating a tempo change, but there should only be one tempo change. Probably the q=120 is being added during this process: the index of the track is being used to insert a tempo by linking it to a staff (one staff for each track), but verovio is trying to do the same with track 0 (doubly) incorrectly, and it makes up q=120 for track 0 since there is no staff labeled
@n=0
.For such a
<tempo>
element added to the<measure>
, only one tempo change should be inserted into the MIDI file in track 0. The tempo change applies to all track so only one is needed, and it is messy to include redundant tempo changes.In this particular case the q=120 tempo change is added after all of the q=35 tempo changes. This causes an error, since the music will be played in q=120, since it is cancelling out all of the q=35 tempo message that occur before it.
Example 3: If you move the tempo change to the
score/scoreDef
, there is no problem: only one q=40 tempo change is added to the MIDI file (although the duplicate q=35 are added by the Grave tempo element in at the bottom of the measure element).Click to view MEI from example 2 is adjusted to add `score/[email protected]="40" tempo
Here is the resulting MIDI output from verovio for the above MEI data:
Track 0 contents:
The q=40 tempo change comes from
score/[email protected]
. This demonstrates where the spurious q=120 tempo change comes from. I would not recommend adding q=120 if there is noscore/[email protected]
attribute: the Standard MIDI File standard states that if no tempo change is given, q=120 should be used.Ideally the q=35 should also not be inserted in such a case. When the tempo is text, then it must be added to the measure. But if someone sets
score/[email protected]
, then that should take priority over a text-based tempo (not for numeric bpm that is added to the first measure, however).But in any case, the duplicate q=35, with one added to each staff, should not be done since it is not possible in MIDI files to have different tempos occurring in parallel. In such case the tempo changes will be interpreted serially, with only the last one at a given tick time having any affect.
The text was updated successfully, but these errors were encountered: