Significantly speed up loading SMF tempo-maps

Files that have many tracks, each with tempo information
were near impossible to load (30+ mins on modern 4.2Ghz CPU!),
because tempo is parsed incrementally:

```
For each new track:
  for each new tempo-event:
    rewind()
    for each loaded track so far:
      for each event on this track so far
```

This reduces the complexity from O(tracks^2 * tempos^2)
to O(tracks * tempos).

"Come Thou Fount Tempo Map.mid" has 238 Tracks and 56168 total
Tempo Changes (236 per track). This now requires only 56168 iterations
in smf_create_tempo_map_and_compute_seconds, rather than 1.64e+9
iterations
This commit is contained in:
Robin Gareus 2022-02-05 17:03:42 +01:00
parent 3da54244ea
commit cd53301d06
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
3 changed files with 8 additions and 1 deletions

View File

@ -525,7 +525,7 @@ smf_track_add_event(smf_track_t *track, smf_event_t *event)
if (smf_event_is_last(event))
maybe_add_to_tempo_map(event);
else
smf_create_tempo_map_and_compute_seconds(event->track->smf);
event->track->smf->need_tempo_map_compute = 1;
}
}
@ -709,6 +709,7 @@ smf_track_get_next_event(smf_track_t *track)
if (track->next_event_number < track->number_of_events) {
next_event = smf_track_get_event_by_number(track, track->next_event_number + 1);
assert(next_event);
assert(next_event->time_pulses >= event->time_pulses);
track->time_of_next_event = next_event->time_pulses;
track->next_event_number++;

View File

@ -251,6 +251,7 @@ struct smf_struct {
/** Private, used by smf.c. */
GPtrArray *tracks_array;
double last_seek_position;
int need_tempo_map_compute;
/** Private, used by smf_tempo.c. */
/** Array of pointers to smf_tempo_struct. */

View File

@ -972,6 +972,11 @@ smf_load_from_memory(void *buffer, const size_t buffer_length)
smf->expected_number_of_tracks = smf->number_of_tracks;
}
/* process tempo-map */
if (smf->need_tempo_map_compute) {
smf_create_tempo_map_and_compute_seconds(smf);
}
smf->file_buffer = NULL;
smf->file_buffer_length = 0;
smf->next_chunk_offset = 0;