MIDI import - for developers
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 inimportmidi_panel.cpp
- the MIDI import panel invokes the meta track info extraction function (
extractMidiTracksMeta
) inimportmidi.cpp
- the
importMidi
function inimportmidi.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 inimportmidi.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 theTrackOperations
struct inimportmidi_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 inimportmidi_opmodel.cpp
- add operation to the
setTrackOperation
function inimportmidi_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.