    //=============================================================================
    //  MuseScore
    //  Music Composition & Notation
    //
    //  Copyright (C) 2002-2013 Werner Schweer
    //
    //  This program is free software; you can redistribute it and/or modify
    //  it under the terms of the GNU General Public License version 2
    //  as published by the Free Software Foundation and appearing in
    //  the file LICENCE.GPL
    //=============================================================================

    #include "midi/midifile.h"
    #include "mscore/preferences.h"
    #include "libmscore/score.h"
    #include "libmscore/key.h"
    #include "libmscore/clef.h"
    #include "libmscore/sig.h"
    #include "libmscore/tempo.h"
    #include "libmscore/note.h"
    #include "libmscore/chord.h"
    #include "libmscore/rest.h"
    #include "libmscore/segment.h"
    #include "libmscore/utils.h"
    #include "libmscore/text.h"
    #include "libmscore/slur.h"
    #include "libmscore/tie.h"
    #include "libmscore/staff.h"
    #include "libmscore/measure.h"
    #include "libmscore/style.h"
    #include "libmscore/part.h"
    #include "libmscore/timesig.h"
    #include "libmscore/barline.h"
    #include "libmscore/pedal.h"
    #include "libmscore/ottava.h"
    #include "libmscore/lyrics.h"
    #include "libmscore/bracket.h"
    #include "libmscore/drumset.h"
    #include "libmscore/box.h"
    #include "libmscore/sym.h"
    #include "libmscore/pitchspelling.h"
    #include "importmidi_meter.h"
    #include "importmidi_chord.h"
    #include "importmidi_quant.h"
    #include "importmidi_tuplet.h"
    #include "importmidi_tuplet_tonotes.h"
    #include "libmscore/tuplet.h"
    #include "libmscore/articulation.h"
    #include "importmidi_swing.h"
    #include "importmidi_fraction.h"
    #include "importmidi_drum.h"
    #include "importmidi_inner.h"
    #include "importmidi_clef.h"
    #include "importmidi_lrhand.h"
    #include "importmidi_lyrics.h"
    #include "importmidi_tie.h"
    #include "importmidi_beat.h"
    #include "importmidi_tempo.h"
    #include "importmidi_simplify.h"
    #include "importmidi_voice.h"
    #include "importmidi_operations.h"
    #include "importmidi_key.h"
    #include "importmidi_instrument.h"
    #include "importmidi_chordname.h"

    #include <set>

    namespace Ms {

    extern Preferences preferences;
    extern void updateNoteLines(Segment*, int track);

    void lengthenTooShortNotes(std::multimap<int, MTrack> &tracks)
          {
          for (auto &track: tracks) {
                MTrack &mtrack = track.second;
                for (auto &chord: mtrack.chords) {
                      for (auto &note: chord.second.notes) {
                            if (note.offTime - chord.first < MChord::minAllowedDuration())
                                  note.offTime = chord.first + MChord::minAllowedDuration();
                            }
                      }
                }
          }

    #ifdef QT_DEBUG

    bool doNotesOverlap(const MTrack &track)
          {
          const auto &chords = track.chords;
          for (auto i1 = chords.begin(); i1 != chords.end(); ++i1) {
                const auto &chord1 = i1->second;
                for (auto noteIt1 = chord1.notes.begin();
                          noteIt1 != chord1.notes.end(); ++noteIt1) {
                      for (auto noteIt2 = std::next(noteIt1);
                                noteIt2 != chord1.notes.end(); ++noteIt2) {
                            if (noteIt2->pitch == noteIt1->pitch)
                                  return true;
                            }
                      for (auto i2 = std::next(i1); i2 != chords.end(); ++i2) {
                            if (i2->first >= noteIt1->offTime)
                                  break;
                            const auto &chord2 = i2->second;
                            if (chord1.voice != chord2.voice)
                                  continue;
                            for (const auto &note2: chord2.notes) {
                                  if (note2.pitch == noteIt1->pitch)
                                        return true;
                                  }
                            }
                      }
                }
          return false;
          }

    bool doNotesOverlap(const std::multimap<int, MTrack> &tracks)
          {
          bool result = false;
          for (const auto &track: tracks)
                result = doNotesOverlap(track.second);
          return result;
          }

    bool noTooShortNotes(const std::multimap<int, MTrack> &tracks)
          {
          for (const auto &track: tracks) {
                const auto &chords = track.second.chords;
                for (const auto &chord: chords) {
                      for (const auto &note: chord.second.notes) {
                            if (note.offTime - chord.first < MChord::minAllowedDuration())
                                  return false;
                            }
                      }
                }
          return true;
          }

    #endif

    std::vector<std::multimap<ReducedFraction, MidiChord> >
    separateDrumChordsTo2Voices(const std::multimap<ReducedFraction, MidiChord> &chords)
          {
          std::vector<std::multimap<ReducedFraction, MidiChord> > separatedChords(2);
          for (const auto &chord: chords) {
                const MidiChord &c = chord.second;

                Q_ASSERT(c.voice == 0 || c.voice == 1);

                separatedChords[c.voice].insert({chord.first, c});
                }
          return separatedChords;
          }

    void setChordVoice(MidiChord &chord, int voice)
          {
          chord.voice = voice;
          if (chord.isInTuplet)
                chord.tuplet->second.voice = voice;
          for (auto &note: chord.notes) {
                if (note.isInTuplet)
                      note.tuplet->second.voice = voice;
                }
          }

    void findAllTupletsForDrums(
                MTrack &mtrack,
                TimeSigMap *sigmap,
                const ReducedFraction &basicQuant)
          {
          const size_t drumVoiceCount = 2;
                // drum track has 2 voices (stem up and stem down),
                // and tuplet detection is applicable for single voice drum tracks,
                // so split track chords into 2 voice groups,
                // detect tuplets and merge chords (and found tuplets) back;
                // it's a small hack due to the fact that tuplet detection
                // is designed to work before voice setting

          std::vector<std::multimap<ReducedFraction, MidiChord> > chords(drumVoiceCount);
          for (const auto &chord: mtrack.chords) {
                const MidiChord &c = chord.second;

                Q_ASSERT(c.voice == 0 || c.voice == 1);

                chords[c.voice].insert({chord.first, c});
                }

          std::vector<std::multimap<ReducedFraction,
                                    MidiTuplet::TupletData> > tuplets(drumVoiceCount);
          for (size_t voice = 0; voice < drumVoiceCount; ++voice) {
                if (!chords[voice].empty())
                      MidiTuplet::findAllTuplets(tuplets[voice], chords[voice], sigmap, basicQuant);
                }
          mtrack.chords.clear();
          for (size_t voice = 0; voice < drumVoiceCount; ++voice) {
                for (auto &chord: chords[voice]) {
                            // correct voice because it can be changed during tuplet detection
                      setChordVoice(chord.second, voice);
                      mtrack.chords.insert({chord.first, chord.second});
                      }
                }
          mtrack.updateTupletsFromChords();
          // note: temporary local tuplets and chords are deleted here
          }

    void quantizeAllTracks(std::multimap<int, MTrack> &tracks,
                           TimeSigMap *sigmap,
                           const ReducedFraction &lastTick)
          {
          auto &opers = preferences.midiImportOperations;

          for (auto &track: tracks) {
                MTrack &mtrack = track.second;
                if (mtrack.chords.empty())
                      continue;
                            // pass current track index through MidiImportOperations
                            // for further usage
                MidiOperations::CurrentTrackSetter setCurrentTrack{opers, mtrack.indexOfOperation};

                if (opers.data()->processingsOfOpenedFile == 0) {
                      opers.data()->trackOpers.isDrumTrack.setValue(
                                              opers.currentTrack(), mtrack.mtrack->drumTrack());
                      if (mtrack.mtrack->drumTrack()) {
                            opers.data()->trackOpers.maxVoiceCount.setValue(
                                              opers.currentTrack(), MidiOperations::VoiceCount::V_1);
                            }
                      }
                const auto basicQuant = Quantize::quantValueToFraction(
                            opers.data()->trackOpers.quantValue.value(mtrack.indexOfOperation));

                Q_ASSERT_X(MChord::isLastTickValid(lastTick, mtrack.chords),
                           "quantizeAllTracks", "Last tick is less than max note off time");

                MChord::setBarIndexes(mtrack.chords, basicQuant, lastTick, sigmap);

                if (mtrack.mtrack->drumTrack())
                      findAllTupletsForDrums(mtrack, sigmap, basicQuant);
                else
                      MidiTuplet::findAllTuplets(mtrack.tuplets, mtrack.chords, sigmap, basicQuant);

                Q_ASSERT_X(!doNotesOverlap(track.second),
                           "quantizeAllTracks",
                           "There are overlapping notes of the same voice that is incorrect");

                            // (4/3 of the smallest duration) tol is less sensitive
                            // to on time inaccuracies than 1/2 earlier
                MChord::collectChords(mtrack, {2, 1}, {4, 3});
                Quantize::quantizeChords(mtrack.chords, sigmap, basicQuant);
                MidiTuplet::removeEmptyTuplets(mtrack);

                Q_ASSERT_X(MidiTuplet::areTupletRangesOk(mtrack.chords, mtrack.tuplets),
                           "quantizeAllTracks", "Tuplet chord/note is outside tuplet "
                            "or non-tuplet chord/note is inside tuplet");
                }
          }

    //---------------------------------------------------------
    //   processMeta
    //---------------------------------------------------------

    void MTrack::processMeta(int tick, const MidiEvent& mm)
          {
          if (!staff) {
                qDebug("processMeta: no staff");
                return;
                }
          const uchar* data = mm.edata();
          Score* cs         = staff->score();

          switch (mm.metaType()) {
                case META_TEXT:
                case META_LYRIC:
                      break;      // lyric and text are added in importmidi_lyrics.cpp
                case META_TRACK_NAME:
                      {
                      const std::string text = MidiCharset::fromUchar(data);

                      auto &opers = preferences.midiImportOperations;
                      if (opers.data()->processingsOfOpenedFile == 0) {
                            const int currentTrack = indexOfOperation;
                            opers.data()->trackOpers.staffName.setValue(currentTrack, text);
                            }

                      if (name.isEmpty())
                            name = MidiCharset::convertToCharset(text);
                      }
                      break;
                case META_TEMPO:  // add later, after adding of notes
                      break;
                case META_KEY_SIGNATURE:
                      {
                      const signed char key = ((const signed char*)data)[0];
                      if (key < -7 || key > 7) {
                            qDebug("ImportMidi: illegal key %d", key);
                            break;
                            }
                      KeySigEvent ke;
                      ke.setKey(Key(key));
                      staff->setKey(tick, ke);
                      hasKey = true;
                      }
                      break;
                case META_COMPOSER:     // mscore extension
                case META_POET:
                case META_TRANSLATOR:
                case META_SUBTITLE:
                case META_TITLE:
                      {
                      Text* text = new Text(cs);
                      switch(mm.metaType()) {
                            case META_COMPOSER:
                                  text->setSubStyle(SubStyle::COMPOSER);
                                  break;
                            case META_TRANSLATOR:
                                  text->setSubStyle(SubStyle::TRANSLATOR);
                                  break;
                            case META_POET:
                                  text->setSubStyle(SubStyle::POET);
                                  break;
                            case META_SUBTITLE:
                                  text->setSubStyle(SubStyle::SUBTITLE);
                                  break;
                            case META_TITLE:
                                  text->setSubStyle(SubStyle::TITLE);
                                  break;
                            }

                      text->setPlainText((const char*)(mm.edata()));

                      MeasureBase* measure = cs->first();
                      if (!measure->isVBox()) {
                            measure = new VBox(cs);
                            measure->setTick(0);
                            measure->setNext(cs->first());
                            cs->measures()->add(measure);
                            }
                      measure->add(text);
                      }
                      break;
                case META_COPYRIGHT:
                      cs->setMetaTag("copyright", QString((const char*)(mm.edata())));
                      break;
                case META_TIME_SIGNATURE:
                      break;                        // added earlier
                case META_PORT_CHANGE:
                      staff->part()->setMidiChannel(-1, (int)data[0]);
                      break;
                default:
                      if (MScore::debugMode)
                            qDebug("unknown meta type 0x%02x", mm.metaType());
                      break;
                }
          }

    QList<std::pair<ReducedFraction, TDuration> >
    MTrack::toDurationList(const Measure *measure,
                           int voice,
                           const ReducedFraction &startTick,
                           const ReducedFraction &len,
                           Meter::DurationType durationType)
          {
                      // find tuplets over which duration goes
          auto barTick = ReducedFraction::fromTicks(measure->tick());
          auto tupletsData = MidiTuplet::findTupletsInBarForDuration(
                            voice, barTick, startTick, len, tuplets);
          struct {
                bool operator()(const MidiTuplet::TupletData &d1,
                                const MidiTuplet::TupletData &d2)
                      {
                      return (d1.len > d2.len);
                      }
                } comparator;
                      // sort by tuplet length in desc order
          sort(tupletsData.begin(), tupletsData.end(), comparator);

          const ReducedFraction startTickInBar = startTick - barTick;
          const ReducedFraction endTickInBar = startTickInBar + len;

          const auto &opers = preferences.midiImportOperations;
          const bool useDots = opers.data()->trackOpers.useDots.value(indexOfOperation);
          return Meter::toDurationList(startTickInBar, endTickInBar,
                                       ReducedFraction(measure->timesig()), tupletsData,
                                       durationType, useDots);
          }

    ReducedFraction splitDurationOnBarBoundary(const ReducedFraction &len,
                                               const ReducedFraction &onTime,
                                               const Measure* measure)
          {
          const ReducedFraction barLimit = ReducedFraction::fromTicks(measure->tick() + measure->ticks());
          if (onTime + len > barLimit)
                return barLimit - onTime;
          return len;
          }

    // fill the gap between successive chords with rests

    void MTrack::fillGapWithRests(Score* score,
                                  int voice,
                                  const ReducedFraction &startChordTickFrac,
                                  const ReducedFraction &restLength,
                                  int track)
          {
          ReducedFraction startChordTick = startChordTickFrac;
          ReducedFraction restLen = restLength;
          while (restLen > ReducedFraction(0, 1)) {
                ReducedFraction len = restLen;
                Measure* measure = score->tick2measure(startChordTick.ticks());
                if (startChordTick >= ReducedFraction::fromTicks(measure->tick() + measure->ticks())) {
                      qDebug("tick2measure: %d end of score?", startChordTick.ticks());
                      startChordTick += restLen;
                      restLen = ReducedFraction(0, 1);
                      break;
                      }
                len = splitDurationOnBarBoundary(len, startChordTick, measure);

                if (len >= ReducedFraction::fromTicks(measure->ticks())) {
                                  // rest to the whole measure
                      len = ReducedFraction::fromTicks(measure->ticks());
                      if (voice == 0) {
                            TDuration duration(TDuration::DurationType::V_MEASURE);
                            Rest* rest = new Rest(score, duration);
                            rest->setDuration(measure->len());
                            rest->setTrack(track);
                            Segment* s = measure->getSegment(SegmentType::ChordRest, startChordTick.ticks());
                            s->add(rest);
                            }
                      restLen -= len;
                      startChordTick += len;
                      }
                else {
                      const auto dl = toDurationList(measure, voice, startChordTick, len,
                                                     Meter::DurationType::REST);
                      if (dl.isEmpty()) {
                            qDebug("cannot create duration list for len %d", len.ticks());
                            restLen = ReducedFraction(0, 1);      // fake
                            break;
                            }
                      for (const auto &durationPair: dl) {
                            const TDuration &duration = durationPair.second;
                            const ReducedFraction &tupletRatio = durationPair.first;
                            len = ReducedFraction(duration.fraction()) / tupletRatio;
                            Rest* rest = new Rest(score, duration);
                            rest->setDuration(duration.fraction());
                            rest->setTrack(track);
                            Segment* s = measure->getSegment(SegmentType::ChordRest,
                                                             startChordTick.ticks());
                            s->add(rest);
                            MidiTuplet::addElementToTuplet(voice, startChordTick, len, rest, tuplets);
                            restLen -= len;
                            startChordTick += len;
                            }
                      }

                }
          }

    void setMusicNotesFromMidi(Score *score,
                               const QList<MidiNote> &midiNotes,
                               Chord *chord,
                               const Drumset *drumset,
                               bool useDrumset)
          {
          for (int i = 0; i < midiNotes.size(); ++i) {
                const MidiNote& mn = midiNotes[i];
                Note* note = new Note(score);
                note->setTrack(chord->track());

                NoteVal nval(mn.pitch);
                note->setNval(nval, chord->tick());
                // TODO - does this need to be key-aware?
                //note->setPitch(mn.pitch);
                //note->setTpcFromPitch();

                chord->add(note);
                note->setVeloType(Note::ValueType::USER_VAL);
                note->setVeloOffset(mn.velo);

                if (useDrumset) {
                      if (!drumset->isValid(mn.pitch))
                            qDebug("unmapped drum note 0x%02x %d", mn.pitch, mn.pitch);
                      else {
                            Direction sd = drumset->stemDirection(mn.pitch);
                            chord->setStemDirection(sd);
                            }
                      }

                if (midiNotes[i].tie) {
                      midiNotes[i].tie->setEndNote(note);
                      midiNotes[i].tie->setTrack(note->track());
                      note->setTieBack(midiNotes[i].tie);
                      }
                }
          }

    void setTies(Chord *chord,
                 Score *score,
                 QList<MidiNote> &midiNotes)
          {
          for (int i = 0; i < midiNotes.size(); ++i) {
                const MidiNote &midiNote = midiNotes[i];
                Note *note = chord->findNote(midiNote.pitch);
                midiNotes[i].tie = new Tie(score);
                midiNotes[i].tie->setStartNote(note);
                note->setTieFor(midiNotes[i].tie);
                }
          }

    // convert midiChords with the same onTime value to music notation
    // and fill the remaining empty duration with rests

    void MTrack::processPendingNotes(QList<MidiChord> &midiChords,
                                     int voice,
                                     const ReducedFraction &startChordTickFrac,
                                     const ReducedFraction &nextChordTick)
          {
          Score* score = staff->score();
          const int track = staff->idx() * VOICES + voice;
          const Drumset* drumset = staff->part()->instrument()->drumset();
          const bool useDrumset  = staff->part()->instrument()->useDrumset();

          const auto& opers = preferences.midiImportOperations.data()->trackOpers;
          const int currentTrack = indexOfOperation;

                      // all midiChords here should have the same onTime value
                      // and all notes in each midiChord should have the same duration
          ReducedFraction startChordTick = startChordTickFrac;
          while (!midiChords.isEmpty()) {
                const ReducedFraction tick = startChordTick;
                ReducedFraction len = nextChordTick - tick;
                if (len <= ReducedFraction(0, 1))
                      break;
                len = MChord::findMinDuration(tick, midiChords, len);
                Measure* measure = score->tick2measure(tick.ticks());
                len = splitDurationOnBarBoundary(len, tick, measure);

                const auto dl = toDurationList(measure, voice, tick, len, Meter::DurationType::NOTE);
                if (dl.isEmpty())
                      break;
                const TDuration &d = dl[0].second;
                const ReducedFraction &tupletRatio = dl[0].first;
                len = ReducedFraction(d.fraction()) / tupletRatio;

                Chord* chord = new Chord(score);
                chord->setTrack(track);
                chord->setDurationType(d);
                chord->setDuration(d.fraction());

                if (opers.showStaccato.value(currentTrack)
                            && startChordTick == startChordTickFrac   // first chord in tied chord sequence
                            && midiChords.begin()->isStaccato()) {
                      Articulation* a = new Articulation(chord->score());
                      a->setSymId(SymId::articStaccatoAbove);
                      chord->add(a);
                      }

                Segment* s = measure->getSegment(SegmentType::ChordRest, tick.ticks());
                s->add(chord);
                MidiTuplet::addElementToTuplet(voice, tick, len, chord, tuplets);

                for (int k = 0; k < midiChords.size(); ++k) {
                      MidiChord& midiChord = midiChords[k];
                      setMusicNotesFromMidi(score, midiChord.notes, chord, drumset, useDrumset);
                      if (!midiChord.notes.empty() && midiChord.notes.first().offTime - tick <= len) {
                            midiChords.removeAt(k);
                            --k;
                            continue;
                            }
                      setTies(chord, score, midiChord.notes);
                      }
                startChordTick += len;
                }
          fillGapWithRests(score, voice, startChordTick, nextChordTick - startChordTick, track);
          }

    void MTrack::createKeys(Key defaultKey, const KeyList &allKeyList)
          {
          KeyList &staffKeyList = *staff->keyList();

          if (!hasKey && !mtrack->drumTrack()) {
                if (allKeyList.empty()) {
                      KeySigEvent ke;
                      ke.setKey(defaultKey);
                      staffKeyList[0] = ke;
                      MidiKey::assignKeyListToStaff(staffKeyList, staff);
                      }
                else {
                      hasKey = true;
                      MidiKey::assignKeyListToStaff(allKeyList, staff);
                      }
                }
          else {
                MidiKey::assignKeyListToStaff(staffKeyList, staff);
                }
          }

    void MTrack::createNotes(const ReducedFraction &lastTick)
          {
          for (int voice = 0; voice < VOICES; ++voice) {
                            // startChordTick is onTime value of all simultaneous notes
                            // chords here are consist of notes with equal durations
                            // several chords may have the same onTime value
                ReducedFraction startChordTick;
                QList<MidiChord> midiChords;

                for (auto it = chords.begin(); it != chords.end();) {
                      const auto &nextChordTick = it->first;
                      const MidiChord& midiChord = it->second;
                      if (midiChord.voice != voice) {
                            ++it;
                            continue;
                            }
                      processPendingNotes(midiChords, voice, startChordTick, nextChordTick);
                                  // now 'midiChords' list is empty
                                  // so - fill it:
                                  // collect all midiChords on current tick position
                      startChordTick = nextChordTick;       // debug
                      for ( ; it != chords.end(); ++it) {
                            const MidiChord& midiChord = it->second;
                            if (it->first != startChordTick)
                                  break;
                            if (midiChord.voice != voice)
                                  continue;
                            midiChords.append(midiChord);
                            }
                      if (midiChords.isEmpty())
                            break;
                      }
                            // process last chords at the end of the score
                processPendingNotes(midiChords, voice, startChordTick, lastTick);
                }
          }

    Fraction metaTimeSignature(const MidiEvent& e)
          {
          const unsigned char* data = e.edata();
          const int z  = data[0];
          const int nn = data[1];
          int n  = 1;
          for (int i = 0; i < nn; ++i)
                n *= 2;
          return Fraction(z, n);
          }

    QList<MTrack> prepareTrackList(const std::multimap<int, MTrack> &tracks)
          {
          QList<MTrack> trackList;
          for (const auto &track: tracks) {
                            // show track even if all initial notes were cleaned up
                if (track.second.hadInitialNotes)
                      trackList.push_back(track.second);
                }
          return trackList;
          }

    std::multimap<int, MTrack> createMTrackList(TimeSigMap *sigmap, const MidiFile *mf)
          {
          sigmap->clear();
          sigmap->add(0, Fraction(4, 4));   // default time signature

          std::multimap<int, MTrack> tracks;   // <track index, track>
          int trackIndex = -1;
          for (const auto &t: mf->tracks()) {
                MTrack track;
                track.mtrack = &t;
                track.division = mf->division();
                track.isDivisionInTps = mf->isDivisionInTps();
                bool hasNotes = false;
                            //  - create time signature list from meta events
                            //  - create MidiChord list
                            //  - extract some information from track: program, min/max pitch
                for (const auto &i: t.events()) {
                      const MidiEvent& e = i.second;
                      const auto tick = toMuseScoreTicks(i.first, track.division,
                                                         track.isDivisionInTps);
                                  // remove time signature events
                      if ((e.type() == ME_META) && (e.metaType() == META_TIME_SIGNATURE)) {
                                        // because file can have incorrect data
                                        // like time sig event not at the beginning of bar
                                        // we need to round tick value to integral bar count
                            int bars, beats, ticks;
                            sigmap->tickValues(tick.ticks(), &bars, &beats, &ticks);
                            sigmap->add(sigmap->bar2tick(bars, 0), metaTimeSignature(e));
                            }
                      else if (e.type() == ME_NOTE) {
                            hasNotes = true;
                            const int pitch = e.pitch();
                            const auto len = toMuseScoreTicks(e.len(), track.division,
                                                              track.isDivisionInTps);
                            MidiNote n;
                            n.pitch           = pitch;
                            n.velo            = e.velo();
                            n.offTime         = tick + len;
                            n.origOnTime      = tick;

                            MidiChord c;
                            c.notes.push_back(n);

                            track.chords.insert({tick, c});
                            }
                      else if (e.type() == ME_PROGRAM)
                            track.program = e.dataB();
                      else if (e.type() == ME_CONTROLLER && e.controller() == CTRL_VOLUME) {
                            track.volumes.insert({tick, e.value()});
                            }
                      }
                if (hasNotes) {
                      ++trackIndex;
                      track.hadInitialNotes = true;
                      const auto *data = preferences.midiImportOperations.data();
                      if (data->processingsOfOpenedFile > 0) {
                            if (data->trackOpers.doImport.value(trackIndex)) {
                                  track.indexOfOperation = trackIndex;
                                  const int reorderedIndex
                                        = data->trackOpers.trackIndexAfterReorder.value(trackIndex);
                                  tracks.insert({reorderedIndex, track});
                                  }
                            }
                      else {            // if it is an initial track-list query from MIDI import panel
                            track.indexOfOperation = trackIndex;
                            tracks.insert({trackIndex, track});
                            }
                      }
                else {
                      track.hadInitialNotes = false;       // it's a tempo track or something else
                      tracks.insert({-1, track});
                      }
                }

          return tracks;
          }

    Measure* barFromIndex(const Score *score, int barIndex)
          {
          const int tick = score->sigmap()->bar2tick(barIndex, 0);
          return score->tick2measure(tick);
          }

    bool isPickupWithLessTimeSig(const Fraction &firstBarTimeSig, const Fraction &secondBarTimeSig)
          {
          return firstBarTimeSig < secondBarTimeSig;
          }

    bool isPickupWithGreaterTimeSig(
                const Fraction &firstBarTimeSig,
                const Fraction &secondBarTimeSig,
                const ReducedFraction &firstTick)
          {
          return firstBarTimeSig == secondBarTimeSig * 2
                      && firstBarTimeSig.numerator() % 3 != 0
                      && firstTick > ReducedFraction(0, 1);
          }

    // search for pickup measure only if next 3 bars have equal time signatures
    bool areNextBarsEqual(const Score *score, int barCount)
          {
          const int baseBarTick = score->sigmap()->bar2tick(1, 0);
          const Fraction baseTimeSig = score->sigmap()->timesig(baseBarTick).timesig();

          const int equalTimeSigCount = 3;
          for (int i = 2; i <= equalTimeSigCount - 1 && i < barCount; ++i) {
                const int barTick = score->sigmap()->bar2tick(i, 0);
                const Fraction timeSig = score->sigmap()->timesig(barTick).timesig();
                if (timeSig != baseTimeSig)
                      return false;
                }
          return true;
          }

    void tryCreatePickupMeasure(
                const ReducedFraction &firstTick,
                Score *score,
                int *begBarIndex,
                int *barCount)
          {
          const int firstBarTick = score->sigmap()->bar2tick(0, 0);
          const int secondBarTick = score->sigmap()->bar2tick(1, 0);
          const Fraction firstTimeSig = score->sigmap()->timesig(firstBarTick).timesig();
          const Fraction secondTimeSig = score->sigmap()->timesig(secondBarTick).timesig();

          if (isPickupWithLessTimeSig(firstTimeSig, secondTimeSig)) {
                Measure* pickup = new Measure(score);
                pickup->setTick(firstBarTick);
                pickup->setNo(0);
                pickup->setIrregular(true);
                pickup->setTimesig(secondTimeSig);       // nominal time signature
                pickup->setLen(firstTimeSig);            // actual length
                score->measures()->add(pickup);
                *begBarIndex = 1;
                }
          else if (isPickupWithGreaterTimeSig(firstTimeSig, secondTimeSig, firstTick)) {
                            // split measure into 2 equal measures
                            // but for simplicity don't treat first bar as a pickup measure:
                            // leave its actual length equal to nominal length
                ++(*barCount);

                score->sigmap()->add(firstBarTick, secondTimeSig);

                Measure* firstBar = new Measure(score);
                firstBar->setTick(firstBarTick);
                firstBar->setNo(0);
                firstBar->setTimesig(secondTimeSig);
                firstBar->setLen(secondTimeSig);
                score->measures()->add(firstBar);

                Measure* secondBar = new Measure(score);
                secondBar->setTick(firstBarTick + secondTimeSig.ticks());
                secondBar->setNo(1);
                secondBar->setTimesig(secondTimeSig);
                secondBar->setLen(secondTimeSig);
                score->measures()->add(secondBar);

                *begBarIndex = 2;
                }
          }

    void createMeasures(const ReducedFraction &firstTick, ReducedFraction &lastTick, Score *score)
          {
          int barCount, beat, tick;
          score->sigmap()->tickValues(lastTick.ticks(), &barCount, &beat, &tick);
          if (beat > 0 || tick > 0)
                ++barCount;           // convert bar index to number of bars

          auto &data = *preferences.midiImportOperations.data();
          if (data.processingsOfOpenedFile == 0) {
                if (!areNextBarsEqual(score, barCount))
                      data.trackOpers.searchPickupMeasure.setValue(false);
                }

          const bool tryDetectPickupMeasure = data.trackOpers.searchPickupMeasure.value();
          int begBarIndex = 0;

          if (tryDetectPickupMeasure && barCount > 1)
                tryCreatePickupMeasure(firstTick, score, &begBarIndex, &barCount);

          for (int i = begBarIndex; i < barCount; ++i) {
                Measure* m = new Measure(score);
                const int tick = score->sigmap()->bar2tick(i, 0);
                m->setTick(tick);
                m->setNo(i);
                const Fraction timeSig = score->sigmap()->timesig(tick).timesig();
                m->setTimesig(timeSig);
                m->setLen(timeSig);
                score->measures()->add(m);
                }

          const Measure *m = score->lastMeasure();
          if (m) {
                score->fixTicks();
                lastTick = ReducedFraction::fromTicks(m->endTick());
                }
          }

    void setTrackInfo(MidiType midiType, MTrack &mt)
          {
          auto &opers = preferences.midiImportOperations;

          const int currentTrack = mt.indexOfOperation;
          const QString instrName = MidiInstr::instrumentName(midiType, mt.program,
                                                              mt.mtrack->drumTrack());
          if (opers.data()->processingsOfOpenedFile == 0) {
                opers.data()->trackOpers.midiInstrName.setValue(currentTrack, instrName);
                            // set channel number (from 1): number = index + 1
                opers.data()->trackOpers.channel.setValue(currentTrack, mt.mtrack->outChannel() + 1);
                }

          const QString msInstrName = MidiInstr::msInstrName(currentTrack);
          const QString trackInstrName = (msInstrName.isEmpty()) ? instrName : msInstrName;

          if (mt.staff->isTop()) {
                Part *part  = mt.staff->part();
                part->setLongName(XmlWriter::xmlString(MidiInstr::concatenateWithComma(trackInstrName, mt.name)));
                part->setPartName(part->longName());
                part->setMidiChannel(mt.mtrack->outChannel());
                int bank = 0;
                if (mt.mtrack->drumTrack())
                      bank = 128;
                part->setMidiProgram(mt.program & 0x7f, bank);  // only GM
                }

          if (mt.name.isEmpty() && !trackInstrName.isEmpty())
                mt.name = trackInstrName;
          }

    void createTimeSignatures(Score *score)
          {
          for (auto is = score->sigmap()->begin(); is != score->sigmap()->end(); ++is) {
                const SigEvent& se = is->second;
                const int tick = is->first;
                Measure* m = score->tick2measure(tick);
                if (!m)
                      continue;
                Fraction newTimeSig = se.timesig();

                const auto& opers = preferences.midiImportOperations;
                const bool pickupMeasure = opers.data()->trackOpers.searchPickupMeasure.value();

                if (pickupMeasure && is == score->sigmap()->begin()) {
                      auto next = std::next(is);
                      if (next != score->sigmap()->end()) {
                            Measure* mm = score->tick2measure(next->first);
                            if (m && mm && m == barFromIndex(score, 0) && mm == barFromIndex(score, 1)
                                        && m->timesig() == mm->timesig() && newTimeSig != mm->timesig())
                                  {
                                              // it's a pickup measure - change timesig to nominal value
                                        newTimeSig = mm->timesig();
                                  }
                            }
                      }
                for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
                      TimeSig* ts = new TimeSig(score);
                      ts->setSig(newTimeSig);
                      ts->setTrack(staffIdx * VOICES);
                      Segment* seg = m->getSegment(SegmentType::TimeSig, tick);
                      seg->add(ts);
                      }
                if (newTimeSig != se.timesig())   // was a pickup measure - skip next timesig
                      ++is;
                }
          }

    void processMeta(MTrack &mt, bool isLyric)
          {
          for (const auto &ie : mt.mtrack->events()) {
                const MidiEvent &e = ie.second;
                const auto tick = toMuseScoreTicks(ie.first, mt.division, mt.isDivisionInTps);
                if ((e.type() == ME_META) && ((e.metaType() == META_LYRIC) == isLyric))
                      mt.processMeta(tick.ticks(), e);
                }
          }

    // key list with key signatures of all tracks
    //   to assign it to tracks without any specified key signature
    KeyList findAllKeyList(const QList<MTrack> &tracks)
          {
          KeyList kl;
          for (int i = 0; i < tracks.size(); ++i) {
                if (tracks[i].hasKey) {
                      for (const auto &key: *tracks[i].staff->keyList())
                            kl.setKey(key.first, key.second);
                      }
                }
          return kl;
          }

    void createNotes(const ReducedFraction &lastTick, QList<MTrack> &tracks)
          {
          for (int i = 0; i < tracks.size(); ++i) {
                MTrack &mt = tracks[i];
                mt.createNotes(lastTick);
                MidiTuplet::createTupletNotes(mt.staff, mt.tuplets);
                }
          }

    void processNonLyricMeta(QList<MTrack> &tracks)
          {
          for (int i = 0; i < tracks.size(); ++i) {
                MTrack &mt = tracks[i];
                processMeta(mt, false);
                }
          }

    void setTrackInfo(QList<MTrack> &tracks, MidiType midiType)
          {
          for (int i = 0; i < tracks.size(); ++i) {
                MTrack &mt = tracks[i];
                if (midiType == MidiType::UNKNOWN)
                      midiType = MidiType::GM;
                setTrackInfo(midiType, mt);
                }
          }

    void processLyricMeta(QList<MTrack> &tracks)
          {
          for (int i = 0; i < tracks.size(); ++i) {
                MTrack &mt = tracks[i];
                processMeta(mt, true);
                }
          }

    void createKeys(QList<MTrack> &tracks)
          {
          const Key defaultKey = Key::C;     // TODO-LIB findKey(mtrack, score->sigmap());
          const KeyList &allKeyList = findAllKeyList(tracks);

          for (int i = 0; i < tracks.size(); ++i) {
                MTrack &mt = tracks[i];
                mt.createKeys(defaultKey, allKeyList);
                }
          }

    void applySwing(QList<MTrack> &tracks)
          {
          for (int i = 0; i < tracks.size(); ++i) {
                MTrack &mt = tracks[i];

                const auto& opers = preferences.midiImportOperations.data()->trackOpers;
                const auto swingType = opers.swing.value(mt.indexOfOperation);
                Swing::detectSwing(mt.staff, swingType);

                Q_ASSERT_X(MidiTie::areTiesConsistent(mt.staff),
                           "applySwing", "Ties are inconsistent");

                Q_ASSERT_X(MidiTuplet::haveTupletsEnoughElements(mt.staff),
                           "MTrack::convertTrack",
                           "Tuplet has less than 2 elements or all elements are rests");
                }
          }

    void createClefs(QList<MTrack> &tracks)
          {
          for (int i = 0; i < tracks.size(); ++i) {
                MTrack &mt = tracks[i];
                MidiClef::createClefs(mt.staff, mt.indexOfOperation, mt.mtrack->drumTrack());
                }
          }

    void setLeftRightHandSplit(const std::multimap<int, MTrack> &tracks)
          {
          for (auto it = tracks.begin(); it != tracks.end(); ++it) {
                int trackIndex = it->first;
                const MTrack &mtrack = it->second;
                if (mtrack.mtrack->drumTrack() || mtrack.chords.empty())
                      continue;

                            // don't split staff if it is already in Grand Staff
                const auto nextIt = std::next(it);
                if (nextIt != tracks.end()) {
                      if (MidiInstr::isGrandStaff(mtrack, nextIt->second)) {
                            ++it;
                            continue;
                            }
                      }

                if (LRHand::needToSplit(mtrack.chords, mtrack.program, mtrack.mtrack->drumTrack())) {
                      preferences.midiImportOperations.data()->trackOpers.doStaffSplit.setValue(
                                                                                  trackIndex, true);
                      }
                }
          }

    ReducedFraction findFirstChordTick(const QList<MTrack> &tracks)
          {
          ReducedFraction firstTick(0, 1);
          for (const auto &track: tracks) {
                if (track.chords.empty())
                      continue;
                const auto &chordTick = track.chords.begin()->first;

                Q_ASSERT(chordTick >= ReducedFraction(0, 1));

                if (firstTick == ReducedFraction(0, 1) || chordTick < firstTick)
                      firstTick = chordTick;
                }
          return firstTick;
          }

    ReducedFraction findLastChordTick(const std::multimap<int, MTrack> &tracks)
          {
          ReducedFraction lastTick(0, 1);
          for (const auto &track: tracks) {
                for (const auto &chord: track.second.chords) {
                      const auto offTime = MChord::maxNoteOffTime(chord.second.notes);
                      if (offTime > lastTick)
                            lastTick = offTime;
                      }
                }
          return lastTick;
          }

    void convertMidi(Score *score, const MidiFile *mf)
          {
          auto *sigmap = score->sigmap();

          auto tracks = createMTrackList(sigmap, mf);

          auto &opers = preferences.midiImportOperations;
          if (opers.data()->processingsOfOpenedFile == 0)         // for newly opened MIDI file
                MidiChordName::findChordNames(tracks);

          lengthenTooShortNotes(tracks);

          if (opers.data()->processingsOfOpenedFile == 0) {       // for newly opened MIDI file
                opers.data()->trackCount = 0;
                for (const auto &track: tracks) {
                      if (track.first != -1)
                            ++opers.data()->trackCount;
                      }
                MidiLyrics::extractLyricsToMidiData(mf);
                }
                      // for newly opened MIDI file - detect if it is a human performance
                      // if so - detect beats and set initial time signature
          if (opers.data()->processingsOfOpenedFile == 0)
                Quantize::setIfHumanPerformance(tracks, sigmap);
          else        // user value
                MidiBeat::setTimeSignature(sigmap);

          Q_ASSERT_X((opers.data()->trackOpers.isHumanPerformance.value())
                            ? Meter::userTimeSigToFraction(opers.data()->trackOpers.timeSigNumerator.value(),
                                                           opers.data()->trackOpers.timeSigDenominator.value())
                              != ReducedFraction(0, 1) : true,
                     "convertMidi", "Null time signature for human-performed MIDI file");

          MChord::collectChords(tracks, {2, 1}, {1, 2});
          MidiBeat::adjustChordsToBeats(tracks);
          MChord::mergeChordsWithEqualOnTimeAndVoice(tracks);

                      // for newly opened MIDI file
          if (opers.data()->processingsOfOpenedFile == 0
                      && opers.data()->trackOpers.doStaffSplit.canRedefineDefaultLater()) {
                setLeftRightHandSplit(tracks);
                }

          MChord::removeOverlappingNotes(tracks);

          Q_ASSERT_X(!doNotesOverlap(tracks),
                     "convertMidi", "There are overlapping notes of the same voice that is incorrect");

          LRHand::splitIntoLeftRightHands(tracks);
          MidiDrum::splitDrumVoices(tracks);
          MidiDrum::splitDrumTracks(tracks);
          ReducedFraction lastTick = findLastChordTick(tracks);
          quantizeAllTracks(tracks, sigmap, lastTick);
          MChord::removeOverlappingNotes(tracks);

          Q_ASSERT_X(!doNotesOverlap(tracks),
                     "convertMidi", "There are overlapping notes of the same voice that is incorrect");
          Q_ASSERT_X(noTooShortNotes(tracks),
                     "convertMidi", "There are notes of length < min allowed duration");

          MChord::mergeChordsWithEqualOnTimeAndVoice(tracks);
          Simplify::simplifyDurationsNotDrums(tracks, sigmap);
          if (MidiVoice::separateVoices(tracks, sigmap))
                Simplify::simplifyDurationsNotDrums(tracks, sigmap);    // again
          Simplify::simplifyDurationsForDrums(tracks, sigmap);
          MChord::splitUnequalChords(tracks);
                      // no more track insertion/reordering/deletion from now
          QList<MTrack> trackList = prepareTrackList(tracks);
          MidiInstr::setGrandStaffProgram(trackList);
          MidiInstr::findInstrumentsForAllTracks(trackList);
          MidiInstr::createInstruments(score, trackList);
          MidiDrum::setStaffBracketForDrums(trackList);

          const auto firstTick = findFirstChordTick(trackList);

          createMeasures(firstTick, lastTick, score);
          processNonLyricMeta(trackList);
          setTrackInfo(trackList, mf->midiType());
          createKeys(trackList);
          MidiKey::recognizeMainKeySig(trackList);
          createNotes(lastTick, trackList);
          processLyricMeta(trackList);
          applySwing(trackList);
          createClefs(trackList);
          createTimeSignatures(score);
          score->connectTies();

          MidiLyrics::setLyricsToScore(trackList);
          MidiTempo::setTempo(tracks, score);
          MidiChordName::setChordNames(trackList);
          }

    void loadMidiData(MidiFile &mf)
          {
          mf.separateChannel();
          MidiType mt = MidiType::UNKNOWN;
          for (auto &track: mf.tracks())
                track.mergeNoteOnOffAndFindMidiType(&mt);
          mf.setMidiType(mt);
          }

    Score::FileError importMidi(MasterScore *score, const QString &name)
          {
          if (name.isEmpty())
                return Score::FileError::FILE_NOT_FOUND;

          auto &opers = preferences.midiImportOperations;

          MidiOperations::CurrentMidiFileSetter setCurrentMidiFile(opers, name);
          if (!opers.hasMidiFile(name))
                opers.addNewMidiFile(name);

          if (opers.data()->processingsOfOpenedFile == 0) {

                QFile fp(name);
                if (!fp.open(QIODevice::ReadOnly)) {
                      qDebug("importMidi: file open error <%s>", qPrintable(name));
                      return Score::FileError::FILE_OPEN_ERROR;
                      }
                MidiFile mf;
                try {
                      mf.read(&fp);
                      }
                catch (QString errorText) {
                      if (!MScore::noGui) {
                            QMessageBox::warning(0,
                               QWidget::tr("Load MIDI"),
                               QWidget::tr("Load failed: ") + errorText,
                               QString::null, QWidget::tr("Quit"), QString::null, 0, 1);
                            }
                      fp.close();
                      qDebug("importMidi: bad file format");
                      return Score::FileError::FILE_BAD_FORMAT;
                      }
                fp.close();

                loadMidiData(mf);
                opers.setMidiFileData(name, mf);
                }

          convertMidi(score, opers.midiFile(name));
          ++opers.data()->processingsOfOpenedFile;

          return Score::FileError::FILE_NO_ERROR;
          }
    }

↑ V560 A part of conditional expression is always true: m.