MIDI import - for developers

Updated 6 years ago

Almost all MIDI import files begin with the “importmidi” prefix and are located in mscore/ directory.

Internally, MIDI import in MuseScore consists of two main stages:

  • track information extraction from the MIDI file to fill the track list in the MIDI import panel
  • import of “music” MIDI events with the default values of applied operations

All of the import is controlled by values of the MIDI import operations (default or user-defined). Those operations are stored in the MidiImportOperations class and may be different for each track. This class also contains MidiData class which is responsible for the storage of contents of the MIDI file (necessary because the user can switch between multiple open MIDI files). The object of MidiImportOperations class is stored in MuseScore Preferences class, which can be found in mscore/preferences.h.

The opening of a MIDI file is followed by a range of internal program actions, the main ones are:

  • musescore.cpp calls the MIDI import panel’s function setMidiFile that is located in importmidi_panel.cpp
  • the MIDI import panel invokes the meta track info extraction function (extractMidiTracksMeta) in importmidi.cpp
  • the importMidi function in importmidi.cpp is called, which performs all necessary calculations and adds the music information from the MIDI file to the new score

If the user changes the operations and clicks the Apply button, the doMidiImport function (from importmidi_panel.cpp) obtains the values of operations from the GUI model and saves it in the preferences.midiImportOperations object. After, the usual importMidi function is called. Immediately after the import function performs its job, the preferences.midiImportOperations object is cleared.

The MIDI “music” import stage reads the MIDI file information previously stored in preferences.midiImportOperations.midiData object and calls convertMidi function from the importmidi.cpp file. After, all operations and algorithmic manipulations are applied to the MIDI file content:

  • list of track objects is created; tracks without note events are omitted
  • each note is stored in object of MidiChord class
  • all notes with very close “on time” events are combined into chords, also in class
  • notes too short are cleaned up
  • overlapping notes of equal pitch are corrected to rid of their intersection (removeOverlappingNotes function, importmidi.cpp)
  • a tuplet search is performed (importmidi_tuplet.cpp); multiple voices are also taken into account
  • after that - quantization of all chords (importmidi_quant.cpp); tuplets and non-tuplet notes are quantized by different grids
  • removeOverlappingNotes is called once again
  • if the user decides - the track is subdivided into left/right hand parts (a purpose mainly for piano tracks) - importmidi_lrhand.cpp
  • chords with notes of different length are separated into different chords with equal notes (splitUnequalChords function in importmidi.cpp)
  • import of drums (importmidi_drum.cpp)
  • instruments are created
  • measures (bars) are created, so now it’s possible to find a bar by tick
  • chord durations are converted into music notation; metrical analysis is performed here to find preferable duration subdivision in bar according to strong/weak bar fractions (see importmidi_meter.cpp)
    after that track meta is set
  • tuplets are added to the score, according to recognized tuplet events that are stored in MTrack::tuplets multimap
  • key signatures are created
  • if the user chooses - the program tries to detect swing: to replace triplet [4th + 8th] by 2 straight 8ths (for usual swing, 2:1) or dotted 8th + 16th by 2 straight 8ths as well (for shuffle, 3:1); the search of patterns and note replacement is performed on score chord/rest segments; code for swing detection is in importmidi_swing.cpp
  • clefs are created, they are of two types: at the beginning of staff (mandatory clefs) and, if the user wishes, clefs may change (added) within a staff depending on average chord pitch to make the score more “smooth”. Tied notes are not supposed to be separated by the clef insertion - algorithm tries to check it (code is in importmidi_clef.cpp)
  • time signatures are created
  • lyrics are imported, if any - importmidi_lyrics.cpp (lyric can be a title, composer or text next to notes - like song words)

The score is then rendered by the MuseScore graphics engine.

***

Currently to add a new operation one should add some pieces of code to several places (maybe it will be possible to bring all this pieces into one place in the future):

  • register the new type of operation in impotmidi_operation.h - MidiOperation::Type
    add operation variable into the TrackOperations struct in importmidi_operations.h and set the default value in C++11 manner
  • insert a new node in importmidi_opmodel.cpp that will be shown in the MIDI import panel; connect to the controller if necessary; controller hides/shows the nodes according to some defined rules (for example, “Allow clef change…” is a hidden node for drum tracks)
  • add operation to the setNodeOperations function in importmidi_opmodel.cpp
  • add operation to the setTrackOperation function in importmidi_trmodel.cpp
  • add operation to the trackOperations function in the same file

The operation then becomes available for the MIDI import algorithms.

***

MIDI import tests are located in mtest/importmidi/tst_importmidi.cpp

To test functions that have only definition in cpp file, the auxiliary file mtest/importmidi/inner_func_decl.h header is used where the headers of such tested functions should be put.